proc_darwin.go 10.4 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 29 30 31

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

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

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

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

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

	for {
D
Derek Parker 已提交
87
		err = dbp.updateThreadListForTask(C.get_task_for_pid(C.int(dbp.pid)))
88 89 90 91 92 93 94 95 96 97 98 99 100
		if err == nil {
			break
		}
		if err != couldNotGetThreadCount && err != couldNotGetThreadList {
			return nil, err
		}
	}

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

	dbp.allGCache = nil
D
Derek Parker 已提交
101
	for _, th := range dbp.threads {
102
		th.CurrentBreakpoint.Clear()
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	}

	trapthread, err := dbp.trapWait(-1)
	if err != nil {
		return nil, err
	}
	if err := dbp.Halt(); err != nil {
		return nil, dbp.exitGuard(err)
	}

	_, err = dbp.waitForStop()
	if err != nil {
		return nil, err
	}

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

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

128 129 130
	return dbp, err
}

131 132 133 134 135 136 137 138 139 140 141 142
// 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)
	}

143 144
	dbp.os.initialized = true

145 146 147 148 149 150 151 152 153 154
	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
	}

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

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

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

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

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

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

216
	for {
217
		count = C.thread_count(task)
218
		if count == -1 {
219
			return couldNotGetThreadCount
220 221 222 223 224
		}
		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
225
		kret = C.get_threads(task, unsafe.Pointer(&list[0]), count)
226 227 228
		if kret != -2 {
			break
		}
D
Derek Parker 已提交
229
	}
230
	if kret != C.KERN_SUCCESS {
231 232 233
		return couldNotGetThreadList
	}

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

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

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

	return nil
}

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

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

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

287 288
		switch port {
		case dbp.os.notificationPort:
289 290 291 292 293 294 295 296
			// 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 已提交
297
				if pidtask := C.get_task_for_pid(C.int(dbp.pid)); pidtask != 0 && dbp.os.task != pidtask {
298 299 300
					continue
				}
			}
D
Derek Parker 已提交
301
			_, status, err := dbp.wait(dbp.pid, 0)
302 303 304
			if err != nil {
				return nil, err
			}
305
			dbp.postExit()
306
			return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
307

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

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

324 325 326 327 328 329 330 331 332 333 334
		// 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)
			}
		}

335 336 337
		// 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 已提交
338
		th, ok := dbp.threads[int(port)]
A
aarzilli 已提交
339
		if !ok {
340 341 342 343
			dbp.haltMu.Lock()
			halt := dbp.halt
			dbp.haltMu.Unlock()
			if halt {
344
				dbp.halt = false
A
aarzilli 已提交
345
				return th, nil
346
			}
A
aarzilli 已提交
347
			if dbp.firstStart || th.singleStepping {
348
				dbp.firstStart = false
A
aarzilli 已提交
349
				return th, nil
350
			}
A
aarzilli 已提交
351
			if err := th.Continue(); err != nil {
352 353 354
				return nil, err
			}
			continue
355
		}
356
		return th, nil
D
Derek Parker 已提交
357 358 359
	}
}

360
func (dbp *Process) waitForStop() ([]int, error) {
D
Derek Parker 已提交
361
	ports := make([]int, 0, len(dbp.threads))
362
	count := 0
363
	for {
364 365 366
		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 {
367 368 369
			count = 0
			ports = append(ports, int(port))
		} else {
370
			n := C.num_running_threads(dbp.os.task)
371 372 373 374 375
			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 已提交
376
				return nil, fmt.Errorf("could not stop process %d", n)
377
			}
378
		}
379 380 381 382 383 384 385 386 387 388
	}
}

func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
	ports, err := dbp.waitForStop()
	if err != nil {
		return err
	}
	trapthread.SetCurrentBreakpoint()
	for _, port := range ports {
D
Derek Parker 已提交
389
		if th, ok := dbp.threads[port]; ok {
390 391 392 393 394 395
			err := th.SetCurrentBreakpoint()
			if err != nil {
				return err
			}
		}
	}
396
	return nil
397 398
}

399
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
400
	wg.Done()
401 402 403
}

func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
D
Derek Parker 已提交
404 405 406 407
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}
408

L
Luke Hoban 已提交
409 410 411 412
func killProcess(pid int) error {
	return sys.Kill(pid, sys.SIGINT)
}

413
func (dbp *Process) exitGuard(err error) error {
414 415 416
	if err != ErrContinueThread {
		return err
	}
D
Derek Parker 已提交
417
	_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
418 419
	if werr == nil && status.Exited() {
		dbp.postExit()
420
		return proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
421
	}
422 423
	return err
}
424 425 426

func (dbp *Process) resume() error {
	// all threads stopped over a breakpoint are made to step over it
D
Derek Parker 已提交
427
	for _, thread := range dbp.threads {
428
		if thread.CurrentBreakpoint.Breakpoint != nil {
429
			if err := thread.StepInstruction(); err != nil {
430 431
				return err
			}
432
			thread.CurrentBreakpoint.Clear()
433 434 435
		}
	}
	// everything is resumed
D
Derek Parker 已提交
436
	for _, thread := range dbp.threads {
437 438 439 440 441 442
		if err := thread.resume(); err != nil {
			return dbp.exitGuard(err)
		}
	}
	return nil
}
443

444
func (dbp *Process) detach(kill bool) error {
445 446
	return PtraceDetach(dbp.pid, 0)
}