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

D
Derek Parker 已提交
3
// #include "proc_darwin.h"
4
// #include "threads_darwin.h"
5
// #include "exec_darwin.h"
6
// #include <stdlib.h>
D
Derek Parker 已提交
7 8
import "C"
import (
9
	"errors"
D
Derek Parker 已提交
10 11
	"fmt"
	"os"
12
	"os/exec"
13
	"path/filepath"
D
Derek Parker 已提交
14 15 16 17
	"sync"
	"unsafe"

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

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

D
Derek Parker 已提交
22
// OSProcessDetails holds Darwin specific information.
D
Derek Parker 已提交
23
type OSProcessDetails struct {
24 25 26
	task             C.task_t      // mach task for the debugged process.
	exceptionPort    C.mach_port_t // mach port for receiving mach exceptions.
	notificationPort C.mach_port_t // mach port for dead name notification (process exit).
27
	initialized      bool
28
	halt             bool
29 30 31 32

	// the main port we use, will return messages from both the
	// exception and notification ports.
	portSet C.mach_port_t
D
Derek Parker 已提交
33 34
}

D
Derek Parker 已提交
35
// Launch creates and begins debugging a new process. Uses a
36
// custom fork/exec process in order to take advantage of
D
Derek Parker 已提交
37 38
// PT_SIGEXC on Darwin which will turn Unix signals into
// Mach exceptions.
E
Evgeny L 已提交
39
func Launch(cmd []string, wd string) (*Process, error) {
40 41
	// check that the argument to Launch is an executable file
	if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
42
		return nil, proc.NotExecutableErr
43
	}
44
	argv0Go, err := filepath.Abs(cmd[0])
45 46 47
	if err != nil {
		return nil, err
	}
48
	// Make sure the binary exists.
49 50 51 52 53 54 55 56 57
	if filepath.Base(cmd[0]) == cmd[0] {
		if _, err := exec.LookPath(cmd[0]); err != nil {
			return nil, err
		}
	}
	if _, err := os.Stat(argv0Go); err != nil {
		return nil, err
	}

58
	argv0 := C.CString(argv0Go)
59
	argvSlice := make([]*C.char, 0, len(cmd)+1)
60 61 62
	for _, arg := range cmd {
		argvSlice = append(argvSlice, C.CString(arg))
	}
63 64
	// argv array must be null terminated.
	argvSlice = append(argvSlice, nil)
65

66 67 68
	dbp := New(0)
	var pid int
	dbp.execPtraceFunc(func() {
69
		ret := C.fork_exec(argv0, &argvSlice[0], C.int(len(argvSlice)),
E
Evgeny L 已提交
70
			C.CString(wd),
71 72
			&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
			&dbp.os.notificationPort)
73 74
		pid = int(ret)
	})
D
Derek Parker 已提交
75
	if pid <= 0 {
D
Derek Parker 已提交
76
		return nil, fmt.Errorf("could not fork/exec")
77
	}
D
Derek Parker 已提交
78
	dbp.pid = pid
79
	dbp.childProcess = true
80 81 82
	for i := range argvSlice {
		C.free(unsafe.Pointer(argvSlice[i]))
	}
D
Derek Parker 已提交
83

84 85 86 87
	// Initialize enough of the Process state so that we can use resume and
	// trapWait to wait until the child process calls execve.

	for {
88 89 90 91 92 93 94 95 96 97 98
		task := C.get_task_for_pid(C.int(dbp.pid))
		// The task_for_pid call races with the fork call. This can
		// result in the parent task being returned instead of the child.
		if task != dbp.os.task {
			err = dbp.updateThreadListForTask(task)
			if err == nil {
				break
			}
			if err != couldNotGetThreadCount && err != couldNotGetThreadList {
				return nil, err
			}
99 100 101 102 103 104 105 106
		}
	}

	if err := dbp.resume(); err != nil {
		return nil, err
	}

	dbp.allGCache = nil
D
Derek Parker 已提交
107
	for _, th := range dbp.threads {
108
		th.CurrentBreakpoint.Clear()
109 110 111 112 113 114
	}

	trapthread, err := dbp.trapWait(-1)
	if err != nil {
		return nil, err
	}
115
	if err := dbp.stop(nil); err != nil {
116 117 118 119
		return nil, err
	}

	dbp.os.initialized = true
120
	dbp, err = initializeDebugProcess(dbp, argv0Go)
121 122 123
	if err != nil {
		return nil, err
	}
124 125 126 127 128

	if err := dbp.SwitchThread(trapthread.ID); err != nil {
		return nil, err
	}

129 130 131
	return dbp, err
}

