ogle/program/server: use dwarf data directly, instead of gosym.
LGTM=r
R=r
https://golang.org/cl/81240045
diff --git a/program/server/dwarf.go b/program/server/dwarf.go
new file mode 100644
index 0000000..44321bc
--- /dev/null
+++ b/program/server/dwarf.go
@@ -0,0 +1,164 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package server
+
+import (
+ "fmt"
+ "regexp"
+
+ "code.google.com/p/ogle/debug/dwarf"
+)
+
+func (s *Server) lookupRE(re *regexp.Regexp) (result []string, err error) {
+ r := s.dwarfData.Reader()
+ for {
+ entry, err := r.Next()
+ if err != nil {
+ return nil, err
+ }
+ if entry == nil {
+ // TODO: why don't we get an error here.
+ break
+ }
+ if entry.Tag != dwarf.TagSubprogram {
+ continue
+ }
+ nameAttr := lookupAttr(entry, dwarf.AttrName)
+ if nameAttr == nil {
+ // TODO: this shouldn't be possible.
+ continue
+ }
+ name, ok := nameAttr.(string)
+ if !ok || !re.MatchString(name) {
+ continue
+ }
+ result = append(result, name)
+ }
+ return result, nil
+}
+
+func (s *Server) lookupSym(name string) (uint64, error) {
+ r := s.dwarfData.Reader()
+ for {
+ entry, err := r.Next()
+ if err != nil {
+ return 0, err
+ }
+ if entry == nil {
+ // TODO: why don't we get an error here.
+ break
+ }
+ if entry.Tag != dwarf.TagSubprogram {
+ continue
+ }
+ nameAttr := lookupAttr(entry, dwarf.AttrName)
+ if nameAttr == nil {
+ // TODO: this shouldn't be possible.
+ continue
+ }
+ if nameAttr.(string) != name {
+ continue
+ }
+ addrAttr := lookupAttr(entry, dwarf.AttrLowpc)
+ if addrAttr == nil {
+ return 0, fmt.Errorf("symbol %q has no LowPC attribute", name)
+ }
+ addr, ok := addrAttr.(uint64)
+ if !ok {
+ return 0, fmt.Errorf("symbol %q has non-uint64 LowPC attribute", name)
+ }
+ return addr, nil
+ }
+ return 0, fmt.Errorf("symbol %q not found", name)
+}
+
+func (s *Server) lookupPC(pc uint64) (string, error) {
+ r := s.dwarfData.Reader()
+ for {
+ entry, err := r.Next()
+ if err != nil {
+ return "", err
+ }
+ if entry == nil {
+ // TODO: why don't we get an error here.
+ break
+ }
+ if entry.Tag != dwarf.TagSubprogram {
+ continue
+ }
+ lowpc, lok := lookupAttr(entry, dwarf.AttrLowpc).(uint64)
+ highpc, hok := lookupAttr(entry, dwarf.AttrHighpc).(uint64)
+ if !lok || !hok || pc < lowpc || highpc <= pc {
+ continue
+ }
+ nameAttr := lookupAttr(entry, dwarf.AttrName)
+ if nameAttr == nil {
+ // TODO: this shouldn't be possible.
+ continue
+ }
+ name, ok := nameAttr.(string)
+ if !ok {
+ return "", fmt.Errorf("name for PC %#x is not a string", pc)
+ }
+ return name, nil
+ }
+ return "", fmt.Errorf("PC %#x not found", pc)
+}
+
+func lookupAttr(e *dwarf.Entry, a dwarf.Attr) interface{} {
+ for _, f := range e.Field {
+ if f.Attr == a {
+ return f.Val
+ }
+ }
+ return nil
+}
+
+func evalLocation(v []uint8) string {
+ if len(v) == 0 {
+ return "<nil>"
+ }
+ if v[0] != 0x9C { // DW_OP_call_frame_cfa
+ return "UNK0"
+ }
+ if len(v) == 1 {
+ return "0"
+ }
+ v = v[1:]
+ if v[0] != 0x11 { // DW_OP_consts
+ return "UNK1"
+ }
+ return fmt.Sprintf("%x", sleb128(v[1:]))
+}
+
+func uleb128(v []uint8) (u uint64) {
+ var shift uint
+ for _, x := range v {
+ u |= (uint64(x) & 0x7F) << shift
+ shift += 7
+ if x&0x80 == 0 {
+ break
+ }
+ }
+ return u
+}
+
+func sleb128(v []uint8) (s int64) {
+ var shift uint
+ var sign int64 = -1
+ for _, x := range v {
+ s |= (int64(x) & 0x7F) << shift
+ shift += 7
+ sign <<= 7
+ if x&0x80 == 0 {
+ if x&0x40 != 0 {
+ s |= sign
+ }
+ break
+ }
+ }
+ // Sign extend?
+ return s
+}
diff --git a/program/server/server.go b/program/server/server.go
index d08ed47..372d172 100644
--- a/program/server/server.go
+++ b/program/server/server.go
@@ -15,11 +15,10 @@
"sync"
"syscall"
- "debug/elf"
- "debug/macho"
- "debug/pe"
+ "code.google.com/p/ogle/debug/dwarf"
+ "code.google.com/p/ogle/debug/elf"
+ "code.google.com/p/ogle/debug/macho"
- "code.google.com/p/ogle/gosym"
"code.google.com/p/ogle/program"
"code.google.com/p/ogle/program/proxyrpc"
)
@@ -31,8 +30,7 @@
type Server struct {
executable string // Name of executable.
- lines *gosym.LineTable
- symbols *gosym.Table
+ dwarfData *dwarf.Data
mu sync.Mutex
@@ -52,19 +50,13 @@
return nil, err
}
defer fd.Close()
- textStart, symtab, pclntab, err := loadTables(fd)
- if err != nil {
- return nil, err
- }
- lines := gosym.NewLineTable(pclntab, textStart)
- symbols, err := gosym.NewTable(symtab, lines)
+ dwarfData, err := loadDwarfData(fd)
if err != nil {
return nil, err
}
srv := &Server{
executable: executable,
- lines: lines,
- symbols: symbols,
+ dwarfData: dwarfData,
fc: make(chan func() error),
ec: make(chan error),
breakpoints: make(map[uint64]breakpoint),
@@ -73,62 +65,14 @@
return srv, nil
}
-// This function is copied from $GOROOT/src/cmd/addr2line/main.go.
-// TODO: Make this architecture-defined? Push into gosym?
-// TODO: Why is the .gosymtab always empty?
-func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
+func loadDwarfData(f *os.File) (*dwarf.Data, error) {
if obj, err := elf.NewFile(f); err == nil {
- if sect := obj.Section(".text"); sect != nil {
- textStart = sect.Addr
- }
- if sect := obj.Section(".gosymtab"); sect != nil {
- if symtab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- if sect := obj.Section(".gopclntab"); sect != nil {
- if pclntab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- return textStart, symtab, pclntab, nil
+ return obj.DWARF()
}
-
if obj, err := macho.NewFile(f); err == nil {
- if sect := obj.Section("__text"); sect != nil {
- textStart = sect.Addr
- }
- if sect := obj.Section("__gosymtab"); sect != nil {
- if symtab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- if sect := obj.Section("__gopclntab"); sect != nil {
- if pclntab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- return textStart, symtab, pclntab, nil
+ return obj.DWARF()
}
-
- if obj, err := pe.NewFile(f); err == nil {
- if sect := obj.Section(".text"); sect != nil {
- textStart = uint64(sect.VirtualAddress)
- }
- if sect := obj.Section(".gosymtab"); sect != nil {
- if symtab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- if sect := obj.Section(".gopclntab"); sect != nil {
- if pclntab, err = sect.Data(); err != nil {
- return 0, nil, nil, err
- }
- }
- return textStart, symtab, pclntab, nil
- }
-
- return 0, nil, nil, fmt.Errorf("unrecognized binary format")
+ return nil, fmt.Errorf("unrecognized binary format")
}
type file struct {
@@ -337,27 +281,19 @@
switch {
case strings.HasPrefix(expr, "re:"):
// Regular expression. Return list of symbols.
- expr = expr[3:]
- re, err := regexp.Compile(expr)
+ re, err := regexp.Compile(expr[3:])
if err != nil {
return nil, err
}
- strs := make([]string, 0, 100)
- for _, f := range s.symbols.Funcs {
- if re.MatchString(f.Sym.Name) {
- strs = append(strs, f.Sym.Name)
- }
- }
- return strs, nil
+ return s.lookupRE(re)
case strings.HasPrefix(expr, "sym:"):
// Symbol lookup. Return address.
- expr = expr[4:]
- sym := s.symbols.LookupFunc(expr)
- if sym == nil {
- return nil, fmt.Errorf("symbol %q not found", expr)
+ addr, err := s.lookupSym(expr[4:])
+ if err != nil {
+ return nil, err
}
- return []string{fmt.Sprintf("%#x", sym.Value)}, nil
+ return []string{fmt.Sprintf("%#x", addr)}, nil
case len(expr) > 0 && '0' <= expr[0] && expr[0] <= '9':
// Numerical address. Return symbol.
@@ -365,11 +301,11 @@
if err != nil {
return nil, err
}
- fun := s.symbols.PCToFunc(addr)
- if fun == nil {
- return nil, fmt.Errorf("address %q has no func", expr)
+ funcName, err := s.lookupPC(addr)
+ if err != nil {
+ return nil, err
}
- return []string{fun.Sym.Name}, nil
+ return []string{funcName}, nil
}
return nil, fmt.Errorf("bad expression syntax: %q", expr)
@@ -379,17 +315,16 @@
// and evaluates it as an address.
func (s *Server) evalAddress(expr string) (uint64, error) {
// Might be a symbol.
- sym := s.symbols.LookupFunc(expr)
- if sym != nil {
- return sym.Value, nil
+ addr, err := s.lookupSym(expr)
+ if err == nil {
+ return addr, nil
}
// Must be a number.
- addr, err := strconv.ParseUint(expr, 0, 0)
+ addr, err = strconv.ParseUint(expr, 0, 0)
if err != nil {
return 0, fmt.Errorf("eval: %q is neither symbol nor number", expr)
}
return addr, nil
-
}