ogle/program/server: provide PC->source evaluation
For now, we must do this with the old Plan 9 symbol
pclntab because the DWARF stuff seems corrupt.
LGTM=nigeltao
R=nigeltao
https://golang.org/cl/86380045
diff --git a/program/server/server.go b/program/server/server.go
index 315ce2d..2835bdc 100644
--- a/program/server/server.go
+++ b/program/server/server.go
@@ -15,6 +15,8 @@
"sync"
"syscall"
+ "code.google.com/p/ogle/gosym"
+
"code.google.com/p/ogle/debug/dwarf"
"code.google.com/p/ogle/debug/elf"
"code.google.com/p/ogle/debug/macho"
@@ -33,6 +35,7 @@
arch arch.Architecture
executable string // Name of executable.
dwarfData *dwarf.Data
+ table *gosym.Table
mu sync.Mutex
@@ -55,7 +58,7 @@
return nil, err
}
defer fd.Close()
- architecture, dwarfData, err := loadExecutable(fd)
+ architecture, dwarfData, table, err := loadExecutable(fd)
if err != nil {
return nil, err
}
@@ -63,6 +66,7 @@
arch: *architecture,
executable: executable,
dwarfData: dwarfData,
+ table: table,
fc: make(chan func() error),
ec: make(chan error),
breakpoints: make(map[uint64]breakpoint),
@@ -71,42 +75,76 @@
return srv, nil
}
-func loadExecutable(f *os.File) (*arch.Architecture, *dwarf.Data, error) {
+func loadExecutable(f *os.File) (*arch.Architecture, *dwarf.Data, *gosym.Table, error) {
// TODO: How do we detect NaCl?
if obj, err := elf.NewFile(f); err == nil {
dwarfData, err := obj.DWARF()
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
+
+ table, err := parseElf(obj)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("parsing go symbol table: %v", err)
+ }
+
switch obj.Machine {
case elf.EM_ARM:
- return &arch.ARM, dwarfData, nil
+ return &arch.ARM, dwarfData, table, nil
case elf.EM_386:
switch obj.Class {
case elf.ELFCLASS32:
- return &arch.X86, dwarfData, nil
+ return &arch.X86, dwarfData, table, nil
case elf.ELFCLASS64:
- return &arch.AMD64, dwarfData, nil
+ return &arch.AMD64, dwarfData, table, nil
}
case elf.EM_X86_64:
- return &arch.AMD64, dwarfData, nil
+ return &arch.AMD64, dwarfData, table, nil
}
- return nil, nil, fmt.Errorf("unrecognized ELF architecture")
+ return nil, nil, nil, fmt.Errorf("unrecognized ELF architecture")
}
if obj, err := macho.NewFile(f); err == nil {
dwarfData, err := obj.DWARF()
if err != nil {
- return nil, nil, err
+ return nil, nil, nil, err
}
+
+ /* TODO
+ table, err := parseMachO(obj)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ */
switch obj.Cpu {
case macho.Cpu386:
- return &arch.X86, dwarfData, nil
+ return &arch.X86, dwarfData, nil, nil
case macho.CpuAmd64:
- return &arch.AMD64, dwarfData, nil
+ return &arch.AMD64, dwarfData, nil, nil
}
- return nil, nil, fmt.Errorf("unrecognized Mach-O architecture")
+ return nil, nil, nil, fmt.Errorf("unrecognized Mach-O architecture")
}
- return nil, nil, fmt.Errorf("unrecognized binary format")
+ return nil, nil, nil, fmt.Errorf("unrecognized binary format")
+}
+
+// parseElf returns the gosym.Table representation of the old symbol tables.
+// TODO: Delete this once we know how to get PC/line data out of DWARF.
+func parseElf(f *elf.File) (*gosym.Table, error) {
+ symdat, err := f.Section(".gosymtab").Data() // TODO unused.
+ if err != nil {
+ return nil, err
+ }
+ pclndat, err := f.Section(".gopclntab").Data()
+ if err != nil {
+ return nil, err
+ }
+
+ pcln := gosym.NewLineTable(pclndat, f.Section(".text").Addr)
+ tab, err := gosym.NewTable(symdat, pcln)
+ if err != nil {
+ return nil, err
+ }
+
+ return tab, nil
}
type file struct {
@@ -369,6 +407,18 @@
}
return []string{fmt.Sprintf("%#x", addr)}, nil
+ case strings.HasPrefix(expr, "src:"):
+ // Numerical address. Return file.go:123.
+ addr, err := strconv.ParseUint(expr[4:], 0, 0)
+ if err != nil {
+ return nil, err
+ }
+ file, line, ok := s.lookupSource(addr)
+ if !ok {
+ return nil, fmt.Errorf("no PC/line data for: %q", expr)
+ }
+ return []string{fmt.Sprintf("%s:%d", file, line)}, nil
+
case len(expr) > 0 && '0' <= expr[0] && expr[0] <= '9':
// Numerical address. Return symbol.
addr, err := strconv.ParseUint(expr, 0, 0)
@@ -385,6 +435,14 @@
return nil, fmt.Errorf("bad expression syntax: %q", expr)
}
+func (s *Server) lookupSource(pc uint64) (file string, line int, ok bool) {
+ if s.table == nil {
+ return
+ }
+ file, line, fn := s.table.PCToLine(pc)
+ return file, line, fn != nil
+}
+
// evalAddress takes a simple expression, either a symbol or hex value,
// and evaluates it as an address.
func (s *Server) evalAddress(expr string) (uint64, error) {