132 133 134 135 136 137 138 139 140 141 142 143
// Attach to an existing process with the given PID.
func Attach(pid int) (*Process, error) {
	dbp := New(pid)

	kret := C.acquire_mach_task(C.int(pid),
		&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
		&dbp.os.notificationPort)

	if kret != C.KERN_SUCCESS {
		return nil, fmt.Errorf("could not attach to %d", pid)
	}

144 145
	dbp.os.initialized = true

146 147 148 149 150 151 152 153 154 155
	var err error
	dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
	if err != nil {
		return nil, err
	}
	_, _, err = dbp.wait(dbp.pid, 0)
	if err != nil {
		return nil, err
	}

156 157 158 159 160 161
	dbp, err = initializeDebugProcess(dbp, "")
	if err != nil {
		dbp.Detach(false)
		return nil, err
	}
	return dbp, nil
162 163
}

D
Derek Parker 已提交
164
// Kill kills the process.
A
aarzilli 已提交
165
func (dbp *Process) kill() (err error) {
166 167 168
	if dbp.exited {
		return nil
	}
D
Derek Parker 已提交
169
	err = sys.Kill(-dbp.pid, sys.SIGKILL)
170 171 172
	if err != nil {
		return errors.New("could not deliver signal: " + err.Error())
	}
D
Derek Parker 已提交
173
	for port := range dbp.threads {
174 175 176 177 178
		if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
			return errors.New("could not resume task")
		}
	}
	for {
179 180
		var task C.task_t
		port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
181 182 183 184
		if port == dbp.os.notificationPort {
			break
		}
	}
185
	dbp.postExit()
186 187 188
	return
}

D
Derek Parker 已提交
189
func (dbp *Process) requestManualStop() (err error) {
D
Derek Parker 已提交
190 191
	var (
		task          = C.mach_port_t(dbp.os.task)
192
		thread        = C.mach_port_t(dbp.currentThread.os.threadAct)
D
Derek Parker 已提交
193 194
		exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
	)
195
	dbp.os.halt = true
D
Derek Parker 已提交
196 197
	kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
	if kret != C.KERN_SUCCESS {
D
Derek Parker 已提交
198
		return fmt.Errorf("could not raise mach exception")
D
Derek Parker 已提交
199 200 201 202
	}
	return nil
}

203 204 205
var couldNotGetThreadCount = errors.New("could not get thread count")
var couldNotGetThreadList = errors.New("could not get thread list")

D
Derek Parker 已提交
206
func (dbp *Process) updateThreadList() error {
207 208 209 210
	return dbp.updateThreadListForTask(dbp.os.task)
}

func (dbp *Process) updateThreadListForTask(task C.task_t) error {
D
Derek Parker 已提交
211 212 213
	var (
		err   error
		kret  C.kern_return_t
214 215
		count C.int
		list  []uint32
D
Derek Parker 已提交
216 217
	)

218
	for {
219
		count = C.thread_count(task)
220
		if count == -1 {
221
			return couldNotGetThreadCount
222 223 224 225 226
		}
		list = make([]uint32, count)

		// TODO(dp) might be better to malloc mem in C and then free it here
		// instead of getting count above and passing in a slice
227
		kret = C.get_threads(task, unsafe.Pointer(&list[0]), count)
228 229 230
		if kret != -2 {
			break
		}
D
Derek Parker 已提交
231
	}
232
	if kret != C.KERN_SUCCESS {
233 234 235
		return couldNotGetThreadList
	}

D
Derek Parker 已提交
236
	for _, thread := range dbp.threads {
237
		thread.os.exists = false
D
Derek Parker 已提交
238 239 240
	}

	for _, port := range list {
D
Derek Parker 已提交
241
		thread, ok := dbp.threads[int(port)]
242 243
		if !ok {
			thread, err = dbp.addThread(int(port), false)
D
Derek Parker 已提交
244 245 246
			if err != nil {
				return err
			}
D
Derek Parker 已提交
247
		}
248 249 250
		thread.os.exists = true
	}

D
Derek Parker 已提交
251
	for threadID, thread := range dbp.threads {
252
		if !thread.os.exists {
D
Derek Parker 已提交
253
			delete(dbp.threads, threadID)
254
		}
D
Derek Parker 已提交
255 256 257 258 259
	}

	return nil
}

