proc_linux.go 10.8 KB
Newer Older
1
package native
D
Derek Parker 已提交
2 3

import (
4
	"bytes"
5
	"errors"
D
Derek Parker 已提交
6
	"fmt"
7
	"io/ioutil"
D
Derek Parker 已提交
8
	"os"
9
	"os/exec"
D
Derek Parker 已提交
10
	"path/filepath"
11
	"regexp"
D
Derek Parker 已提交
12
	"strconv"
13
	"strings"
D
Derek Parker 已提交
14 15
	"sync"
	"syscall"
16
	"time"
D
Derek Parker 已提交
17 18

	sys "golang.org/x/sys/unix"
19 20

	"github.com/derekparker/delve/pkg/proc"
D
Derek Parker 已提交
21 22
)

D
Derek Parker 已提交
23
// Process statuses
D
Derek Parker 已提交
24
const (
D
Derek Parker 已提交
25 26 27 28
	StatusSleeping  = 'S'
	StatusRunning   = 'R'
	StatusTraceStop = 't'
	StatusZombie    = 'Z'
D
Derek Parker 已提交
29

J
Joe Grasse 已提交
30
	// Kernel 2.6 has TraceStop as T
31 32 33
	// TODO(derekparker) Since this means something different based on the
	// version of the kernel ('T' is job control stop on modern 3.x+ kernels) we
	// may want to differentiate at some point.
J
Joe Grasse 已提交
34
	StatusTraceStopT = 'T'
D
Derek Parker 已提交
35 36
)

D
Derek Parker 已提交
37 38
// OSProcessDetails contains Linux specific
// process details.
39 40 41
type OSProcessDetails struct {
	comm string
}
D
Derek Parker 已提交
42

D
Derek Parker 已提交
43
// Launch creates and begins debugging a new process. First entry in
44
// `cmd` is the program to run, and then rest are the arguments
E
Evgeny L 已提交
45 46
// to be supplied to that process. `wd` is working directory of the program.
func Launch(cmd []string, wd string) (*Process, error) {
47
	var (
48 49
		process *exec.Cmd
		err     error
50
	)
51 52
	// check that the argument to Launch is an executable file
	if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
53
		return nil, proc.NotExecutableErr
54
	}
55 56
	dbp := New(0)
	dbp.execPtraceFunc(func() {
57 58 59 60 61
		process = exec.Command(cmd[0])
		process.Args = cmd
		process.Stdout = os.Stdout
		process.Stderr = os.Stderr
		process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
E
Evgeny L 已提交
62
		if wd != "" {
63
			process.Dir = wd
E
Evgeny L 已提交
64
		}
65
		err = process.Start()
66 67
	})
	if err != nil {
68 69
		return nil, err
	}
70
	dbp.pid = process.Process.Pid
71
	dbp.childProcess = true
72
	_, _, err = dbp.wait(process.Process.Pid, 0)
73 74 75
	if err != nil {
		return nil, fmt.Errorf("waiting for target execve failed: %s", err)
	}
76
	return initializeDebugProcess(dbp, process.Path, false)
77 78
}

79 80 81 82 83
// Attach to an existing process with the given PID.
func Attach(pid int) (*Process, error) {
	return initializeDebugProcess(New(pid), "", true)
}

D
Derek Parker 已提交
84
// Kill kills the target process.
85
func (dbp *Process) Kill() (err error) {
86 87 88
	if dbp.exited {
		return nil
	}
D
Derek Parker 已提交
89
	if !dbp.threads[dbp.pid].Stopped() {
90 91
		return errors.New("process must be stopped in order to kill it")
	}
D
Derek Parker 已提交
92
	if err = sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
93 94
		return errors.New("could not deliver signal " + err.Error())
	}
D
Derek Parker 已提交
95
	if _, _, err = dbp.wait(dbp.pid, 0); err != nil {
96 97
		return
	}
98
	dbp.postExit()
99 100 101
	return
}

D
Derek Parker 已提交
102
func (dbp *Process) requestManualStop() (err error) {
D
Derek Parker 已提交
103
	return sys.Kill(dbp.pid, sys.SIGTRAP)
D
Derek Parker 已提交
104 105 106 107
}

