proc_darwin.go 9.5 KB
Newer Older
D
Derek Parker 已提交
1
package proc
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 9
import "C"
import (
	"debug/gosym"
10
	"errors"
D
Derek Parker 已提交
11 12
	"fmt"
	"os"
13
	"os/exec"
14
	"path/filepath"
D
Derek Parker 已提交
15 16 17
	"sync"
	"unsafe"

A
Alessandro Arzilli 已提交
18
	"github.com/derekparker/delve/dwarf/debug/macho"
19

D
Derek Parker 已提交
20
	"github.com/derekparker/delve/dwarf/frame"
D
Derek Parker 已提交
21
	"github.com/derekparker/delve/dwarf/line"
D
Derek Parker 已提交
22 23 24
	sys "golang.org/x/sys/unix"
)

D
Derek Parker 已提交
25
// OSProcessDetails holds Darwin specific information.
D
Derek Parker 已提交
26
type OSProcessDetails struct {
27 28 29
	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).
30 31 32 33

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

D
Derek Parker 已提交
36
// Launch creates and begins debugging a new process. Uses a
37
// custom fork/exec process in order to take advantage of
D
Derek Parker 已提交
38 39
// PT_SIGEXC on Darwin which will turn Unix signals into
// Mach exceptions.
D
Derek Parker 已提交
40
func Launch(cmd []string) (*Process, error) {
41
	argv0Go, err := filepath.Abs(cmd[0])
42 43 44
	if err != nil {
		return nil, err
	}
45
	// Make sure the binary exists.
46 47 48 49 50 51 52 53 54
	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
	}

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

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

79
	dbp, err = initializeDebugProcess(dbp, argv0Go, false)
80 81 82
	if err != nil {
		return nil, err
	}
A
aarzilli 已提交
83
	err = dbp.Continue()
84 85 86
	return dbp, err
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
// 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)
	}

	return initializeDebugProcess(dbp, "", true)
}

D
Derek Parker 已提交
102
// Kill kills the process.
103
func (dbp *Process) Kill() (err error) {
104 105 106
	if dbp.exited {
		return nil
	}
107
	err = sys.Kill(-dbp.Pid, sys.SIGKILL)
108 109 110 111 112 113 114 115 116
	if err != nil {
		return errors.New("could not deliver signal: " + err.Error())
	}
	for port := range dbp.Threads {
		if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
			return errors.New("could not resume task")
		}
	}
	for {
117
		port := C.mach_port_wait(dbp.os.portSet, C.int(0))
118 119 120 121
		if port == dbp.os.notificationPort {
			break
		}
	}
122
	dbp.postExit()
123 124 125
	return
}

D
Derek Parker 已提交
126
func (dbp *Process) requestManualStop() (err error) {
D
Derek Parker 已提交
127 128
	var (
		task          = C.mach_port_t(dbp.os.task)
D
Derek Parker 已提交
129
		thread        = C.mach_port_t(dbp.CurrentThread.os.threadAct)
D
Derek Parker 已提交
130 131 132 133
		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 已提交
134
		return fmt.Errorf("could not raise mach exception")
D
Derek Parker 已提交
135 136 137 138
	}
	return nil
}

D
Derek Parker 已提交
139
func (dbp *Process) updateThreadList() error {
D
Derek Parker 已提交
140 141 142
	var (
		err   error
		kret  C.kern_return_t
143 144
		count C.int
		list  []uint32
D
Derek Parker 已提交
145 146
	)

147
	for {
148
		count = C.thread_count(dbp.os.task)
149 150 151 152 153 154 155
		if count == -1 {
			return fmt.Errorf("could not get thread count")
		}
		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
156
		kret = C.get_threads(dbp.os.task, unsafe.Pointer(&list[0]), count)
157 158 159
		if kret != -2 {
			break
		}
D
Derek Parker 已提交
160
	}
161
	if kret != C.KERN_SUCCESS {
D
Derek Parker 已提交
162
		return fmt.Errorf("could not get thread list")
D
Derek Parker 已提交
163 164 165
	}

	for _, port := range list {
D
Derek Parker 已提交
166 167 168 169 170
		if _, ok := dbp.Threads[int(port)]; !ok {
			_, err = dbp.addThread(int(port), false)
			if err != nil {
				return err
			}
D
Derek Parker 已提交
171 172 173 174 175 176
		}
	}

	return nil
}

