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
|
// MaxArrayValues is the maximum number of array items that the commands
|
||||||
// print, locals, args and vars should read (in verbose mode).
|
// print, locals, args and vars should read (in verbose mode).
|
||||||
MaxArrayValues *int `yaml:"max-array-values,omitempty"`
|
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.
|
// LoadConfig attempts to populate a Config object from the config.yml file.
|
||||||
@ -139,6 +143,15 @@ aliases:
|
|||||||
# commands.
|
# commands.
|
||||||
substitute-path:
|
substitute-path:
|
||||||
# - {from: path, to: 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
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"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
|
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 {
|
func callframecfa(opcode Opcode, ctxt *context) error {
|
||||||
if ctxt.CFA == 0 {
|
if ctxt.CFA == 0 {
|
||||||
return fmt.Errorf("Could not retrieve CFA for current PC")
|
return fmt.Errorf("Could not retrieve CFA for current PC")
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/macho"
|
"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
|
// 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
|
// that don't correspond to a single memory address (registers, composite
|
||||||
// locations).
|
// 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)
|
a := entry.Val(attr)
|
||||||
if a == nil {
|
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 {
|
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)
|
off, ok := a.(int64)
|
||||||
if !ok {
|
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 {
|
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)
|
instr := bi.loclistEntry(off, pc)
|
||||||
if instr == nil {
|
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,
|
// 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 {
|
if err != nil {
|
||||||
return 0
|
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
|
return fb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,6 +95,8 @@ type Variable struct {
|
|||||||
|
|
||||||
loaded bool
|
loaded bool
|
||||||
Unreadable error
|
Unreadable error
|
||||||
|
|
||||||
|
LocationExpr string // location expression
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoadConfig struct {
|
type LoadConfig struct {
|
||||||
@ -777,7 +779,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable,
|
|||||||
return nil, err
|
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
|
mem := scope.Mem
|
||||||
if pieces != nil {
|
if pieces != nil {
|
||||||
addr = fakeAddress
|
addr = fakeAddress
|
||||||
@ -785,6 +787,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable,
|
|||||||
}
|
}
|
||||||
|
|
||||||
v := scope.newVariable(n, uintptr(addr), t, mem)
|
v := scope.newVariable(n, uintptr(addr), t, mem)
|
||||||
|
v.LocationExpr = descr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Unreadable = err
|
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 {
|
if val.Kind == reflect.Interface && len(val.Children) > 0 {
|
||||||
fmt.Printf("Concrete type: %s\n", val.Children[0].Type)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -73,14 +73,14 @@ func configureList(t *Term) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !field.IsNil() {
|
if field.Kind() == reflect.Ptr {
|
||||||
if field.Kind() == reflect.Ptr {
|
if !field.IsNil() {
|
||||||
fmt.Fprintf(w, "%s\t%v\n", fieldName, field.Elem())
|
fmt.Fprintf(w, "%s\t%v\n", fieldName, field.Elem())
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "%s\t%v\n", fieldName, field)
|
fmt.Fprintf(w, "%s\t<not defined>\n", fieldName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "%s\t<not defined>\n", fieldName)
|
fmt.Fprintf(w, "%s\t%v\n", fieldName, field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return w.Flush()
|
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(nil), fmt.Errorf("argument to %q must be a number", cfgname)
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(&n), nil
|
return reflect.ValueOf(&n), nil
|
||||||
|
case reflect.Bool:
|
||||||
|
v := rest == "true"
|
||||||
|
return reflect.ValueOf(&v), nil
|
||||||
default:
|
default:
|
||||||
return reflect.ValueOf(nil), fmt.Errorf("unsupported type for configuration key %q", cfgname)
|
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,
|
Cap: v.Cap,
|
||||||
Flags: VariableFlags(v.Flags),
|
Flags: VariableFlags(v.Flags),
|
||||||
Base: v.Base,
|
Base: v.Base,
|
||||||
|
|
||||||
|
LocationExpr: v.LocationExpr,
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Type = prettyTypeName(v.DwarfType)
|
r.Type = prettyTypeName(v.DwarfType)
|
||||||
|
|||||||
@ -213,6 +213,9 @@ type Variable struct {
|
|||||||
|
|
||||||
// Unreadable addresses will have this field set
|
// Unreadable addresses will have this field set
|
||||||
Unreadable string `json:"unreadable"`
|
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
|
// LoadConfig describes how to load values from target's memory
|
||||||
|
|||||||
Reference in New Issue
Block a user