mirror of
https://github.com/go-delve/delve.git
synced 2025-10-30 02:07:58 +08:00
pkg/proc: add support for additional stack-switching functions on loong64 (#4100)
Extend loong64SwitchStack to handle: - cgo-related functions (Fixes #4099) - runtime.mstart, runtime.newstack, runtime.systemstack Co-authored-by: Huang Qiqi <huangqiqi@loongson.cn>
This commit is contained in:
@ -23,8 +23,7 @@ Tests skipped by each supported backend:
|
||||
* 2 not working on linux/386
|
||||
* linux/386/pie skipped = 1
|
||||
* 1 broken
|
||||
* linux/loong64 skipped = 2
|
||||
* 1 broken - cgo stacktraces
|
||||
* linux/loong64 skipped = 1
|
||||
* 1 not working on linux/loong64
|
||||
* linux/ppc64le skipped = 3
|
||||
* 1 broken - cgo stacktraces
|
||||
@ -36,8 +35,8 @@ Tests skipped by each supported backend:
|
||||
* linux/riscv64 skipped = 2
|
||||
* 1 broken - cgo stacktraces
|
||||
* 1 not working on linux/riscv64
|
||||
* loong64 skipped = 9
|
||||
* 2 broken
|
||||
* loong64 skipped = 8
|
||||
* 1 broken
|
||||
* 1 broken - global variable symbolication
|
||||
* 6 not implemented
|
||||
* pie skipped = 2
|
||||
|
||||
@ -89,6 +89,8 @@ func loong64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Bina
|
||||
return fctxt
|
||||
}
|
||||
|
||||
const loong64cgocallSPOffsetSaveSlot = 0x8
|
||||
|
||||
func loong64SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool {
|
||||
if it.frame.Current.Fn == nil {
|
||||
if it.systemstack && it.g != nil && it.top {
|
||||
@ -101,26 +103,121 @@ func loong64SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) boo
|
||||
return false
|
||||
}
|
||||
switch it.frame.Current.Fn.Name {
|
||||
case "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
|
||||
case "runtime.goexit", "runtime.rt0_go":
|
||||
// Look for "top of stack" functions.
|
||||
it.atend = true
|
||||
return true
|
||||
|
||||
default:
|
||||
if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.throw" && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
|
||||
// The runtime switches to the system stack in multiple places.
|
||||
// This usually happens through a call to runtime.systemstack but there
|
||||
// are functions that switch to the system stack manually (for example
|
||||
// runtime.morestack).
|
||||
// Since we are only interested in printing the system stack for cgo
|
||||
// calls we switch directly to the goroutine stack if we detect that the
|
||||
// function at the top of the stack is a runtime function.
|
||||
if err := it.switchToGoroutineStack(); err != nil {
|
||||
it.err = err
|
||||
return false
|
||||
}
|
||||
case "runtime.mcall":
|
||||
if it.systemstack && it.g != nil {
|
||||
it.switchToGoroutineStack()
|
||||
return true
|
||||
}
|
||||
it.atend = true
|
||||
return true
|
||||
|
||||
case "runtime.asmcgocall":
|
||||
if it.top || !it.systemstack {
|
||||
return false
|
||||
}
|
||||
// This function is called by a goroutine to execute a C function and
|
||||
// switches from the goroutine stack to the system stack.
|
||||
// Since we are unwinding the stack from callee to caller we have to switch
|
||||
// from the system stack to the goroutine stack.
|
||||
oldsp := it.regs.SP()
|
||||
off, _ := readIntRaw(it.mem, oldsp+loong64cgocallSPOffsetSaveSlot, int64(it.bi.Arch.PtrSize()))
|
||||
newsp := uint64(int64(it.stackhi) - off)
|
||||
|
||||
// The runtime.asmcgocall prologue contains: addi.d $sp, $sp, -8,
|
||||
// hence we require newsp + 8 at this point
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = newsp + 8
|
||||
|
||||
// runtime.asmcgocall can also be called from inside the system stack,
|
||||
// in that case no stack switch actually happens
|
||||
if it.regs.SP() == oldsp {
|
||||
return false
|
||||
}
|
||||
|
||||
it.top = false
|
||||
it.systemstack = false
|
||||
// The return value is stored in the LR register which is saved at -8(SP).
|
||||
addrret := uint64(int64(it.regs.SP()) - int64(it.bi.Arch.PtrSize()))
|
||||
it.frame.Ret, _ = readUintRaw(it.mem, addrret, int64(it.bi.Arch.PtrSize()))
|
||||
it.pc = it.frame.Ret
|
||||
return true
|
||||
|
||||
case "runtime.cgocallback_gofunc", "runtime.cgocallback":
|
||||
// For a detailed description of how this works read the long comment at
|
||||
// the start of $GOROOT/src/runtime/cgocall.go and the source code of
|
||||
// runtime.cgocallback_gofunc in $GOROOT/src/runtime/asm_loong64.s
|
||||
//
|
||||
// When a C functions calls back into go it will eventually call into
|
||||
// runtime.cgocallback_gofunc which is the function that does the stack
|
||||
// switch from the system stack back into the goroutine stack
|
||||
// Since we are going backwards on the stack here we see the transition
|
||||
// as goroutine stack -> system stack.
|
||||
if it.top || it.systemstack {
|
||||
return false
|
||||
}
|
||||
|
||||
it.loadG0SchedSP()
|
||||
if it.g0_sched_sp <= 0 {
|
||||
return false
|
||||
}
|
||||
// entering the system stack
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g0_sched_sp
|
||||
// reads the previous value of g0.sched.sp that runtime.cgocallback_gofunc saved on the stack
|
||||
it.g0_sched_sp, _ = readUintRaw(it.mem, it.regs.SP()+loong64cgocallSPOffsetSaveSlot, int64(it.bi.Arch.PtrSize()))
|
||||
it.top = false
|
||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||
frameOnSystemStack := it.newStackframe(ret, retaddr)
|
||||
it.pc = frameOnSystemStack.Ret
|
||||
it.regs = callFrameRegs
|
||||
it.systemstack = true
|
||||
return true
|
||||
|
||||
case "crosscall2":
|
||||
// The offsets get from runtime/cgo/asm_loong64.s:25
|
||||
newsp, _ := readUintRaw(it.mem, it.regs.SP()+8*23, int64(it.bi.Arch.PtrSize()))
|
||||
newbp, _ := readUintRaw(it.mem, it.regs.SP()+8*4, int64(it.bi.Arch.PtrSize()))
|
||||
newlr, _ := readUintRaw(it.mem, it.regs.SP()+8*22, int64(it.bi.Arch.PtrSize()))
|
||||
if it.regs.Reg(it.regs.BPRegNum) != nil {
|
||||
it.regs.Reg(it.regs.BPRegNum).Uint64Val = newbp
|
||||
} else {
|
||||
reg, _ := it.readRegisterAt(it.regs.BPRegNum, it.regs.SP()+8*4)
|
||||
it.regs.AddReg(it.regs.BPRegNum, reg)
|
||||
}
|
||||
it.regs.Reg(it.regs.LRRegNum).Uint64Val = newlr
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = newsp
|
||||
it.pc = newlr
|
||||
return true
|
||||
|
||||
case "runtime.mstart":
|
||||
// Calls to runtime.systemstack will switch to the systemstack then:
|
||||
// 1. alter the goroutine stack so that it looks like systemstack_switch
|
||||
// was called
|
||||
// 2. alter the system stack so that it looks like the bottom-most frame
|
||||
// belongs to runtime.mstart
|
||||
// If we find a runtime.mstart frame on the system stack of a goroutine
|
||||
// parked on runtime.systemstack_switch we assume runtime.systemstack was
|
||||
// called and continue tracing from the parked position.
|
||||
|
||||
if it.top || !it.systemstack || it.g == nil {
|
||||
return false
|
||||
}
|
||||
if fn := it.bi.PCToFunc(it.g.PC); fn == nil || fn.Name != "runtime.systemstack_switch" {
|
||||
return false
|
||||
}
|
||||
|
||||
it.switchToGoroutineStack()
|
||||
return true
|
||||
|
||||
case "runtime.newstack", "runtime.systemstack":
|
||||
if it.systemstack && it.g != nil {
|
||||
it.switchToGoroutineStack()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
@ -2961,7 +2961,6 @@ func TestCgoStacktrace(t *testing.T) {
|
||||
skipOn(t, "broken - cgo stacktraces", "windows", "arm64")
|
||||
skipOn(t, "broken - cgo stacktraces", "linux", "ppc64le")
|
||||
skipOn(t, "broken - cgo stacktraces", "linux", "riscv64")
|
||||
skipOn(t, "broken - cgo stacktraces", "linux", "loong64")
|
||||
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 21) {
|
||||
skipOn(t, "broken - cgo stacktraces", "windows", "arm64")
|
||||
}
|
||||
@ -4200,7 +4199,6 @@ func TestCgoStacktrace2(t *testing.T) {
|
||||
skipOn(t, "broken - cgo stacktraces", "darwin", "arm64")
|
||||
skipOn(t, "broken", "ppc64le")
|
||||
skipOn(t, "broken", "riscv64")
|
||||
skipOn(t, "broken", "loong64")
|
||||
protest.MustHaveCgo(t)
|
||||
// If a panic happens during cgo execution the stacktrace should show the C
|
||||
// function that caused the problem.
|
||||
|
||||
Reference in New Issue
Block a user