D
Derek Parker 已提交
260
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
D
Derek Parker 已提交
261
	if thread, ok := dbp.threads[port]; ok {
D
Derek Parker 已提交
262 263
		return thread, nil
	}
D
Derek Parker 已提交
264
	thread := &Thread{
D
Derek Parker 已提交
265
		ID:  port,
266 267
		dbp: dbp,
		os:  new(OSSpecificDetails),
D
Derek Parker 已提交
268
	}
D
Derek Parker 已提交
269
	dbp.threads[port] = thread
D
Derek Parker 已提交
270
	thread.os.threadAct = C.thread_act_t(port)
271
	if dbp.currentThread == nil {
D
Derek Parker 已提交
272
		dbp.SwitchThread(thread.ID)
D
Derek Parker 已提交
273
	}
D
Derek Parker 已提交
274 275 276
	return thread, nil
}

277
func findExecutable(path string, pid int) string {
278
	if path == "" {
279
		path = C.GoString(C.find_executable(C.int(pid)))
D
Derek Parker 已提交
280
	}
281
	return path
D
Derek Parker 已提交
282 283
}

D
Derek Parker 已提交
284
func (dbp *Process) trapWait(pid int) (*Thread, error) {
285
	for {
286 287
		task := dbp.os.task
		port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
288

289 290
		switch port {
		case dbp.os.notificationPort:
291 292 293 294 295 296 297 298
			// on macOS >= 10.12.1 the task_t changes after an execve, we could
			// receive the notification for the death of the pre-execve task_t,
			// this could also happen *before* we are notified that our task_t has
			// changed.
			if dbp.os.task != task {
				continue
			}
			if !dbp.os.initialized {
D
Derek Parker 已提交
299
				if pidtask := C.get_task_for_pid(C.int(dbp.pid)); pidtask != 0 && dbp.os.task != pidtask {
300 301 302
					continue
				}
			}
D
Derek Parker 已提交
303
			_, status, err := dbp.wait(dbp.pid, 0)
304 305 306
			if err != nil {
				return nil, err
			}
307
			dbp.postExit()
308
			return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
309

310
		case C.MACH_RCV_INTERRUPTED:
311 312 313
			dbp.stopMu.Lock()
			halt := dbp.os.halt
			dbp.stopMu.Unlock()
314
			if !halt {
315 316 317
				// Call trapWait again, it seems
				// MACH_RCV_INTERRUPTED is emitted before
				// process natural death _sometimes_.
318
				continue
319
			}
D
Derek Parker 已提交
320
			return nil, nil
321

322
		case 0:
D
Derek Parker 已提交
323
			return nil, fmt.Errorf("error while waiting for task")
324 325
		}

326 327 328 329 330 331 332 333 334 335 336
		// In macOS 10.12.1 if we received a notification for a task other than
		// the inferior's task and the inferior's task is no longer valid, this
		// means inferior called execve and its task_t changed.
		if dbp.os.task != task && C.task_is_valid(dbp.os.task) == 0 {
			dbp.os.task = task
			kret := C.reset_exception_ports(dbp.os.task, &dbp.os.exceptionPort, &dbp.os.notificationPort)
			if kret != C.KERN_SUCCESS {
				return nil, fmt.Errorf("could not follow task across exec: %d\n", kret)
			}
		}

337 338 339
		// Since we cannot be notified of new threads on OS X
		// this is as good a time as any to check for them.
		dbp.updateThreadList()
D
Derek Parker 已提交
340
		th, ok := dbp.threads[int(port)]
A
aarzilli 已提交
341
		if !ok {
342 343 344
			dbp.stopMu.Lock()
			halt := dbp.os.halt
			dbp.stopMu.Unlock()
345
			if halt {
346
				dbp.os.halt = false
A
aarzilli 已提交
347
				return th, nil
348
			}
A
aarzilli 已提交
349
			if dbp.firstStart || th.singleStepping {
350
				dbp.firstStart = false
A
aarzilli 已提交
351
				return th, nil
352
			}
A
aarzilli 已提交
353
			if err := th.Continue(); err != nil {
354 355 356
				return nil, err
			}
			continue
357
		}
358
		return th, nil
D
Derek Parker 已提交
359 360 361
	}
}

