mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-01 03:42:59 +08:00 
			
		
		
		
	Add command to print all goroutines info
This commit is contained in:
		| @ -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 { | ||||
|  | ||||
| @ -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] | ||||
|  | ||||
| @ -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) | ||||
| } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Derek Parker
					Derek Parker