Skip to content

Commit

Permalink
Merge pull request #660 from kakkoyun/refactor_debugfile
Browse files Browse the repository at this point in the history
symbolizer: Introduce DebugInfo file
  • Loading branch information
metalmatze committed Feb 22, 2022
2 parents f15b4c6 + 069470d commit a3a5522
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 210 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ require (
github.com/go-kit/log v0.2.0
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/goburrow/cache v0.1.4
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd
github.com/google/pprof v0.0.0-20220218203455-0368bd9e19a7
github.com/google/uuid v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware/providers/kit/v2 v2.0.0-20201002093600-73cf2ae9d891
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2.0.20201207153454-9f6bf00c00a7
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3
github.com/hashicorp/go-multierror v1.1.1
github.com/ianlancetaylor/demangle v0.0.0-20211126204342-3ad08eb09c01
github.com/ianlancetaylor/demangle v0.0.0-20220203202831-b7f99f1dbc96
github.com/improbable-eng/grpc-web v0.15.0
github.com/oklog/run v1.1.0
github.com/prometheus/client_golang v1.12.1
Expand All @@ -41,6 +41,7 @@ require (
go.uber.org/atomic v1.9.0
golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5
google.golang.org/grpc v1.44.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -961,8 +961,9 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211122183932-1daafda22083/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20220218203455-0368bd9e19a7 h1:IR7vKogOtQRR3JhSl1UftKpFLnuJ2o+woqCFXqJ7wXw=
github.com/google/pprof v0.0.0-20220218203455-0368bd9e19a7/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Expand Down Expand Up @@ -1117,8 +1118,8 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/ianlancetaylor/demangle v0.0.0-20211126204342-3ad08eb09c01 h1:+0qIm4/XbPn2PYkj6QM6CX/FJN5DGvFOaMkSyB1xuh8=
github.com/ianlancetaylor/demangle v0.0.0-20211126204342-3ad08eb09c01/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/ianlancetaylor/demangle v0.0.0-20220203202831-b7f99f1dbc96 h1:OOB/uHFWWV4Ek3aRFgRaVwSw5IuQw4csg4mKF6fCbN4=
github.com/ianlancetaylor/demangle v0.0.0-20220203202831-b7f99f1dbc96/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
Expand Down Expand Up @@ -2276,8 +2277,9 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
Expand Down
212 changes: 10 additions & 202 deletions pkg/symbol/addr2line/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,149 +14,47 @@
package addr2line

import (
"debug/dwarf"
"errors"
"fmt"
"io"
"sort"

"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/dwarf/reader"
"github.com/go-kit/log"
"github.com/go-kit/log/level"

pb "github.com/parca-dev/parca/gen/proto/go/parca/metastore/v1alpha1"
"github.com/parca-dev/parca/internal/go/debug/elf"
"github.com/parca-dev/parca/pkg/metastore"
"github.com/parca-dev/parca/pkg/symbol/demangle"
"github.com/parca-dev/parca/pkg/symbol/elfutils"
)

var ErrLocationFailedBefore = errors.New("failed to symbolize location, attempts are exhausted")

type DwarfLiner struct {
logger log.Logger
demangler *demangle.Demangler
logger log.Logger

mapping *pb.Mapping
data *dwarf.Data
lineEntries map[dwarf.Offset][]dwarf.LineEntry
subprograms map[dwarf.Offset][]*godwarf.Tree
abstractSubprograms map[dwarf.Offset]*dwarf.Entry
dbgFile elfutils.DebugInfoFile

attemptThreshold int
attempts map[uint64]int
failed map[uint64]struct{}
}

func DWARF(logger log.Logger, demangler *demangle.Demangler, attemptThreshold int, m *pb.Mapping, path string) (*DwarfLiner, error) {
// TODO(kakkoyun): Handle offset, start and limit for dynamically linked libraries.
//f, err := s.bu.Open(file, m.Start, m.Limit, m.Offset)
//if err != nil {
// return nil, fmt.Errorf("open object file: %w", err)
//}
f, err := elf.Open(path)
// DWARF is a symbolizer that uses DWARF debug info to symbolize addresses.
func DWARF(logger log.Logger, path string, m *pb.Mapping, demangler *demangle.Demangler, attemptThreshold int) (*DwarfLiner, error) {
dbgFile, err := elfutils.NewDebugInfoFile(path, m, demangler)
if err != nil {
return nil, fmt.Errorf("failed to open elf: %w", err)
}
defer f.Close()

data, err := f.DWARF()
if err != nil {
return nil, fmt.Errorf("failed to read DWARF data: %w", err)
return nil, err
}

return &DwarfLiner{
logger: logger,
demangler: demangler,

mapping: m,
data: data,

lineEntries: map[dwarf.Offset][]dwarf.LineEntry{},
subprograms: map[dwarf.Offset][]*godwarf.Tree{},
abstractSubprograms: map[dwarf.Offset]*dwarf.Entry{},
logger: logger,
dbgFile: dbgFile,

attemptThreshold: attemptThreshold,
attempts: map[uint64]int{},
failed: map[uint64]struct{}{},
}, nil
}

func (dl *DwarfLiner) ensureLookUpTablesBuilt(cu *dwarf.Entry) error {
if _, ok := dl.lineEntries[cu.Offset]; ok {
// Already created.
return nil
}

// The reader is positioned at byte offset 0 in the DWARF “line” section.
lr, err := dl.data.LineReader(cu)
if err != nil {
return err
}
if lr == nil {
return errors.New("failed to initialize line reader")
}

for {
le := dwarf.LineEntry{}
err := lr.Next(&le)
if err != nil {
break
}
if le.IsStmt {
dl.lineEntries[cu.Offset] = append(dl.lineEntries[cu.Offset], le)
}
}

er := dl.data.Reader()
// The reader is positioned at byte offset of compile unit in the DWARF “info” section.
er.Seek(cu.Offset)
entry, err := er.Next()
if err != nil || entry == nil {
return errors.New("failed to read entry for compile unit")
}

if entry.Tag != dwarf.TagCompileUnit {
return errors.New("failed to find entry for compile unit")
}

outer:
for {
entry, err := er.Next()
if err != nil {
if err == io.EOF {
break
}
continue
}
if entry == nil {
break
}
if entry.Tag == dwarf.TagCompileUnit {
// Reached to another compile unit.
break
}

if entry.Tag == dwarf.TagSubprogram {
for _, field := range entry.Field {
if field.Attr == dwarf.AttrInline {
dl.abstractSubprograms[entry.Offset] = entry
continue outer
}
}

tr, err := godwarf.LoadTree(entry.Offset, dl.data, 0)
if err != nil {
return fmt.Errorf("failed to extract dwarf tree: %w", err)
}

dl.subprograms[cu.Offset] = append(dl.subprograms[cu.Offset], tr)
}
}

return nil
}

func (dl *DwarfLiner) PCToLines(addr uint64) (lines []metastore.LocationLine, err error) {
// Check if we already attempt to symbolize this location and failed.
if _, failedBefore := dl.failed[addr]; failedBefore {
Expand All @@ -170,7 +68,7 @@ func (dl *DwarfLiner) PCToLines(addr uint64) (lines []metastore.LocationLine, er
}
}()

lines, err = dl.sourceLines(addr)
lines, err = dl.dbgFile.SourceLines(addr)
if err != nil {
return nil, dl.handleError(addr, err)
}
Expand Down Expand Up @@ -198,93 +96,3 @@ func (dl *DwarfLiner) handleError(addr uint64, err error) error {
dl.attempts[addr] = 1
return err
}

func (dl *DwarfLiner) sourceLines(addr uint64) ([]metastore.LocationLine, error) {
// The reader is positioned at byte offset 0 in the DWARF “info” section.
er := dl.data.Reader()
cu, err := er.SeekPC(addr)
if err != nil {
return nil, err
}
if cu == nil {
return nil, errors.New("failed to find a corresponding dwarf entry for given address")
}

if err := dl.ensureLookUpTablesBuilt(cu); err != nil {
return nil, err
}

lines := []metastore.LocationLine{}
var tr *godwarf.Tree
for _, t := range dl.subprograms[cu.Offset] {
if t.ContainsPC(addr) {
tr = t
break
}
}
if tr == nil {
return lines, nil
}

name := tr.Entry.Val(dwarf.AttrName).(string)
file, line := findLineInfo(dl.lineEntries[cu.Offset], tr.Ranges)
lines = append(lines, metastore.LocationLine{
Line: line,
Function: dl.demangler.Demangle(&pb.Function{
Name: name,
Filename: file,
}),
})

// If pc is 0 then all inlined calls will be returned.
for _, ch := range reader.InlineStack(tr, addr) {
var name string
if ch.Tag == dwarf.TagSubprogram {
name = tr.Entry.Val(dwarf.AttrName).(string)
} else {
abstractOrigin := dl.abstractSubprograms[ch.Entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)]
name = getFunctionName(abstractOrigin)
}

file, line := findLineInfo(dl.lineEntries[cu.Offset], ch.Ranges)
lines = append(lines, metastore.LocationLine{
Line: line,
Function: dl.demangler.Demangle(&pb.Function{
Name: name,
Filename: file,
}),
})
}

return lines, nil
}

func findLineInfo(entries []dwarf.LineEntry, rg [][2]uint64) (string, int64) {
file := "?"
var line int64
i := sort.Search(len(entries), func(i int) bool {
return entries[i].Address >= rg[0][0]
})
if i >= len(entries) {
return file, line
}

le := dwarf.LineEntry{}
pc := entries[i].Address
if rg[0][0] <= pc && pc < rg[0][1] {
le = entries[i]
return le.File.Name, int64(le.Line)
}

return file, line
}

func getFunctionName(entry *dwarf.Entry) string {
var name string
for _, field := range entry.Field {
if field.Attr == dwarf.AttrName {
name = field.Val.(string)
}
}
return name
}
4 changes: 3 additions & 1 deletion pkg/symbol/addr2line/go.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func (gl *GoLiner) PCToLines(addr uint64) (lines []metastore.LocationLine, err e
}
}()

// TODO(kakkoyun): Do we need to consider the base address for any part of Go binaries?
file, line, fn := gl.symtab.PCToLine(addr)
name := "?"
if fn != nil {
Expand All @@ -61,7 +62,8 @@ func (gl *GoLiner) PCToLines(addr uint64) (lines []metastore.LocationLine, err e
line = 0
}

// TODO(kakkoyun): Find a way to symbolize inline functions.
// TODO(kakkoyun): These lines miss the inline functions.
// - Find a way to symbolize inline functions.
lines = append(lines, metastore.LocationLine{
Line: int64(line),
Function: &pb.Function{
Expand Down
Loading

0 comments on commit a3a5522

Please sign in to comment.