362
func (dbp *Process) waitForStop() ([]int, error) {
D
Derek Parker 已提交
363
	ports := make([]int, 0, len(dbp.threads))
364
	count := 0
365
	for {
366 367 368
		var task C.task_t
		port := C.mach_port_wait(dbp.os.portSet, &task, C.int(1))
		if port != 0 && port != dbp.os.notificationPort && port != C.MACH_RCV_INTERRUPTED {
369 370 371
			count = 0
			ports = append(ports, int(port))
		} else {
372
			n := C.num_running_threads(dbp.os.task)
373 374 375 376 377
			if n == 0 {
				return ports, nil
			} else if n < 0 {
				return nil, fmt.Errorf("error waiting for thread stop %d", n)
			} else if count > 16 {
D
Derek Parker 已提交
378
				return nil, fmt.Errorf("could not stop process %d", n)
379
			}
380
		}
381 382 383
	}
}

384
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
385
	wg.Done()
386 387 388
}

func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
D
Derek Parker 已提交
389 390 391 392
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}
393

L
Luke Hoban 已提交
394 395 396 397
func killProcess(pid int) error {
	return sys.Kill(pid, sys.SIGINT)
}

398
func (dbp *Process) exitGuard(err error) error {
399 400 401
	if err != ErrContinueThread {
		return err
	}
D
Derek Parker 已提交
402
	_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
403 404
	if werr == nil && status.Exited() {
		dbp.postExit()
405
		return proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
406
	}
407 408
	return err
}
409 410 411

func (dbp *Process) resume() error {
	// all threads stopped over a breakpoint are made to step over it
D
Derek Parker 已提交
412
	for _, thread := range dbp.threads {
413
		if thread.CurrentBreakpoint.Breakpoint != nil {
414
			if err := thread.StepInstruction(); err != nil {
415 416
				return err
			}
417
			thread.CurrentBreakpoint.Clear()
418 419 420
		}
	}
	// everything is resumed
D
Derek Parker 已提交
421
	for _, thread := range dbp.threads {
422 423 424 425 426 427
		if err := thread.resume(); err != nil {
			return dbp.exitGuard(err)
		}
	}
	return nil
}
428

429
// stop stops all running threads and sets breakpoints
430 431 432 433 434 435
func (dbp *Process) stop(trapthread *Thread) (err error) {
	if dbp.exited {
		return &proc.ProcessExitedError{Pid: dbp.Pid()}
	}
	for _, th := range dbp.threads {
		if !th.Stopped() {
436
			if err := th.stop(); err != nil {
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
				return dbp.exitGuard(err)
			}
		}
	}

	ports, err := dbp.waitForStop()
	if err != nil {
		return err
	}
	if !dbp.os.initialized {
		return nil
	}
	trapthread.SetCurrentBreakpoint()
	for _, port := range ports {
		if th, ok := dbp.threads[port]; ok {
			err := th.SetCurrentBreakpoint()
			if err != nil {
				return err
			}
		}
	}
	return nil
}

461
func (dbp *Process) detach(kill bool) error {
462 463
	return PtraceDetach(dbp.pid, 0)
}