Add command to print all goroutines info

This commit is contained in:
Derek Parker
2014-11-08 08:02:31 -06:00
parent 2e99a24b4e
commit 7d69c16512
3 changed files with 187 additions and 33 deletions

View File

@ -32,6 +32,7 @@ func DebugCommands() *Commands {
"clear": clear,
"print": printVar,
"threads": threads,
"goroutines": goroutines,
"": nullCommand,
}
@ -89,6 +90,10 @@ func threads(p *proctl.DebuggedProcess, ars ...string) error {
return p.PrintThreadInfo()
}
func goroutines(p *proctl.DebuggedProcess, ars ...string) error {
return p.PrintGoroutinesInfo()
}
func cont(p *proctl.DebuggedProcess, ars ...string) error {
err := p.Continue()
if err != nil {

View File

@ -2,12 +2,14 @@ package op
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/derekparker/delve/dwarf/util"
)
const (
DW_OP_addr = 0x3
DW_OP_call_frame_cfa = 0x9c
DW_OP_plus = 0x22
DW_OP_consts = 0x11
@ -19,6 +21,7 @@ var oplut = map[byte]stackfn{
DW_OP_call_frame_cfa: callframecfa,
DW_OP_plus: plus,
DW_OP_consts: consts,
DW_OP_addr: addr,
}
func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
@ -44,7 +47,15 @@ func callframecfa(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error)
return append(stack, int64(cfa)), nil
}
func addr(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
return append(stack, int64(binary.LittleEndian.Uint64(buf.Next(8)))), nil
}
func plus(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
if len(stack) == 1 {
return stack, nil
}
var (
slen = len(stack)
digits = stack[slen-2 : slen]

View File

@ -19,6 +19,137 @@ type Variable struct {
Type string
}
func (dbp *DebuggedProcess) PrintGoroutinesInfo() error {
data, err := dbp.Executable.DWARF()
if err != nil {
return err
}
allglen, err := allglenval(dbp, data)
if err != nil {
return err
}
goidoffset, err := parsegoidoffset(dbp, data)
if err != nil {
return err
}
schedoffset, err := parseschedoffset(dbp, data)
if err != nil {
return err
}
allgentryaddr, err := allgentryptr(dbp, data)
if err != nil {
return err
}
fmt.Printf("[%d goroutines]\n", allglen)
faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), 8)
allg := binary.LittleEndian.Uint64(faddr)
fmt.Println("sched", schedoffset)
for i := uint64(0); i < allglen; i++ {
err = printGoroutineInfo(dbp, allg+(i*8), goidoffset, schedoffset)
if err != nil {
return err
}
}
return nil
}
func printGoroutineInfo(dbp *DebuggedProcess, addr uint64, goidoffset, schedoffset uint64) error {
gaddrbytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
if err != nil {
return fmt.Errorf("error derefing *G %s", err)
}
gaddr := binary.LittleEndian.Uint64(gaddrbytes)
goidbytes, err := dbp.CurrentThread.readMemory(uintptr(gaddr+goidoffset), 8)
if err != nil {
return fmt.Errorf("error reading goid %s", err)
}
schedbytes, err := dbp.CurrentThread.readMemory(uintptr(gaddr+schedoffset+8), 8)
if err != nil {
return fmt.Errorf("error reading goid %s", err)
}
gopc := binary.LittleEndian.Uint64(schedbytes)
f, l, _ := dbp.GoSymTable.PCToLine(gopc)
fmt.Printf("Goroutine %d - %s:%d\n", binary.LittleEndian.Uint64(goidbytes), f, l)
return nil
}
func allglenval(dbp *DebuggedProcess, data *dwarf.Data) (uint64, error) {
entry, err := findDwarfEntry("runtime.allglen", data)
if err != nil {
return 0, err
}
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
if !ok {
return 0, fmt.Errorf("type assertion failed")
}
addr, err := op.ExecuteStackProgram(0, instructions)
if err != nil {
return 0, err
}
val, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint64(val), nil
}
func allgentryptr(dbp *DebuggedProcess, data *dwarf.Data) (uint64, error) {
entry, err := findDwarfEntry("runtime.allg", data)
if err != nil {
return 0, err
}
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
if !ok {
return 0, fmt.Errorf("type assertion failed")
}
addr, err := op.ExecuteStackProgram(0, instructions)
if err != nil {
return 0, err
}
return uint64(addr), nil
}
func parsegoidoffset(dbp *DebuggedProcess, data *dwarf.Data) (uint64, error) {
entry, err := findDwarfEntry("goid", data)
if err != nil {
return 0, err
}
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
if !ok {
return 0, fmt.Errorf("type assertion failed")
}
offset, err := op.ExecuteStackProgram(0, instructions)
if err != nil {
return 0, err
}
return uint64(offset), nil
}
func parseschedoffset(dbp *DebuggedProcess, data *dwarf.Data) (uint64, error) {
entry, err := findDwarfEntry("sched", data)
if err != nil {
return 0, err
}
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
if !ok {
return 0, fmt.Errorf("type assertion failed")
}
offset, err := op.ExecuteStackProgram(0, instructions)
if err != nil {
return 0, err
}
return uint64(offset), nil
}
// Returns the value of the named symbol.
func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) {
data, err := thread.Process.Executable.DWARF()
@ -26,25 +157,14 @@ func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) {
return nil, err
}
reader := data.Reader()
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
entry, err := findDwarfEntry(name, data)
if err != nil {
return nil, err
}
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter {
continue
}
n, ok := entry.Val(dwarf.AttrName).(string)
if !ok || n != name {
continue
}
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
if !ok {
continue
return nil, fmt.Errorf("type assertion failed")
}
t, err := data.Type(offset)
@ -54,7 +174,7 @@ func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) {
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
if !ok {
continue
return nil, fmt.Errorf("type assertion failed")
}
val, err := thread.extractValue(instructions, 0, t)
@ -62,9 +182,27 @@ func (thread *ThreadContext) EvalSymbol(name string) (*Variable, error) {
return nil, err
}
return &Variable{Name: n, Type: t.String(), Value: val}, nil
return &Variable{Name: name, Type: t.String(), Value: val}, nil
}
func findDwarfEntry(name string, data *dwarf.Data) (*dwarf.Entry, error) {
reader := data.Reader()
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagMember {
continue
}
n, ok := entry.Val(dwarf.AttrName).(string)
if !ok || n != name {
continue
}
return entry, nil
}
return nil, fmt.Errorf("could not find symbol value for %s", name)
}