From 4d88d9ed8dcc25c07c8eccba6a0585a8c0da109c Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Wed, 4 Feb 2015 18:14:08 -0600 Subject: [PATCH] Prefer binary search over tree lookup for FDEs FDEs previously were loaded into a red/black tree and searched. This is significantly more expensive than a binary search over a slice. Not sure what I was thinking using a red/black tree - this binary search implementation is significantly more efficient. --- dwarf/frame/entries.go | 62 ++++++++++++++++++------------------- dwarf/frame/entries_test.go | 20 ++++++------ dwarf/frame/parser.go | 6 ++-- proctl/proctl.go | 2 +- 4 files changed, 44 insertions(+), 46 deletions(-) diff --git a/dwarf/frame/entries.go b/dwarf/frame/entries.go index b5e74529..e7081d8f 100644 --- a/dwarf/frame/entries.go +++ b/dwarf/frame/entries.go @@ -1,10 +1,6 @@ package frame -import ( - "fmt" - - "github.com/derekparker/rbtree" -) +import "fmt" // Represents a Common Information Entry in // the Dwarf .debug_frame section. @@ -23,7 +19,6 @@ func (fde *FrameDescriptionEntry) Cover(addr uint64) bool { if (addr - fde.begin) < fde.end { return true } - return false } @@ -53,39 +48,42 @@ func (fde *FrameDescriptionEntry) ReturnAddressOffset(pc uint64) int64 { return frame.cfa.offset + frame.regs[fde.CIE.ReturnAddressRegister].offset } -type FrameDescriptionEntries struct { - *rbtree.RedBlackTree +type FrameDescriptionEntries []*FrameDescriptionEntry + +func NewFrameIndex() FrameDescriptionEntries { + return make(FrameDescriptionEntries, 0, 1000) } -func NewFrameIndex() *FrameDescriptionEntries { - return &FrameDescriptionEntries{rbtree.New()} -} - -func (fdes *FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) { - node, ok := fdes.Find(Addr(pc)) - if !ok { - return nil, fmt.Errorf("Could not find FDE for %#v", pc) +func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) { + frame := find(fdes, pc) + if frame == nil { + return nil, fmt.Errorf("could not find FDE for %#v", pc) } - - return node.(*FrameDescriptionEntry), nil + return frame, nil } -func (frame *FrameDescriptionEntry) Less(item rbtree.Item) bool { - return frame.Begin() < item.(*FrameDescriptionEntry).Begin() +func find(fdes FrameDescriptionEntries, pc uint64) *FrameDescriptionEntry { + if len(fdes) == 0 { + return nil + } + idx := len(fdes) / 2 + frame := fdes[idx] + if frame.Cover(pc) { + return frame + } + if frame.Less(pc) { + return find(fdes[:idx], pc) + } + if frame.More(pc) { + return find(fdes[idx:], pc) + } + return nil } -func (frame *FrameDescriptionEntry) More(item rbtree.Item) bool { - f := item.(*FrameDescriptionEntry) - return frame.End() > f.End() +func (frame *FrameDescriptionEntry) Less(pc uint64) bool { + return frame.Begin() > pc } -type Addr uint64 - -func (a Addr) Less(item rbtree.Item) bool { - return uint64(a) < item.(*FrameDescriptionEntry).Begin() -} - -func (a Addr) More(item rbtree.Item) bool { - f := item.(*FrameDescriptionEntry) - return uint64(a) > f.End() +func (frame *FrameDescriptionEntry) More(pc uint64) bool { + return frame.End() < pc } diff --git a/dwarf/frame/entries_test.go b/dwarf/frame/entries_test.go index 6e854699..7440133e 100644 --- a/dwarf/frame/entries_test.go +++ b/dwarf/frame/entries_test.go @@ -7,23 +7,23 @@ import ( ) func TestFDEForPC(t *testing.T) { - fde1 := &FrameDescriptionEntry{begin: 100, end: 200} + fde1 := &FrameDescriptionEntry{begin: 0, end: 49} fde2 := &FrameDescriptionEntry{begin: 50, end: 99} - fde3 := &FrameDescriptionEntry{begin: 0, end: 49} + fde3 := &FrameDescriptionEntry{begin: 100, end: 200} fde4 := &FrameDescriptionEntry{begin: 201, end: 245} - tree := NewFrameIndex() - tree.Put(fde1) - tree.Put(fde2) - tree.Put(fde3) - tree.Put(fde4) + frames := NewFrameIndex() + frames = append(frames, fde1) + frames = append(frames, fde2) + frames = append(frames, fde3) + frames = append(frames, fde4) - node, ok := tree.Find(Addr(35)) - if !ok { + node, err := frames.FDEForPC(35) + if err != nil { t.Fatal("Could not find FDE") } - if node != fde3 { + if node != fde1 { t.Fatal("Got incorrect fde") } } diff --git a/dwarf/frame/parser.go b/dwarf/frame/parser.go index 204a3773..381cc355 100644 --- a/dwarf/frame/parser.go +++ b/dwarf/frame/parser.go @@ -14,7 +14,7 @@ type parsefunc func(*parseContext) parsefunc type parseContext struct { Buf *bytes.Buffer - Entries *FrameDescriptionEntries + Entries FrameDescriptionEntries Common *CommonInformationEntry Frame *FrameDescriptionEntry Length uint32 @@ -23,7 +23,7 @@ type parseContext struct { // Parse takes in data (a byte slice) and returns a slice of // CommonInformationEntry structures. Each CommonInformationEntry // has a slice of FrameDescriptionEntry structures. -func Parse(data []byte) *FrameDescriptionEntries { +func Parse(data []byte) FrameDescriptionEntries { var ( buf = bytes.NewBuffer(data) pctx = &parseContext{Buf: buf, Entries: NewFrameIndex()} @@ -62,7 +62,7 @@ func parseFDE(ctx *parseContext) parsefunc { // Insert into the tree after setting address range begin // otherwise compares won't work. - ctx.Entries.Put(ctx.Frame) + ctx.Entries = append(ctx.Entries, ctx.Frame) // The rest of this entry consists of the instructions // so we can just grab all of the data from the buffer diff --git a/proctl/proctl.go b/proctl/proctl.go index b8f32c43..72a42b4d 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -27,7 +27,7 @@ type DebuggedProcess struct { Process *os.Process Dwarf *dwarf.Data GoSymTable *gosym.Table - FrameEntries *frame.FrameDescriptionEntries + FrameEntries frame.FrameDescriptionEntries HWBreakPoints [4]*BreakPoint BreakPoints map[uint64]*BreakPoint Threads map[int]*ThreadContext