D
Derek Parker 已提交
177
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
D
Derek Parker 已提交
178 179 180
	if thread, ok := dbp.Threads[port]; ok {
		return thread, nil
	}
D
Derek Parker 已提交
181
	thread := &Thread{
D
Derek Parker 已提交
182
		ID:  port,
183 184
		dbp: dbp,
		os:  new(OSSpecificDetails),
D
Derek Parker 已提交
185 186
	}
	dbp.Threads[port] = thread
D
Derek Parker 已提交
187
	thread.os.threadAct = C.thread_act_t(port)
D
Derek Parker 已提交
188
	if dbp.CurrentThread == nil {
D
Derek Parker 已提交
189
		dbp.SwitchThread(thread.ID)
D
Derek Parker 已提交
190
	}
D
Derek Parker 已提交
191 192 193
	return thread, nil
}

D
Derek Parker 已提交
194
func (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
195 196
	defer wg.Done()

197 198 199 200
	debugFrameSec := exe.Section("__debug_frame")
	debugInfoSec := exe.Section("__debug_info")

	if debugFrameSec != nil && debugInfoSec != nil {
D
Derek Parker 已提交
201 202 203 204 205
		debugFrame, err := exe.Section("__debug_frame").Data()
		if err != nil {
			fmt.Println("could not get __debug_frame section", err)
			os.Exit(1)
		}
206 207 208 209 210 211
		dat, err := debugInfoSec.Data()
		if err != nil {
			fmt.Println("could not get .debug_info section", err)
			os.Exit(1)
		}
		dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
D
Derek Parker 已提交
212 213 214 215 216 217
	} else {
		fmt.Println("could not find __debug_frame section in binary")
		os.Exit(1)
	}
}

D
Derek Parker 已提交
218
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
	defer wg.Done()

	var (
		symdat  []byte
		pclndat []byte
		err     error
	)

	if sec := exe.Section("__gosymtab"); sec != nil {
		symdat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gosymtab section", err)
			os.Exit(1)
		}
	}

	if sec := exe.Section("__gopclntab"); sec != nil {
		pclndat, err = sec.Data()
		if err != nil {
			fmt.Println("could not get .gopclntab section", err)
			os.Exit(1)
		}
	}

	pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
	tab, err := gosym.NewTable(symdat, pcln)
	if err != nil {
		fmt.Println("could not get initialize line table", err)
		os.Exit(1)
	}

250
	dbp.goSymTable = tab
D
Derek Parker 已提交
251 252
}

D
Derek Parker 已提交
253
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
254 255 256 257 258 259 260 261
	defer wg.Done()

	if sec := exe.Section("__debug_line"); sec != nil {
		debugLine, err := exe.Section("__debug_line").Data()
		if err != nil {
			fmt.Println("could not get __debug_line section", err)
			os.Exit(1)
		}
262
		dbp.lineInfo = line.Parse(debugLine)
D
Derek Parker 已提交
263 264 265 266 267 268
	} else {
		fmt.Println("could not find __debug_line section in binary")
		os.Exit(1)
	}
}

D
Derek Parker 已提交
269
func (dbp *Process) findExecutable(path string) (*macho.File, error) {
270 271
	if path == "" {
		path = C.GoString(C.find_executable(C.int(dbp.Pid)))
D
Derek Parker 已提交
272
	}
273
	exe, err := macho.Open(path)
D
Derek Parker 已提交
274 275 276
	if err != nil {
		return nil, err
	}
277
	dbp.dwarf, err = exe.DWARF()
D
Derek Parker 已提交
278 279 280 281
	if err != nil {
		return nil, err
	}
	return exe, nil
D
Derek Parker 已提交
282 283
}

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