// Attach to a newly created thread, and store that thread in our list of
// known threads.
D
Derek Parker 已提交
108
func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
D
Derek Parker 已提交
109
	if thread, ok := dbp.threads[tid]; ok {
D
Derek Parker 已提交
110 111 112
		return thread, nil
	}

113
	var err error
D
Derek Parker 已提交
114
	if attach {
115
		dbp.execPtraceFunc(func() { err = sys.PtraceAttach(tid) })
D
Derek Parker 已提交
116 117 118 119 120 121 122
		if err != nil && err != sys.EPERM {
			// Do not return err if err == EPERM,
			// we may already be tracing this thread due to
			// PTRACE_O_TRACECLONE. We will surely blow up later
			// if we truly don't have permissions.
			return nil, fmt.Errorf("could not attach to new thread %d %s", tid, err)
		}
123
		pid, status, err := dbp.waitFast(tid)
D
Derek Parker 已提交
124 125 126 127 128 129 130 131
		if err != nil {
			return nil, err
		}
		if status.Exited() {
			return nil, fmt.Errorf("thread already exited %d", pid)
		}
	}

132
	dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
D
Derek Parker 已提交
133
	if err == syscall.ESRCH {
134
		if _, _, err = dbp.waitFast(tid); err != nil {
D
Derek Parker 已提交
135 136
			return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
		}
137
		dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
138 139 140
		if err == syscall.ESRCH {
			return nil, err
		}
D
Derek Parker 已提交
141 142 143 144 145
		if err != nil {
			return nil, fmt.Errorf("could not set options for new traced thread %d %s", tid, err)
		}
	}

D
Derek Parker 已提交
146
	dbp.threads[tid] = &Thread{
D
Derek Parker 已提交
147
		ID:  tid,
148 149
		dbp: dbp,
		os:  new(OSSpecificDetails),
D
Derek Parker 已提交
150
	}
D
Derek Parker 已提交
151
	if dbp.currentThread == nil {
152
		dbp.SwitchThread(tid)
D
Derek Parker 已提交
153
	}
D
Derek Parker 已提交
154
	return dbp.threads[tid], nil
D
Derek Parker 已提交
155 156
}

D
Derek Parker 已提交
157
func (dbp *Process) updateThreadList() error {
D
Derek Parker 已提交
158
	tids, _ := filepath.Glob(fmt.Sprintf("/proc/%d/task/*", dbp.pid))
D
Derek Parker 已提交
159 160 161 162 163
	for _, tidpath := range tids {
		tidstr := filepath.Base(tidpath)
		tid, err := strconv.Atoi(tidstr)
		if err != nil {
			return err
D
Derek Parker 已提交
164
		}
D
Derek Parker 已提交
165
		if _, err := dbp.addThread(tid, tid != dbp.pid); err != nil {
D
Derek Parker 已提交
166 167 168
			return err
		}
	}
D
Derek Parker 已提交
169
	return nil
D
Derek Parker 已提交
170 171
}

172
func findExecutable(path string, pid int) string {
173
	if path == "" {
174
		path = fmt.Sprintf("/proc/%d/exe", pid)
D
Derek Parker 已提交
175
	}
176
	return path
D
Derek Parker 已提交
177 178
}

