diff --git a/command/command.go b/command/command.go index 197db3181a8f62713804e4c93c70413fbef83269..ef0af1c39ef43dd54f6e6294c590f82f5e392d35 100644 --- a/command/command.go +++ b/command/command.go @@ -158,7 +158,21 @@ func thread(p *proctl.DebuggedProcess, args ...string) error { } func goroutines(p *proctl.DebuggedProcess, args ...string) error { - return p.PrintGoroutinesInfo() + var fname string + gs, err := p.GoroutinesInfo() + if err != nil { + return err + } + + fmt.Printf("[%d goroutines]\n", len(gs)) + for _, g := range gs { + if g.Func != nil { + fname = g.Func.Name + } + fmt.Printf("Goroutine %d - %s:%d %s\n", g.Id, g.File, g.Line, fname) + } + + return nil } func cont(p *proctl.DebuggedProcess, args ...string) error { diff --git a/proctl/proctl.go b/proctl/proctl.go index 459050f005477d10590864a139b11da04bd2e121..4f7f9b99d3947a32388bed87f6637cd8d266dee5 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -5,6 +5,7 @@ package proctl import ( "debug/dwarf" "debug/gosym" + "encoding/binary" "fmt" "os" "os/exec" @@ -296,7 +297,7 @@ func (dbp *DebuggedProcess) next() error { } // Make sure we're on the same goroutine. // TODO(dp) take into account goroutine exit. - if tg.id == curg.id { + if tg.Id == curg.Id { if dbp.CurrentThread != thread { dbp.SwitchThread(thread.Id) } @@ -377,6 +378,34 @@ func (dbp *DebuggedProcess) SwitchThread(tid int) error { return fmt.Errorf("thread %d does not exist", tid) } +func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) { + var ( + allg []*G + reader = dbp.dwarf.Reader() + ) + + allglen, err := allglenval(dbp, reader) + if err != nil { + return nil, err + } + reader.Seek(0) + allgentryaddr, err := addressFor(dbp, "runtime.allg", reader) + if err != nil { + return nil, err + } + faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), ptrsize) + allgptr := binary.LittleEndian.Uint64(faddr) + + for i := uint64(0); i < allglen; i++ { + g, err := parseG(dbp, allgptr+(i*uint64(ptrsize)), reader) + if err != nil { + return nil, err + } + allg = append(allg, g) + } + return allg, nil +} + // Obtains register values from what Delve considers to be the current // thread of the traced process. func (dbp *DebuggedProcess) Registers() (Registers, error) { diff --git a/proctl/variables.go b/proctl/variables.go index 60998daa7e5b9790b22355917979e999c9dbb28d..4554bc459c5228e558675a527653f3f0e5ed6cb3 100644 --- a/proctl/variables.go +++ b/proctl/variables.go @@ -3,6 +3,7 @@ package proctl import ( "bytes" "debug/dwarf" + "debug/gosym" "encoding/binary" "fmt" "strconv" @@ -32,8 +33,11 @@ type M struct { } type G struct { - id int - pc uint64 + Id int + PC uint64 + File string + Line int + Func *gosym.Func } const ptrsize uintptr = unsafe.Sizeof(int(1)) @@ -205,37 +209,6 @@ func parseAllMPtr(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) { return uint64(addr), nil } -func (dbp *DebuggedProcess) PrintGoroutinesInfo() error { - reader := dbp.dwarf.Reader() - - allglen, err := allglenval(dbp, reader) - if err != nil { - return err - } - reader.Seek(0) - allgentryaddr, err := addressFor(dbp, "runtime.allg", reader) - if err != nil { - return err - } - fmt.Printf("[%d goroutines]\n", allglen) - faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), ptrsize) - allg := binary.LittleEndian.Uint64(faddr) - - for i := uint64(0); i < allglen; i++ { - g, err := parseG(dbp, allg+(i*uint64(ptrsize)), reader) - if err != nil { - return err - } - f, l, fn := dbp.goSymTable.PCToLine(g.pc) - fname := "" - if fn != nil { - fname = fn.Name - } - fmt.Printf("Goroutine %d - %s:%d %s\n", g.id, f, l, fname) - } - return nil -} - func parseG(dbp *DebuggedProcess, addr uint64, reader *dwarf.Reader) (*G, error) { gaddrbytes, err := dbp.CurrentThread.readMemory(uintptr(addr), ptrsize) if err != nil { @@ -263,7 +236,15 @@ func parseG(dbp *DebuggedProcess, addr uint64, reader *dwarf.Reader) (*G, error) return nil, fmt.Errorf("error reading sched %s", err) } gopc := binary.LittleEndian.Uint64(schedbytes) - return &G{id: int(binary.LittleEndian.Uint64(goidbytes)), pc: gopc}, nil + f, l, fn := dbp.goSymTable.PCToLine(gopc) + g := &G{ + Id: int(binary.LittleEndian.Uint64(goidbytes)), + PC: gopc, + File: f, + Line: l, + Func: fn, + } + return g, nil } func allglenval(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) {