github.com/undoio/delve@v1.9.0/pkg/proc/native/proc_freebsd.go (about) 1 package native 2 3 // #cgo LDFLAGS: -lprocstat 4 // #include <stdlib.h> 5 // #include "proc_freebsd.h" 6 import "C" 7 import ( 8 "fmt" 9 "os/exec" 10 "os/signal" 11 "strings" 12 "syscall" 13 "unsafe" 14 15 sys "golang.org/x/sys/unix" 16 17 "github.com/undoio/delve/pkg/proc" 18 "github.com/undoio/delve/pkg/proc/internal/ebpf" 19 20 isatty "github.com/mattn/go-isatty" 21 ) 22 23 // Process statuses 24 const ( 25 statusIdle = 1 26 statusRunning = 2 27 statusSleeping = 3 28 statusStopped = 4 29 statusZombie = 5 30 statusWaiting = 6 31 statusLocked = 7 32 ) 33 34 // osProcessDetails contains FreeBSD specific 35 // process details. 36 type osProcessDetails struct { 37 comm string 38 tid int 39 } 40 41 func (os *osProcessDetails) Close() {} 42 43 // Launch creates and begins debugging a new process. First entry in 44 // `cmd` is the program to run, and then rest are the arguments 45 // to be supplied to that process. `wd` is working directory of the program. 46 // If the DWARF information cannot be found in the binary, Delve will look 47 // for external debug files in the directories passed in. 48 func Launch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []string, tty string, redirects [3]string) (*proc.Target, error) { 49 var ( 50 process *exec.Cmd 51 err error 52 ) 53 54 foreground := flags&proc.LaunchForeground != 0 55 56 stdin, stdout, stderr, closefn, err := openRedirects(redirects, foreground) 57 if err != nil { 58 return nil, err 59 } 60 61 if stdin == nil || !isatty.IsTerminal(stdin.Fd()) { 62 // exec.(*Process).Start will fail if we try to send a process to 63 // foreground but we are not attached to a terminal. 64 foreground = false 65 } 66 67 dbp := newProcess(0) 68 defer func() { 69 if err != nil && dbp.pid != 0 { 70 _ = dbp.Detach(true) 71 } 72 }() 73 dbp.execPtraceFunc(func() { 74 process = exec.Command(cmd[0]) 75 process.Args = cmd 76 process.Stdin = stdin 77 process.Stdout = stdout 78 process.Stderr = stderr 79 process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground} 80 process.Env = proc.DisableAsyncPreemptEnv() 81 if foreground { 82 signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN) 83 } 84 if tty != "" { 85 dbp.ctty, err = attachProcessToTTY(process, tty) 86 if err != nil { 87 return 88 } 89 } 90 if wd != "" { 91 process.Dir = wd 92 } 93 err = process.Start() 94 }) 95 closefn() 96 if err != nil { 97 return nil, err 98 } 99 dbp.pid = process.Process.Pid 100 dbp.childProcess = true 101 _, _, err = dbp.wait(process.Process.Pid, 0) 102 if err != nil { 103 return nil, fmt.Errorf("waiting for target execve failed: %s", err) 104 } 105 tgt, err := dbp.initialize(cmd[0], debugInfoDirs) 106 if err != nil { 107 return nil, err 108 } 109 return tgt, nil 110 } 111 112 // Attach to an existing process with the given PID. Once attached, if 113 // the DWARF information cannot be found in the binary, Delve will look 114 // for external debug files in the directories passed in. 115 func Attach(pid int, debugInfoDirs []string) (*proc.Target, error) { 116 dbp := newProcess(pid) 117 118 var err error 119 dbp.execPtraceFunc(func() { err = ptraceAttach(dbp.pid) }) 120 if err != nil { 121 return nil, err 122 } 123 _, _, err = dbp.wait(dbp.pid, 0) 124 if err != nil { 125 return nil, err 126 } 127 128 tgt, err := dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs) 129 if err != nil { 130 dbp.Detach(false) 131 return nil, err 132 } 133 return tgt, nil 134 } 135 136 func initialize(dbp *nativeProcess) error { 137 comm, _ := C.find_command_name(C.int(dbp.pid)) 138 defer C.free(unsafe.Pointer(comm)) 139 comm_str := C.GoString(comm) 140 dbp.os.comm = strings.Replace(string(comm_str), "%", "%%", -1) 141 return nil 142 } 143 144 // kill kills the target process. 145 func (dbp *nativeProcess) kill() (err error) { 146 if dbp.exited { 147 return nil 148 } 149 dbp.execPtraceFunc(func() { err = ptraceCont(dbp.pid, int(sys.SIGKILL)) }) 150 if err != nil { 151 return err 152 } 153 if _, _, err = dbp.wait(dbp.pid, 0); err != nil { 154 return err 155 } 156 dbp.postExit() 157 return nil 158 } 159 160 // Used by RequestManualStop 161 func (dbp *nativeProcess) requestManualStop() (err error) { 162 return sys.Kill(dbp.pid, sys.SIGTRAP) 163 } 164 165 // Attach to a newly created thread, and store that thread in our list of 166 // known threads. 167 func (dbp *nativeProcess) addThread(tid int, attach bool) (*nativeThread, error) { 168 if thread, ok := dbp.threads[tid]; ok { 169 return thread, nil 170 } 171 172 var err error 173 dbp.execPtraceFunc(func() { err = sys.PtraceLwpEvents(dbp.pid, 1) }) 174 if err == syscall.ESRCH { 175 if _, _, err = dbp.waitFast(dbp.pid); err != nil { 176 return nil, fmt.Errorf("error while waiting after adding process: %d %s", dbp.pid, err) 177 } 178 } 179 180 dbp.threads[tid] = &nativeThread{ 181 ID: tid, 182 dbp: dbp, 183 os: new(osSpecificDetails), 184 } 185 186 if dbp.memthread == nil { 187 dbp.memthread = dbp.threads[tid] 188 } 189 190 return dbp.threads[tid], nil 191 } 192 193 // Used by initialize 194 func (dbp *nativeProcess) updateThreadList() error { 195 var tids []int32 196 dbp.execPtraceFunc(func() { tids = ptraceGetLwpList(dbp.pid) }) 197 for _, tid := range tids { 198 if _, err := dbp.addThread(int(tid), false); err != nil { 199 return err 200 } 201 } 202 dbp.os.tid = int(tids[0]) 203 return nil 204 } 205 206 // Used by Attach 207 func findExecutable(path string, pid int) string { 208 if path == "" { 209 cstr := C.find_executable(C.int(pid)) 210 defer C.free(unsafe.Pointer(cstr)) 211 path = C.GoString(cstr) 212 } 213 return path 214 } 215 216 func (dbp *nativeProcess) trapWait(pid int) (*nativeThread, error) { 217 return dbp.trapWaitInternal(pid, false) 218 } 219 220 // Used by stop and trapWait 221 func (dbp *nativeProcess) trapWaitInternal(pid int, halt bool) (*nativeThread, error) { 222 for { 223 wpid, status, err := dbp.wait(pid, 0) 224 if err != nil { 225 return nil, fmt.Errorf("wait err %s %d", err, pid) 226 } 227 if status.Killed() { 228 // "Killed" status may arrive as a result of a Process.Kill() of some other process in 229 // the system performed by the same tracer (e.g. in the previous test) 230 continue 231 } 232 if status.Exited() { 233 dbp.postExit() 234 return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()} 235 } 236 237 var info sys.PtraceLwpInfoStruct 238 dbp.execPtraceFunc(func() { info, err = ptraceGetLwpInfo(wpid) }) 239 if err != nil { 240 return nil, fmt.Errorf("ptraceGetLwpInfo err %s %d", err, pid) 241 } 242 tid := int(info.Lwpid) 243 pl_flags := int(info.Flags) 244 th, ok := dbp.threads[tid] 245 if ok { 246 th.Status = (*waitStatus)(status) 247 } 248 249 if status.StopSignal() == sys.SIGTRAP { 250 if pl_flags&sys.PL_FLAG_EXITED != 0 { 251 delete(dbp.threads, tid) 252 dbp.execPtraceFunc(func() { err = ptraceCont(tid, 0) }) 253 if err != nil { 254 return nil, err 255 } 256 continue 257 } else if pl_flags&sys.PL_FLAG_BORN != 0 { 258 th, err = dbp.addThread(int(tid), false) 259 if err != nil { 260 if err == sys.ESRCH { 261 // process died while we were adding it 262 continue 263 } 264 return nil, err 265 } 266 if halt { 267 return nil, nil 268 } 269 if err = th.Continue(); err != nil { 270 if err == sys.ESRCH { 271 // thread died while we were adding it 272 delete(dbp.threads, int(tid)) 273 continue 274 } 275 return nil, fmt.Errorf("could not continue new thread %d %s", tid, err) 276 } 277 continue 278 } 279 } 280 281 if th == nil { 282 continue 283 } 284 285 if (halt && status.StopSignal() == sys.SIGSTOP) || (status.StopSignal() == sys.SIGTRAP) { 286 return th, nil 287 } 288 289 // TODO(dp) alert user about unexpected signals here. 290 if err := th.resumeWithSig(int(status.StopSignal())); err != nil { 291 if err == sys.ESRCH { 292 return nil, proc.ErrProcessExited{Pid: dbp.pid} 293 } 294 return nil, err 295 } 296 } 297 } 298 299 // Helper function used here and in threads_freebsd.go 300 // Return the status code 301 func status(pid int) rune { 302 status := rune(C.find_status(C.int(pid))) 303 return status 304 } 305 306 // Used by stop and singleStep 307 // waitFast is like wait but does not handle process-exit correctly 308 func (dbp *nativeProcess) waitFast(pid int) (int, *sys.WaitStatus, error) { 309 var s sys.WaitStatus 310 wpid, err := sys.Wait4(pid, &s, 0, nil) 311 return wpid, &s, err 312 } 313 314 // Only used in this file 315 func (dbp *nativeProcess) wait(pid, options int) (int, *sys.WaitStatus, error) { 316 var s sys.WaitStatus 317 wpid, err := sys.Wait4(pid, &s, options, nil) 318 return wpid, &s, err 319 } 320 321 // Only used in this file 322 func (dbp *nativeProcess) exitGuard(err error) error { 323 if err != sys.ESRCH { 324 return err 325 } 326 if status(dbp.pid) == statusZombie { 327 _, err := dbp.trapWaitInternal(-1, false) 328 return err 329 } 330 331 return err 332 } 333 334 // Used by ContinueOnce 335 func (dbp *nativeProcess) resume() error { 336 // all threads stopped over a breakpoint are made to step over it 337 for _, thread := range dbp.threads { 338 if thread.CurrentBreakpoint.Breakpoint != nil { 339 if err := thread.StepInstruction(); err != nil { 340 return err 341 } 342 thread.CurrentBreakpoint.Clear() 343 } 344 } 345 // all threads are resumed 346 var err error 347 dbp.execPtraceFunc(func() { err = ptraceCont(dbp.pid, 0) }) 348 return err 349 } 350 351 // Used by ContinueOnce 352 // stop stops all running threads and sets breakpoints 353 func (dbp *nativeProcess) stop(cctx *proc.ContinueOnceContext, trapthread *nativeThread) (*nativeThread, error) { 354 if dbp.exited { 355 return nil, proc.ErrProcessExited{Pid: dbp.pid} 356 } 357 // set breakpoints on all threads 358 for _, th := range dbp.threads { 359 if th.CurrentBreakpoint.Breakpoint == nil { 360 if err := th.SetCurrentBreakpoint(true); err != nil { 361 return nil, err 362 } 363 } 364 } 365 return trapthread, nil 366 } 367 368 // Used by Detach 369 func (dbp *nativeProcess) detach(kill bool) error { 370 return ptraceDetach(dbp.pid) 371 } 372 373 // Used by PostInitializationSetup 374 // EntryPoint will return the process entry point address, useful for debugging PIEs. 375 func (dbp *nativeProcess) EntryPoint() (uint64, error) { 376 ep, err := C.get_entry_point(C.int(dbp.pid)) 377 return uint64(ep), err 378 } 379 380 func (dbp *nativeProcess) SupportsBPF() bool { 381 return false 382 } 383 384 func (dbp *nativeProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf.UProbeArgMap) error { 385 panic("not implemented") 386 } 387 388 func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams { 389 panic("not implemented") 390 } 391 392 // Usedy by Detach 393 func killProcess(pid int) error { 394 return sys.Kill(pid, sys.SIGINT) 395 }