proc_darwin.go 9.1 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 10
import "C"
import (
	"debug/gosym"
	"debug/macho"
11
	"errors"
D
Derek Parker 已提交
12 13
	"fmt"
	"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"
)

24
// 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
}

35 36
// Create and begin debugging a new process. Uses a
// 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)
}

101
func (dbp *Process) Kill() (err error) {
102 103 104
	if dbp.exited {
		return nil
	}
105
	err = sys.Kill(-dbp.Pid, sys.SIGKILL)
106 107 108 109 110 111 112 113 114
	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 {
115
		port := C.mach_port_wait(dbp.os.portSet, C.int(0))
116 117 118 119
		if port == dbp.os.notificationPort {
			break
		}
	}
120
	dbp.postExit()
121 122 123
	return
}

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

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

145 146 147 148 149 150 151 152 153 154 155 156 157
	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 已提交
158
	}
159
	if kret != C.KERN_SUCCESS {
D
Derek Parker 已提交
160
		return fmt.Errorf("could not get thread list")
D
Derek Parker 已提交
161 162 163
	}

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

	return nil
}

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

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

	if sec := exe.Section("__debug_frame"); sec != nil {
		debugFrame, err := exe.Section("__debug_frame").Data()
		if err != nil {
			fmt.Println("could not get __debug_frame section", err)
			os.Exit(1)
		}
201
		dbp.frameEntries = frame.Parse(debugFrame)
D
Derek Parker 已提交
202 203 204 205 206 207
	} else {
		fmt.Println("could not find __debug_frame section in binary")
		os.Exit(1)
	}
}

D
Derek Parker 已提交
208
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
	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)
	}

240
	dbp.goSymTable = tab
D
Derek Parker 已提交
241 242
}

D
Derek Parker 已提交
243
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
244 245 246 247 248 249 250 251
	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)
		}
252
		dbp.lineInfo = line.Parse(debugLine)
D
Derek Parker 已提交
253 254 255 256 257 258
	} else {
		fmt.Println("could not find __debug_line section in binary")
		os.Exit(1)
	}
}

D
Derek Parker 已提交
259
func (dbp *Process) findExecutable(path string) (*macho.File, error) {
260 261
	if path == "" {
		path = C.GoString(C.find_executable(C.int(dbp.Pid)))
D
Derek Parker 已提交
262
	}
263
	exe, err := macho.Open(path)
D
Derek Parker 已提交
264 265 266 267 268 269 270
	if err != nil {
		return nil, err
	}
	data, err := exe.DWARF()
	if err != nil {
		return nil, err
	}
271
	dbp.dwarf = data
D
Derek Parker 已提交
272
	return exe, nil
D
Derek Parker 已提交
273 274
}

D
Derek Parker 已提交
275
func (dbp *Process) trapWait(pid int) (*Thread, error) {
276
	for {
277
		port := C.mach_port_wait(dbp.os.portSet, C.int(0))
278

279 280
		switch port {
		case dbp.os.notificationPort:
281
			_, status, err := dbp.wait(dbp.Pid, 0)
282 283 284
			if err != nil {
				return nil, err
			}
285
			dbp.postExit()
286
			return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
287

288 289 290 291 292
		case C.MACH_RCV_INTERRUPTED:
			if !dbp.halt {
				// Call trapWait again, it seems
				// MACH_RCV_INTERRUPTED is emitted before
				// process natural death _sometimes_.
293
				continue
294
			}
D
Derek Parker 已提交
295
			return nil, nil
296

297
		case 0:
D
Derek Parker 已提交
298
			return nil, fmt.Errorf("error while waiting for task")
299 300 301 302 303
		}

		// 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 已提交
304 305
		th, ok := dbp.Threads[int(port)]
		if !ok {
306 307
			if dbp.halt {
				dbp.halt = false
A
aarzilli 已提交
308
				return th, nil
309
			}
A
aarzilli 已提交
310
			if dbp.firstStart || th.singleStepping {
311
				dbp.firstStart = false
A
aarzilli 已提交
312
				return th, nil
313
			}
A
aarzilli 已提交
314
			if err := th.Continue(); err != nil {
315 316 317
				return nil, err
			}
			continue
318
		}
319
		return th, nil
D
Derek Parker 已提交
320 321 322
	}
}

323 324 325
func (dbp *Process) waitForStop() ([]int, error) {
	ports := make([]int, 0, len(dbp.Threads))
	count := 0
326 327
	for {
		port := C.mach_port_wait(dbp.os.portSet, C.int(1))
328 329 330 331 332 333 334 335 336 337
		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 已提交
338
				return nil, fmt.Errorf("could not stop process %d", n)
339
			}
340
		}
341 342 343 344 345 346 347 348 349 350 351
	}
}

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 {
352 353 354 355 356 357
			err := th.SetCurrentBreakpoint()
			if err != nil {
				return err
			}
		}
	}
358
	return nil
359 360
}

361
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
362
	wg.Done()
363 364 365
}

func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
D
Derek Parker 已提交
366 367 368 369
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}
370 371

func (dbp *Process) exitGuard(err error) error {
372 373 374 375 376 377 378 379
	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()}
	}
380 381
	return err
}
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

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
}