Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle one file at a time in local symbolization. #848

Merged
merged 1 commit into from
Apr 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 71 additions & 106 deletions internal/symbolizer/symbolizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,80 @@ func doLocalSymbolize(prof *profile.Profile, fast, force bool, obj plugin.ObjToo
}
}

mt, err := newMapping(prof, obj, ui, force)
if err != nil {
return err
functions := map[profile.Function]*profile.Function{}
addFunction := func(f *profile.Function) *profile.Function {
if fp := functions[*f]; fp != nil {
return fp
}
functions[*f] = f
f.ID = uint64(len(prof.Function)) + 1
prof.Function = append(prof.Function, f)
return f
}

missingBinaries := false
mappingLocs := map[*profile.Mapping][]*profile.Location{}
for _, l := range prof.Location {
mappingLocs[l.Mapping] = append(mappingLocs[l.Mapping], l)
}
defer mt.close()
for midx, m := range prof.Mapping {
locs := mappingLocs[m]
if len(locs) == 0 {
// The mapping is dangling and has no locations pointing to it.
continue
}
// Do not attempt to re-symbolize a mapping that has already been symbolized.
if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
continue
}
if m.File == "" {
if midx == 0 {
ui.PrintErr("Main binary filename not available.")
continue
}
missingBinaries = true
continue
}
if m.Unsymbolizable() {
// Skip well-known system mappings
continue
}
if m.BuildID == "" {
if u, err := url.Parse(m.File); err == nil && u.IsAbs() && strings.Contains(strings.ToLower(u.Scheme), "http") {
// Skip mappings pointing to a source URL
continue
}
}

functions := make(map[profile.Function]*profile.Function)
for _, l := range mt.prof.Location {
m := l.Mapping
segment := mt.segments[m]
if segment == nil {
// Nothing to do.
name := filepath.Base(m.File)
if m.BuildID != "" {
name += fmt.Sprintf(" (build ID %s)", m.BuildID)
}
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
if err != nil {
ui.PrintErr("Local symbolization failed for ", name, ": ", err)
missingBinaries = true
continue
}
if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch")
f.Close()
continue
}
symbolizeOneMapping(m, locs, f, addFunction)
f.Close()
}

stack, err := segment.SourceLine(l.Address)
if missingBinaries {
ui.PrintErr("Some binary filenames not available. Symbolization may be incomplete.\n" +
"Try setting PPROF_BINARY_PATH to the search path for local binaries.")
}
return nil
}

func symbolizeOneMapping(m *profile.Mapping, locs []*profile.Location, obj plugin.ObjFile, addFunction func(*profile.Function) *profile.Function) {
for _, l := range locs {
stack, err := obj.SourceLine(l.Address)
if err != nil || len(stack) == 0 {
// No answers from addr2line.
continue
Expand All @@ -166,18 +224,11 @@ func doLocalSymbolize(prof *profile.Profile, fast, force bool, obj plugin.ObjToo
if frame.Line != 0 {
m.HasLineNumbers = true
}
f := &profile.Function{
f := addFunction(&profile.Function{
Name: frame.Func,
SystemName: frame.Func,
Filename: frame.File,
}
if fp := functions[*f]; fp != nil {
f = fp
} else {
functions[*f] = f
f.ID = uint64(len(mt.prof.Function)) + 1
mt.prof.Function = append(mt.prof.Function, f)
}
})
l.Line[i] = profile.Line{
Function: f,
Line: int64(frame.Line),
Expand All @@ -189,8 +240,6 @@ func doLocalSymbolize(prof *profile.Profile, fast, force bool, obj plugin.ObjToo
m.HasInlineFrames = true
}
}

return nil
}

// Demangle updates the function names in a profile with demangled C++
Expand Down Expand Up @@ -294,87 +343,3 @@ func removeMatching(name string, start, end byte) string {
}
return name
}

// newMapping creates a mappingTable for a profile.
func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
mt := &mappingTable{
prof: prof,
segments: make(map[*profile.Mapping]plugin.ObjFile),
}

// Identify used mappings
mappings := make(map[*profile.Mapping]bool)
for _, l := range prof.Location {
mappings[l.Mapping] = true
}

missingBinaries := false
for midx, m := range prof.Mapping {
if !mappings[m] {
continue
}

// Do not attempt to re-symbolize a mapping that has already been symbolized.
if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
continue
}

if m.File == "" {
if midx == 0 {
ui.PrintErr("Main binary filename not available.")
continue
}
missingBinaries = true
continue
}

// Skip well-known system mappings
if m.Unsymbolizable() {
continue
}

// Skip mappings pointing to a source URL
if m.BuildID == "" {
if u, err := url.Parse(m.File); err == nil && u.IsAbs() && strings.Contains(strings.ToLower(u.Scheme), "http") {
continue
}
}

name := filepath.Base(m.File)
if m.BuildID != "" {
name += fmt.Sprintf(" (build ID %s)", m.BuildID)
}
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
if err != nil {
ui.PrintErr("Local symbolization failed for ", name, ": ", err)
missingBinaries = true
continue
}
if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch")
f.Close()
continue
}

mt.segments[m] = f
}
if missingBinaries {
ui.PrintErr("Some binary filenames not available. Symbolization may be incomplete.\n" +
"Try setting PPROF_BINARY_PATH to the search path for local binaries.")
}
return mt, nil
}

// mappingTable contains the mechanisms for symbolization of a
// profile.
type mappingTable struct {
prof *profile.Profile
segments map[*profile.Mapping]plugin.ObjFile
}

// close releases any external processes being used for the mapping.
func (mt *mappingTable) close() {
for _, segment := range mt.segments {
segment.Close()
}
}
Loading