D
Derek Parker 已提交
179
func (dbp *Process) trapWait(pid int) (*Thread, error) {
D
Derek Parker 已提交
180
	for {
181
		wpid, status, err := dbp.wait(pid, 0)
D
Derek Parker 已提交
182
		if err != nil {
183
			return nil, fmt.Errorf("wait err %s %d", err, pid)
D
Derek Parker 已提交
184 185 186 187
		}
		if wpid == 0 {
			continue
		}
D
Derek Parker 已提交
188
		th, ok := dbp.threads[wpid]
189
		if ok {
L
Luke Hoban 已提交
190
			th.Status = (*WaitStatus)(status)
D
Derek Parker 已提交
191
		}
192
		if status.Exited() {
D
Derek Parker 已提交
193
			if wpid == dbp.pid {
194
				dbp.postExit()
195
				return nil, proc.ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
196
			}
D
Derek Parker 已提交
197
			delete(dbp.threads, wpid)
198
			continue
D
Derek Parker 已提交
199 200 201 202
		}
		if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
			// A traced thread has cloned a new thread, grab the pid and
			// add it to our list of traced threads.
203 204
			var cloned uint
			dbp.execPtraceFunc(func() { cloned, err = sys.PtraceGetEventMsg(wpid) })
D
Derek Parker 已提交
205
			if err != nil {
206 207 208 209
				if err == sys.ESRCH {
					// thread died while we were adding it
					continue
				}
210
				return nil, fmt.Errorf("could not get event message: %s", err)
D
Derek Parker 已提交
211
			}
212
			th, err = dbp.addThread(int(cloned), false)
D
Derek Parker 已提交
213
			if err != nil {
214 215 216 217
				if err == sys.ESRCH {
					// thread died while we were adding it
					continue
				}
218
				return nil, err
D
Derek Parker 已提交
219
			}
220
			if err = th.Continue(); err != nil {
221 222
				if err == sys.ESRCH {
					// thread died while we were adding it
D
Derek Parker 已提交
223
					delete(dbp.threads, th.ID)
224 225
					continue
				}
226
				return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
D
Derek Parker 已提交
227
			}
D
Derek Parker 已提交
228
			if err = dbp.threads[int(wpid)].Continue(); err != nil {
229 230 231
				if err != sys.ESRCH {
					return nil, fmt.Errorf("could not continue existing thread %d %s", wpid, err)
				}
D
Derek Parker 已提交
232
			}
D
Derek Parker 已提交
233 234
			continue
		}
235 236 237 238
		if th == nil {
			// Sometimes we get an unknown thread, ignore it?
			continue
		}
239 240 241 242
		dbp.haltMu.Lock()
		halt := dbp.halt
		dbp.haltMu.Unlock()
		if status.StopSignal() == sys.SIGTRAP && halt {
243
			th.running = false
D
Derek Parker 已提交
244
			dbp.halt = false
245 246
			return th, nil
		}
D
Derek Parker 已提交
247
		if status.StopSignal() == sys.SIGTRAP {
248
			th.running = false
A
aarzilli 已提交
249
			return th, nil
D
Derek Parker 已提交
250
		}
251
		if th != nil {
252
			// TODO(dp) alert user about unexpected signals here.
253 254
			if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
				if err == sys.ESRCH {
255
					return nil, proc.ProcessExitedError{Pid: dbp.pid}
256
				}
257 258 259
				return nil, err
			}
		}
D
Derek Parker 已提交
260 261 262
	}
}

263 264 265
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
	defer wg.Done()

D
Derek Parker 已提交
266
	comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
267 268 269 270 271 272
	if err == nil {
		// removes newline character
		comm = bytes.TrimSuffix(comm, []byte("\n"))
	}

	if comm == nil || len(comm) <= 0 {
D
Derek Parker 已提交
273
		stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
274 275 276 277
		if err != nil {
			fmt.Printf("Could not read proc stat: %v\n", err)
			os.Exit(1)
		}
D
Derek Parker 已提交
278
		expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
279 280 281 282 283 284 285
		rexp, err := regexp.Compile(expr)
		if err != nil {
			fmt.Printf("Regexp compile error: %v\n", err)
			os.Exit(1)
		}
		match := rexp.FindSubmatch(stat)
		if match == nil {
D
Derek Parker 已提交
286
			fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.pid)
287 288 289
			os.Exit(1)
		}
		comm = match[1]
290
	}
291
	dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
292 293 294
}

func status(pid int, comm string) rune {
295 296
	f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
	if err != nil {
297
		return '\000'
298 299 300 301 302 303 304
	}
	defer f.Close()

	var (
		p     int
		state rune
	)
305

306 307 308 309
	// The second field of /proc/pid/stat is the name of the task in parenthesis.
	// The name of the task is the base name of the executable for this process limited to TASK_COMM_LEN characters
	// Since both parenthesis and spaces can appear inside the name of the task and no escaping happens we need to read the name of the executable first
	// See: include/linux/sched.c:315 and include/linux/sched.c:1510
310
	fmt.Fscanf(f, "%d ("+comm+")  %c", &p, &state)
311 312 313
	return state
}

