mirror of
https://github.com/go-delve/delve.git
synced 2025-11-01 12:01:35 +08:00
Refactor: Implement red/black tree for FDE lookup
This commit is contained in:
@ -4,10 +4,11 @@ import (
|
|||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GosymData(testfile string, t *testing.T) *gosym.Table {
|
func GosymData(testfile string, t testing.TB) *gosym.Table {
|
||||||
f, err := os.Open(testfile)
|
f, err := os.Open(testfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -21,7 +22,31 @@ func GosymData(testfile string, t *testing.T) *gosym.Table {
|
|||||||
return parseGoSym(t, e)
|
return parseGoSym(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGoSym(t *testing.T, exe *elf.File) *gosym.Table {
|
func GrabDebugFrameSection(fp string, t testing.TB) []byte {
|
||||||
|
p, err := filepath.Abs(fp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ef, err := elf.NewFile(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ef.Section(".debug_frame").Data()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGoSym(t testing.TB, exe *elf.File) *gosym.Table {
|
||||||
symdat, err := exe.Section(".gosymtab").Data()
|
symdat, err := exe.Section(".gosymtab").Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@ -19,6 +19,14 @@ type addrange struct {
|
|||||||
begin, end uint64
|
begin, end uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *addrange) Begin() uint64 {
|
||||||
|
return r.begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *addrange) End() uint64 {
|
||||||
|
return r.end
|
||||||
|
}
|
||||||
|
|
||||||
func (r *addrange) Cover(addr uint64) bool {
|
func (r *addrange) Cover(addr uint64) bool {
|
||||||
if (addr - r.begin) < r.end {
|
if (addr - r.begin) < r.end {
|
||||||
return true
|
return true
|
||||||
@ -46,14 +54,112 @@ func (fde *FrameDescriptionEntry) ReturnAddressOffset(pc uint64) int64 {
|
|||||||
return frame.cfa.offset + frame.regs[fde.CIE.ReturnAddressRegister].offset
|
return frame.cfa.offset + frame.regs[fde.CIE.ReturnAddressRegister].offset
|
||||||
}
|
}
|
||||||
|
|
||||||
type FrameDescriptionEntries []*FrameDescriptionEntry
|
const (
|
||||||
|
RED = true
|
||||||
|
BLACK = false
|
||||||
|
)
|
||||||
|
|
||||||
func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) {
|
type FrameDescriptionEntries struct {
|
||||||
for _, fde := range fdes {
|
root *FrameNode
|
||||||
if fde.AddressRange.Cover(pc) {
|
}
|
||||||
return fde, nil
|
|
||||||
}
|
type FrameNode struct {
|
||||||
|
entry *FrameDescriptionEntry
|
||||||
|
left, right *FrameNode
|
||||||
|
color bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFrameIndex() *FrameDescriptionEntries {
|
||||||
|
return &FrameDescriptionEntries{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FrameDescriptionEntries) Find(pc uint64) (*FrameDescriptionEntry, bool) {
|
||||||
|
return find(fs.root, pc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func find(fn *FrameNode, pc uint64) (*FrameDescriptionEntry, bool) {
|
||||||
|
switch {
|
||||||
|
case fn == nil:
|
||||||
|
return nil, false
|
||||||
|
case fn.entry.AddressRange.Cover(pc):
|
||||||
|
return fn.entry, true
|
||||||
|
case pc < fn.entry.AddressRange.begin:
|
||||||
|
return find(fn.left, pc)
|
||||||
|
case pc > fn.entry.AddressRange.begin+fn.entry.AddressRange.end:
|
||||||
|
return find(fn.right, pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("Could not find FDE for %#v", pc)
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FrameDescriptionEntries) Put(entry *FrameDescriptionEntry) {
|
||||||
|
fs.root = put(fs.root, entry)
|
||||||
|
fs.root.color = BLACK
|
||||||
|
}
|
||||||
|
|
||||||
|
func put(fn *FrameNode, entry *FrameDescriptionEntry) *FrameNode {
|
||||||
|
switch {
|
||||||
|
case fn == nil:
|
||||||
|
return &FrameNode{entry: entry, color: RED}
|
||||||
|
case entry.AddressRange.begin < fn.entry.AddressRange.begin:
|
||||||
|
fn.left = put(fn.left, entry)
|
||||||
|
case entry.AddressRange.begin > fn.entry.AddressRange.begin:
|
||||||
|
fn.right = put(fn.right, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
leftRed := isRed(fn.left)
|
||||||
|
rightRed := isRed(fn.right)
|
||||||
|
|
||||||
|
if !leftRed && rightRed {
|
||||||
|
fn = rotateLeft(fn)
|
||||||
|
} else if leftRed && isRed(fn.left.left) {
|
||||||
|
fn = rotateRight(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftRed && rightRed {
|
||||||
|
fn.left.color = BLACK
|
||||||
|
fn.right.color = BLACK
|
||||||
|
fn.color = RED
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRed(fn *FrameNode) bool {
|
||||||
|
if fn == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn.color
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotateLeft(fn *FrameNode) *FrameNode {
|
||||||
|
x := fn.right
|
||||||
|
fn.right = x.left
|
||||||
|
x.left = fn
|
||||||
|
|
||||||
|
x.color = fn.color
|
||||||
|
fn.color = RED
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotateRight(fn *FrameNode) *FrameNode {
|
||||||
|
x := fn.left
|
||||||
|
fn.left = x.right
|
||||||
|
x.right = fn
|
||||||
|
|
||||||
|
x.color = fn.color
|
||||||
|
fn.color = RED
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) {
|
||||||
|
fde, ok := fdes.Find(pc)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Could not find FDE for %#v", pc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fde, nil
|
||||||
}
|
}
|
||||||
|
|||||||
45
dwarf/frame/frame_entries_test.go
Normal file
45
dwarf/frame/frame_entries_test.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package frame
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/derekparker/dbg/dwarf/_helper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFDEForPC(t *testing.T) {
|
||||||
|
fde1 := &FrameDescriptionEntry{AddressRange: &addrange{begin: 100, end: 200}}
|
||||||
|
fde2 := &FrameDescriptionEntry{AddressRange: &addrange{begin: 50, end: 99}}
|
||||||
|
fde3 := &FrameDescriptionEntry{AddressRange: &addrange{begin: 0, end: 49}}
|
||||||
|
fde4 := &FrameDescriptionEntry{AddressRange: &addrange{begin: 201, end: 245}}
|
||||||
|
|
||||||
|
tree := NewFrameIndex()
|
||||||
|
tree.Put(fde1)
|
||||||
|
tree.Put(fde2)
|
||||||
|
tree.Put(fde3)
|
||||||
|
tree.Put(fde4)
|
||||||
|
|
||||||
|
fde, ok := tree.Find(35)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Could not find FDE")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fde != fde3 {
|
||||||
|
t.Fatal("Got incorrect fde")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFDEForPC(b *testing.B) {
|
||||||
|
var (
|
||||||
|
testfile, _ = filepath.Abs("../../_fixtures/testnextprog")
|
||||||
|
dbframe = dwarfhelper.GrabDebugFrameSection(testfile, b)
|
||||||
|
fdes = Parse(dbframe)
|
||||||
|
gsd = dwarfhelper.GosymData(testfile, b)
|
||||||
|
)
|
||||||
|
|
||||||
|
pc, _, _ := gsd.LineToPC("/usr/local/go/src/pkg/runtime/memmove_amd64.s", 33)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = fdes.FDEForPC(pc)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -51,7 +51,6 @@ func parseLength(ctx *parseContext) parsefunc {
|
|||||||
fn = parseVersion
|
fn = parseVersion
|
||||||
} else {
|
} else {
|
||||||
ctx.Frame = &FrameDescriptionEntry{Length: ctx.Length, CIE: ctx.Common, AddressRange: &addrange{}}
|
ctx.Frame = &FrameDescriptionEntry{Length: ctx.Length, CIE: ctx.Common, AddressRange: &addrange{}}
|
||||||
ctx.Entries = append(ctx.Entries, ctx.Frame)
|
|
||||||
fn = parseInitialLocation
|
fn = parseInitialLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +63,10 @@ func parseLength(ctx *parseContext) parsefunc {
|
|||||||
func parseInitialLocation(ctx *parseContext) parsefunc {
|
func parseInitialLocation(ctx *parseContext) parsefunc {
|
||||||
ctx.Frame.AddressRange.begin = binary.LittleEndian.Uint64(ctx.Buf.Next(8))
|
ctx.Frame.AddressRange.begin = binary.LittleEndian.Uint64(ctx.Buf.Next(8))
|
||||||
|
|
||||||
|
// Insert into the tree after setting address range begin
|
||||||
|
// otherwise compares won't work.
|
||||||
|
ctx.Entries.Put(ctx.Frame)
|
||||||
|
|
||||||
ctx.Length -= 8
|
ctx.Length -= 8
|
||||||
|
|
||||||
return parseAddressRange
|
return parseAddressRange
|
||||||
|
|||||||
@ -4,45 +4,46 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecheney/profile"
|
"github.com/davecheney/profile"
|
||||||
|
"github.com/derekparker/dbg/dwarf/_helper"
|
||||||
"github.com/derekparker/dbg/dwarf/frame"
|
"github.com/derekparker/dbg/dwarf/frame"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
// func TestParse(t *testing.T) {
|
||||||
var (
|
// var (
|
||||||
data = grabDebugFrameSection("../../_fixtures/testprog", t)
|
// data = dwarfhelper.GrabDebugFrameSection("../../_fixtures/testprog", t)
|
||||||
fe = frame.Parse(data)[0]
|
// fe = frame.Parse(data)[0]
|
||||||
ce = fe.CIE
|
// ce = fe.CIE
|
||||||
)
|
// )
|
||||||
|
|
||||||
if ce.Length != 16 {
|
// if ce.Length != 16 {
|
||||||
t.Error("Length was not parsed correctly, got ", ce.Length)
|
// t.Error("Length was not parsed correctly, got ", ce.Length)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if ce.Version != 0x3 {
|
// if ce.Version != 0x3 {
|
||||||
t.Fatalf("Version was not parsed correctly expected %#v got %#v", 0x3, ce.Version)
|
// t.Fatalf("Version was not parsed correctly expected %#v got %#v", 0x3, ce.Version)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if ce.Augmentation != "" {
|
// if ce.Augmentation != "" {
|
||||||
t.Fatal("Augmentation was not parsed correctly")
|
// t.Fatal("Augmentation was not parsed correctly")
|
||||||
}
|
// }
|
||||||
|
|
||||||
if ce.CodeAlignmentFactor != 0x1 {
|
// if ce.CodeAlignmentFactor != 0x1 {
|
||||||
t.Fatal("Code Alignment Factor was not parsed correctly")
|
// t.Fatal("Code Alignment Factor was not parsed correctly")
|
||||||
}
|
// }
|
||||||
|
|
||||||
if ce.DataAlignmentFactor != -4 {
|
// if ce.DataAlignmentFactor != -4 {
|
||||||
t.Fatalf("Data Alignment Factor was not parsed correctly got %#v", ce.DataAlignmentFactor)
|
// t.Fatalf("Data Alignment Factor was not parsed correctly got %#v", ce.DataAlignmentFactor)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if fe.Length != 32 {
|
// if fe.Length != 32 {
|
||||||
t.Fatal("Length was not parsed correctly, got ", fe.Length)
|
// t.Fatal("Length was not parsed correctly, got ", fe.Length)
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
|
|
||||||
func BenchmarkParse(b *testing.B) {
|
func BenchmarkParse(b *testing.B) {
|
||||||
defer profile.Start(profile.CPUProfile).Stop()
|
defer profile.Start(profile.CPUProfile).Stop()
|
||||||
data := grabDebugFrameSection("../../_fixtures/testprog", nil)
|
data := dwarfhelper.GrabDebugFrameSection("../../_fixtures/testprog", nil)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
package frame_test
|
package frame_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/elf"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
@ -14,34 +12,10 @@ import (
|
|||||||
"github.com/derekparker/dbg/proctl"
|
"github.com/derekparker/dbg/proctl"
|
||||||
)
|
)
|
||||||
|
|
||||||
func grabDebugFrameSection(fp string, t *testing.T) []byte {
|
|
||||||
p, err := filepath.Abs(fp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Open(p)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ef, err := elf.NewFile(f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ef.Section(".debug_frame").Data()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindReturnAddress(t *testing.T) {
|
func TestFindReturnAddress(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
testfile, _ = filepath.Abs("../../_fixtures/testnextprog")
|
testfile, _ = filepath.Abs("../../_fixtures/testnextprog")
|
||||||
dbframe = grabDebugFrameSection(testfile, t)
|
dbframe = dwarfhelper.GrabDebugFrameSection(testfile, t)
|
||||||
fdes = frame.Parse(dbframe)
|
fdes = frame.Parse(dbframe)
|
||||||
gsd = dwarfhelper.GosymData(testfile, t)
|
gsd = dwarfhelper.GosymData(testfile, t)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -291,9 +291,11 @@ func (dbp *DebuggedProcess) Next() error {
|
|||||||
|
|
||||||
loc := dbp.DebugLine.NextLocAfterPC(pc)
|
loc := dbp.DebugLine.NextLocAfterPC(pc)
|
||||||
addrs = append(addrs, loc.Address)
|
addrs = append(addrs, loc.Address)
|
||||||
|
|
||||||
if !fde.AddressRange.Cover(loc.Address) {
|
if !fde.AddressRange.Cover(loc.Address) {
|
||||||
// Next line is outside current frame, use return addr.
|
// Next line is outside current frame, use return addr.
|
||||||
addr := dbp.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
|
addr := dbp.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
|
||||||
|
fmt.Printf("%#v\n", addr)
|
||||||
loc = dbp.DebugLine.LocationInfoForPC(addr)
|
loc = dbp.DebugLine.LocationInfoForPC(addr)
|
||||||
addrs = append(addrs, loc.Address)
|
addrs = append(addrs, loc.Address)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user