diff --git a/pkg/config/config.go b/pkg/config/config.go index 35d2a63a8523942d46b44fa136c2eca430d262c6..1345aad9507c4b65b2cf4a6c65c5c0badca8f0ff 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -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 } diff --git a/pkg/dwarf/op/op.go b/pkg/dwarf/op/op.go index 7ac4cd9e229455b11b200d85bd1e3a53fc58352e..3d53be6d8a0ff4cb9bdac210872a7592aea7476b 100644 --- a/pkg/dwarf/op/op.go +++ b/pkg/dwarf/op/op.go @@ -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") diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index d7b7c50fe969beca252dc5c07eb476d2aac4be8a..4f0fea010901f8ee42d350721ef9efbd1a88bf74 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -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, diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 55ccd30a173649d246d174577a8a1c3302bb271a..f0346dfad253a2b7e7d0d14a598f01bdf4961c7f 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -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 } diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 346ce2db870473b814adb239c8e4fdf8ae78b13e..eb89ec75978a5b461d7c3287df229d75415220c8 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -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 } diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index 6d2ca864732e16f9536a06d8ae9149d7de5a634f..8c8f855a52234dbf99613051acfcd44eac04c32a 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -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 } diff --git a/pkg/terminal/config.go b/pkg/terminal/config.go index bb034c35f20a7a743fb8f703b3c8091f326103fd..dbf05d2f1405861b7a468529a4670c03e272ac38 100644 --- a/pkg/terminal/config.go +++ b/pkg/terminal/config.go @@ -73,14 +73,14 @@ func configureList(t *Term) error { 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()) } else { - fmt.Fprintf(w, "%s\t%v\n", fieldName, field) + fmt.Fprintf(w, "%s\t\n", fieldName) } } else { - fmt.Fprintf(w, "%s\t\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) } diff --git a/service/api/conversions.go b/service/api/conversions.go index 8745cad3b287afe45a7c61c5c585a10ed706cd97..9be97e681e322ba75f8649c837a3ee21051ceb88 100644 --- a/service/api/conversions.go +++ b/service/api/conversions.go @@ -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) diff --git a/service/api/types.go b/service/api/types.go index a6ce44ba76e83f9d75f701fcfd7bd7079110e03e..643d8366a0fdeba4e30f550b0ec9515d8206ecfc 100644 --- a/service/api/types.go +++ b/service/api/types.go @@ -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