proc: implement target.Interface for core files
This commit is contained in:
parent
3dacc25d2e
commit
c1879472a1
@ -1,5 +1,6 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
panic("BOOM!")
|
||||
msg := "BOOM!"
|
||||
panic(msg)
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ consider compiling debugging binaries with -gcflags="-N -l".`,
|
||||
return nil
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
os.Exit(execute(0, args, conf, executingExistingFile))
|
||||
os.Exit(execute(0, args, conf, "", executingExistingFile))
|
||||
},
|
||||
}
|
||||
RootCommand.AddCommand(execCommand)
|
||||
@ -205,6 +205,24 @@ to know what functions your process is executing.`,
|
||||
traceCommand.Flags().IntVarP(&traceStackDepth, "stack", "s", 0, "Show stack trace with given depth.")
|
||||
RootCommand.AddCommand(traceCommand)
|
||||
|
||||
coreCommand := &cobra.Command{
|
||||
Use: "core <executable> <core>",
|
||||
Short: "Examine a core dump.",
|
||||
Long: `Examine a core dump.
|
||||
|
||||
The core command will open the specified core file and the associated
|
||||
executable and let you examine the state of the process when the
|
||||
core dump was taken.`,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.New("you must provide a core file and an executable")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Run: coreCmd,
|
||||
}
|
||||
RootCommand.AddCommand(coreCommand)
|
||||
|
||||
// 'version' subcommand.
|
||||
versionCommand := &cobra.Command{
|
||||
Use: "version",
|
||||
@ -243,7 +261,7 @@ func debugCmd(cmd *cobra.Command, args []string) {
|
||||
return 1
|
||||
}
|
||||
processArgs := append([]string{abs}, targetArgs...)
|
||||
return execute(0, processArgs, conf, executingGeneratedFile)
|
||||
return execute(0, processArgs, conf, "", executingGeneratedFile)
|
||||
}()
|
||||
os.Exit(status)
|
||||
}
|
||||
@ -332,7 +350,7 @@ func testCmd(cmd *cobra.Command, args []string) {
|
||||
defer os.Remove("./" + testdebugname)
|
||||
processArgs := append([]string{"./" + testdebugname}, targetArgs...)
|
||||
|
||||
return execute(0, processArgs, conf, executingOther)
|
||||
return execute(0, processArgs, conf, "", executingOther)
|
||||
}()
|
||||
os.Exit(status)
|
||||
}
|
||||
@ -343,7 +361,11 @@ func attachCmd(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintf(os.Stderr, "Invalid pid: %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(execute(pid, nil, conf, executingOther))
|
||||
os.Exit(execute(pid, nil, conf, "", executingOther))
|
||||
}
|
||||
|
||||
func coreCmd(cmd *cobra.Command, args []string) {
|
||||
os.Exit(execute(0, []string{args[0]}, conf, args[1], executingOther))
|
||||
}
|
||||
|
||||
func connectCmd(cmd *cobra.Command, args []string) {
|
||||
@ -382,7 +404,7 @@ const (
|
||||
executingOther
|
||||
)
|
||||
|
||||
func execute(attachPid int, processArgs []string, conf *config.Config, kind executeKind) int {
|
||||
func execute(attachPid int, processArgs []string, conf *config.Config, coreFile string, kind executeKind) int {
|
||||
// Make a TCP listener
|
||||
listener, err := net.Listen("tcp", Addr)
|
||||
if err != nil {
|
||||
@ -410,6 +432,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, kind exec
|
||||
AcceptMulti: AcceptMulti,
|
||||
APIVersion: APIVersion,
|
||||
WorkingDir: WorkingDir,
|
||||
CoreFile: coreFile,
|
||||
}, Log)
|
||||
default:
|
||||
fmt.Printf("Unknown API version: %d\n", APIVersion)
|
||||
|
215
pkg/proc/core.go
215
pkg/proc/core.go
@ -1,8 +1,12 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MemoryReader is like io.ReaderAt, but the offset is a uintptr so that it
|
||||
@ -144,3 +148,214 @@ type OffsetReaderAt struct {
|
||||
func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
|
||||
return r.reader.ReadAt(buf, int64(addr-r.offset))
|
||||
}
|
||||
|
||||
type CoreProcess struct {
|
||||
bi BinaryInfo
|
||||
core *Core
|
||||
breakpoints map[uint64]*Breakpoint
|
||||
currentThread *LinuxPrStatus
|
||||
selectedGoroutine *G
|
||||
allGCache []*G
|
||||
}
|
||||
|
||||
type CoreThread struct {
|
||||
th *LinuxPrStatus
|
||||
p *CoreProcess
|
||||
}
|
||||
|
||||
var ErrWriteCore = errors.New("can not to core process")
|
||||
var ErrShortRead = errors.New("short read")
|
||||
var ErrContinueCore = errors.New("can not continue execution of core process")
|
||||
|
||||
func OpenCore(corePath, exePath string) (*CoreProcess, error) {
|
||||
core, err := readCore(corePath, exePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &CoreProcess{
|
||||
core: core,
|
||||
breakpoints: make(map[uint64]*Breakpoint),
|
||||
bi: NewBinaryInfo("linux", "amd64"),
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
p.bi.LoadBinaryInfo(exePath, &wg)
|
||||
wg.Wait()
|
||||
|
||||
for _, th := range p.core.Threads {
|
||||
p.currentThread = th
|
||||
break
|
||||
}
|
||||
|
||||
scope := &EvalScope{0, 0, p.CurrentThread(), nil, &p.bi}
|
||||
ver, isextld, err := scope.getGoInformation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.bi.arch.SetGStructOffset(ver, isextld)
|
||||
p.selectedGoroutine, _ = GetG(p.CurrentThread())
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) BinInfo() *BinaryInfo {
|
||||
return &p.bi
|
||||
}
|
||||
|
||||
func (thread *CoreThread) readMemory(addr uintptr, size int) (data []byte, err error) {
|
||||
data = make([]byte, size)
|
||||
n, err := thread.p.core.ReadMemory(data, addr)
|
||||
if err == nil && n != len(data) {
|
||||
err = ErrShortRead
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (thread *CoreThread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||
return 0, ErrWriteCore
|
||||
}
|
||||
|
||||
func (t *CoreThread) Location() (*Location, error) {
|
||||
f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
|
||||
return &Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
func (t *CoreThread) Breakpoint() (*Breakpoint, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (t *CoreThread) ThreadID() int {
|
||||
return int(t.th.Pid)
|
||||
}
|
||||
|
||||
func (t *CoreThread) Registers(floatingPoint bool) (Registers, error) {
|
||||
//TODO(aarzilli): handle floating point registers
|
||||
return &t.th.Reg, nil
|
||||
}
|
||||
|
||||
func (t *CoreThread) Arch() Arch {
|
||||
return t.p.bi.arch
|
||||
}
|
||||
|
||||
func (t *CoreThread) BinInfo() *BinaryInfo {
|
||||
return &t.p.bi
|
||||
}
|
||||
|
||||
func (t *CoreThread) StepInstruction() error {
|
||||
return ErrContinueCore
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Breakpoints() map[uint64]*Breakpoint {
|
||||
return p.breakpoints
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
|
||||
return nil, NoBreakpointError{addr: addr}
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ClearInternalBreakpoints() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ContinueOnce() (IThread, error) {
|
||||
return nil, ErrContinueCore
|
||||
}
|
||||
|
||||
func (p *CoreProcess) StepInstruction() error {
|
||||
return ErrContinueCore
|
||||
}
|
||||
|
||||
func (p *CoreProcess) RequestManualStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) CurrentThread() IThread {
|
||||
return &CoreThread{p.currentThread, p}
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Detach(bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Exited() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) {
|
||||
return FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
|
||||
return FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
return FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) AllGCache() *[]*G {
|
||||
return &p.allGCache
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Kill() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Pid() int {
|
||||
return p.core.Pid
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Running() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *CoreProcess) SelectedGoroutine() *G {
|
||||
return p.selectedGoroutine
|
||||
}
|
||||
|
||||
func (p *CoreProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
|
||||
return nil, ErrWriteCore
|
||||
}
|
||||
|
||||
func (p *CoreProcess) SwitchGoroutine(gid int) error {
|
||||
g, err := FindGoroutine(p, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.thread != nil {
|
||||
return p.SwitchThread(g.thread.ThreadID())
|
||||
}
|
||||
p.selectedGoroutine = g
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) SwitchThread(tid int) error {
|
||||
if th, ok := p.core.Threads[tid]; ok {
|
||||
p.currentThread = th
|
||||
p.selectedGoroutine, _ = GetG(p.CurrentThread())
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ThreadList() []IThread {
|
||||
r := make([]IThread, 0, len(p.core.Threads))
|
||||
for _, v := range p.core.Threads {
|
||||
r = append(r, &CoreThread{v, p})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FindThread(threadID int) (IThread, bool) {
|
||||
t, ok := p.core.Threads[threadID]
|
||||
return &CoreThread{t, p}, ok
|
||||
}
|
||||
|
@ -2,14 +2,16 @@ package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"fmt"
|
||||
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc/test"
|
||||
)
|
||||
@ -122,9 +124,12 @@ func TestSplicedReader(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadCore(t *testing.T) {
|
||||
func TestCore(t *testing.T) {
|
||||
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
|
||||
return
|
||||
}
|
||||
// This is all very fragile and won't work on hosts with non-default core patterns.
|
||||
// Might be better to check in the core?
|
||||
// Might be better to check in the binary and core?
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -132,20 +137,72 @@ func TestReadCore(t *testing.T) {
|
||||
fix := test.BuildFixture("panic")
|
||||
bashCmd := fmt.Sprintf("cd %v && ulimit -c unlimited && GOTRACEBACK=crash %v", tempDir, fix.Path)
|
||||
exec.Command("bash", "-c", bashCmd).Run()
|
||||
corePath := path.Join(tempDir, "core")
|
||||
cores, err := filepath.Glob(path.Join(tempDir, "core*"))
|
||||
switch {
|
||||
case err != nil || len(cores) > 1:
|
||||
t.Fatalf("Got %v, wanted one file named core* in %v", cores, tempDir)
|
||||
case len(cores) == 0:
|
||||
t.Logf("core file was not produced, could not run test")
|
||||
return
|
||||
}
|
||||
corePath := cores[0]
|
||||
|
||||
core, err := readCore(corePath, fix.Path)
|
||||
p, err := OpenCore(corePath, fix.Path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
pat, err := ioutil.ReadFile("/proc/sys/kernel/core_pattern")
|
||||
t.Errorf("read core_pattern: %q, %v", pat, err)
|
||||
apport, err := ioutil.ReadFile("/var/log/apport.log")
|
||||
t.Errorf("read apport log: %q, %v", apport, err)
|
||||
t.Fatalf("ReadCore() failed: %v", err)
|
||||
}
|
||||
if len(core.Threads) == 0 {
|
||||
t.Error("expected at least one thread")
|
||||
gs, err := GoroutinesInfo(p)
|
||||
if err != nil || len(gs) == 0 {
|
||||
t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err)
|
||||
}
|
||||
// Punch through the abstraction to verify that we got some mappings.
|
||||
spliced := core.MemoryReader.(*SplicedMemory)
|
||||
// There should be at least an RO section, RW section, RX section, the heap, and a thread stack.
|
||||
if len(spliced.readers) < 5 {
|
||||
t.Errorf("expected at least 5 memory regions, got only %v", len(spliced.readers))
|
||||
|
||||
var panicking *G
|
||||
var panickingStack []Stackframe
|
||||
for _, g := range gs {
|
||||
stack, err := g.Stacktrace(10)
|
||||
if err != nil {
|
||||
t.Errorf("Stacktrace() on goroutine %v = %v", g, err)
|
||||
}
|
||||
for _, frame := range stack {
|
||||
if strings.Contains(frame.Current.Fn.Name, "panic") {
|
||||
panicking = g
|
||||
panickingStack = stack
|
||||
}
|
||||
}
|
||||
}
|
||||
if panicking == nil {
|
||||
t.Fatalf("Didn't find a call to panic in goroutine stacks: %v", gs)
|
||||
}
|
||||
|
||||
var mainFrame *Stackframe
|
||||
// Walk backward, because the current function seems to be main.main
|
||||
// in the actual call to panic().
|
||||
for i := len(panickingStack) - 1; i >= 0; i-- {
|
||||
if panickingStack[i].Current.Fn.Name == "main.main" {
|
||||
mainFrame = &panickingStack[i]
|
||||
}
|
||||
}
|
||||
if mainFrame == nil {
|
||||
t.Fatalf("Couldn't find main in stack %v", panickingStack)
|
||||
}
|
||||
msg, err := FrameToScope(p, *mainFrame).EvalVariable("msg", LoadConfig{MaxStringLen: 64})
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't EvalVariable(msg, ...): %v", err)
|
||||
}
|
||||
if constant.StringVal(msg.Value) != "BOOM!" {
|
||||
t.Errorf("main.msg = %q, want %q", msg.Value, "BOOM!")
|
||||
}
|
||||
|
||||
regs, err := p.CurrentThread().Registers(true)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't get current thread registers: %v", err)
|
||||
}
|
||||
regslice := regs.Slice()
|
||||
for _, reg := range regslice {
|
||||
t.Logf("%s = %s", reg.Name, reg.Value)
|
||||
}
|
||||
// Would be good to test more stuff but not sure what without reading debug information, etc.
|
||||
}
|
@ -3,16 +3,286 @@ package proc
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/debug/elf"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
|
||||
// Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on
|
||||
// all systems.
|
||||
type LinuxCoreRegisters struct {
|
||||
R15 uint64
|
||||
R14 uint64
|
||||
R13 uint64
|
||||
R12 uint64
|
||||
Rbp uint64
|
||||
Rbx uint64
|
||||
R11 uint64
|
||||
R10 uint64
|
||||
R9 uint64
|
||||
R8 uint64
|
||||
Rax uint64
|
||||
Rcx uint64
|
||||
Rdx uint64
|
||||
Rsi uint64
|
||||
Rdi uint64
|
||||
Orig_rax uint64
|
||||
Rip uint64
|
||||
Cs uint64
|
||||
Eflags uint64
|
||||
Rsp uint64
|
||||
Ss uint64
|
||||
Fs_base uint64
|
||||
Gs_base uint64
|
||||
Ds uint64
|
||||
Es uint64
|
||||
Fs uint64
|
||||
Gs uint64
|
||||
}
|
||||
|
||||
// Copied from golang.org/x/sys/unix.Timeval since it's not available on all
|
||||
// systems.
|
||||
type LinuxCoreTimeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
const NT_FILE elf.NType = 0x46494c45 // "FILE".
|
||||
|
||||
func (r *LinuxCoreRegisters) PC() uint64 {
|
||||
return r.Rip
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) SP() uint64 {
|
||||
return r.Rsp
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) BP() uint64 {
|
||||
return r.Rbp
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) CX() uint64 {
|
||||
return r.Rcx
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) TLS() uint64 {
|
||||
return r.Fs_base
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) GAddr() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.Rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.Rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.Rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.Rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.Rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.Rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.Rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.Rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.Rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.Rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.Rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.Rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.R8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.R9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.R10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.R11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.R12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.R13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.R14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.R15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.Rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.Rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.Rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.Rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.Rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.Rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.Rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.Rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.R8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.R9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.R10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.R11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.R12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.R13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.R14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.R15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.Rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.Rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.Rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.Rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.Rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.Rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.Rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.Rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.R8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.R9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.R10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.R11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.R12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.R13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.R14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.R15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.Rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.Rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.Rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.Rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.Rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.Rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.Rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.Rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.R8, nil
|
||||
case x86asm.R9:
|
||||
return r.R9, nil
|
||||
case x86asm.R10:
|
||||
return r.R10, nil
|
||||
case x86asm.R11:
|
||||
return r.R11, nil
|
||||
case x86asm.R12:
|
||||
return r.R12, nil
|
||||
case x86asm.R13:
|
||||
return r.R13, nil
|
||||
case x86asm.R14:
|
||||
return r.R14, nil
|
||||
case x86asm.R15:
|
||||
return r.R15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) SetPC(IThread, uint64) error {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) Slice() []Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.Rip},
|
||||
{"Rsp", r.Rsp},
|
||||
{"Rax", r.Rax},
|
||||
{"Rbx", r.Rbx},
|
||||
{"Rcx", r.Rcx},
|
||||
{"Rdx", r.Rdx},
|
||||
{"Rdi", r.Rdi},
|
||||
{"Rsi", r.Rsi},
|
||||
{"Rbp", r.Rbp},
|
||||
{"R8", r.R8},
|
||||
{"R9", r.R9},
|
||||
{"R10", r.R10},
|
||||
{"R11", r.R11},
|
||||
{"R12", r.R12},
|
||||
{"R13", r.R13},
|
||||
{"R14", r.R14},
|
||||
{"R15", r.R15},
|
||||
{"Orig_rax", r.Orig_rax},
|
||||
{"Cs", r.Cs},
|
||||
{"Eflags", r.Eflags},
|
||||
{"Ss", r.Ss},
|
||||
{"Fs_base", r.Fs_base},
|
||||
{"Gs_base", r.Gs_base},
|
||||
{"Ds", r.Ds},
|
||||
{"Es", r.Es},
|
||||
{"Fs", r.Fs},
|
||||
{"Gs", r.Gs},
|
||||
}
|
||||
out := make([]Register, 0, len(regs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// readCore reads a core file from corePath corresponding to the executable at
|
||||
// exePath. For details on the Linux ELF core format, see:
|
||||
// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
|
||||
@ -234,8 +504,8 @@ type LinuxPrStatus struct {
|
||||
Sigpend uint64
|
||||
Sighold uint64
|
||||
Pid, Ppid, Pgrp, Sid int32
|
||||
Utime, Stime, CUtime, CStime unix.Timeval
|
||||
Reg unix.PtraceRegs
|
||||
Utime, Stime, CUtime, CStime LinuxCoreTimeval
|
||||
Reg LinuxCoreRegisters
|
||||
Fpvalid int32
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ type Registers interface {
|
||||
CX() uint64
|
||||
TLS() uint64
|
||||
Get(int) (uint64, error)
|
||||
SetPC(*Thread, uint64) error
|
||||
SetPC(IThread, uint64) error
|
||||
Slice() []Register
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,8 @@ func (r *Regs) TLS() uint64 {
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
|
||||
func (r *Regs) SetPC(t IThread, pc uint64) error {
|
||||
thread := t.(*Thread)
|
||||
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not set pc")
|
||||
|
@ -84,7 +84,8 @@ func (r *Regs) TLS() uint64 {
|
||||
}
|
||||
|
||||
// SetPC sets RIP to the value specified by 'pc'.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) (err error) {
|
||||
func (r *Regs) SetPC(t IThread, pc uint64) (err error) {
|
||||
thread := t.(*Thread)
|
||||
r.regs.SetPC(pc)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
|
||||
return
|
||||
|
@ -125,7 +125,8 @@ func (r *Regs) TLS() uint64 {
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
|
||||
func (r *Regs) SetPC(t IThread, pc uint64) error {
|
||||
thread := t.(*Thread)
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
|
@ -90,3 +90,4 @@ type VariableEval interface {
|
||||
}
|
||||
|
||||
var _ Interface = &proc.Process{}
|
||||
var _ Interface = &proc.CoreProcess{}
|
||||
|
@ -25,4 +25,7 @@ type Config struct {
|
||||
AcceptMulti bool
|
||||
// APIVersion selects which version of the API to serve (default: 1).
|
||||
APIVersion int
|
||||
|
||||
// CoreFile specifies the path to the core dump to open
|
||||
CoreFile string
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ type Config struct {
|
||||
// AttachPid is the PID of an existing process to which the debugger should
|
||||
// attach.
|
||||
AttachPid int
|
||||
|
||||
// CoreFile specifies the path to the core dump to open
|
||||
CoreFile string
|
||||
}
|
||||
|
||||
// New creates a new Debugger.
|
||||
@ -57,14 +60,24 @@ func New(config *Config) (*Debugger, error) {
|
||||
}
|
||||
|
||||
// Create the process by either attaching or launching.
|
||||
if d.config.AttachPid > 0 {
|
||||
switch {
|
||||
case d.config.AttachPid > 0:
|
||||
log.Printf("attaching to pid %d", d.config.AttachPid)
|
||||
p, err := proc.Attach(d.config.AttachPid)
|
||||
if err != nil {
|
||||
return nil, attachErrorMessage(d.config.AttachPid, err)
|
||||
}
|
||||
d.target = p
|
||||
} else {
|
||||
|
||||
case d.config.CoreFile != "":
|
||||
log.Printf("opening core file %s (executable %s)", d.config.CoreFile, d.config.ProcessArgs[0])
|
||||
p, err := proc.OpenCore(d.config.CoreFile, d.config.ProcessArgs[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.target = p
|
||||
|
||||
default:
|
||||
log.Printf("launching process with args: %v", d.config.ProcessArgs)
|
||||
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
||||
if err != nil {
|
||||
@ -113,6 +126,10 @@ func (d *Debugger) Restart() ([]api.DiscardedBreakpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if d.config.CoreFile != "" {
|
||||
return nil, errors.New("can not restart core dump")
|
||||
}
|
||||
|
||||
if !d.target.Exited() {
|
||||
if d.target.Running() {
|
||||
d.target.Halt()
|
||||
|
@ -114,6 +114,7 @@ func (s *ServerImpl) Run() error {
|
||||
ProcessArgs: s.config.ProcessArgs,
|
||||
AttachPid: s.config.AttachPid,
|
||||
WorkingDir: s.config.WorkingDir,
|
||||
CoreFile: s.config.CoreFile,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user