mirror of
https://github.com/go-delve/delve.git
synced 2025-10-30 10:17:03 +08:00
proc: added fake runtime.curg variable
This commit is contained in:
@ -54,7 +54,9 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
|||||||
case *ast.SelectorExpr: // <expression>.<identifier>
|
case *ast.SelectorExpr: // <expression>.<identifier>
|
||||||
// try to interpret the selector as a package variable
|
// try to interpret the selector as a package variable
|
||||||
if maybePkg, ok := node.X.(*ast.Ident); ok {
|
if maybePkg, ok := node.X.(*ast.Ident); ok {
|
||||||
if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
|
if maybePkg.Name == "runtime" && node.Sel.Name == "curg" {
|
||||||
|
return scope.Thread.getGVariable()
|
||||||
|
} else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -497,7 +497,11 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
|||||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||||
|
|
||||||
for i := uint64(0); i < allglen; i++ {
|
for i := uint64(0); i < allglen; i++ {
|
||||||
g, err := parseG(dbp.CurrentThread, allgptr+(i*uint64(dbp.arch.PtrSize())), true)
|
gvar, err := dbp.CurrentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g, err := gvar.parseG()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"debug/dwarf"
|
||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -247,6 +248,48 @@ func (thread *Thread) SetPC(pc uint64) error {
|
|||||||
return regs.SetPC(thread, pc)
|
return regs.SetPC(thread, pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (thread *Thread) getGVariable() (*Variable, error) {
|
||||||
|
regs, err := thread.Registers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if thread.dbp.arch.GStructOffset() == 0 {
|
||||||
|
// GetG was called through SwitchThread / updateThreadList during initialization
|
||||||
|
// thread.dbp.arch isn't setup yet (it needs a CurrentThread to read global variables from)
|
||||||
|
return nil, fmt.Errorf("g struct offset not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), thread.dbp.arch.PtrSize())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
gaddr := uintptr(binary.LittleEndian.Uint64(gaddrbs))
|
||||||
|
|
||||||
|
// On Windows, the value at TLS()+GStructOffset() is a
|
||||||
|
// pointer to the G struct.
|
||||||
|
needsDeref := runtime.GOOS == "windows"
|
||||||
|
|
||||||
|
return thread.newGVariable(gaddr, needsDeref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) {
|
||||||
|
typ, err := thread.dbp.findType("runtime.g")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name := ""
|
||||||
|
|
||||||
|
if deref {
|
||||||
|
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.arch.PtrSize()), ""}, typ}
|
||||||
|
} else {
|
||||||
|
name = "runtime.curg"
|
||||||
|
}
|
||||||
|
|
||||||
|
return thread.newVariable(name, gaddr, typ), nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetG returns information on the G (goroutine) that is executing on this thread.
|
// GetG returns information on the G (goroutine) that is executing on this thread.
|
||||||
//
|
//
|
||||||
// The G structure for a thread is stored in thread local storage. Here we simply
|
// The G structure for a thread is stored in thread local storage. Here we simply
|
||||||
@ -262,26 +305,12 @@ func (thread *Thread) SetPC(pc uint64) error {
|
|||||||
// In order to get around all this craziness, we read the address of the G structure for
|
// In order to get around all this craziness, we read the address of the G structure for
|
||||||
// the current thread from the thread local storage area.
|
// the current thread from the thread local storage area.
|
||||||
func (thread *Thread) GetG() (g *G, err error) {
|
func (thread *Thread) GetG() (g *G, err error) {
|
||||||
regs, err := thread.Registers()
|
gaddr, err := thread.getGVariable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if thread.dbp.arch.GStructOffset() == 0 {
|
|
||||||
// GetG was called through SwitchThread / updateThreadList during initialization
|
g, err = gaddr.parseG()
|
||||||
// thread.dbp.arch isn't setup yet (it needs a CurrentThread to read global variables from)
|
|
||||||
return nil, fmt.Errorf("g struct offset not initialized")
|
|
||||||
}
|
|
||||||
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), thread.dbp.arch.PtrSize())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
gaddr := binary.LittleEndian.Uint64(gaddrbs)
|
|
||||||
|
|
||||||
// On Windows, the value at TLS()+GStructOffset() is a
|
|
||||||
// pointer to the G struct.
|
|
||||||
needsDeref := runtime.GOOS == "windows"
|
|
||||||
|
|
||||||
g, err = parseG(thread, gaddr, needsDeref)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
g.thread = thread
|
g.thread = thread
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,6 +143,10 @@ func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType dwarf.T
|
|||||||
return newVariable(name, addr, dwarfType, scope.Thread.dbp, scope.Thread)
|
return newVariable(name, addr, dwarfType, scope.Thread.dbp, scope.Thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Thread) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
|
||||||
|
return newVariable(name, addr, dwarfType, t.dbp, t)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Variable) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
|
func (v *Variable) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
|
||||||
return newVariable(name, addr, dwarfType, v.dbp, v.mem)
|
return newVariable(name, addr, dwarfType, v.dbp, v.mem)
|
||||||
}
|
}
|
||||||
@ -338,32 +342,40 @@ func (ng NoGError) Error() string {
|
|||||||
return fmt.Sprintf("no G executing on thread %d", ng.tid)
|
return fmt.Sprintf("no G executing on thread %d", ng.tid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
|
func (gvar *Variable) parseG() (*G, error) {
|
||||||
initialInstructions := make([]byte, thread.dbp.arch.PtrSize()+1)
|
mem := gvar.mem
|
||||||
|
dbp := gvar.dbp
|
||||||
|
gaddr := uint64(gvar.Addr)
|
||||||
|
_, deref := gvar.RealType.(*dwarf.PtrType)
|
||||||
|
|
||||||
|
initialInstructions := make([]byte, dbp.arch.PtrSize()+1)
|
||||||
initialInstructions[0] = op.DW_OP_addr
|
initialInstructions[0] = op.DW_OP_addr
|
||||||
binary.LittleEndian.PutUint64(initialInstructions[1:], gaddr)
|
binary.LittleEndian.PutUint64(initialInstructions[1:], gaddr)
|
||||||
if deref {
|
if deref {
|
||||||
gaddrbytes, err := thread.readMemory(uintptr(gaddr), thread.dbp.arch.PtrSize())
|
gaddrbytes, err := mem.readMemory(uintptr(gaddr), dbp.arch.PtrSize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error derefing *G %s", err)
|
return nil, fmt.Errorf("error derefing *G %s", err)
|
||||||
}
|
}
|
||||||
initialInstructions = append([]byte{op.DW_OP_addr}, gaddrbytes...)
|
initialInstructions = append([]byte{op.DW_OP_addr}, gaddrbytes...)
|
||||||
gaddr = binary.LittleEndian.Uint64(gaddrbytes)
|
gaddr = binary.LittleEndian.Uint64(gaddrbytes)
|
||||||
if gaddr == 0 {
|
if gaddr == 0 {
|
||||||
return nil, NoGError{tid: thread.ID}
|
id := 0
|
||||||
|
if thread, ok := mem.(*Thread); ok {
|
||||||
|
id = thread.ID
|
||||||
|
}
|
||||||
|
return nil, NoGError{tid: id}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rdr := thread.dbp.DwarfReader()
|
rdr := dbp.DwarfReader()
|
||||||
rdr.Seek(0)
|
rdr.Seek(0)
|
||||||
entry, err := rdr.SeekToTypeNamed("runtime.g")
|
entry, err := rdr.SeekToTypeNamed("runtime.g")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var mem memoryReadWriter = thread
|
if gtype, err := dbp.dwarf.Type(entry.Offset); err == nil {
|
||||||
if gtype, err := thread.dbp.dwarf.Type(entry.Offset); err == nil {
|
mem = cacheMemory(mem, uintptr(gaddr), int(gtype.Size()))
|
||||||
mem = cacheMemory(thread, uintptr(gaddr), int(gtype.Size()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse defer
|
// Parse defer
|
||||||
@ -373,7 +385,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
|
|||||||
}
|
}
|
||||||
var deferPC uint64
|
var deferPC uint64
|
||||||
// Dereference *defer pointer
|
// Dereference *defer pointer
|
||||||
deferAddrBytes, err := mem.readMemory(uintptr(deferAddr), thread.dbp.arch.PtrSize())
|
deferAddrBytes, err := mem.readMemory(uintptr(deferAddr), dbp.arch.PtrSize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error derefing defer %s", err)
|
return nil, fmt.Errorf("error derefing defer %s", err)
|
||||||
}
|
}
|
||||||
@ -412,7 +424,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pc, err := readUintRaw(mem, uintptr(schedAddr+uint64(thread.dbp.arch.PtrSize())), 8)
|
pc, err := readUintRaw(mem, uintptr(schedAddr+uint64(dbp.arch.PtrSize())), 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -436,7 +448,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
waitreason, _, err := readString(mem, thread.dbp.arch, uintptr(waitReasonAddr))
|
waitreason, _, err := readString(mem, dbp.arch, uintptr(waitReasonAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -450,7 +462,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
f, l, fn := thread.dbp.goSymTable.PCToLine(pc)
|
f, l, fn := dbp.goSymTable.PCToLine(pc)
|
||||||
g := &G{
|
g := &G{
|
||||||
ID: int(goid),
|
ID: int(goid),
|
||||||
GoPC: gopc,
|
GoPC: gopc,
|
||||||
@ -460,7 +472,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) {
|
|||||||
WaitReason: waitreason,
|
WaitReason: waitreason,
|
||||||
DeferPC: deferPC,
|
DeferPC: deferPC,
|
||||||
Status: atomicStatus,
|
Status: atomicStatus,
|
||||||
dbp: thread.dbp,
|
dbp: dbp,
|
||||||
}
|
}
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user