A
Alessandro Arzilli 已提交
314 315 316 317 318 319 320
// waitFast is like wait but does not handle process-exit correctly
func (dbp *Process) waitFast(pid int) (int, *sys.WaitStatus, error) {
	var s sys.WaitStatus
	wpid, err := sys.Wait4(pid, &s, sys.WALL, nil)
	return wpid, &s, err
}

321
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
322
	var s sys.WaitStatus
D
Derek Parker 已提交
323
	if (pid != dbp.pid) || (options != 0) {
324 325
		wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
		return wpid, &s, err
D
Derek Parker 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
	}
	// If we call wait4/waitpid on a thread that is the leader of its group,
	// with options == 0, while ptracing and the thread leader has exited leaving
	// zombies of its own then waitpid hangs forever this is apparently intended
	// behaviour in the linux kernel because it's just so convenient.
	// Therefore we call wait4 in a loop with WNOHANG, sleeping a while between
	// calls and exiting when either wait4 succeeds or we find out that the thread
	// has become a zombie.
	// References:
	// https://sourceware.org/bugzilla/show_bug.cgi?id=12702
	// https://sourceware.org/bugzilla/show_bug.cgi?id=10095
	// https://sourceware.org/bugzilla/attachment.cgi?id=5685
	for {
		wpid, err := sys.Wait4(pid, &s, sys.WNOHANG|sys.WALL|options, nil)
		if err != nil {
			return 0, nil, err
		}
		if wpid != 0 {
			return wpid, &s, err
		}
		if status(pid, dbp.os.comm) == StatusZombie {
			return pid, nil, nil
348
		}
D
Derek Parker 已提交
349
		time.Sleep(200 * time.Millisecond)
350
	}
D
Derek Parker 已提交
351
}
352

A
aarzilli 已提交
353
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
D
Derek Parker 已提交
354
	for _, th := range dbp.threads {
355 356 357 358 359 360 361 362 363 364
		if th.CurrentBreakpoint == nil {
			err := th.SetCurrentBreakpoint()
			if err != nil {
				return err
			}
		}
	}
	return nil
}

365 366 367 368
func (dbp *Process) exitGuard(err error) error {
	if err != sys.ESRCH {
		return err
	}
D
Derek Parker 已提交
369
	if status(dbp.pid, dbp.os.comm) == StatusZombie {
370 371 372 373 374 375
		_, err := dbp.trapWait(-1)
		return err
	}

	return err
}
376 377 378

func (dbp *Process) resume() error {
	// all threads stopped over a breakpoint are made to step over it
D
Derek Parker 已提交
379
	for _, thread := range dbp.threads {
380
		if thread.CurrentBreakpoint != nil {
381
			if err := thread.StepInstruction(); err != nil {
382 383 384 385 386 387
				return err
			}
			thread.CurrentBreakpoint = nil
		}
	}
	// everything is resumed
D
Derek Parker 已提交
388
	for _, thread := range dbp.threads {
389 390 391 392 393 394
		if err := thread.resume(); err != nil && err != sys.ESRCH {
			return err
		}
	}
	return nil
}
L
Luke Hoban 已提交
395

396
func (dbp *Process) detach(kill bool) error {
397 398 399 400 401 402
	for threadID := range dbp.threads {
		err := PtraceDetach(threadID, 0)
		if err != nil {
			return err
		}
	}
403 404 405
	if kill {
		return nil
	}
406 407 408 409 410 411 412 413 414 415 416
	// For some reason the process will sometimes enter stopped state after a
	// detach, this doesn't happen immediately either.
	// We have to wait a bit here, then check if the main thread is stopped and
	// SIGCONT it if it is.
	time.Sleep(50 * time.Millisecond)
	if s := status(dbp.pid, dbp.os.comm); s == 'T' {
		sys.Kill(dbp.pid, sys.SIGCONT)
	}
	return nil
}

L
Luke Hoban 已提交
417 418 419
func killProcess(pid int) error {
	return sys.Kill(pid, sys.SIGINT)
}