pkg/proc/core: Clean up some repetitive code (#3834)

Cleans up repetitive code in pkg/proc/core and also adds coredumpctl
support to our test suite.
This commit is contained in:
Alessandro Arzilli 2024-10-21 18:12:34 +02:00 committed by GitHub
parent 05dc760877
commit 064ef226ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 81 deletions

@ -168,14 +168,14 @@ type process struct {
// thread represents a thread in the core file being debugged. // thread represents a thread in the core file being debugged.
type thread struct { type thread struct {
th osThread osThread
p *process p *process
common proc.CommonThread common proc.CommonThread
} }
type osThread interface { type osThread interface {
registers() (proc.Registers, error) Registers() (proc.Registers, error)
pid() int ThreadID() int
} }
var ( var (
@ -322,7 +322,7 @@ func (t *thread) ProcessMemory() proc.MemoryReadWriter {
// Location returns the location of this thread based on // Location returns the location of this thread based on
// the value of the instruction pointer register. // the value of the instruction pointer register.
func (t *thread) Location() (*proc.Location, error) { func (t *thread) Location() (*proc.Location, error) {
regs, err := t.th.registers() regs, err := t.Registers()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -338,16 +338,6 @@ func (t *thread) Breakpoint() *proc.BreakpointState {
return &proc.BreakpointState{} return &proc.BreakpointState{}
} }
// ThreadID returns the ID for this thread.
func (t *thread) ThreadID() int {
return t.th.pid()
}
// Registers returns the current value of the registers for this thread.
func (t *thread) Registers() (proc.Registers, error) {
return t.th.registers()
}
// RestoreRegisters will only return an error for core files, // RestoreRegisters will only return an error for core files,
// you cannot change register values for core files. // you cannot change register values for core files.
func (t *thread) RestoreRegisters(proc.Registers) error { func (t *thread) RestoreRegisters(proc.Registers) error {

@ -213,8 +213,13 @@ func withCoreFile(t *testing.T, name, args string) *proc.TargetGroup {
case err != nil || len(cores) > 1: case err != nil || len(cores) > 1:
t.Fatalf("Got %v, wanted one file named core* in %v", cores, tempDir) t.Fatalf("Got %v, wanted one file named core* in %v", cores, tempDir)
case len(cores) == 0: case len(cores) == 0:
t.Skipf("core file was not produced, could not run test") cores = []string{fix.Path + ".dump"}
return nil err := exec.Command("coredumpctl", "--output="+cores[0], "dump", fix.Path).Run()
if err != nil {
t.Skipf("core file was not produced, could not run test, coredumpctl error: %v", err)
return nil
}
test.PathsToRemove = append(test.PathsToRemove, cores[0])
} }
corePath := cores[0] corePath := cores[0]

@ -130,11 +130,11 @@ type delveThread struct {
regs *delveRegisters regs *delveRegisters
} }
func (th *delveThread) pid() int { func (th *delveThread) ThreadID() int {
return int(th.id) return int(th.id)
} }
func (th *delveThread) registers() (proc.Registers, error) { func (th *delveThread) Registers() (proc.Registers, error) {
return th.regs, nil return th.regs, nil
} }

@ -48,50 +48,38 @@ const elfErrorBadMagicNumber = "bad magic number"
func linuxThreadsFromNotes(p *process, notes []*note, machineType elf.Machine) proc.Thread { func linuxThreadsFromNotes(p *process, notes []*note, machineType elf.Machine) proc.Thread {
var currentThread proc.Thread var currentThread proc.Thread
var lastThreadAMD *linuxAMD64Thread var lastThread osThread
var lastThreadARM *linuxARM64Thread
var lastThreadRISCV *linuxRISCV64Thread
for _, note := range notes { for _, note := range notes {
switch note.Type { switch note.Type {
case elf.NT_PRSTATUS: case elf.NT_PRSTATUS:
if machineType == _EM_X86_64 { switch machineType {
case _EM_X86_64:
t := note.Desc.(*linuxPrStatusAMD64) t := note.Desc.(*linuxPrStatusAMD64)
lastThreadAMD = &linuxAMD64Thread{linutil.AMD64Registers{Regs: &t.Reg}, t} lastThread = &linuxAMD64Thread{linutil.AMD64Registers{Regs: &t.Reg}, t}
p.Threads[int(t.Pid)] = &thread{lastThreadAMD, p, proc.CommonThread{}} case _EM_AARCH64:
if currentThread == nil {
currentThread = p.Threads[int(t.Pid)]
}
} else if machineType == _EM_AARCH64 {
t := note.Desc.(*linuxPrStatusARM64) t := note.Desc.(*linuxPrStatusARM64)
lastThreadARM = &linuxARM64Thread{linutil.ARM64Registers{Regs: &t.Reg}, t} lastThread = &linuxARM64Thread{linutil.ARM64Registers{Regs: &t.Reg}, t}
p.Threads[int(t.Pid)] = &thread{lastThreadARM, p, proc.CommonThread{}} case _EM_RISCV:
if currentThread == nil {
currentThread = p.Threads[int(t.Pid)]
}
} else if machineType == _EM_RISCV {
t := note.Desc.(*linuxPrStatusRISCV64) t := note.Desc.(*linuxPrStatusRISCV64)
lastThreadRISCV = &linuxRISCV64Thread{linutil.RISCV64Registers{Regs: &t.Reg}, t} lastThread = &linuxRISCV64Thread{linutil.RISCV64Registers{Regs: &t.Reg}, t}
p.Threads[int(t.Pid)] = &thread{lastThreadRISCV, p, proc.CommonThread{}} default:
if currentThread == nil { continue
currentThread = p.Threads[int(t.Pid)] }
} p.Threads[lastThread.ThreadID()] = &thread{lastThread, p, proc.CommonThread{}}
if currentThread == nil {
currentThread = p.Threads[lastThread.ThreadID()]
} }
case _NT_FPREGSET: case _NT_FPREGSET:
if machineType == _EM_AARCH64 { switch th := lastThread.(type) {
if lastThreadARM != nil { case *linuxARM64Thread:
lastThreadARM.regs.Fpregs = note.Desc.(*linutil.ARM64PtraceFpRegs).Decode() th.regs.Fpregs = note.Desc.(*linutil.ARM64PtraceFpRegs).Decode()
} case *linuxRISCV64Thread:
} else if machineType == _EM_RISCV { th.regs.Fpregs = note.Desc.(*linutil.RISCV64PtraceFpRegs).Decode()
if lastThreadRISCV != nil {
lastThreadRISCV.regs.Fpregs = note.Desc.(*linutil.RISCV64PtraceFpRegs).Decode()
}
} }
case _NT_X86_XSTATE: case _NT_X86_XSTATE:
if machineType == _EM_X86_64 { if lastThread != nil {
if lastThreadAMD != nil { lastThread.(*linuxAMD64Thread).regs.Fpregs = note.Desc.(*amd64util.AMD64Xstate).Decode()
lastThreadAMD.regs.Fpregs = note.Desc.(*amd64util.AMD64Xstate).Decode()
}
} }
case elf.NT_PRPSINFO: case elf.NT_PRPSINFO:
p.pid = int(note.Desc.(*linuxPrPsInfo).Pid) p.pid = int(note.Desc.(*linuxPrPsInfo).Pid)
@ -100,6 +88,12 @@ func linuxThreadsFromNotes(p *process, notes []*note, machineType elf.Machine) p
return currentThread return currentThread
} }
var supportedLinuxMachines = map[elf.Machine]string{
_EM_X86_64: "amd64",
_EM_AARCH64: "arm64",
_EM_RISCV: "riscv64",
}
// readLinuxOrPlatformIndependentCore reads a core file from corePath // readLinuxOrPlatformIndependentCore reads a core file from corePath
// corresponding to the executable at exePath. For details on the Linux ELF // corresponding to the executable at exePath. For details on the Linux ELF
// core format, see: // core format, see:
@ -155,17 +149,10 @@ func readLinuxOrPlatformIndependentCore(corePath, exePath string) (*process, pro
return nil, nil, err return nil, nil, err
} }
bi = proc.NewBinaryInfo(goos, goarch) bi = proc.NewBinaryInfo(goos, goarch)
} else if goarch, ok := supportedLinuxMachines[machineType]; ok {
bi = proc.NewBinaryInfo("linux", goarch)
} else { } else {
switch machineType { return nil, nil, errors.New("unsupported machine type")
case _EM_X86_64:
bi = proc.NewBinaryInfo("linux", "amd64")
case _EM_AARCH64:
bi = proc.NewBinaryInfo("linux", "arm64")
case _EM_RISCV:
bi = proc.NewBinaryInfo("linux", "riscv64")
default:
return nil, nil, errors.New("unsupported machine type")
}
} }
entryPoint := findEntryPoint(notes, bi.Arch.PtrSize()) entryPoint := findEntryPoint(notes, bi.Arch.PtrSize())
@ -202,36 +189,36 @@ type linuxRISCV64Thread struct {
t *linuxPrStatusRISCV64 t *linuxPrStatusRISCV64
} }
func (t *linuxAMD64Thread) registers() (proc.Registers, error) { func (t *linuxAMD64Thread) Registers() (proc.Registers, error) {
var r linutil.AMD64Registers var r linutil.AMD64Registers
r.Regs = t.regs.Regs r.Regs = t.regs.Regs
r.Fpregs = t.regs.Fpregs r.Fpregs = t.regs.Fpregs
return &r, nil return &r, nil
} }
func (t *linuxARM64Thread) registers() (proc.Registers, error) { func (t *linuxARM64Thread) Registers() (proc.Registers, error) {
var r linutil.ARM64Registers var r linutil.ARM64Registers
r.Regs = t.regs.Regs r.Regs = t.regs.Regs
r.Fpregs = t.regs.Fpregs r.Fpregs = t.regs.Fpregs
return &r, nil return &r, nil
} }
func (t *linuxRISCV64Thread) registers() (proc.Registers, error) { func (t *linuxRISCV64Thread) Registers() (proc.Registers, error) {
var r linutil.RISCV64Registers var r linutil.RISCV64Registers
r.Regs = t.regs.Regs r.Regs = t.regs.Regs
r.Fpregs = t.regs.Fpregs r.Fpregs = t.regs.Fpregs
return &r, nil return &r, nil
} }
func (t *linuxAMD64Thread) pid() int { func (t *linuxAMD64Thread) ThreadID() int {
return int(t.t.Pid) return int(t.t.Pid)
} }
func (t *linuxARM64Thread) pid() int { func (t *linuxARM64Thread) ThreadID() int {
return int(t.t.Pid) return int(t.t.Pid)
} }
func (t *linuxRISCV64Thread) pid() int { func (t *linuxRISCV64Thread) ThreadID() int {
return int(t.t.Pid) return int(t.t.Pid)
} }
@ -360,19 +347,12 @@ func readNote(r io.ReadSeeker, machineType elf.Machine) (*note, error) {
note.Desc = desc note.Desc = desc
case _NT_FPREGSET: case _NT_FPREGSET:
if machineType == _EM_AARCH64 { if machineType == _EM_AARCH64 {
fpregs := &linutil.ARM64PtraceFpRegs{} err = readFpregsetNote(note, &linutil.ARM64PtraceFpRegs{}, desc[:_ARM_FP_HEADER_START])
rdr := bytes.NewReader(desc[:_ARM_FP_HEADER_START])
if err := binary.Read(rdr, binary.LittleEndian, fpregs.Byte()); err != nil {
return nil, err
}
note.Desc = fpregs
} else if machineType == _EM_RISCV { } else if machineType == _EM_RISCV {
fpregs := &linutil.RISCV64PtraceFpRegs{} err = readFpregsetNote(note, &linutil.RISCV64PtraceFpRegs{}, desc)
rdr := bytes.NewReader(desc) }
if err := binary.Read(rdr, binary.LittleEndian, fpregs.Byte()); err != nil { if err != nil {
return nil, err return nil, err
}
note.Desc = fpregs
} }
} }
if err := skipPadding(r, 4); err != nil { if err := skipPadding(r, 4); err != nil {
@ -381,6 +361,15 @@ func readNote(r io.ReadSeeker, machineType elf.Machine) (*note, error) {
return note, nil return note, nil
} }
func readFpregsetNote(note *note, fpregs interface{ Byte() []byte }, desc []byte) error {
rdr := bytes.NewReader(desc)
if err := binary.Read(rdr, binary.LittleEndian, fpregs.Byte()); err != nil {
return err
}
note.Desc = fpregs
return nil
}
// skipPadding moves r to the next multiple of pad. // skipPadding moves r to the next multiple of pad.
func skipPadding(r io.ReadSeeker, pad int64) error { func skipPadding(r io.ReadSeeker, pad int64) error {
pos, err := r.Seek(0, io.SeekCurrent) pos, err := r.Seek(0, io.SeekCurrent)

@ -57,10 +57,10 @@ type windowsAMD64Thread struct {
th *minidump.Thread th *minidump.Thread
} }
func (th *windowsAMD64Thread) pid() int { func (th *windowsAMD64Thread) ThreadID() int {
return int(th.th.ID) return int(th.th.ID)
} }
func (th *windowsAMD64Thread) registers() (proc.Registers, error) { func (th *windowsAMD64Thread) Registers() (proc.Registers, error) {
return winutil.NewAMD64Registers(&th.th.Context, th.th.TEB), nil return winutil.NewAMD64Registers(&th.th.Context, th.th.TEB), nil
} }