proc: implement stacktrace of arm64 (#1780)
* proc: separate amd64-arch code separate amd64 code about stacktrace, so we can add arm64 stacktrace code. * proc: implemente stacktrace of arm64 * delve now can use stack, frame commands on arm64-arch debug. Co-authored-by: tykcd996 <tang.yuke@zte.com.cn> Co-authored-by: hengwu0 <wu.heng@zte.com.cn> * test: remove skip-code of stacktrace on arm64 * add LR DWARF register and remove skip-code for fixed tests * proc: fix the Continue command after the hardcoded breakpoint on arm64 Arm64 use hardware breakpoint, and it will not set PC to the next instruction like amd64. We should move PC in both runtime.breakpoints and hardcoded breakpoints(probably cgo). * proc: implement cgo stacktrace on arm64 * proc: combine amd64_stack.go and arm64_stack.go file * proc: reorganize the stacktrace code * move SwitchStack function arch-related * fix Continue command after manual stop on arm64 * add timeout flag to make.go to enable infinite timeouts Co-authored-by: aarzilli <alessandro.arzilli@gmail.com> Co-authored-by: hengwu0 <wu.heng@zte.com.cn> Co-authored-by: tykcd996 <56993522+tykcd996@users.noreply.github.com> Co-authored-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
This commit is contained in:
parent
7fe81ca1d6
commit
3f7571ec30
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
// #cgo CFLAGS: -g -Wall -O0
|
||||
|
||||
/*
|
||||
void sigsegv(int x) {
|
||||
int *p = NULL;
|
||||
|
@ -2,7 +2,11 @@
|
||||
|
||||
#include "_cgo_export.h"
|
||||
|
||||
#ifdef __amd64__
|
||||
#define BREAKPOINT asm("int3;")
|
||||
#elif __aarch64__
|
||||
#define BREAKPOINT asm("brk 0;")
|
||||
#endif
|
||||
|
||||
void helloworld_pt2(int x) {
|
||||
BREAKPOINT;
|
||||
|
@ -17,6 +17,7 @@ type DwarfRegisters struct {
|
||||
PCRegNum uint64
|
||||
SPRegNum uint64
|
||||
BPRegNum uint64
|
||||
LRRegNum uint64
|
||||
}
|
||||
|
||||
type DwarfRegister struct {
|
||||
|
@ -2,6 +2,7 @@ package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/frame"
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
@ -159,6 +160,124 @@ func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *
|
||||
return fctxt
|
||||
}
|
||||
|
||||
// cgocallSPOffsetSaveSlot is the offset from systemstack.SP where
|
||||
// (goroutine.SP - StackHi) is saved in runtime.asmcgocall after the stack
|
||||
// switch happens.
|
||||
const amd64cgocallSPOffsetSaveSlot = 0x28
|
||||
|
||||
// SwitchStack will use the current frame to determine if it's time to
|
||||
// switch between the system stack and the goroutine stack or vice versa.
|
||||
// Sets it.atend when the top of the stack is reached.
|
||||
func (a *AMD64) SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
|
||||
if it.frame.Current.Fn == nil {
|
||||
return false
|
||||
}
|
||||
switch it.frame.Current.Fn.Name {
|
||||
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.
|
||||
off, _ := readIntRaw(it.mem, uintptr(it.regs.SP()+amd64cgocallSPOffsetSaveSlot), int64(it.bi.Arch.PtrSize())) // reads "offset of SP from StackHi" from where runtime.asmcgocall saved it
|
||||
oldsp := it.regs.SP()
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = uint64(int64(it.stackhi) - off)
|
||||
|
||||
// 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.systemstack = false
|
||||
|
||||
// advances to the next frame in the call stack
|
||||
it.frame.addrret = uint64(int64(it.regs.SP()) + int64(it.bi.Arch.PtrSize()))
|
||||
it.frame.Ret, _ = readUintRaw(it.mem, uintptr(it.frame.addrret), int64(it.bi.Arch.PtrSize()))
|
||||
it.pc = it.frame.Ret
|
||||
|
||||
it.top = false
|
||||
return true
|
||||
|
||||
case "runtime.cgocallback_gofunc":
|
||||
// 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_amd64.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
|
||||
}
|
||||
|
||||
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, uintptr(it.regs.SP()), 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 "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
|
||||
// Look for "top of stack" functions.
|
||||
it.atend = true
|
||||
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
|
||||
|
||||
default:
|
||||
if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && 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.
|
||||
//
|
||||
// The function "runtime.fatalthrow" is deliberately excluded from this
|
||||
// because it can end up in the stack during a cgo call and switching to
|
||||
// the goroutine stack will exclude all the C functions from the stack
|
||||
// trace.
|
||||
it.switchToGoroutineStack()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// RegSize returns the size (in bytes) of register regnum.
|
||||
// The mapping between hardware registers and DWARF registers is specified
|
||||
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
|
||||
@ -288,7 +407,7 @@ func (a *AMD64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.
|
||||
|
||||
// AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in
|
||||
// PC, SP, and BP registers in the format used by the DWARF expression interpreter.
|
||||
func (a *AMD64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp uint64) op.DwarfRegisters {
|
||||
func (a *AMD64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
|
||||
dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1)
|
||||
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc)
|
||||
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp)
|
||||
|
@ -17,9 +17,10 @@ type Arch interface {
|
||||
BreakpointSize() int
|
||||
DerefTLS() bool
|
||||
FixFrameUnwindContext(*frame.FrameContext, uint64, *BinaryInfo) *frame.FrameContext
|
||||
SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool
|
||||
RegSize(uint64) int
|
||||
RegistersToDwarfRegisters(uint64, Registers) op.DwarfRegisters
|
||||
AddrAndStackRegsToDwarfRegisters(uint64, uint64, uint64, uint64) op.DwarfRegisters
|
||||
AddrAndStackRegsToDwarfRegisters(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -2,6 +2,7 @@ package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/frame"
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
@ -28,6 +29,7 @@ type ARM64 struct {
|
||||
const (
|
||||
arm64DwarfIPRegNum uint64 = 32
|
||||
arm64DwarfSPRegNum uint64 = 31
|
||||
arm64DwarfLRRegNum uint64 = 30
|
||||
arm64DwarfBPRegNum uint64 = 29
|
||||
)
|
||||
|
||||
@ -154,10 +156,116 @@ func (a *ARM64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *
|
||||
Offset: 0,
|
||||
}
|
||||
}
|
||||
if fctxt.Regs[arm64DwarfLRRegNum].Rule == frame.RuleUndefined {
|
||||
fctxt.Regs[arm64DwarfLRRegNum] = frame.DWRule{
|
||||
Rule: frame.RuleFramePointer,
|
||||
Reg: arm64DwarfLRRegNum,
|
||||
Offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
return fctxt
|
||||
}
|
||||
|
||||
const arm64cgocallSPOffsetSaveSlot = 0x8
|
||||
const prevG0schedSPOffsetSaveSlot = 0x10
|
||||
const spAlign = 16
|
||||
|
||||
func (a *ARM64) SwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool {
|
||||
if it.frame.Current.Fn != nil {
|
||||
switch it.frame.Current.Fn.Name {
|
||||
case "runtime.asmcgocall", "runtime.cgocallback_gofunc", "runtime.sigpanic":
|
||||
//do nothing
|
||||
case "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
|
||||
// Look for "top of stack" functions.
|
||||
it.atend = true
|
||||
return true
|
||||
case "crosscall2":
|
||||
//The offsets get from runtime/cgo/asm_arm64.s:10
|
||||
newsp, _ := readUintRaw(it.mem, uintptr(it.regs.SP()+8*24), int64(it.bi.Arch.PtrSize()))
|
||||
newbp, _ := readUintRaw(it.mem, uintptr(it.regs.SP()+8*14), int64(it.bi.Arch.PtrSize()))
|
||||
newlr, _ := readUintRaw(it.mem, uintptr(it.regs.SP()+8*15), int64(it.bi.Arch.PtrSize()))
|
||||
if it.regs.Reg(it.regs.BPRegNum) != nil {
|
||||
it.regs.Reg(it.regs.BPRegNum).Uint64Val = uint64(newbp)
|
||||
} else {
|
||||
reg, _ := it.readRegisterAt(it.regs.BPRegNum, it.regs.SP()+8*14)
|
||||
it.regs.AddReg(it.regs.BPRegNum, reg)
|
||||
}
|
||||
it.regs.Reg(it.regs.LRRegNum).Uint64Val = uint64(newlr)
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = uint64(newsp)
|
||||
it.pc = newlr
|
||||
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.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.
|
||||
it.switchToGoroutineStack()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, _, fn := it.bi.PCToLine(it.frame.Ret)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.asmcgocall":
|
||||
if !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.
|
||||
off, _ := readIntRaw(it.mem, uintptr(callFrameRegs.SP()+arm64cgocallSPOffsetSaveSlot), int64(it.bi.Arch.PtrSize()))
|
||||
oldsp := callFrameRegs.SP()
|
||||
newsp := uint64(int64(it.stackhi) - off)
|
||||
|
||||
// runtime.asmcgocall can also be called from inside the system stack,
|
||||
// in that case no stack switch actually happens
|
||||
if newsp == oldsp {
|
||||
return false
|
||||
}
|
||||
it.systemstack = false
|
||||
callFrameRegs.Reg(callFrameRegs.SPRegNum).Uint64Val = uint64(int64(newsp))
|
||||
return false
|
||||
|
||||
case "runtime.cgocallback_gofunc":
|
||||
// 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_arm64.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.systemstack {
|
||||
return false
|
||||
}
|
||||
|
||||
if it.g0_sched_sp <= 0 {
|
||||
return false
|
||||
}
|
||||
// entering the system stack
|
||||
callFrameRegs.Reg(callFrameRegs.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, uintptr(callFrameRegs.SP()+prevG0schedSPOffsetSaveSlot), int64(it.bi.Arch.PtrSize()))
|
||||
it.systemstack = true
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *ARM64) RegSize(regnum uint64) int {
|
||||
// fp registers
|
||||
if regnum >= 64 && regnum <= 95 {
|
||||
@ -257,6 +365,9 @@ func (a *ARM64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.
|
||||
dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
|
||||
dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP())
|
||||
dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP())
|
||||
if lr, err := regs.Get(int(arm64asm.X30)); err != nil {
|
||||
dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr)
|
||||
}
|
||||
|
||||
for dwarfReg, asmReg := range arm64DwarfToHardware {
|
||||
v, err := regs.Get(int(asmReg))
|
||||
@ -272,16 +383,18 @@ func (a *ARM64) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.
|
||||
PCRegNum: arm64DwarfIPRegNum,
|
||||
SPRegNum: arm64DwarfSPRegNum,
|
||||
BPRegNum: arm64DwarfBPRegNum,
|
||||
LRRegNum: arm64DwarfLRRegNum,
|
||||
}
|
||||
}
|
||||
|
||||
// AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in
|
||||
// PC, SP, and BP registers in the format used by the DWARF expression interpreter.
|
||||
func (a *ARM64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp uint64) op.DwarfRegisters {
|
||||
func (a *ARM64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
|
||||
dregs := make([]*op.DwarfRegister, arm64DwarfIPRegNum+1)
|
||||
dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc)
|
||||
dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp)
|
||||
dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp)
|
||||
dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr)
|
||||
|
||||
return op.DwarfRegisters{
|
||||
StaticBase: staticBase,
|
||||
@ -290,5 +403,6 @@ func (a *ARM64) AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp uint64)
|
||||
PCRegNum: arm64DwarfIPRegNum,
|
||||
SPRegNum: arm64DwarfSPRegNum,
|
||||
BPRegNum: arm64DwarfBPRegNum,
|
||||
LRRegNum: arm64DwarfLRRegNum,
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func (r *ARM64Registers) GAddr() (uint64, bool) {
|
||||
return r.Regs.Regs[28], true
|
||||
}
|
||||
|
||||
// Get returns the value of the n-th register (in x86asm order).
|
||||
// Get returns the value of the n-th register (in arm64asm order).
|
||||
func (r *ARM64Registers) Get(n int) (uint64, error) {
|
||||
reg := arm64asm.Reg(n)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -212,13 +213,14 @@ func Continue(dbp Process) error {
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
g, _ := GetG(curthread)
|
||||
arch := dbp.BinInfo().Arch
|
||||
|
||||
switch {
|
||||
case loc.Fn.Name == "runtime.breakpoint":
|
||||
// In linux-arm64, PtraceSingleStep seems cannot step over BRK instruction
|
||||
// (linux-arm64 feature or kernel bug maybe).
|
||||
if !curthread.Arch().BreakInstrMovesPC() {
|
||||
curthread.SetPC(loc.PC + uint64(curthread.Arch().BreakpointSize()))
|
||||
if !arch.BreakInstrMovesPC() {
|
||||
curthread.SetPC(loc.PC + uint64(arch.BreakpointSize()))
|
||||
}
|
||||
// Single-step current thread until we exit runtime.breakpoint and
|
||||
// runtime.Breakpoint.
|
||||
@ -229,7 +231,15 @@ func Continue(dbp Process) error {
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
case g == nil || dbp.Common().fncallForG[g.ID] == nil:
|
||||
// a hardcoded breakpoint somewhere else in the code (probably cgo)
|
||||
// a hardcoded breakpoint somewhere else in the code (probably cgo), or manual stop in cgo
|
||||
if !arch.BreakInstrMovesPC() {
|
||||
bpsize := arch.BreakpointSize()
|
||||
bp := make([]byte, bpsize)
|
||||
_, err = dbp.CurrentThread().ReadMemory(bp, uintptr(loc.PC))
|
||||
if bytes.Equal(bp, arch.BreakpointInstruction()) {
|
||||
curthread.SetPC(loc.PC + uint64(bpsize))
|
||||
}
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
case curbp.Active && curbp.Internal:
|
||||
|
@ -820,9 +820,6 @@ func (l1 *loc) match(l2 proc.Stackframe) bool {
|
||||
}
|
||||
|
||||
func TestStacktrace(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
stacks := [][]loc{
|
||||
{{4, "main.stacktraceme"}, {8, "main.func1"}, {16, "main.main"}},
|
||||
{{4, "main.stacktraceme"}, {8, "main.func1"}, {12, "main.func2"}, {17, "main.main"}},
|
||||
@ -858,9 +855,6 @@ func TestStacktrace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStacktrace2(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
withTestProcess("retstack", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
|
||||
@ -909,9 +903,6 @@ func stackMatch(stack []loc, locations []proc.Stackframe, skipRuntime bool) bool
|
||||
}
|
||||
|
||||
func TestStacktraceGoroutine(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
mainStack := []loc{{14, "main.stacktraceme"}, {29, "main.main"}}
|
||||
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
|
||||
mainStack[0].line = 15
|
||||
@ -1235,9 +1226,6 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFrameEvaluation(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("goroutinestackprog", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
setFunctionBreakpoint(p, t, "main.stacktraceme")
|
||||
@ -1718,9 +1706,6 @@ func TestIssue384(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue332_Part1(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// Next shouldn't step inside a function call
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("issue332", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
@ -1742,9 +1727,6 @@ func TestIssue332_Part1(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue332_Part2(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// Step should skip a function's prologue
|
||||
// In some parts of the prologue, for some functions, the FDE data is incorrect
|
||||
// which leads to 'next' and 'stack' failing with error "could not find FDE for PC: <garbage>"
|
||||
@ -1902,9 +1884,6 @@ func TestCmdLineArgs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue462(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// Stacktrace of Goroutine 0 fails with an error
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
@ -1934,9 +1913,6 @@ func TestNextParked(t *testing.T) {
|
||||
if runtime.GOOS == "freebsd" {
|
||||
t.Skip("test is not valid on FreeBSD")
|
||||
}
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("parallel_next", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
bp := setFunctionBreakpoint(p, t, "main.sayhi")
|
||||
@ -1987,9 +1963,6 @@ func TestNextParked(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStepParked(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
if runtime.GOOS == "freebsd" {
|
||||
t.Skip("test is not valid on FreeBSD")
|
||||
}
|
||||
@ -2335,9 +2308,6 @@ func TestStepConcurrentDirect(t *testing.T) {
|
||||
if runtime.GOOS == "freebsd" {
|
||||
t.Skip("test is not valid on FreeBSD")
|
||||
}
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
bp := setFileBreakpoint(p, t, fixture.Source, 37)
|
||||
@ -2713,9 +2683,6 @@ func getg(goid int, gs []*proc.G) *proc.G {
|
||||
}
|
||||
|
||||
func TestStacktraceWithBarriers(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// Go's Garbage Collector will insert stack barriers into stacks.
|
||||
// This stack barrier is inserted by overwriting the return address for the
|
||||
// stack frame with the address of runtime.stackBarrier.
|
||||
@ -3281,9 +3248,6 @@ func frameInFile(frame proc.Stackframe, file string) bool {
|
||||
}
|
||||
|
||||
func TestCgoStacktrace(t *testing.T) {
|
||||
if runtime.GOARCH != "amd64" {
|
||||
t.Skip("amd64 only")
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
ver, _ := goversion.Parse(runtime.Version())
|
||||
if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
|
||||
@ -3389,9 +3353,6 @@ func TestCgoStacktrace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCgoSources(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Cgo-debug for now")
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
ver, _ := goversion.Parse(runtime.Version())
|
||||
if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
|
||||
@ -3417,9 +3378,6 @@ func TestCgoSources(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSystemstackStacktrace(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// check that we can follow a stack switch initiated by runtime.systemstack()
|
||||
withTestProcess("panic", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
setFunctionBreakpoint(p, t, "runtime.startpanic_m")
|
||||
@ -3438,9 +3396,6 @@ func TestSystemstackStacktrace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSystemstackOnRuntimeNewstack(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// The bug being tested here manifests as follows:
|
||||
// - set a breakpoint somewhere or interrupt the program with Ctrl-C
|
||||
// - try to look at stacktraces of other goroutines
|
||||
@ -3474,9 +3429,6 @@ func TestSystemstackOnRuntimeNewstack(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue1034(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace and Cgo-debug for now")
|
||||
}
|
||||
// The external linker on macOS produces an abbrev for DW_TAG_subprogram
|
||||
// without the "has children" flag, we should support this.
|
||||
withTestProcess("cgostacktest/", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
@ -3494,9 +3446,6 @@ func TestIssue1034(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue1008(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace and Cgo-debug for now")
|
||||
}
|
||||
// The external linker on macOS inserts "end of sequence" extended opcodes
|
||||
// in debug_line. which we should support correctly.
|
||||
withTestProcess("cgostacktest/", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
@ -3675,9 +3624,6 @@ func TestAllPCsForFileLines(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInlinedStacktraceAndVariables(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
|
||||
// Versions of go before 1.10 do not have DWARF information for inlined calls
|
||||
t.Skip("inlining not supported")
|
||||
@ -4124,7 +4070,7 @@ func TestNextUnknownInstr(t *testing.T) {
|
||||
|
||||
func TestReadDeferArgs(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
t.Skip("arm64 does not support ReadDeferArgs for now")
|
||||
}
|
||||
var tests = []struct {
|
||||
frame, deferCall int
|
||||
@ -4475,9 +4421,6 @@ func TestIssue1615(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCgoStacktrace2(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace and Cgo-debug for now")
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture crashes go runtime on windows")
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/frame"
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
@ -127,7 +126,7 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
|
||||
so := g.variable.bi.PCToImage(g.PC)
|
||||
return newStackIterator(
|
||||
g.variable.bi, g.variable.mem,
|
||||
g.variable.bi.Arch.AddrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP),
|
||||
g.variable.bi.Arch.AddrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR),
|
||||
g.stackhi, stkbar, g.stkbarPos, g, opts), nil
|
||||
}
|
||||
|
||||
@ -242,6 +241,7 @@ func (it *stackIterator) Next() bool {
|
||||
if it.err != nil || it.atend {
|
||||
return false
|
||||
}
|
||||
|
||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||
it.frame = it.newStackframe(ret, retaddr)
|
||||
|
||||
@ -252,7 +252,7 @@ func (it *stackIterator) Next() bool {
|
||||
}
|
||||
|
||||
if it.opts&StacktraceSimple == 0 {
|
||||
if it.switchStack() {
|
||||
if it.bi.Arch.SwitchStack(it, &callFrameRegs) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -268,131 +268,15 @@ func (it *stackIterator) Next() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// asmcgocallSPOffsetSaveSlot is the offset from systemstack.SP where
|
||||
// (goroutine.SP - StackHi) is saved in runtime.asmcgocall after the stack
|
||||
// switch happens.
|
||||
const asmcgocallSPOffsetSaveSlot = 0x28
|
||||
|
||||
// switchStack will use the current frame to determine if it's time to
|
||||
// switch between the system stack and the goroutine stack or vice versa.
|
||||
// Sets it.atend when the top of the stack is reached.
|
||||
func (it *stackIterator) switchStack() bool {
|
||||
if it.frame.Current.Fn == nil {
|
||||
return false
|
||||
}
|
||||
switch it.frame.Current.Fn.Name {
|
||||
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 switch
|
||||
// from the system stack to the goroutine stack.
|
||||
|
||||
off, _ := readIntRaw(it.mem, uintptr(it.regs.SP()+asmcgocallSPOffsetSaveSlot), int64(it.bi.Arch.PtrSize())) // reads "offset of SP from StackHi" from where runtime.asmcgocall saved it
|
||||
oldsp := it.regs.SP()
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = uint64(int64(it.stackhi) - off)
|
||||
|
||||
// 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.systemstack = false
|
||||
|
||||
// advances to the next frame in the call stack
|
||||
it.frame.addrret = uint64(int64(it.regs.SP()) + int64(it.bi.Arch.PtrSize()))
|
||||
it.frame.Ret, _ = readUintRaw(it.mem, uintptr(it.frame.addrret), int64(it.bi.Arch.PtrSize()))
|
||||
it.pc = it.frame.Ret
|
||||
|
||||
it.top = false
|
||||
return true
|
||||
|
||||
case "runtime.cgocallback_gofunc":
|
||||
// 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_amd64.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
|
||||
}
|
||||
|
||||
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, uintptr(it.regs.SP()), 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 "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
|
||||
// Look for "top of stack" functions.
|
||||
it.atend = true
|
||||
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
|
||||
|
||||
default:
|
||||
if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && 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.
|
||||
//
|
||||
// The function "runtime.fatalthrow" is deliberately excluded from this
|
||||
// because it can end up in the stack during a cgo call and switching to
|
||||
// the goroutine stack will exclude all the C functions from the stack
|
||||
// trace.
|
||||
it.switchToGoroutineStack()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (it *stackIterator) switchToGoroutineStack() {
|
||||
it.systemstack = false
|
||||
it.top = false
|
||||
it.pc = it.g.PC
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g.SP
|
||||
it.regs.Reg(it.regs.BPRegNum).Uint64Val = it.g.BP
|
||||
if _, ok := it.bi.Arch.(*ARM64); ok {
|
||||
it.regs.Reg(it.regs.LRRegNum).Uint64Val = it.g.LR
|
||||
}
|
||||
}
|
||||
|
||||
// Frame returns the frame the iterator is pointing at.
|
||||
@ -552,7 +436,7 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
||||
|
||||
callimage := it.bi.PCToImage(it.pc)
|
||||
|
||||
callFrameRegs = op.DwarfRegisters{StaticBase: callimage.StaticBase, ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum, BPRegNum: it.regs.BPRegNum}
|
||||
callFrameRegs = op.DwarfRegisters{StaticBase: callimage.StaticBase, ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum, BPRegNum: it.regs.BPRegNum, LRRegNum: it.regs.LRRegNum}
|
||||
|
||||
// According to the standard the compiler should be responsible for emitting
|
||||
// rules for the RSP register so that it can then be used to calculate CFA,
|
||||
@ -561,7 +445,11 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
||||
// implicit.
|
||||
// See also the comment in dwarf2_frame_default_init in
|
||||
// $GDB_SOURCE/dwarf2-frame.c
|
||||
callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg)
|
||||
if _, ok := it.bi.Arch.(*ARM64); ok {
|
||||
callFrameRegs.AddReg(uint64(arm64DwarfSPRegNum), cfareg)
|
||||
} else {
|
||||
callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg)
|
||||
}
|
||||
|
||||
for i, regRule := range framectx.Regs {
|
||||
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
||||
@ -579,6 +467,12 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := it.bi.Arch.(*ARM64); ok {
|
||||
if ret == 0 && it.regs.Regs[it.regs.LRRegNum] != nil {
|
||||
ret = it.regs.Regs[it.regs.LRRegNum].Uint64Val
|
||||
}
|
||||
}
|
||||
|
||||
return callFrameRegs, ret, retaddr
|
||||
}
|
||||
|
||||
@ -589,6 +483,9 @@ func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, c
|
||||
case frame.RuleUndefined:
|
||||
return nil, nil
|
||||
case frame.RuleSameVal:
|
||||
if it.regs.Reg(regnum) == nil {
|
||||
return nil, nil
|
||||
}
|
||||
reg := *it.regs.Reg(regnum)
|
||||
return ®, nil
|
||||
case frame.RuleOffset:
|
||||
|
@ -2,7 +2,6 @@ package proc_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
@ -10,9 +9,6 @@ import (
|
||||
)
|
||||
|
||||
func TestGoroutineCreationLocation(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support GetStackInfo for now")
|
||||
}
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("goroutinestackprog", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
bp := setFunctionBreakpoint(p, t, "main.agoroutine")
|
||||
|
@ -190,6 +190,7 @@ type G struct {
|
||||
PC uint64 // PC of goroutine when it was parked.
|
||||
SP uint64 // SP of goroutine when it was parked.
|
||||
BP uint64 // BP of goroutine when it was parked (go >= 1.7).
|
||||
LR uint64 // LR of goroutine when it was parked.
|
||||
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
||||
StartPC uint64 // PC of the first function run on this goroutine.
|
||||
WaitReason string // Reason for goroutine being parked.
|
||||
@ -550,10 +551,13 @@ func (v *Variable) parseG() (*G, error) {
|
||||
schedVar := v.fieldVariable("sched")
|
||||
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
||||
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
||||
var bp int64
|
||||
var bp, lr int64
|
||||
if bpvar := schedVar.fieldVariable("bp"); bpvar != nil && bpvar.Value != nil {
|
||||
bp, _ = constant.Int64Val(bpvar.Value)
|
||||
}
|
||||
if bpvar := schedVar.fieldVariable("lr"); bpvar != nil && bpvar.Value != nil {
|
||||
lr, _ = constant.Int64Val(bpvar.Value)
|
||||
}
|
||||
id, _ := constant.Int64Val(v.fieldVariable("goid").Value)
|
||||
gopc, _ := constant.Int64Val(v.fieldVariable("gopc").Value)
|
||||
startpc, _ := constant.Int64Val(v.fieldVariable("startpc").Value)
|
||||
@ -594,6 +598,7 @@ func (v *Variable) parseG() (*G, error) {
|
||||
PC: uint64(pc),
|
||||
SP: uint64(sp),
|
||||
BP: uint64(bp),
|
||||
LR: uint64(lr),
|
||||
WaitReason: waitReason,
|
||||
Status: uint64(status),
|
||||
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
||||
|
@ -295,9 +295,6 @@ func TestExitStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestScopePrefix(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
const goroutinesLinePrefix = " Goroutine "
|
||||
const goroutinesCurLinePrefix = "* Goroutine "
|
||||
test.AllowRecording(t)
|
||||
@ -866,9 +863,6 @@ func TestIssue1090(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPrintContextParkedGoroutine(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
|
||||
term.MustExec("break stacktraceme")
|
||||
term.MustExec("continue")
|
||||
@ -942,9 +936,6 @@ func TestOptimizationCheck(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTruncateStacktrace(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
withTestTerminal("stacktraceprog", t, func(term *FakeTerminal) {
|
||||
term.MustExec("break main.stacktraceme")
|
||||
term.MustExec("continue")
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
const DelveMainPackagePath = "github.com/go-delve/delve/cmd/dlv"
|
||||
|
||||
var Verbose bool
|
||||
var NOTimeout bool
|
||||
var TestSet, TestRegex, TestBackend, TestBuildMode string
|
||||
|
||||
func NewMakeCommands() *cobra.Command {
|
||||
@ -80,6 +81,7 @@ Use the flags -s, -r and -b to specify which tests to run. Specifying nothing is
|
||||
Run: testCmd,
|
||||
}
|
||||
test.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "Verbose tests")
|
||||
test.PersistentFlags().BoolVarP(&NOTimeout, "timeout", "t", false, "Set infinite timeouts")
|
||||
test.PersistentFlags().StringVarP(&TestSet, "test-set", "s", "", `Select the set of tests to run, one of either:
|
||||
all tests all packages
|
||||
basic tests proc, integration and terminal
|
||||
@ -259,6 +261,10 @@ func testFlags() []string {
|
||||
if Verbose {
|
||||
testFlags = append(testFlags, "-v")
|
||||
}
|
||||
if NOTimeout {
|
||||
testFlags = append(testFlags, "-timeout")
|
||||
testFlags = append(testFlags, "0")
|
||||
}
|
||||
if runtime.GOOS == "darwin" {
|
||||
testFlags = append(testFlags, "-exec="+wd+"/scripts/testsign")
|
||||
}
|
||||
|
@ -724,9 +724,6 @@ func Test1ClientServer_SetVariable(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1ClientServer_FullStacktrace(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
withTestClient1("goroutinestackprog", t, func(c *rpc1.RPCClient) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.stacktraceme", Line: -1})
|
||||
assertNoError(err, t, "CreateBreakpoint()")
|
||||
@ -799,9 +796,6 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test1Issue355(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// After the target process has terminated should return an error but not crash
|
||||
withTestClient1("continuetestprog", t, func(c *rpc1.RPCClient) {
|
||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1})
|
||||
|
@ -803,9 +803,6 @@ func TestClientServer_SetVariable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
protest.AllowRecording(t)
|
||||
withTestClient2("goroutinestackprog", t, func(c service.Client) {
|
||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.stacktraceme", Line: -1})
|
||||
@ -879,9 +876,6 @@ func TestClientServer_FullStacktrace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssue355(t *testing.T) {
|
||||
if runtime.GOARCH == "arm64" {
|
||||
t.Skip("arm64 does not support Stacktrace for now")
|
||||
}
|
||||
// After the target process has terminated should return an error but not crash
|
||||
protest.AllowRecording(t)
|
||||
withTestClient2("continuetestprog", t, func(c service.Client) {
|
||||
|
Loading…
Reference in New Issue
Block a user