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
	"fmt"
12
	"golang.org/x/debug/macho"
D
Derek Parker 已提交
13
	"os"
14
	"os/exec"
15
	"path/filepath"
D
Derek Parker 已提交
16 17 18 19
	"sync"
	"unsafe"

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

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

	// 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.
D
Derek Parker 已提交
39
func Launch(cmd []string) (*Process, error) {
40
	argv0Go, err := filepath.Abs(cmd[0])
41 42 43
	if err != nil {
		return nil, err
	}
44
	// Make sure the binary exists.
45 46 47 48 49 50 51 52 53
	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
	}

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

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

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

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
// 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 已提交
101
// Kill kills the process.
102
func (dbp *Process) Kill() (err error) {
103 104 105
	if dbp.exited {
		return nil
	}
106
	err = sys.Kill(-dbp.Pid, sys.SIGKILL)
107 108 109 110 111 112 113 114 115
	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 {
116
		port := C.mach_port_wait(dbp.os.portSet, C.int(0))
117 118 119 120
		if port == dbp.os.notificationPort {
			break
		}
	}
121
	dbp.postExit()
122 123 124
	return
}

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

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

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

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

	return nil
}

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

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

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

	if debugFrameSec != nil && debugInfoSec != nil {
D
Derek Parker 已提交
200 201 202 203 204
		debugFrame, err := exe.Section("__debug_frame").Data()
		if err != nil {
			fmt.Println("could not get __debug_frame section", err)
			os.Exit(1)
		}
205 206 207 208 209 210
		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 已提交
211 212 213 214 215 216
	} else {
		fmt.Println("could not find __debug_frame section in binary")
		os.Exit(1)
	}
}

D
Derek Parker 已提交
217
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
218 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
	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)
	}

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

D
Derek Parker 已提交
252
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
253 254 255 256 257 258 259 260
	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)
		}
261
		dbp.lineInfo = line.Parse(debugLine)
D
Derek Parker 已提交
262 263 264 265 266 267
	} else {
		fmt.Println("could not find __debug_line section in binary")
		os.Exit(1)
	}
}

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

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

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

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

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

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

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

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 {
360 361 362 363 364 365
			err := th.SetCurrentBreakpoint()
			if err != nil {
				return err
			}
		}
	}
366
	return nil
367 368
}

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

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

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

383
func (dbp *Process) exitGuard(err error) error {
384 385 386 387 388 389 390 391
	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()}
	}
392 393
	return err
}
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

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 {
			if err := thread.Step(); err != nil {
				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
}