From a08855918ad637037ccacf83f8a7c25fe4d0cc1a Mon Sep 17 00:00:00 2001 From: Francisco Boni Date: Sun, 1 Sep 2024 12:40:00 -0300 Subject: [PATCH] Use llvm-symbolizer's JSON output to provide function start lines When analyzing a perf.data profile converted automatically via perf_to_profile via pprof -raw perf.data, no function start lines (s=0) are present in any of the locations. With https://github.com/google/pprof/commit/813a5fbdbec8a66f7a5aedb876e1b2c3ee0f99ac, this can be easily solved by using the same JSON frame data from llvm-symbolizer to provide StartLine for Function.start_line. This solves https://github.com/google/pprof/issues/823. --- driver/driver.go | 9 +++++---- internal/binutils/addr2liner_llvm.go | 11 ++++++----- internal/binutils/binutils_test.go | 7 ++++--- internal/binutils/testdata/fake-llvm-symbolizer | 2 +- internal/plugin/plugin.go | 9 +++++---- internal/symbolizer/symbolizer.go | 1 + 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/driver/driver.go b/driver/driver.go index 6cbf6693..989aac32 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -186,10 +186,11 @@ type ObjFile interface { // A Frame describes a single line in a source file. type Frame struct { - Func string // name of function - File string // source file name - Line int // line in file - Column int // column in file + Func string // name of function + File string // source file name + Line int // line in file + Column int // column in file + StartLine int // start line of function (if available) } // A Sym describes a single symbol in an object file. diff --git a/internal/binutils/addr2liner_llvm.go b/internal/binutils/addr2liner_llvm.go index 5e51644f..2f5d97e8 100644 --- a/internal/binutils/addr2liner_llvm.go +++ b/internal/binutils/addr2liner_llvm.go @@ -151,10 +151,11 @@ func (d *llvmSymbolizer) readCodeFrames() ([]plugin.Frame, error) { Address string `json:"Address"` ModuleName string `json:"ModuleName"` Symbol []struct { - Line int `json:"Line"` - Column int `json:"Column"` - FunctionName string `json:"FunctionName"` - FileName string `json:"FileName"` + Line int `json:"Line"` + Column int `json:"Column"` + FunctionName string `json:"FunctionName"` + FileName string `json:"FileName"` + StartLine int `json:"StartLine"` } `json:"Symbol"` } if err := json.Unmarshal([]byte(line), &frame); err != nil { @@ -162,7 +163,7 @@ func (d *llvmSymbolizer) readCodeFrames() ([]plugin.Frame, error) { } var stack []plugin.Frame for _, s := range frame.Symbol { - stack = append(stack, plugin.Frame{Func: s.FunctionName, File: s.FileName, Line: s.Line, Column: s.Column}) + stack = append(stack, plugin.Frame{Func: s.FunctionName, File: s.FileName, Line: s.Line, Column: s.Column, StartLine: s.StartLine}) } return stack, nil } diff --git a/internal/binutils/binutils_test.go b/internal/binutils/binutils_test.go index dd931f4b..44739460 100644 --- a/internal/binutils/binutils_test.go +++ b/internal/binutils/binutils_test.go @@ -373,7 +373,7 @@ func TestObjFile(t *testing.T) { t.Fatalf("SourceLine: unexpected error %v", err) } wantFrames := []plugin.Frame{ - {Func: "main", File: "/tmp/hello.c", Line: 3}, + {Func: "main", File: "/tmp/hello.c", Line: 3, StartLine: 3}, } if !reflect.DeepEqual(gotFrames, wantFrames) { t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, wantFrames) @@ -461,8 +461,8 @@ func TestLLVMSymbolizer(t *testing.T) { frames []plugin.Frame }{ {0x10, false, []plugin.Frame{ - {Func: "Inlined_0x10", File: "foo.h", Line: 0, Column: 0}, - {Func: "Func_0x10", File: "foo.c", Line: 2, Column: 1}, + {Func: "Inlined_0x10", File: "foo.h", Line: 0, Column: 0, StartLine: 0}, + {Func: "Func_0x10", File: "foo.c", Line: 2, Column: 1, StartLine: 2}, }}, {0x20, true, []plugin.Frame{ {Func: "foo_0x20", File: "0x20 8"}, @@ -480,6 +480,7 @@ func TestLLVMSymbolizer(t *testing.T) { defer symbolizer.rw.close() frames, err := symbolizer.addrInfo(c.addr) + t.Logf("EITA expect %v; got %v\n", c.frames, frames) if err != nil { t.Fatalf("LLVM: unexpected error %v", err) } diff --git a/internal/binutils/testdata/fake-llvm-symbolizer b/internal/binutils/testdata/fake-llvm-symbolizer index 507761c9..491a2cd3 100755 --- a/internal/binutils/testdata/fake-llvm-symbolizer +++ b/internal/binutils/testdata/fake-llvm-symbolizer @@ -29,7 +29,7 @@ while read line; do addr=$3 case ${kind} in CODE) - echo "{\"Address\":\"${addr}\",\"ModuleName\":\"${fname}\",\"Symbol\":[{\"Column\":0,\"FileName\":\"${fname}.h\",\"FunctionName\":\"Inlined_${addr}\",\"Line\":0},{\"Column\":1,\"FileName\":\"${fname}.c\",\"FunctionName\":\"Func_${addr}\",\"Line\":2}]}" + echo "{\"Address\":\"${addr}\",\"ModuleName\":\"${fname}\",\"Symbol\":[{\"Column\":0,\"FileName\":\"${fname}.h\",\"FunctionName\":\"Inlined_${addr}\",\"Line\":0,\"StartLine\":0},{\"Column\":1,\"FileName\":\"${fname}.c\",\"FunctionName\":\"Func_${addr}\",\"Line\":2,\"StartLine\":2}]}" ;; DATA) echo "{\"Address\":\"${addr}\",\"ModuleName\":\"${fname}\",\"Data\":{\"Name\":\"${fname}_${addr}\",\"Size\":\"0x8\",\"Start\":\"${addr}\"}}" diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go index f2ef9871..2692c722 100644 --- a/internal/plugin/plugin.go +++ b/internal/plugin/plugin.go @@ -159,10 +159,11 @@ type ObjFile interface { // A Frame describes a location in a single line in a source file. type Frame struct { - Func string // name of function - File string // source file name - Line int // line in file - Column int // column in line (if available) + Func string // name of function + File string // source file name + Line int // line in file + Column int // column in line (if available) + StartLine int // start line of function (if available) } // A Sym describes a single symbol in an object file. diff --git a/internal/symbolizer/symbolizer.go b/internal/symbolizer/symbolizer.go index 70b40472..0d451364 100644 --- a/internal/symbolizer/symbolizer.go +++ b/internal/symbolizer/symbolizer.go @@ -228,6 +228,7 @@ func symbolizeOneMapping(m *profile.Mapping, locs []*profile.Location, obj plugi Name: frame.Func, SystemName: frame.Func, Filename: frame.File, + StartLine: int64(frame.StartLine), }) l.Line[i] = profile.Line{ Function: f,