mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 09:46:56 +08:00
pkg/terminal: print DWARF location expression with whatis
Adds a configuration option (show-location-expr) that when activated will cause the whatis command to also print the DWARF location expression for a variable.
This commit is contained in:
@ -39,6 +39,10 @@ type Config struct {
|
||||
// MaxArrayValues is the maximum number of array items that the commands
|
||||
// print, locals, args and vars should read (in verbose mode).
|
||||
MaxArrayValues *int `yaml:"max-array-values,omitempty"`
|
||||
|
||||
// If ShowLocationExpr is true whatis will print the DWARF location
|
||||
// expression for its argument.
|
||||
ShowLocationExpr bool `yaml:"show-location-expr"`
|
||||
}
|
||||
|
||||
// LoadConfig attempts to populate a Config object from the config.yml file.
|
||||
@ -139,6 +143,15 @@ aliases:
|
||||
# commands.
|
||||
substitute-path:
|
||||
# - {from: path, to: path}
|
||||
|
||||
# Maximum number of elements loaded from an array.
|
||||
# max-array-values: 64
|
||||
|
||||
# Maximum loaded string length.
|
||||
# max-string-len: 64
|
||||
|
||||
# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
|
||||
# show-location-expr: true
|
||||
`)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
@ -73,6 +74,56 @@ func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, []Pie
|
||||
return ctxt.stack[len(ctxt.stack)-1], nil, nil
|
||||
}
|
||||
|
||||
// PrettyPrint prints instructions to out.
|
||||
func PrettyPrint(out io.Writer, instructions []byte) {
|
||||
in := bytes.NewBuffer(instructions)
|
||||
|
||||
for {
|
||||
opcode, err := in.ReadByte()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if name, hasname := opcodeName[Opcode(opcode)]; hasname {
|
||||
io.WriteString(out, name)
|
||||
out.Write([]byte{' '})
|
||||
} else {
|
||||
fmt.Fprintf(out, "%#x ", opcode)
|
||||
}
|
||||
for _, arg := range opcodeArgs[Opcode(opcode)] {
|
||||
switch arg {
|
||||
case 's':
|
||||
n, _ := util.DecodeSLEB128(in)
|
||||
fmt.Fprintf(out, "%#x ", n)
|
||||
case 'u':
|
||||
n, _ := util.DecodeULEB128(in)
|
||||
fmt.Fprintf(out, "%#x ", n)
|
||||
case '1':
|
||||
var x uint8
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case '2':
|
||||
var x uint16
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case '4':
|
||||
var x uint32
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case '8':
|
||||
var x uint64
|
||||
binary.Read(in, binary.LittleEndian, &x)
|
||||
fmt.Fprintf(out, "%#x ", x)
|
||||
case 'B':
|
||||
sz, _ := util.DecodeULEB128(in)
|
||||
data := make([]byte, sz)
|
||||
sz2, _ := in.Read(data)
|
||||
data = data[:sz2]
|
||||
fmt.Fprintf(out, "%d [%x] ", sz, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func callframecfa(opcode Opcode, ctxt *context) error {
|
||||
if ctxt.CFA == 0 {
|
||||
return fmt.Errorf("Could not retrieve CFA for current PC")
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
@ -341,26 +342,34 @@ func (bi *BinaryInfo) loclistInit(data []byte) {
|
||||
// This will either be an int64 address or a slice of Pieces for locations
|
||||
// that don't correspond to a single memory address (registers, composite
|
||||
// locations).
|
||||
func (bi *BinaryInfo) Location(entry *dwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, error) {
|
||||
func (bi *BinaryInfo) Location(entry *dwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, string, error) {
|
||||
a := entry.Val(attr)
|
||||
if a == nil {
|
||||
return 0, nil, fmt.Errorf("no location attribute %s", attr)
|
||||
return 0, nil, "", fmt.Errorf("no location attribute %s", attr)
|
||||
}
|
||||
if instr, ok := a.([]byte); ok {
|
||||
return op.ExecuteStackProgram(regs, instr)
|
||||
var descr bytes.Buffer
|
||||
fmt.Fprintf(&descr, "[block] ")
|
||||
op.PrettyPrint(&descr, instr)
|
||||
addr, pieces, err := op.ExecuteStackProgram(regs, instr)
|
||||
return addr, pieces, descr.String(), err
|
||||
}
|
||||
off, ok := a.(int64)
|
||||
if !ok {
|
||||
return 0, nil, fmt.Errorf("could not interpret location attribute %s", attr)
|
||||
return 0, nil, "", fmt.Errorf("could not interpret location attribute %s", attr)
|
||||
}
|
||||
if bi.loclist.data == nil {
|
||||
return 0, nil, fmt.Errorf("could not find loclist entry at %#x for address %#x (no debug_loc section found)", off, pc)
|
||||
return 0, nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x (no debug_loc section found)", off, pc)
|
||||
}
|
||||
instr := bi.loclistEntry(off, pc)
|
||||
if instr == nil {
|
||||
return 0, nil, fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
|
||||
return 0, nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
|
||||
}
|
||||
return op.ExecuteStackProgram(regs, instr)
|
||||
var descr bytes.Buffer
|
||||
fmt.Fprintf(&descr, "[%#x:%#x] ", off, pc)
|
||||
op.PrettyPrint(&descr, instr)
|
||||
addr, pieces, err := op.ExecuteStackProgram(regs, instr)
|
||||
return addr, pieces, descr.String(), err
|
||||
}
|
||||
|
||||
// loclistEntry returns the loclist entry in the loclist starting at off,
|
||||
|
||||
@ -332,7 +332,7 @@ func (it *stackIterator) frameBase(fn *Function) int64 {
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
fb, _, _ := it.bi.Location(e, dwarf.AttrFrameBase, it.pc, it.regs)
|
||||
fb, _, _, _ := it.bi.Location(e, dwarf.AttrFrameBase, it.pc, it.regs)
|
||||
return fb
|
||||
}
|
||||
|
||||
|
||||
@ -95,6 +95,8 @@ type Variable struct {
|
||||
|
||||
loaded bool
|
||||
Unreadable error
|
||||
|
||||
LocationExpr string // location expression
|
||||
}
|
||||
|
||||
type LoadConfig struct {
|
||||
@ -777,7 +779,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr, pieces, err := scope.BinInfo.Location(entry, dwarf.AttrLocation, scope.PC, scope.Regs)
|
||||
addr, pieces, descr, err := scope.BinInfo.Location(entry, dwarf.AttrLocation, scope.PC, scope.Regs)
|
||||
mem := scope.Mem
|
||||
if pieces != nil {
|
||||
addr = fakeAddress
|
||||
@ -785,6 +787,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable,
|
||||
}
|
||||
|
||||
v := scope.newVariable(n, uintptr(addr), t, mem)
|
||||
v.LocationExpr = descr
|
||||
if err != nil {
|
||||
v.Unreadable = err
|
||||
}
|
||||
|
||||
@ -981,6 +981,9 @@ func whatisCommand(t *Term, ctx callContext, args string) error {
|
||||
if val.Kind == reflect.Interface && len(val.Children) > 0 {
|
||||
fmt.Printf("Concrete type: %s\n", val.Children[0].Type)
|
||||
}
|
||||
if t.conf.ShowLocationExpr && val.LocationExpr != "" {
|
||||
fmt.Printf("location: %s\n", val.LocationExpr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -73,14 +73,14 @@ func configureList(t *Term) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if !field.IsNil() {
|
||||
if field.Kind() == reflect.Ptr {
|
||||
if !field.IsNil() {
|
||||
fmt.Fprintf(w, "%s\t%v\n", fieldName, field.Elem())
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%v\n", fieldName, field)
|
||||
fmt.Fprintf(w, "%s\t<not defined>\n", fieldName)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t<not defined>\n", fieldName)
|
||||
fmt.Fprintf(w, "%s\t%v\n", fieldName, field)
|
||||
}
|
||||
}
|
||||
return w.Flush()
|
||||
@ -116,6 +116,9 @@ func configureSet(t *Term, args string) error {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("argument to %q must be a number", cfgname)
|
||||
}
|
||||
return reflect.ValueOf(&n), nil
|
||||
case reflect.Bool:
|
||||
v := rest == "true"
|
||||
return reflect.ValueOf(&v), nil
|
||||
default:
|
||||
return reflect.ValueOf(nil), fmt.Errorf("unsupported type for configuration key %q", cfgname)
|
||||
}
|
||||
|
||||
@ -121,6 +121,8 @@ func ConvertVar(v *proc.Variable) *Variable {
|
||||
Cap: v.Cap,
|
||||
Flags: VariableFlags(v.Flags),
|
||||
Base: v.Base,
|
||||
|
||||
LocationExpr: v.LocationExpr,
|
||||
}
|
||||
|
||||
r.Type = prettyTypeName(v.DwarfType)
|
||||
|
||||
@ -213,6 +213,9 @@ type Variable struct {
|
||||
|
||||
// Unreadable addresses will have this field set
|
||||
Unreadable string `json:"unreadable"`
|
||||
|
||||
// LocationExpr describes the location expression of this variable's address
|
||||
LocationExpr string
|
||||
}
|
||||
|
||||
// LoadConfig describes how to load values from target's memory
|
||||
|
||||
Reference in New Issue
Block a user