diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 61ab05ae..25890e3f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -144,6 +144,23 @@ jobs: run: | google-chrome --version + - name: Add LLVM 14.0 repository to ensure llvm-symbolizer 14.0.0+ on Ubuntu 20.04 + if: matrix.os == 'ubuntu-20.04' + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main" + sudo apt-get update + + - name: Install llvm-symbolizer + run: | + if [ "${{ matrix.os }}" = "ubuntu-20.04" ]; then + sudo apt-get install -y llvm-14 clang-14 + sudo update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-14 100 + else + sudo apt-get update + sudo apt-get install -y llvm clang + fi + - name: Fetch dependencies run: | sudo apt-get install graphviz @@ -154,6 +171,10 @@ jobs: # Add PATH for installed tools. echo "$GOPATH/bin:$PATH" >> $GITHUB_PATH + - name: Check llvm-symbolizer installation + run: | + llvm-symbolizer --version + - name: Run the script run: | go version 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..76570887 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) @@ -402,17 +402,17 @@ func TestMachoFiles(t *testing.T) { {"normal mapping", "exe_mac_64", 0x100000000, math.MaxUint64, 0, 0x100000f50, "_main", []plugin.Frame{ - {Func: "main", File: "/tmp/hello.c", Line: 3}, + {Func: "main", File: "/tmp/hello.c", Line: 3, StartLine: 3}, }}, {"other mapping", "exe_mac_64", 0x200000000, math.MaxUint64, 0, 0x200000f50, "_main", []plugin.Frame{ - {Func: "main", File: "/tmp/hello.c", Line: 3}, + {Func: "main", File: "/tmp/hello.c", Line: 3, StartLine: 3}, }}, {"lib normal mapping", "lib_mac_64", 0, math.MaxUint64, 0, 0xfa0, "_bar", []plugin.Frame{ - {Func: "bar", File: "/tmp/lib.c", Line: 5}, + {Func: "bar", File: "/tmp/lib.c", Line: 5, StartLine: 5}, }}, } { t.Run(tc.desc, func(t *testing.T) { @@ -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"}, @@ -532,7 +532,7 @@ func TestPEFile(t *testing.T) { t.Fatalf("SourceLine: unexpected error %v", err) } wantFrames := []plugin.Frame{ - {Func: "main", File: "hello.c", Line: 3, Column: 12}, + {Func: "main", File: "hello.c", Line: 3, Column: 12, StartLine: 3}, } if !reflect.DeepEqual(gotFrames, wantFrames) { t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, wantFrames) 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,