70 lines
1.6 KiB
Go
70 lines
1.6 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build linux
|
|
|
|
package debug
|
|
|
|
import (
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// StopReason describes why the debuggee stopped.
|
|
type StopReason int
|
|
|
|
const (
|
|
StopNone StopReason = iota
|
|
StopBreakpoint // software breakpoint hit
|
|
StopWatchpoint // hardware watchpoint triggered
|
|
StopSingleStep // single-step completed
|
|
StopSignal // stopped by a signal
|
|
StopExited // process exited
|
|
)
|
|
|
|
// siginfo_t layout (Linux): si_signo, si_errno, si_code, then union.
|
|
// The si_addr field is at offset 16 on all supported architectures.
|
|
type siginfoT struct {
|
|
SiSigno int32
|
|
SiErrno int32
|
|
SiCode int32
|
|
_pad [125]byte
|
|
}
|
|
|
|
const (
|
|
trapBRKPT = 1 // software breakpoint
|
|
trapHWBRKPT = 4 // hardware watchpoint
|
|
)
|
|
|
|
// StopInfo returns the reason the debuggee stopped and the faulting address
|
|
// (for watchpoints, the watched address that was accessed).
|
|
func (s *Session) StopInfo() (StopReason, uint64) {
|
|
if s.exited {
|
|
return StopExited, 0
|
|
}
|
|
var info siginfoT
|
|
_, _, errno := syscall.Syscall6(
|
|
syscall.SYS_PTRACE,
|
|
uintptr(syscall.PTRACE_GETSIGINFO),
|
|
uintptr(s.pid),
|
|
0,
|
|
uintptr(unsafe.Pointer(&info)),
|
|
0, 0,
|
|
)
|
|
if errno != 0 {
|
|
return StopNone, 0
|
|
}
|
|
if info.SiSigno != int32(syscall.SIGTRAP) {
|
|
return StopSignal, uint64(info.SiCode)
|
|
}
|
|
switch info.SiCode {
|
|
case trapBRKPT:
|
|
return StopBreakpoint, 0
|
|
case trapHWBRKPT:
|
|
addr := *(*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(&info)) + 16))
|
|
return StopWatchpoint, addr
|
|
default:
|
|
return StopSingleStep, 0
|
|
}
|
|
}
|