proc_darwin.go 7.6 KB
Newer Older
D
Derek Parker 已提交
1
package proc
D
Derek Parker 已提交
2

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

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

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

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

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

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

77
	dbp, err = initializeDebugProcess(dbp, argv0Go, false)
78 79 80 81 82 83 84
	if err != nil {
		return nil, err
	}
	err = dbp.Continue()
	return dbp, err
}

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

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

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

D
Derek Parker 已提交
136
func (dbp *Process) updateThreadList() error {
D
Derek Parker 已提交
137 138 139 140 141
	var (
		err   error
		kret  C.kern_return_t
		count = C.thread_count(C.task_t(dbp.os.task))
	)
D
Derek Parker 已提交
142
	if count == -1 {
D
Derek Parker 已提交
143
		return fmt.Errorf("could not get thread count")
D
Derek Parker 已提交
144 145
	}
	list := make([]uint32, count)
D
Derek Parker 已提交
146

D
Derek Parker 已提交
147
	// TODO(dp) might be better to malloc mem in C and then free it here
D
Derek Parker 已提交
148 149 150
	// instead of getting count above and passing in a slice
	kret = C.get_threads(C.task_t(dbp.os.task), unsafe.Pointer(&list[0]))
	if kret != C.KERN_SUCCESS {
D
Derek Parker 已提交
151
		return fmt.Errorf("could not get thread list")
D
Derek Parker 已提交
152 153
	}
	if count < 0 {
D
Derek Parker 已提交
154
		return fmt.Errorf("could not get thread list")
D
Derek Parker 已提交
155 156 157
	}

	for _, port := range list {
D
Derek Parker 已提交
158 159 160 161 162
		if _, ok := dbp.Threads[int(port)]; !ok {
			_, err = dbp.addThread(int(port), false)
			if err != nil {
				return err
			}
D
Derek Parker 已提交
163 164 165 166 167 168
		}
	}

	return nil
}

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

D
Derek Parker 已提交
186
func (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
187 188 189 190 191 192 193 194
	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)
		}
195
		dbp.frameEntries = frame.Parse(debugFrame)
D
Derek Parker 已提交
196 197 198 199 200 201
	} else {
		fmt.Println("could not find __debug_frame section in binary")
		os.Exit(1)
	}
}

D
Derek Parker 已提交
202
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
D
Derek Parker 已提交
203 204 205 206 207 208 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
	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)
	}

234
	dbp.goSymTable = tab
D
Derek Parker 已提交
235 236
}

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

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

D
Derek Parker 已提交
269
func (dbp *Process) trapWait(pid int) (*Thread, error) {
270 271
	for {
		port := C.mach_port_wait(dbp.os.portSet)
272

273 274
		switch port {
		case dbp.os.notificationPort:
275
			_, status, err := dbp.wait(dbp.Pid, 0)
276 277 278 279 280
			if err != nil {
				return nil, err
			}
			dbp.exited = true
			return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
281

282 283 284 285 286
		case C.MACH_RCV_INTERRUPTED:
			if !dbp.halt {
				// Call trapWait again, it seems
				// MACH_RCV_INTERRUPTED is emitted before
				// process natural death _sometimes_.
287
				continue
288
			}
D
Derek Parker 已提交
289
			return nil, nil
290

291
		case 0:
D
Derek Parker 已提交
292
			return nil, fmt.Errorf("error while waiting for task")
293 294 295 296 297
		}

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

321 322 323 324
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
}

func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
D
Derek Parker 已提交
325 326 327 328
	var status sys.WaitStatus
	wpid, err := sys.Wait4(pid, &status, options, nil)
	return wpid, &status, err
}