288 289
		switch port {
		case dbp.os.notificationPort:
290
			_, status, err := dbp.wait(dbp.Pid, 0)
291 292 293
			if err != nil {
				return nil, err
			}
294
			dbp.postExit()
295
			return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
296

297 298 299 300 301
		case C.MACH_RCV_INTERRUPTED:
			if !dbp.halt {
				// Call trapWait again, it seems
				// MACH_RCV_INTERRUPTED is emitted before
				// process natural death _sometimes_.
302
				continue
303
			}
D
Derek Parker 已提交
304
			return nil, nil
305

306
		case 0:
D
Derek Parker 已提交
307
			return nil, fmt.Errorf("error while waiting for task")
308 309 310 311 312
		}

		// 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()
A
aarzilli 已提交
313 314
		th, ok := dbp.Threads[int(port)]
		if !ok {
315 316
			if dbp.halt {
				dbp.halt = false
A
aarzilli 已提交
317
				return th, nil
318
			}
A
aarzilli 已提交
319
			if dbp.firstStart || th.singleStepping {
320
				dbp.firstStart = false
A
aarzilli 已提交
321
				return th, nil
322
			}
A
aarzilli 已提交
323
			if err := th.Continue(); err != nil {
324 325 326
				return nil, err
			}
			continue
327
		}
328
		return th, nil
D
Derek Parker 已提交
329 330 331
	}
}

332 333 334
func (dbp *Process) waitForStop() ([]int, error) {
	ports := make([]int, 0, len(dbp.Threads))
	count := 0
335 336
	for {
		port := C.mach_port_wait(dbp.os.portSet, C.int(1))
337 338 339 340
		if port != 0 {
			count = 0
			ports = append(ports, int(port))
		} else {
341
			n := C.num_running_threads(dbp.os.task)
342 343 344 345 346
			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 已提交
347
				return nil, fmt.Errorf("could not stop process %d", n)
348
			}
349
		}
350 351 352 353 354 355 356 357 358 359 360
	}
}

func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
	ports, err := dbp.waitForStop()
	if err != nil {
		return err
	}
	trapthread.SetCurrentBreakpoint()
	for _, port := range ports {
		if th, ok := dbp.Threads[port]; ok {
361 362 363 364 365 366
			err := th.SetCurrentBreakpoint()
			if err != nil {
				return err
			}
		}
	}
367
	return nil
368 369
}

370
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
371
	wg.Done()
372 373 374
}

func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
D
Derek Parker 已提交
375 376 377 378
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}
379

L
Luke Hoban 已提交
380 381 382 383
func killProcess(pid int) error {
	return sys.Kill(pid, sys.SIGINT)
}

384
func (dbp *Process) exitGuard(err error) error {
385 386 387 388 389 390 391 392
	if err != ErrContinueThread {
		return err
	}
	_, status, werr := dbp.wait(dbp.Pid, sys.WNOHANG)
	if werr == nil && status.Exited() {
		dbp.postExit()
		return ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
	}
393 394
	return err
}
395 396 397 398 399

func (dbp *Process) resume() error {
	// all threads stopped over a breakpoint are made to step over it
	for _, thread := range dbp.Threads {
		if thread.CurrentBreakpoint != nil {
400
			if err := thread.StepInstruction(); err != nil {
401 402 403 404 405 406 407 408 409 410 411 412 413
				return err
			}
			thread.CurrentBreakpoint = nil
		}
	}
	// everything is resumed
	for _, thread := range dbp.Threads {
		if err := thread.resume(); err != nil {
			return dbp.exitGuard(err)
		}
	}
	return nil
}