delve/pkg/proc/gdbserial/gdbserver.go

2259 lines
65 KiB
Go
Raw Normal View History

// This file and its companion gdbserver_conn implement a target.Interface
// backed by a connection to a debugger speaking the "Gdb Remote Serial
// Protocol".
//
// The "Gdb Remote Serial Protocol" is a low level debugging protocol
// originally designed so that gdb could be used to debug programs running
// in embedded environments but it was later extended to support programs
// running on any environment and a variety of debuggers support it:
// gdbserver, lldb-server, macOS's debugserver and rr.
//
// The protocol is specified at:
// https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html
// with additional documentation for lldb specific extensions described at:
// https://github.com/llvm/llvm-project/blob/main/lldb/docs/resources/lldbgdbremote.md
//
// Terminology:
// * inferior: the program we are trying to debug
// * stub: the debugger on the other side of the protocol's connection (for
// example lldb-server)
// * gdbserver: stub version of gdb
// * lldb-server: stub version of lldb
// * debugserver: a different stub version of lldb, installed with lldb on
// macOS.
// * mozilla rr: a stub that records the full execution of a program
// and can then play it back.
//
// Implementations of the protocol vary wildly between stubs, while there is
// a command to query the stub about supported features (qSupported) this
// only covers *some* of the more recent additions to the protocol and most
// of the older packets are optional and *not* implemented by all stubs.
// For example gdbserver implements 'g' (read all registers) but not 'p'
// (read single register) while lldb-server implements 'p' but not 'g'.
//
// The protocol is also underspecified with regards to how the stub should
// handle a multithreaded inferior. Its default mode of operation is
// "all-stop mode", when a thread hits a breakpoint all other threads are
// also stopped. But the protocol doesn't say what happens if a second
// thread hits a breakpoint while the stub is in the process of stopping all
// other threads.
//
// In practice the stub is allowed to swallow the second breakpoint hit or
// to return it at a later time. If the stub chooses the latter behavior
// (like gdbserver does) it is allowed to return delayed events on *any*
// vCont packet. This is incredibly inconvenient since if we get notified
// about a delayed breakpoint while we are trying to singlestep another
// thread it's impossible to know when the singlestep we requested ended.
//
// What this means is that gdbserver can only be supported for multithreaded
// inferiors by selecting non-stop mode, which behaves in a markedly
// different way from all-stop mode and isn't supported by anything except
// gdbserver.
//
// lldb-server/debugserver takes a different approach, only the first stop
// event is reported, if any other event happens "simultaneously" they are
// suppressed by the stub and the debugger can query for them using
// qThreadStopInfo. This is much easier for us to implement and the
2018-03-20 10:05:35 +00:00
// implementation gracefully degrades to the case where qThreadStopInfo is
// unavailable but the inferior is run in single threaded mode.
//
// Therefore the following code will assume lldb-server-like behavior.
package gdbserial
import (
"bytes"
"debug/macho"
"encoding/binary"
"errors"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"time"
isatty "github.com/mattn/go-isatty"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/elfwriter"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/internal/ebpf"
"github.com/go-delve/delve/pkg/proc/linutil"
"github.com/go-delve/delve/pkg/proc/macutil"
)
const (
gdbWireFullStopPacket = false
gdbWireMaxLen = 120
maxTransmitAttempts = 3 // number of retransmission attempts on failed checksum
initialInputBufferSize = 2048 // size of the input buffer for gdbConn
debugServerEnvVar = "DELVE_DEBUGSERVER_PATH" // use this environment variable to override the path to debugserver used by Launch/Attach
)
const heartbeatInterval = 10 * time.Second
// Relative to $(xcode-select --print-path)/../
// xcode-select typically returns the path to the Developer directory, which is a sibling to SharedFrameworks.
var debugserverXcodeRelativeExecutablePath = "SharedFrameworks/LLDB.framework/Versions/A/Resources/debugserver"
var debugserverExecutablePaths = []string{
"debugserver",
"/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/debugserver",
// Function returns the active developer directory provided by xcode-select to compute a debugserver path.
func() string {
if _, err := exec.LookPath("xcode-select"); err != nil {
return ""
}
stdout, err := exec.Command("xcode-select", "--print-path").Output()
if err != nil {
return ""
}
xcodePath := strings.TrimSpace(string(stdout))
if xcodePath == "" {
return ""
}
// xcode-select prints the path to the active Developer directory, which is typically a sibling to SharedFrameworks.
return filepath.Join(xcodePath, "..", debugserverXcodeRelativeExecutablePath)
}(),
}
// ErrDirChange is returned when trying to change execution direction
// while there are still internal breakpoints set.
var ErrDirChange = errors.New("direction change with internal breakpoints")
// ErrStartCallInjectionBackwards is returned when trying to start a call
// injection while the recording is being run backwards.
var ErrStartCallInjectionBackwards = errors.New("can not start a call injection while running backwards")
var checkCanUnmaskSignalsOnce sync.Once
var canUnmaskSignalsCached bool
// gdbProcess implements proc.Process using a connection to a debugger stub
// that understands Gdb Remote Serial Protocol.
type gdbProcess struct {
bi *proc.BinaryInfo
regnames *gdbRegnames
conn gdbConn
threads map[int]*gdbThread
currentThread *gdbThread
exited, detached bool
almostExited bool // true if 'rr' has sent its synthetic SIGKILL
ctrlC bool // ctrl-c was sent to stop inferior
breakpoints proc.BreakpointMap
gcmdok bool // true if the stub supports g and (maybe) G commands
_Gcmdok bool // true if the stub supports G command
threadStopInfo bool // true if the stub supports qThreadStopInfo
tracedir string // if attached to rr the path to the trace directory
loadGInstrAddr uint64 // address of the g loading instruction, zero if we couldn't allocate it
breakpointKind int // breakpoint kind to pass to 'z' and 'Z' when creating software breakpoints
process *os.Process
waitChan chan *os.ProcessState
onDetach func() // called after a successful detach
}
var _ proc.RecordingManipulationInternal = &gdbProcess{}
// gdbThread represents an operating system thread.
type gdbThread struct {
ID int
strID string
regs gdbRegisters
CurrentBreakpoint proc.BreakpointState
p *gdbProcess
sig uint8 // signal received by thread after last stop
setbp bool // thread was stopped because of a breakpoint
watchAddr uint64 // if > 0 this is the watchpoint address
watchReg int // if < 0 there are no active watchpoints returned
common proc.CommonThread
}
// ErrBackendUnavailable is returned when the stub program can not be found.
type ErrBackendUnavailable struct{}
func (err *ErrBackendUnavailable) Error() string {
return "backend unavailable"
}
// gdbRegisters represents the current value of the registers of a thread.
// The storage space for all the registers is allocated as a single memory
// block in buf, the value field inside an individual gdbRegister will be a
// slice of the global buf field.
type gdbRegisters struct {
regs map[string]gdbRegister
regsInfo []gdbRegisterInfo
tls uint64
gaddr uint64
hasgaddr bool
buf []byte
arch *proc.Arch
regnames *gdbRegnames
}
type gdbRegister struct {
value []byte
regnum int
ignoreOnWrite bool
}
// gdbRegname records names of important CPU registers
type gdbRegnames struct {
PC, SP, BP, CX, FsBase string
}
// newProcess creates a new Process instance.
// If process is not nil it is the stub's process and will be killed after
// Detach.
// Use Listen, Dial or Connect to complete connection.
func newProcess(process *os.Process) *gdbProcess {
logger := logflags.GdbWireLogger()
p := &gdbProcess{
conn: gdbConn{
maxTransmitAttempts: maxTransmitAttempts,
inbuf: make([]byte, 0, initialInputBufferSize),
direction: proc.Forward,
log: logger,
goarch: runtime.GOARCH,
goos: runtime.GOOS,
},
threads: make(map[int]*gdbThread),
bi: proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
regnames: new(gdbRegnames),
breakpoints: proc.NewBreakpointMap(),
gcmdok: true,
threadStopInfo: true,
process: process,
}
switch p.bi.Arch.Name {
default:
fallthrough
case "amd64":
p.breakpointKind = 1
case "arm64":
p.breakpointKind = 4
}
p.regnames.PC = registerName(p.bi.Arch, p.bi.Arch.PCRegNum)
p.regnames.SP = registerName(p.bi.Arch, p.bi.Arch.SPRegNum)
p.regnames.BP = registerName(p.bi.Arch, p.bi.Arch.BPRegNum)
switch p.bi.Arch.Name {
case "arm64":
p.regnames.BP = "fp"
p.regnames.CX = "x0"
case "amd64":
p.regnames.CX = "rcx"
p.regnames.FsBase = "fs_base"
default:
panic("not implemented")
}
if process != nil {
p.waitChan = make(chan *os.ProcessState)
go func() {
state, _ := process.Wait()
p.waitChan <- state
}()
}
return p
}
// Listen waits for a connection from the stub.
func (p *gdbProcess) Listen(listener net.Listener, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
acceptChan := make(chan net.Conn)
go func() {
conn, _ := listener.Accept()
acceptChan <- conn
}()
select {
case conn := <-acceptChan:
listener.Close()
if conn == nil {
return nil, errors.New("could not connect")
}
return p.Connect(conn, path, cmdline, pid, debugInfoDirs, stopReason)
case status := <-p.waitChan:
listener.Close()
if err := checkRosettaExpensive(); err != nil {
return nil, err
}
return nil, fmt.Errorf("stub exited while waiting for connection: %v", status)
}
}
// Dial attempts to connect to the stub.
func (p *gdbProcess) Dial(addr string, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
for {
conn, err := net.Dial("tcp", addr)
if err == nil {
return p.Connect(conn, path, cmdline, pid, debugInfoDirs, stopReason)
}
select {
case status := <-p.waitChan:
if err := checkRosettaExpensive(); err != nil {
return nil, err
}
return nil, fmt.Errorf("stub exited while attempting to connect: %v", status)
default:
}
time.Sleep(time.Second)
}
}
// Connect connects to a stub and performs a handshake.
//
// Path and pid are, respectively, the path to the executable of the target
// program and the PID of the target process, both are optional, however
// some stubs do not provide ways to determine path and pid automatically
// and Connect will be unable to function without knowing them.
func (p *gdbProcess) Connect(conn net.Conn, path, cmdline string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
p.conn.conn = conn
p.conn.pid = pid
err := p.conn.handshake(p.regnames)
if err != nil {
conn.Close()
return nil, err
}
if p.conn.isDebugserver {
// There are multiple problems with the 'g'/'G' commands on debugserver.
// On version 902 it used to crash the server completely (https://bugs.llvm.org/show_bug.cgi?id=36968),
// on arm64 it results in E74 being returned (https://bugs.llvm.org/show_bug.cgi?id=50169)
// and on systems where AVX-512 is used it returns the floating point
// registers scrambled and sometimes causes the mask registers to be
// zeroed out (https://github.com/go-delve/delve/pull/2498).
// All of these bugs stem from the fact that the main consumer of
// debugserver, lldb, never uses 'g' or 'G' which would make Delve the
// sole tester of those codepaths.
// Therefore we disable it here. The associated code is kept around to be
// used with Mozilla RR.
p.gcmdok = false
}
tgt, err := p.initialize(path, cmdline, debugInfoDirs, stopReason)
if err != nil {
return nil, err
}
if p.bi.Arch.Name != "arm64" {
// None of the stubs we support returns the value of fs_base or gs_base
// along with the registers, therefore we have to resort to executing a MOV
// instruction on the inferior to find out where the G struct of a given
// thread is located.
// Here we try to allocate some memory on the inferior which we will use to
// store the MOV instruction.
// If the stub doesn't support memory allocation reloadRegisters will
// overwrite some existing memory to store the MOV.
if ginstr, err := p.loadGInstr(); err == nil {
if addr, err := p.conn.allocMemory(256); err == nil {
if _, err := p.conn.writeMemory(addr, ginstr); err == nil {
p.loadGInstrAddr = addr
}
}
}
}
return tgt, nil
}
func (p *gdbProcess) SupportsBPF() bool {
return false
}
func (p *gdbProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
return nil
}
func (p *gdbProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf.UProbeArgMap) error {
panic("not implemented")
}
// unusedPort returns an unused tcp port
// This is a hack and subject to a race condition with other running
// programs, but most (all?) OS will cycle through all ephemeral ports
// before reassigning one port they just assigned, unless there's heavy
// churn in the ephemeral range this should work.
func unusedPort() string {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return ":8081"
}
port := listener.Addr().(*net.TCPAddr).Port
listener.Close()
return fmt.Sprintf(":%d", port)
}
// getDebugServerAbsolutePath returns a string of the absolute path to the debugserver binary IFF it is
// found in the system path ($PATH), the Xcode bundle or the standalone CLT location.
func getDebugServerAbsolutePath() string {
if path := os.Getenv(debugServerEnvVar); path != "" {
return path
}
for _, debugServerPath := range debugserverExecutablePaths {
if debugServerPath == "" {
continue
}
if _, err := exec.LookPath(debugServerPath); err == nil {
return debugServerPath
}
}
return ""
}
func canUnmaskSignals(debugServerExecutable string) bool {
checkCanUnmaskSignalsOnce.Do(func() {
buf, _ := exec.Command(debugServerExecutable, "--unmask-signals").CombinedOutput()
canUnmaskSignalsCached = !strings.Contains(string(buf), "unrecognized option")
})
return canUnmaskSignalsCached
}
// commandLogger is a wrapper around the exec.Command() function to log the arguments prior to
// starting the process
func commandLogger(binary string, arguments ...string) *exec.Cmd {
logflags.GdbWireLogger().Debugf("executing %s %v", binary, arguments)
return exec.Command(binary, arguments...)
}
// ErrUnsupportedOS is returned when trying to use the lldb backend on Windows.
var ErrUnsupportedOS = errors.New("lldb backend not supported on Windows")
func getLdEnvVars() []string {
var result []string
environ := os.Environ()
for i := 0; i < len(environ); i++ {
if strings.HasPrefix(environ[i], "LD_") ||
strings.HasPrefix(environ[i], "DYLD_") {
result = append(result, "-e", environ[i])
}
}
return result
}
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
// it to launch the specified target program with the specified arguments
// (cmd) on the specified directory wd.
func LLDBLaunch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []string, tty string, redirects [3]string) (*proc.TargetGroup, error) {
if runtime.GOOS == "windows" {
return nil, ErrUnsupportedOS
}
if err := macutil.CheckRosetta(); err != nil {
return nil, err
}
foreground := flags&proc.LaunchForeground != 0
var (
isDebugserver bool
listener net.Listener
port string
process *exec.Cmd
err error
hasRedirects bool
)
if debugserverExecutable := getDebugServerAbsolutePath(); debugserverExecutable != "" {
listener, err = net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return nil, err
}
ldEnvVars := getLdEnvVars()
args := make([]string, 0, len(cmd)+4+len(ldEnvVars))
args = append(args, ldEnvVars...)
if tty != "" {
args = append(args, "--stdio-path", tty)
} else {
found := [3]bool{}
names := [3]string{"stdin", "stdout", "stderr"}
for i := range redirects {
if redirects[i] != "" {
found[i] = true
hasRedirects = true
args = append(args, fmt.Sprintf("--%s-path", names[i]), redirects[i])
}
}
if foreground || hasRedirects {
for i := range found {
if !found[i] {
args = append(args, fmt.Sprintf("--%s-path", names[i]), "/dev/"+names[i])
}
}
}
}
if logflags.LLDBServerOutput() {
args = append(args, "-g", "-l", "stdout")
}
if flags&proc.LaunchDisableASLR != 0 {
args = append(args, "-D")
}
if canUnmaskSignals(debugserverExecutable) {
args = append(args, "--unmask-signals")
}
args = append(args, "-F", "-R", fmt.Sprintf("127.0.0.1:%d", listener.Addr().(*net.TCPAddr).Port), "--")
args = append(args, cmd...)
isDebugserver = true
process = commandLogger(debugserverExecutable, args...)
} else {
if _, err = exec.LookPath("lldb-server"); err != nil {
return nil, &ErrBackendUnavailable{}
}
port = unusedPort()
args := make([]string, 0, len(cmd)+3)
2020-03-18 22:45:29 +00:00
args = append(args, "gdbserver", port, "--")
args = append(args, cmd...)
process = commandLogger("lldb-server", args...)
}
if logflags.LLDBServerOutput() || logflags.GdbWire() || foreground || hasRedirects {
process.Stdout = os.Stdout
process.Stderr = os.Stderr
}
if foreground || hasRedirects {
if isatty.IsTerminal(os.Stdin.Fd()) {
foregroundSignalsIgnore()
}
process.Stdin = os.Stdin
}
if wd != "" {
process.Dir = wd
}
if isatty.IsTerminal(os.Stdin.Fd()) {
process.SysProcAttr = sysProcAttr(foreground)
}
if runtime.GOOS == "darwin" {
process.Env = proc.DisableAsyncPreemptEnv()
// Filter out DYLD_INSERT_LIBRARIES on Darwin.
// This is needed since macOS Ventura, loading custom dylib into debugserver
// using DYLD_INSERT_LIBRARIES leads to a crash.
// This is unlike other protected processes, where they just strip it out.
env := make([]string, 0, len(process.Env))
for _, v := range process.Env {
if !strings.HasPrefix(v, "DYLD_INSERT_LIBRARIES") {
env = append(env, v)
}
}
process.Env = env
}
if err = process.Start(); err != nil {
return nil, err
}
p := newProcess(process.Process)
p.conn.isDebugserver = isDebugserver
var grp *proc.TargetGroup
if listener != nil {
grp, err = p.Listen(listener, cmd[0], strings.Join(cmd, " "), 0, debugInfoDirs, proc.StopLaunched)
} else {
grp, err = p.Dial(port, cmd[0], strings.Join(cmd, " "), 0, debugInfoDirs, proc.StopLaunched)
}
if p.conn.pid != 0 && foreground && isatty.IsTerminal(os.Stdin.Fd()) {
// Make the target process the controlling process of the tty if it is a foreground process.
err := tcsetpgrp(os.Stdin.Fd(), p.conn.pid)
if err != nil {
logflags.DebuggerLogger().Errorf("could not set controlling process: %v", err)
}
}
return grp, err
}
// LLDBAttach starts an instance of lldb-server and connects to it, asking
// it to attach to the specified pid.
// Path is path to the target's executable, path only needs to be specified
// for some stubs that do not provide an automated way of determining it
// (for example debugserver).
func LLDBAttach(pid int, path string, waitFor *proc.WaitFor, debugInfoDirs []string) (*proc.TargetGroup, error) {
if runtime.GOOS == "windows" {
return nil, ErrUnsupportedOS
}
if err := macutil.CheckRosetta(); err != nil {
return nil, err
}
var (
isDebugserver bool
process *exec.Cmd
listener net.Listener
port string
err error
)
if debugserverExecutable := getDebugServerAbsolutePath(); debugserverExecutable != "" {
isDebugserver = true
listener, err = net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return nil, err
}
args := []string{"-R", fmt.Sprintf("127.0.0.1:%d", listener.Addr().(*net.TCPAddr).Port)}
if waitFor.Valid() {
duration := int(waitFor.Duration.Seconds())
if duration == 0 && waitFor.Duration != 0 {
// If duration is below the (second) resolution of debugserver pass 1
// second (0 means infinite).
duration = 1
}
args = append(args, "--waitfor="+waitFor.Name, fmt.Sprintf("--waitfor-interval=%d", waitFor.Interval.Microseconds()), fmt.Sprintf("--waitfor-duration=%d", duration))
} else {
args = append(args, "--attach="+strconv.Itoa(pid))
}
if canUnmaskSignals(debugserverExecutable) {
args = append(args, "--unmask-signals")
}
process = commandLogger(debugserverExecutable, args...)
} else {
if waitFor.Valid() {
return nil, proc.ErrWaitForNotImplemented
}
if _, err = exec.LookPath("lldb-server"); err != nil {
return nil, &ErrBackendUnavailable{}
}
port = unusedPort()
process = commandLogger("lldb-server", "gdbserver", "--attach", strconv.Itoa(pid), port)
}
process.Stdout = os.Stdout
process.Stderr = os.Stderr
process.SysProcAttr = sysProcAttr(false)
if err = process.Start(); err != nil {
return nil, err
}
p := newProcess(process.Process)
p.conn.isDebugserver = isDebugserver
var grp *proc.TargetGroup
if listener != nil {
grp, err = p.Listen(listener, path, "", pid, debugInfoDirs, proc.StopAttached)
} else {
grp, err = p.Dial(port, path, "", pid, debugInfoDirs, proc.StopAttached)
}
return grp, err
}
// EntryPoint will return the process entry point address, useful for
// debugging PIEs.
func (p *gdbProcess) EntryPoint() (uint64, error) {
var entryPoint uint64
if p.bi.GOOS == "darwin" {
// There is no auxv on darwin, however, we can get the location of the mach-o
// header from the debugserver by going through the loaded libraries, which includes
// the exe itself
images, _ := p.conn.getLoadedDynamicLibraries()
for _, image := range images {
if image.MachHeader.FileType == macho.TypeExec {
// This is a bit hacky. This is technically not the entrypoint,
// but rather we use the variable to points at the mach-o header,
// so we can get the offset in bininfo
entryPoint = image.LoadAddress
break
}
}
} else if auxv, err := p.conn.readAuxv(); err == nil {
// If we can't read the auxiliary vector it just means it's not supported
// by the OS or by the stub. If we are debugging a PIE and the entry point
// is needed proc.LoadBinaryInfo will complain about it.
entryPoint = linutil.EntryPointFromAuxv(auxv, p.BinInfo().Arch.PtrSize())
}
return entryPoint, nil
}
// initialize uses qProcessInfo to load the inferior's PID and
// executable path. This command is not supported by all stubs and not all
// stubs will report both the PID and executable path.
func (p *gdbProcess) initialize(path, cmdline string, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) {
var err error
if path == "" {
// If we are attaching to a running process and the user didn't specify
// the executable file manually we must ask the stub for it.
// We support both qXfer:exec-file:read:: (the gdb way) and calling
// qProcessInfo (the lldb way).
// Unfortunately debugserver on macOS supports neither.
path, err = p.conn.readExecFile()
if err != nil {
if isProtocolErrorUnsupported(err) {
_, path, err = queryProcessInfo(p, p.Pid())
if err != nil {
p.conn.conn.Close()
return nil, err
}
} else {
p.conn.conn.Close()
return nil, fmt.Errorf("could not determine executable path: %v", err)
}
}
}
if path == "" {
// try using jGetLoadedDynamicLibrariesInfos which is the only way to do
// this supported on debugserver (but only on macOS >= 12.10)
images, _ := p.conn.getLoadedDynamicLibraries()
for _, image := range images {
if image.MachHeader.FileType == macho.TypeExec {
path = image.Pathname
break
}
}
}
err = p.updateThreadList(&threadUpdater{p: p}, nil)
if err != nil {
p.conn.conn.Close()
p.bi.Close()
return nil, err
}
p.clearThreadSignals()
if p.conn.pid <= 0 {
p.conn.pid, _, err = queryProcessInfo(p, 0)
if err != nil && !isProtocolErrorUnsupported(err) {
p.conn.conn.Close()
p.bi.Close()
return nil, err
}
}
grp, addTarget := proc.NewGroup(p, proc.NewTargetGroupConfig{
DebugInfoDirs: debugInfoDirs,
DisableAsyncPreempt: runtime.GOOS == "darwin",
StopReason: stopReason,
CanDump: runtime.GOOS == "darwin",
})
_, err = addTarget(p, p.conn.pid, p.currentThread, path, stopReason, cmdline)
if err != nil {
p.Detach(p.conn.pid, true)
return nil, err
}
return grp, nil
}
func queryProcessInfo(p *gdbProcess, pid int) (int, string, error) {
pi, err := p.conn.queryProcessInfo(pid)
if err != nil {
return 0, "", err
}
if pid == 0 {
n, _ := strconv.ParseUint(pi["pid"], 16, 64)
pid = int(n)
}
return pid, pi["name"], nil
}
// BinInfo returns information on the binary.
func (p *gdbProcess) BinInfo() *proc.BinaryInfo {
return p.bi
}
// Recorded returns whether or not we are debugging
// a recorded "traced" program.
func (p *gdbProcess) Recorded() (bool, string) {
return p.tracedir != "", p.tracedir
}
// Pid returns the process ID.
func (p *gdbProcess) Pid() int {
return p.conn.pid
}
// Valid returns true if we are not detached
// and the process has not exited.
func (p *gdbProcess) Valid() (bool, error) {
if p.detached {
return false, proc.ErrProcessDetached
}
if p.exited {
return false, proc.ErrProcessExited{Pid: p.Pid()}
}
if p.almostExited && p.conn.direction == proc.Forward {
return false, proc.ErrProcessExited{Pid: p.Pid()}
}
return true, nil
}
// FindThread returns the thread with the given ID.
func (p *gdbProcess) FindThread(threadID int) (proc.Thread, bool) {
thread, ok := p.threads[threadID]
return thread, ok
}
// ThreadList returns all threads in the process.
func (p *gdbProcess) ThreadList() []proc.Thread {
r := make([]proc.Thread, 0, len(p.threads))
for _, thread := range p.threads {
r = append(r, thread)
}
return r
}
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
// Memory returns the process memory.
func (p *gdbProcess) Memory() proc.MemoryReadWriter {
return p
}
const (
interruptSignal = 0x2
breakpointSignal = 0x5
faultSignal = 0xb
childSignal = 0x11
stopSignal = 0x13
_SIGILL = 0x4
_SIGFPE = 0x8
_SIGKILL = 0x9
debugServerTargetExcBadAccess = 0x91
debugServerTargetExcBadInstruction = 0x92
debugServerTargetExcArithmetic = 0x93
debugServerTargetExcEmulation = 0x94
debugServerTargetExcSoftware = 0x95
debugServerTargetExcBreakpoint = 0x96
)
func (p *gdbProcess) ContinueOnce(cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
if p.exited {
return nil, proc.StopExited, proc.ErrProcessExited{Pid: p.conn.pid}
}
if p.almostExited {
if p.conn.direction == proc.Forward {
return nil, proc.StopExited, proc.ErrProcessExited{Pid: p.conn.pid}
}
p.almostExited = false
}
if p.conn.direction == proc.Forward {
// step threads stopped at any breakpoint over their breakpoint
for _, thread := range p.threads {
// Do not single step over a breakpoint if it is a watchpoint. The PC will already have advanced and we
// won't experience the same stutter effect as with a breakpoint. Also, there is a bug in certain versions
// of the MacOS mach kernel where single stepping when we have hardware watchpoints set will cause the
// kernel to send a spurious mach exception.
// See: https://github.com/llvm/llvm-project/blob/b9f2c16b50f68c978e90190f46a7c0db3f39e98c/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp#L814
// TODO(deparker): We can skip single stepping in the case of thread.BinInfo().Arch.BreakInstrMovesPC().
if thread.CurrentBreakpoint.Breakpoint != nil && thread.CurrentBreakpoint.WatchType == 0 {
if err := thread.stepInstruction(); err != nil {
return nil, proc.StopUnknown, err
}
}
}
}
for _, th := range p.threads {
th.clearBreakpointState()
}
p.setCtrlC(cctx, false)
// resume all threads
var threadID string
var trapthread *gdbThread
var tu = threadUpdater{p: p}
var atstart bool
var trapThreadRegs map[uint64]uint64
continueLoop:
for {
tu.Reset()
sp, err := p.conn.resume(cctx, p.threads, &tu)
threadID = sp.threadID
if err != nil {
if _, exited := err.(proc.ErrProcessExited); exited {
p.exited = true
return nil, proc.StopExited, err
}
return nil, proc.StopUnknown, err
}
// For stubs that support qThreadStopInfo updateThreadList will
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
// find out the reason why each thread stopped.
proc/gdbserver: ignore spurious stop packet from debugserver (#3021) When we send an interrupt request to debugserver we, sometimes, get one extra spurious stop packet back. This stop packet gets interpreted as a response to a qThreadStopInfo request we make causing the protocol to become desynchronized for a while, until eventually some kind of error appears. Here's an example of this problem, distilled from issue #3013: 1 <- $vCont;c#a8 2 <- interrupt 3 -> $T05thread:12efb47;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... 4 <- $qThreadStopInfo12efb8e#28 5 -> $T05thread:12efb47;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... 6 <- $qThreadStopInfo12efb8f#29 7 -> $T00thread:12efb8e;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... 8 <- $qThreadStopInfo12efb90#f4 9 -> $T00thread:12efb8f;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... 10 <- $qThreadStopInfo12efb91#f5 11 -> $T00thread:12efb90;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... 12 <- $qThreadStopInfo12efb47#f6 13 -> $T00thread:12efb91;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... 14 <- $qThreadStopInfo12efb8d#27 15 -> $T05thread:12efb47;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... 16 <- $p0;thread:12efb8e;#f5 17 -> $T00thread:12efb8d;threads:12efb47,12efb8d,12efb8e,12efb8f,12efb90,12efb91;thread-pcs:10abe83,7ff81b20e2be,7ff81b20e3ea,... response (3) is interpreted as the response to the vCont request at (1). We then make a qThreadStopInfo request (4) and receive a stop packet in response (5). Packet (5) is interpreted as the response to (4) but it actually isn't, note how the thread ID is different, packet (5) is actually a spurious stop packet sent by debug server. From response (5) onward the protocol is desynchronized, none of the response we process are actually the response to the preceding request. This eventually causes a failure at packet (17) which debugserver sent as the response to request (14) but we interpret as the response to (16). Fixes #3013
2022-06-08 17:56:50 +00:00
// NOTE: because debugserver will sometimes send two stop packets after a
// continue it is important that this is the very first thing we do after
// resume(). See comment in threadStopInfo for an explanation.
p.updateThreadList(&tu, sp.jstopInfo)
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
trapthread = p.findThreadByStrID(threadID)
if trapthread != nil && !p.threadStopInfo {
// For stubs that do not support qThreadStopInfo we manually set the
// reason the thread returned by resume() stopped.
trapthread.sig = sp.sig
trapthread.watchAddr = sp.watchAddr
trapthread.watchReg = sp.watchReg
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
}
var shouldStop, shouldExitErr bool
trapthread, atstart, shouldStop, shouldExitErr = p.handleThreadSignals(cctx, trapthread)
if shouldExitErr {
p.almostExited = true
return nil, proc.StopExited, proc.ErrProcessExited{Pid: p.conn.pid}
}
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
if shouldStop {
trapThreadRegs = sp.regs
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
break continueLoop
}
}
p.clearThreadRegisters()
// TODO(deparker): Can we support this optimization for the RR backend?
if p.conn.isDebugserver {
trapthread.reloadRegisters(trapThreadRegs)
}
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
stopReason := proc.StopUnknown
if atstart {
stopReason = proc.StopLaunched
}
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
if p.BinInfo().GOOS == "linux" {
if err := linutil.ElfUpdateSharedObjects(p); err != nil {
return nil, stopReason, err
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
}
}
if err := p.setCurrentBreakpoints(); err != nil {
return nil, stopReason, err
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
}
if trapthread == nil {
return nil, stopReason, fmt.Errorf("could not find thread %s", threadID)
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
}
err := machTargetExcToError(trapthread.sig)
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
if err != nil {
// the signals that are reported here can not be propagated back to the target process.
trapthread.sig = 0
}
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
p.currentThread = trapthread
return trapthread, stopReason, err
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
}
func (p *gdbProcess) findThreadByStrID(threadID string) *gdbThread {
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
for _, thread := range p.threads {
if thread.strID == threadID {
return thread
}
}
return nil
}
// handleThreadSignals looks at the signals received by each thread and
// decides which ones to mask and which ones to propagate back to the target
// and returns true if we should stop execution in response to one of the
// signals and return control to the user.
// Adjusts trapthread to a thread that we actually want to stop at.
func (p *gdbProcess) handleThreadSignals(cctx *proc.ContinueOnceContext, trapthread *gdbThread) (trapthreadOut *gdbThread, atstart, shouldStop, shouldExitErr bool) {
var trapthreadCandidate *gdbThread
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
for _, th := range p.threads {
isStopSignal := false
// 0x5 is always a breakpoint, a manual stop either manifests as 0x13
// (lldb), 0x11 (debugserver) or 0x2 (gdbserver).
// Since 0x2 could also be produced by the user
// pressing ^C (in which case it should be passed to the inferior) we need
// the ctrlC flag to know that we are the originators.
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
switch th.sig {
case interruptSignal: // interrupt
if p.getCtrlC(cctx) {
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
isStopSignal = true
}
case breakpointSignal: // breakpoint
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
isStopSignal = true
case childSignal: // stop on debugserver but SIGCHLD on lldb-server/linux
if p.conn.isDebugserver {
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
isStopSignal = true
}
case stopSignal: // stop
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
isStopSignal = true
case _SIGKILL:
if p.tracedir != "" {
// RR will send a synthetic SIGKILL packet right before the program
// exits, even if the program exited normally.
// Treat this signal as if the process had exited because right after
// this it is still possible to set breakpoints and rewind the process.
shouldExitErr = true
isStopSignal = true
}
// The following are fake BSD-style signals sent by debugserver
// Unfortunately debugserver can not convert them into signals for the
// process so we must stop here.
case debugServerTargetExcBadAccess, debugServerTargetExcBadInstruction, debugServerTargetExcArithmetic, debugServerTargetExcEmulation, debugServerTargetExcSoftware, debugServerTargetExcBreakpoint:
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
trapthreadCandidate = th
shouldStop = true
// Signal 0 is returned by rr when it reaches the start of the process
// in backward continue mode.
case 0:
if p.conn.direction == proc.Backward && th == trapthread {
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
isStopSignal = true
atstart = true
}
default:
// any other signal is always propagated to inferior
}
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
if isStopSignal {
if trapthreadCandidate == nil {
trapthreadCandidate = th
}
th.sig = 0
shouldStop = true
}
}
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
if (trapthread == nil || trapthread.sig != 0) && trapthreadCandidate != nil {
// proc.Continue wants us to return one of the threads that we should stop
// at, if the thread returned by vCont received a signal that we want to
// propagate back to the target thread but there were also other threads
// that we wish to stop at we should pick one of those.
trapthread = trapthreadCandidate
}
if p.getCtrlC(cctx) || cctx.GetManualStopRequested() {
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
// If we request an interrupt and a target thread simultaneously receives
// an unrelated signal debugserver will discard our interrupt request and
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
// report the signal but we should stop anyway.
shouldStop = true
}
return trapthread, atstart, shouldStop, shouldExitErr
}
// RequestManualStop will attempt to stop the process
2020-08-24 17:19:50 +00:00
// without a breakpoint or signal having been received.
func (p *gdbProcess) RequestManualStop(cctx *proc.ContinueOnceContext) error {
if !p.conn.running {
return nil
}
p.ctrlC = true
return p.conn.sendCtrlC()
}
func (p *gdbProcess) setCtrlC(cctx *proc.ContinueOnceContext, v bool) {
cctx.StopMu.Lock()
p.ctrlC = v
cctx.StopMu.Unlock()
}
func (p *gdbProcess) getCtrlC(cctx *proc.ContinueOnceContext) bool {
cctx.StopMu.Lock()
defer cctx.StopMu.Unlock()
return p.ctrlC
}
// Detach will detach from the target process,
// if 'kill' is true it will also kill the process.
// The _pid argument is unused as follow exec
// mode is not implemented with this backend.
func (p *gdbProcess) Detach(_pid int, kill bool) error {
if kill && !p.exited {
err := p.conn.kill()
if err != nil {
if _, exited := err.(proc.ErrProcessExited); !exited {
return err
}
p.exited = true
}
}
if !p.exited {
if err := p.conn.detach(); err != nil {
return err
}
}
if p.process != nil {
p.process.Kill()
<-p.waitChan
p.process = nil
}
p.detached = true
return nil
}
func (p *gdbProcess) Close() error {
if p.onDetach != nil {
p.onDetach()
}
return p.bi.Close()
}
// Restart will restart the process from the given position.
func (p *gdbProcess) Restart(cctx *proc.ContinueOnceContext, pos string) (proc.Thread, error) {
if p.tracedir == "" {
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
return nil, proc.ErrNotRecorded
}
p.exited = false
p.almostExited = false
for _, th := range p.threads {
th.clearBreakpointState()
}
p.ctrlC = false
err := p.conn.restart(pos)
if err != nil {
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
return nil, err
}
// for some reason we have to send a vCont;c after a vRun to make rr behave
// properly, because that's what gdb does.
_, err = p.conn.resume(cctx, nil, nil)
if err != nil {
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
return nil, err
}
err = p.updateThreadList(&threadUpdater{p: p}, nil)
if err != nil {
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
return nil, err
}
p.clearThreadSignals()
p.clearThreadRegisters()
for _, bp := range p.breakpoints.M {
p.WriteBreakpoint(bp)
}
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
return p.currentThread, p.setCurrentBreakpoints()
}
// When executes the 'when' command for the Mozilla RR backend.
// This command will return rr's internal event number.
func (p *gdbProcess) When() (string, error) {
if p.tracedir == "" {
return "", proc.ErrNotRecorded
}
event, err := p.conn.qRRCmd("when")
if err != nil {
return "", err
}
return strings.TrimSpace(event), nil
}
const (
checkpointPrefix = "Checkpoint "
)
// Checkpoint creates a checkpoint from which you can restart the program.
func (p *gdbProcess) Checkpoint(where string) (int, error) {
if p.tracedir == "" {
return -1, proc.ErrNotRecorded
}
resp, err := p.conn.qRRCmd("checkpoint", where)
if err != nil {
return -1, err
}
if !strings.HasPrefix(resp, checkpointPrefix) {
return -1, fmt.Errorf("can not parse checkpoint response %q", resp)
}
idstr := resp[len(checkpointPrefix):]
space := strings.Index(idstr, " ")
if space < 0 {
return -1, fmt.Errorf("can not parse checkpoint response %q", resp)
}
idstr = idstr[:space]
cpid, err := strconv.Atoi(idstr)
if err != nil {
return -1, err
}
return cpid, nil
}
// Checkpoints returns a list of all checkpoints set.
func (p *gdbProcess) Checkpoints() ([]proc.Checkpoint, error) {
if p.tracedir == "" {
return nil, proc.ErrNotRecorded
}
resp, err := p.conn.qRRCmd("info checkpoints")
if err != nil {
return nil, err
}
lines := strings.Split(resp, "\n")
r := make([]proc.Checkpoint, 0, len(lines)-1)
for _, line := range lines[1:] {
if line == "" {
continue
}
fields := strings.Split(line, "\t")
if len(fields) != 3 {
return nil, fmt.Errorf("can not parse \"info checkpoints\" output line %q", line)
}
cpid, err := strconv.Atoi(fields[0])
if err != nil {
return nil, fmt.Errorf("can not parse \"info checkpoints\" output line %q: %v", line, err)
}
2019-07-01 18:10:34 +00:00
r = append(r, proc.Checkpoint{ID: cpid, When: fields[1], Where: fields[2]})
}
return r, nil
}
const deleteCheckpointPrefix = "Deleted checkpoint "
// ClearCheckpoint clears the checkpoint for the given ID.
func (p *gdbProcess) ClearCheckpoint(id int) error {
if p.tracedir == "" {
return proc.ErrNotRecorded
}
resp, err := p.conn.qRRCmd("delete checkpoint", strconv.Itoa(id))
if err != nil {
return err
}
if !strings.HasPrefix(resp, deleteCheckpointPrefix) {
return errors.New(resp)
}
return nil
}
// ChangeDirection sets whether to run the program forwards or in reverse execution.
func (p *gdbProcess) ChangeDirection(dir proc.Direction) error {
if p.tracedir == "" {
if dir != proc.Forward {
return proc.ErrNotRecorded
}
return nil
}
if p.conn.conn == nil {
return proc.ErrProcessExited{Pid: p.conn.pid}
}
if p.conn.direction == dir {
return nil
}
if p.Breakpoints().HasSteppingBreakpoints() {
return ErrDirChange
}
p.conn.direction = dir
return nil
}
// StartCallInjection notifies the backend that we are about to inject a function call.
func (p *gdbProcess) StartCallInjection() (func(), error) {
if p.tracedir == "" {
return func() {}, nil
}
if p.conn.conn == nil {
return nil, proc.ErrProcessExited{Pid: p.conn.pid}
}
if p.conn.direction != proc.Forward {
return nil, ErrStartCallInjectionBackwards
}
// Normally it's impossible to inject function calls in a recorded target
// because the sequence of instructions that the target will execute is
// predetermined.
// RR however allows this in a "diversion". When a diversion is started rr
// takes the current state of the process and runs it forward as a normal
// process, not following the recording.
// The gdb serial protocol does not have a way to start a diversion and gdb
// (the main frontend of rr) does not know how to do it. Instead a
// diversion is started by reading siginfo, because that's the first
// request gdb does when starting a function call injection.
_, err := p.conn.qXfer("siginfo", "", true)
if err != nil {
return nil, err
}
return func() {
_ = p.conn.qXferWrite("siginfo", "") // rr always returns an error for qXfer:siginfo:write... even though it works
}, nil
}
// GetDirection returns the current direction of execution.
func (p *gdbProcess) GetDirection() proc.Direction {
return p.conn.direction
}
// Breakpoints returns the list of breakpoints currently set.
func (p *gdbProcess) Breakpoints() *proc.BreakpointMap {
return &p.breakpoints
}
// FindBreakpoint returns the breakpoint at the given address.
func (p *gdbProcess) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
// Directly use addr to lookup breakpoint.
if bp, ok := p.breakpoints.M[pc]; ok {
return bp, true
}
return nil, false
}
func watchTypeToBreakpointType(wtype proc.WatchType) breakpointType {
switch {
case wtype.Read() && wtype.Write():
return accessWatchpoint
case wtype.Write():
return writeWatchpoint
case wtype.Read():
return readWatchpoint
default:
return swBreakpoint
}
}
func (p *gdbProcess) WriteBreakpoint(bp *proc.Breakpoint) error {
kind := p.breakpointKind
if bp.WatchType != 0 {
kind = bp.WatchType.Size()
}
return p.conn.setBreakpoint(bp.Addr, watchTypeToBreakpointType(bp.WatchType), kind)
}
func (p *gdbProcess) EraseBreakpoint(bp *proc.Breakpoint) error {
kind := p.breakpointKind
if bp.WatchType != 0 {
kind = bp.WatchType.Size()
}
return p.conn.clearBreakpoint(bp.Addr, watchTypeToBreakpointType(bp.WatchType), kind)
}
// FollowExec enables (or disables) follow exec mode
func (p *gdbProcess) FollowExec(bool) error {
return errors.New("follow exec not supported")
}
type threadUpdater struct {
p *gdbProcess
seen map[int]bool
done bool
}
func (tu *threadUpdater) Reset() {
tu.done = false
tu.seen = nil
}
func (tu *threadUpdater) Add(threads []string) error {
if tu.done {
panic("threadUpdater: Add after Finish")
}
if tu.seen == nil {
tu.seen = map[int]bool{}
}
for _, threadID := range threads {
b := threadID
if period := strings.Index(b, "."); period >= 0 {
b = b[period+1:]
}
n, err := strconv.ParseUint(b, 16, 32)
if err != nil {
return &GdbMalformedThreadIDError{threadID}
}
tid := int(n)
tu.seen[tid] = true
if _, found := tu.p.threads[tid]; !found {
tu.p.threads[tid] = &gdbThread{ID: tid, strID: threadID, p: tu.p}
}
}
return nil
}
func (tu *threadUpdater) Finish() {
tu.done = true
for threadID := range tu.p.threads {
_, threadSeen := tu.seen[threadID]
if threadSeen {
continue
}
delete(tu.p.threads, threadID)
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
if tu.p.currentThread != nil && tu.p.currentThread.ID == threadID {
tu.p.currentThread = nil
}
}
if tu.p.currentThread != nil {
if _, exists := tu.p.threads[tu.p.currentThread.ID]; !exists {
// current thread was removed
tu.p.currentThread = nil
}
}
if tu.p.currentThread == nil {
for _, thread := range tu.p.threads {
tu.p.currentThread = thread
break
}
}
}
// updateThreadList retrieves the list of inferior threads from
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
// the stub and passes it to threadUpdater.
// Some stubs will return the list of running threads in the stop packet, if
// this happens the threadUpdater will know that we have already updated the
// thread list and the first step of updateThreadList will be skipped.
func (p *gdbProcess) updateThreadList(tu *threadUpdater, jstopInfo map[int]stopPacket) error {
if !tu.done {
first := true
for {
threads, err := p.conn.queryThreads(first)
if err != nil {
return err
}
if len(threads) == 0 {
break
}
first = false
if err := tu.Add(threads); err != nil {
return err
}
}
tu.Finish()
}
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
for _, th := range p.threads {
queryThreadInfo := true
if jstopInfo != nil {
// TODO(derekparker): Use jstopInfo directly, if present, instead of
// issuing another stop info request.
_, queryThreadInfo = jstopInfo[th.ID]
}
if p.threadStopInfo && queryThreadInfo {
sp, err := p.conn.threadStopInfo(th.strID)
if err != nil {
if isProtocolErrorUnsupported(err) {
p.threadStopInfo = false
break
}
return err
}
th.setbp = (sp.reason == "breakpoint" || (sp.reason == "" && sp.sig == breakpointSignal) || (sp.watchAddr > 0) || (sp.watchReg >= 0))
th.sig = sp.sig
th.watchAddr = sp.watchAddr
th.watchReg = sp.watchReg
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
} else {
th.clearBreakpointState()
}
}
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
return nil
}
// clearThreadRegisters clears the memoized thread register state.
func (p *gdbProcess) clearThreadRegisters() {
for _, thread := range p.threads {
thread.regs.regs = nil
}
}
func (p *gdbProcess) clearThreadSignals() {
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
for _, th := range p.threads {
th.sig = 0
}
}
func (p *gdbProcess) setCurrentBreakpoints() error {
if p.threadStopInfo {
for _, th := range p.threads {
if th.setbp {
err := th.SetCurrentBreakpoint(true)
if err != nil {
return err
}
}
}
}
if !p.threadStopInfo {
for _, th := range p.threads {
if th.CurrentBreakpoint.Breakpoint == nil {
err := th.SetCurrentBreakpoint(true)
if err != nil {
return err
}
}
}
}
return nil
}
// ReadMemory will read into 'data' memory at the address provided.
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
func (p *gdbProcess) ReadMemory(data []byte, addr uint64) (n int, err error) {
err = p.conn.readMemory(data, addr)
if err != nil {
return 0, err
}
return len(data), nil
}
// WriteMemory will write into the memory at 'addr' the data provided.
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
func (p *gdbProcess) WriteMemory(addr uint64, data []byte) (written int, err error) {
return p.conn.writeMemory(addr, data)
}
func (p *gdbProcess) StepInstruction(threadID int) error {
return p.threads[threadID].stepInstruction()
}
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
func (t *gdbThread) ProcessMemory() proc.MemoryReadWriter {
return t.p
}
// Location returns the current location of this thread.
func (t *gdbThread) Location() (*proc.Location, error) {
regs, err := t.Registers()
if err != nil {
return nil, err
}
if pcreg, ok := regs.(*gdbRegisters).regs[regs.(*gdbRegisters).regnames.PC]; !ok {
t.p.conn.log.Errorf("thread %d could not find RIP register", t.ID)
} else if len(pcreg.value) < t.p.bi.Arch.PtrSize() {
t.p.conn.log.Errorf("thread %d bad length for RIP register: %d", t.ID, len(pcreg.value))
}
pc := regs.PC()
f, l, fn := t.p.bi.PCToLine(pc)
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
}
// Breakpoint returns the current active breakpoint for this thread.
func (t *gdbThread) Breakpoint() *proc.BreakpointState {
return &t.CurrentBreakpoint
}
// ThreadID returns this threads ID.
func (t *gdbThread) ThreadID() int {
return t.ID
}
// Registers returns the CPU registers for this thread.
func (t *gdbThread) Registers() (proc.Registers, error) {
if t.regs.regs == nil {
if err := t.reloadRegisters(nil); err != nil {
return nil, err
}
}
return &t.regs, nil
}
// RestoreRegisters will set the CPU registers the value of those provided.
func (t *gdbThread) RestoreRegisters(savedRegs proc.Registers) error {
copy(t.regs.buf, savedRegs.(*gdbRegisters).buf)
return t.writeRegisters()
}
// BinInfo will return information on the binary being debugged.
func (t *gdbThread) BinInfo() *proc.BinaryInfo {
return t.p.bi
}
// Common returns common information across Process implementations.
func (t *gdbThread) Common() *proc.CommonThread {
return &t.common
}
// StepInstruction will step exactly 1 CPU instruction.
func (t *gdbThread) stepInstruction() error {
pc := t.regs.PC()
if bp, atbp := t.p.breakpoints.M[pc]; atbp && bp.WatchType == 0 {
err := t.p.conn.clearBreakpoint(pc, swBreakpoint, t.p.breakpointKind)
if err != nil {
return err
}
defer t.p.conn.setBreakpoint(pc, swBreakpoint, t.p.breakpointKind)
}
// Reset thread registers so the next call to
// Thread.Registers will not be cached.
t.regs.regs = nil
return t.p.conn.step(t, &threadUpdater{p: t.p}, false)
}
// SoftExc returns true if this thread received a software exception during the last resume.
func (t *gdbThread) SoftExc() bool {
return t.setbp
}
// Blocked returns true if the thread is blocked in runtime or kernel code.
func (t *gdbThread) Blocked() bool {
regs, err := t.Registers()
if err != nil {
return false
}
pc := regs.PC()
f, ln, fn := t.BinInfo().PCToLine(pc)
if fn == nil {
if f == "" && ln == 0 {
return true
}
return false
}
switch fn.Name {
case "runtime.futex", "runtime.usleep", "runtime.clone":
return true
case "runtime.kevent":
return true
case "runtime.mach_semaphore_wait", "runtime.mach_semaphore_timedwait":
return true
default:
return strings.HasPrefix(fn.Name, "syscall.Syscall") || strings.HasPrefix(fn.Name, "syscall.RawSyscall")
}
}
// loadGInstr returns the correct MOV instruction for the current
// OS/architecture that can be executed to load the address of G from an
// inferior's thread.
func (p *gdbProcess) loadGInstr() ([]byte, error) {
var op []byte
switch p.bi.GOOS {
case "windows", "darwin", "freebsd":
// mov rcx, QWORD PTR gs:{uint32(off)}
op = []byte{0x65, 0x48, 0x8b, 0x0c, 0x25}
case "linux":
// mov rcx,QWORD PTR fs:{uint32(off)}
op = []byte{0x64, 0x48, 0x8B, 0x0C, 0x25}
default:
panic("unsupported operating system attempting to find Goroutine on Thread")
}
offset, err := p.bi.GStructOffset(p.Memory())
if err != nil {
return nil, err
}
buf := &bytes.Buffer{}
buf.Write(op)
binary.Write(buf, binary.LittleEndian, uint32(offset))
return buf.Bytes(), nil
}
func (p *gdbProcess) MemoryMap() ([]proc.MemoryMapEntry, error) {
r := []proc.MemoryMapEntry{}
addr := uint64(0)
for addr != ^uint64(0) {
mri, err := p.conn.memoryRegionInfo(addr)
if err != nil {
return nil, err
}
if addr+mri.size <= addr {
return nil, errors.New("qMemoryRegionInfo response wrapped around the address space or stuck")
}
if mri.permissions != "" {
var mme proc.MemoryMapEntry
mme.Addr = addr
mme.Size = mri.size
mme.Read = strings.Contains(mri.permissions, "r")
mme.Write = strings.Contains(mri.permissions, "w")
mme.Exec = strings.Contains(mri.permissions, "x")
r = append(r, mme)
}
addr += mri.size
}
return r, nil
}
func (p *gdbProcess) DumpProcessNotes(notes []elfwriter.Note, threadDone func()) (threadsDone bool, out []elfwriter.Note, err error) {
return false, notes, nil
}
func (regs *gdbRegisters) init(regsInfo []gdbRegisterInfo, arch *proc.Arch, regnames *gdbRegnames) {
regs.arch = arch
regs.regnames = regnames
regs.regs = make(map[string]gdbRegister)
regs.regsInfo = regsInfo
regsz := 0
for _, reginfo := range regsInfo {
if endoff := reginfo.Offset + (reginfo.Bitsize / 8); endoff > regsz {
regsz = endoff
}
}
regs.buf = make([]byte, regsz)
for _, reginfo := range regsInfo {
regs.regs[reginfo.Name] = regs.gdbRegisterNew(&reginfo)
}
}
func (regs *gdbRegisters) gdbRegisterNew(reginfo *gdbRegisterInfo) gdbRegister {
return gdbRegister{regnum: reginfo.Regnum, value: regs.buf[reginfo.Offset : reginfo.Offset+reginfo.Bitsize/8], ignoreOnWrite: reginfo.ignoreOnWrite}
}
// reloadRegisters loads the current value of the thread's registers.
// It will also load the address of the thread's G.
// Loading the address of G can be done in one of two ways reloadGAlloc, if
// the stub can allocate memory, or reloadGAtPC, if the stub can't.
func (t *gdbThread) reloadRegisters(regs map[uint64]uint64) error {
if t.regs.regs == nil {
t.regs.init(t.p.conn.regsInfo, t.p.bi.Arch, t.p.regnames)
}
if regs == nil {
if t.p.gcmdok {
if err := t.p.conn.readRegisters(t.strID, t.regs.buf); err != nil {
gdberr, isProt := err.(*GdbProtocolError)
if isProtocolErrorUnsupported(err) || (t.p.conn.isDebugserver && isProt && gdberr.code == "E74") {
t.p.gcmdok = false
} else {
return err
}
}
}
if !t.p.gcmdok {
for _, reginfo := range t.p.conn.regsInfo {
if err := t.p.conn.readRegister(t.strID, reginfo.Regnum, t.regs.regs[reginfo.Name].value); err != nil {
return err
}
}
}
} else {
for _, r := range t.p.conn.regsInfo {
if val, ok := regs[uint64(r.Regnum)]; ok {
switch r.Bitsize / 8 {
case 8:
binary.BigEndian.PutUint64(t.regs.regs[r.Name].value, val)
case 4:
binary.BigEndian.PutUint32(t.regs.regs[r.Name].value, uint32(val))
}
} else {
if err := t.p.conn.readRegister(t.strID, r.Regnum, t.regs.regs[r.Name].value); err != nil {
return err
}
}
}
}
if t.p.bi.GOOS == "linux" {
if reg, hasFsBase := t.regs.regs[t.p.regnames.FsBase]; hasFsBase {
t.regs.gaddr = 0
t.regs.tls = binary.LittleEndian.Uint64(reg.value)
t.regs.hasgaddr = false
return nil
}
}
if t.p.bi.Arch.Name == "arm64" {
// no need to play around with the GInstr on ARM64 because
// the G addr is stored in a register
t.regs.gaddr = t.regs.byName("x28")
t.regs.hasgaddr = true
t.regs.tls = 0
} else {
if t.p.loadGInstrAddr > 0 {
return t.reloadGAlloc()
}
return t.reloadGAtPC()
}
return nil
}
func (t *gdbThread) writeSomeRegisters(regNames ...string) error {
if t.p.gcmdok {
return t.p.conn.writeRegisters(t.strID, t.regs.buf)
}
for _, regName := range regNames {
if err := t.p.conn.writeRegister(t.strID, t.regs.regs[regName].regnum, t.regs.regs[regName].value); err != nil {
return err
}
}
return nil
}
func (t *gdbThread) writeRegisters() error {
if t.p.gcmdok && t.p._Gcmdok {
err := t.p.conn.writeRegisters(t.strID, t.regs.buf)
if isProtocolErrorUnsupported(err) {
t.p._Gcmdok = false
} else {
return err
}
}
for _, r := range t.regs.regs {
if r.ignoreOnWrite {
continue
}
if err := t.p.conn.writeRegister(t.strID, r.regnum, r.value); err != nil {
return err
}
}
return nil
}
func (t *gdbThread) readSomeRegisters(regNames ...string) error {
if t.p.gcmdok {
return t.p.conn.readRegisters(t.strID, t.regs.buf)
}
for _, regName := range regNames {
err := t.p.conn.readRegister(t.strID, t.regs.regs[regName].regnum, t.regs.regs[regName].value)
if err != nil {
return err
}
}
return nil
}
// reloadGAtPC overwrites the instruction that the thread is stopped at with
// the MOV instruction used to load current G, executes this single
// instruction and then puts everything back the way it was.
func (t *gdbThread) reloadGAtPC() error {
movinstr, err := t.p.loadGInstr()
if err != nil {
return err
}
if t.Blocked() {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
return nil
}
cx := t.regs.CX()
pc := t.regs.PC()
// We are partially replicating the code of GdbserverThread.stepInstruction
// here.
// The reason is that lldb-server has a bug with writing to memory and
// setting/clearing breakpoints to that same memory which we must work
// around by clearing and re-setting the breakpoint in a specific sequence
// with the memory writes.
// Additionally all breakpoints in [pc, pc+len(movinstr)] need to be removed
for addr, bp := range t.p.breakpoints.M {
if bp.WatchType != 0 {
continue
}
if addr >= pc && addr <= pc+uint64(len(movinstr)) {
err := t.p.conn.clearBreakpoint(addr, swBreakpoint, t.p.breakpointKind)
if err != nil {
return err
}
defer t.p.conn.setBreakpoint(addr, swBreakpoint, t.p.breakpointKind)
}
}
savedcode := make([]byte, len(movinstr))
_, err = t.p.ReadMemory(savedcode, pc)
if err != nil {
return err
}
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
_, err = t.p.WriteMemory(pc, movinstr)
if err != nil {
return err
}
defer func() {
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
_, err0 := t.p.WriteMemory(pc, savedcode)
if err == nil {
err = err0
}
t.regs.setPC(pc)
t.regs.setCX(cx)
err1 := t.writeSomeRegisters(t.p.regnames.PC, t.p.regnames.CX)
if err == nil {
err = err1
}
}()
err = t.p.conn.step(t, nil, true)
if err != nil {
if err == errThreadBlocked {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
return nil
}
return err
}
if err := t.readSomeRegisters(t.p.regnames.PC, t.p.regnames.CX); err != nil {
return err
}
t.regs.gaddr = t.regs.CX()
t.regs.hasgaddr = true
return err
}
// reloadGAlloc makes the specified thread execute one instruction stored at
// t.p.loadGInstrAddr then restores the value of the thread's registers.
// t.p.loadGInstrAddr must point to valid memory on the inferior, containing
// a MOV instruction that loads the address of the current G in the RCX
// register.
func (t *gdbThread) reloadGAlloc() error {
if t.Blocked() {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
return nil
}
cx := t.regs.CX()
pc := t.regs.PC()
t.regs.setPC(t.p.loadGInstrAddr)
if err := t.writeSomeRegisters(t.p.regnames.PC); err != nil {
return err
}
var err error
defer func() {
t.regs.setPC(pc)
t.regs.setCX(cx)
err1 := t.writeSomeRegisters(t.p.regnames.PC, t.p.regnames.CX)
if err == nil {
err = err1
}
}()
err = t.p.conn.step(t, nil, true)
if err != nil {
if err == errThreadBlocked {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
return nil
}
return err
}
if err := t.readSomeRegisters(t.p.regnames.CX); err != nil {
return err
}
t.regs.gaddr = t.regs.CX()
t.regs.hasgaddr = true
return err
}
func (t *gdbThread) clearBreakpointState() {
t.setbp = false
t.watchAddr = 0
t.watchReg = -1
t.CurrentBreakpoint.Clear()
}
// SetCurrentBreakpoint will find and set the threads current breakpoint.
func (t *gdbThread) SetCurrentBreakpoint(adjustPC bool) error {
// adjustPC is ignored, it is the stub's responsibility to set the PC
// address correctly after hitting a breakpoint.
t.CurrentBreakpoint.Clear()
// t.watchAddr on certain mach kernel versions could contain the address of a
// software breakpoint, hardcoded breakpoint (e.g. runtime.Breakpoint) or a
// hardware watchpoint. The mach exception produced by the kernel *should* disambiguate
// but it doesn't.
if t.watchAddr > 0 {
t.CurrentBreakpoint.Breakpoint = t.p.Breakpoints().M[t.watchAddr]
if t.CurrentBreakpoint.Breakpoint == nil {
buf := make([]byte, t.BinInfo().Arch.BreakpointSize())
_, err := t.p.ReadMemory(buf, t.watchAddr)
isHardcodedBreakpoint := err == nil && (bytes.Equal(t.BinInfo().Arch.BreakpointInstruction(), buf) || bytes.Equal(t.BinInfo().Arch.AltBreakpointInstruction(), buf))
if isHardcodedBreakpoint {
// This is a hardcoded breakpoint, ignore.
// TODO(deparker): There's an optimization here since we will do this
// again at a higher level to determine if we've stopped at a hardcoded breakpoint.
// We could set some state here so that we don't do extra work later.
t.watchAddr = 0
return nil
}
return fmt.Errorf("could not find watchpoint at address %#x", t.watchAddr)
}
return nil
}
if t.watchReg >= 0 {
for _, bp := range t.p.Breakpoints().M {
if bp.WatchType != 0 && bp.HWBreakIndex == uint8(t.watchReg) {
t.CurrentBreakpoint.Breakpoint = bp
return nil
}
}
}
regs, err := t.Registers()
if err != nil {
return err
}
pc := regs.PC()
if bp, ok := t.p.FindBreakpoint(pc); ok {
if t.regs.PC() != bp.Addr {
if err := t.setPC(bp.Addr); err != nil {
return err
}
}
t.CurrentBreakpoint.Breakpoint = bp
}
return nil
}
func (regs *gdbRegisters) PC() uint64 {
return binary.LittleEndian.Uint64(regs.regs[regs.regnames.PC].value)
}
func (regs *gdbRegisters) setPC(value uint64) {
binary.LittleEndian.PutUint64(regs.regs[regs.regnames.PC].value, value)
}
func (regs *gdbRegisters) SP() uint64 {
return binary.LittleEndian.Uint64(regs.regs[regs.regnames.SP].value)
}
func (regs *gdbRegisters) BP() uint64 {
return binary.LittleEndian.Uint64(regs.regs[regs.regnames.BP].value)
}
func (regs *gdbRegisters) CX() uint64 {
return binary.LittleEndian.Uint64(regs.regs[regs.regnames.CX].value)
}
func (regs *gdbRegisters) setCX(value uint64) {
binary.LittleEndian.PutUint64(regs.regs[regs.regnames.CX].value, value)
}
func (regs *gdbRegisters) TLS() uint64 {
return regs.tls
}
func (regs *gdbRegisters) GAddr() (uint64, bool) {
return regs.gaddr, regs.hasgaddr
}
func (regs *gdbRegisters) LR() uint64 {
return binary.LittleEndian.Uint64(regs.regs["lr"].value)
}
func (regs *gdbRegisters) byName(name string) uint64 {
reg, ok := regs.regs[name]
if !ok {
return 0
}
return binary.LittleEndian.Uint64(reg.value)
}
func (regs *gdbRegisters) FloatLoadError() error {
return nil
}
// SetPC will set the value of the PC register to the given value.
func (t *gdbThread) setPC(pc uint64) error {
_, _ = t.Registers() // Registers must be loaded first
t.regs.setPC(pc)
if t.p.gcmdok {
return t.p.conn.writeRegisters(t.strID, t.regs.buf)
}
reg := t.regs.regs[t.regs.regnames.PC]
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
}
// SetReg will change the value of a list of registers
func (t *gdbThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
regName := registerName(t.p.bi.Arch, regNum)
_, _ = t.Registers() // Registers must be loaded first
gdbreg, ok := t.regs.regs[regName]
if !ok && strings.HasPrefix(regName, "xmm") {
// XMMn and YMMn are the same amd64 register (in different sizes), if we
// don't find XMMn try YMMn or ZMMn instead.
gdbreg, ok = t.regs.regs["y"+regName[1:]]
if !ok {
gdbreg, ok = t.regs.regs["z"+regName[1:]]
}
}
if !ok && t.p.bi.Arch.Name == "arm64" && regName == "x30" {
gdbreg, ok = t.regs.regs["lr"]
}
if !ok && regName == "rflags" {
// rr has eflags instead of rflags
regName = "eflags"
gdbreg, ok = t.regs.regs[regName]
if ok {
reg.FillBytes()
reg.Bytes = reg.Bytes[:4]
}
}
if !ok {
return fmt.Errorf("could not set register %s: not found", regName)
}
reg.FillBytes()
wrongSizeErr := func(n int) error {
return fmt.Errorf("could not set register %s: wrong size, expected %d got %d", regName, n, len(reg.Bytes))
}
if len(reg.Bytes) == len(gdbreg.value) {
copy(gdbreg.value, reg.Bytes)
err := t.p.conn.writeRegister(t.strID, gdbreg.regnum, gdbreg.value)
if err != nil {
return err
}
if t.p.conn.workaroundReg != nil && len(gdbreg.value) > 16 {
// This is a workaround for a bug in debugserver where register writes (P
// packet) on AVX-2 and AVX-512 registers are ignored unless they are
// followed by a write to an AVX register.
// See:
// Issue #2767
// https://bugs.llvm.org/show_bug.cgi?id=52362
reg := t.regs.gdbRegisterNew(t.p.conn.workaroundReg)
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
}
} else if len(reg.Bytes) == 2*len(gdbreg.value) && strings.HasPrefix(regName, "xmm") {
// rr uses xmmN for the low part of the register and ymmNh for the high part
gdbregh, ok := t.regs.regs["y"+regName[1:]+"h"]
if !ok {
return wrongSizeErr(len(gdbreg.value))
}
if len(reg.Bytes) != len(gdbreg.value)+len(gdbregh.value) {
return wrongSizeErr(len(gdbreg.value) + len(gdbregh.value))
}
copy(gdbreg.value, reg.Bytes[:len(gdbreg.value)])
copy(gdbregh.value, reg.Bytes[len(gdbreg.value):])
err := t.p.conn.writeRegister(t.strID, gdbreg.regnum, gdbreg.value)
if err != nil {
return err
}
err = t.p.conn.writeRegister(t.strID, gdbregh.regnum, gdbregh.value)
if err != nil {
return err
}
} else {
return wrongSizeErr(len(gdbreg.value))
}
return nil
}
func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) {
r := make([]proc.Register, 0, len(regs.regsInfo))
for _, reginfo := range regs.regsInfo {
if reginfo.Group == "float" && !floatingPoint {
continue
}
switch {
case reginfo.Name == "eflags":
r = proc.AppendBytesRegister(r, "Rflags", regs.regs[reginfo.Name].value)
case reginfo.Name == "mxcsr":
r = proc.AppendBytesRegister(r, reginfo.Name, regs.regs[reginfo.Name].value)
case reginfo.Bitsize == 16:
r = proc.AppendBytesRegister(r, reginfo.Name, regs.regs[reginfo.Name].value)
case reginfo.Bitsize == 32:
r = proc.AppendBytesRegister(r, reginfo.Name, regs.regs[reginfo.Name].value)
case reginfo.Bitsize == 64:
r = proc.AppendBytesRegister(r, reginfo.Name, regs.regs[reginfo.Name].value)
case reginfo.Bitsize == 80:
if !floatingPoint {
continue
}
idx := 0
for _, stprefix := range []string{"stmm", "st"} {
if strings.HasPrefix(reginfo.Name, stprefix) {
idx, _ = strconv.Atoi(reginfo.Name[len(stprefix):])
break
}
}
r = proc.AppendBytesRegister(r, fmt.Sprintf("ST(%d)", idx), regs.regs[reginfo.Name].value)
case reginfo.Bitsize == 128:
if floatingPoint {
name := reginfo.Name
if last := name[len(name)-1]; last == 'h' || last == 'H' {
name = name[:len(name)-1]
}
r = proc.AppendBytesRegister(r, strings.ToUpper(name), regs.regs[reginfo.Name].value)
}
case reginfo.Bitsize == 256:
if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") || !floatingPoint {
continue
}
value := regs.regs[reginfo.Name].value
xmmName := "x" + reginfo.Name[1:]
r = proc.AppendBytesRegister(r, strings.ToUpper(xmmName), value)
case reginfo.Bitsize == 512:
if !strings.HasPrefix(strings.ToLower(reginfo.Name), "zmm") || !floatingPoint {
continue
}
value := regs.regs[reginfo.Name].value
xmmName := "x" + reginfo.Name[1:]
r = proc.AppendBytesRegister(r, strings.ToUpper(xmmName), value)
}
}
return r, nil
}
func (regs *gdbRegisters) Copy() (proc.Registers, error) {
savedRegs := &gdbRegisters{}
savedRegs.init(regs.regsInfo, regs.arch, regs.regnames)
copy(savedRegs.buf, regs.buf)
return savedRegs, nil
}
func registerName(arch *proc.Arch, regNum uint64) string {
regName, _, _ := arch.DwarfRegisterToString(int(regNum), nil)
return strings.ToLower(regName)
}
func machTargetExcToError(sig uint8) error {
switch sig {
case 0x91:
return errors.New("bad access")
case 0x92:
return errors.New("bad instruction")
case 0x93:
return errors.New("arithmetic exception")
case 0x94:
return errors.New("emulation exception")
case 0x95:
return errors.New("software exception")
case 0x96:
return errors.New("breakpoint exception")
}
return nil
}
func checkRosettaExpensive() error {
if runtime.GOOS != "darwin" {
return nil
}
if runtime.GOARCH != "arm64" {
return nil
}
// Additionally check the output of 'uname -m' if it's x86_64 it means that
// the shell we are running on is being emulated by Rosetta even though our
// process isn't. In this condition debugserver will crash.
out, err := exec.Command("uname", "-m").Output()
if err != nil {
return nil
}
s := strings.TrimSpace(string(out))
if s == "x86_64" {
return errors.New("can not run under Rosetta, check that the terminal/shell in use is right for your CPU architecture")
}
return nil
}