// Copyright 2014 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package symbolizer import ( "fmt" "regexp" "sort" "strings" "testing" "github.com/google/pprof/internal/plugin" "github.com/google/pprof/internal/proftest" "github.com/google/pprof/profile" ) var testM = []*profile.Mapping{ { ID: 1, Start: 0x1000, Limit: 0x5000, File: "mapping", }, } var testL = []*profile.Location{ { ID: 1, Mapping: testM[0], Address: 1000, }, { ID: 2, Mapping: testM[0], Address: 2000, }, { ID: 3, Mapping: testM[0], Address: 3000, }, { ID: 4, Mapping: testM[0], Address: 4000, }, { ID: 5, Mapping: testM[0], Address: 5000, }, } var testProfile = profile.Profile{ DurationNanos: 10e9, SampleType: []*profile.ValueType{ {Type: "cpu", Unit: "cycles"}, }, Sample: []*profile.Sample{ { Location: []*profile.Location{testL[0]}, Value: []int64{1}, }, { Location: []*profile.Location{testL[1], testL[0]}, Value: []int64{10}, }, { Location: []*profile.Location{testL[2], testL[0]}, Value: []int64{100}, }, { Location: []*profile.Location{testL[3], testL[0]}, Value: []int64{1}, }, { Location: []*profile.Location{testL[4], testL[3], testL[0]}, Value: []int64{10000}, }, }, Location: testL, Mapping: testM, PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"}, Period: 10, } func TestSymbolization(t *testing.T) { sSym := symbolzSymbolize lSym := localSymbolize defer func() { symbolzSymbolize = sSym localSymbolize = lSym demangleFunction = Demangle }() symbolzSymbolize = symbolzMock localSymbolize = localMock demangleFunction = demangleMock type testcase struct { mode string wantComment string } s := Symbolizer{ Obj: mockObjTool{}, UI: &proftest.TestUI{T: t}, } for i, tc := range []testcase{ { "local", "local=[]", }, { "fastlocal", "local=[fast]", }, { "remote", "symbolz=[]", }, { "", "local=[]:symbolz=[]", }, { "demangle=none", "demangle=[none]:force:local=[force]:symbolz=[force]", }, { "remote:demangle=full", "demangle=[full]:force:symbolz=[force]", }, { "local:demangle=templates", "demangle=[templates]:force:local=[force]", }, { "force:remote", "force:symbolz=[force]", }, } { prof := testProfile.Copy() if err := s.Symbolize(tc.mode, nil, prof); err != nil { t.Errorf("symbolize #%d: %v", i, err) continue } sort.Strings(prof.Comments) if got, want := strings.Join(prof.Comments, ":"), tc.wantComment; got != want { t.Errorf("%q: got %s, want %s", tc.mode, got, want) continue } } } func symbolzMock(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error { var args []string if force { args = append(args, "force") } p.Comments = append(p.Comments, "symbolz=["+strings.Join(args, ",")+"]") return nil } func localMock(p *profile.Profile, fast, force bool, obj plugin.ObjTool, ui plugin.UI) error { var args []string if fast { args = append(args, "fast") } if force { args = append(args, "force") } p.Comments = append(p.Comments, "local=["+strings.Join(args, ",")+"]") return nil } func demangleMock(p *profile.Profile, force bool, mode string) { if force { p.Comments = append(p.Comments, "force") } if mode != "" { p.Comments = append(p.Comments, "demangle=["+mode+"]") } } func TestLocalSymbolization(t *testing.T) { prof := testProfile.Copy() if prof.HasFunctions() { t.Error("unexpected function names") } if prof.HasFileLines() { t.Error("unexpected filenames or line numbers") } b := mockObjTool{} if err := localSymbolize(prof, false, false, b, &proftest.TestUI{T: t}); err != nil { t.Fatalf("localSymbolize(): %v", err) } for _, loc := range prof.Location { if err := checkSymbolizedLocation(loc.Address, loc.Line); err != nil { t.Errorf("location %d: %v", loc.Address, err) } } if !prof.HasFunctions() { t.Error("missing function names") } if !prof.HasFileLines() { t.Error("missing filenames or line numbers") } } func checkSymbolizedLocation(a uint64, got []profile.Line) error { want, ok := mockAddresses[a] if !ok { return fmt.Errorf("unexpected address") } if len(want) != len(got) { return fmt.Errorf("want len %d, got %d", len(want), len(got)) } for i, w := range want { g := got[i] if g.Function.Name != w.Func { return fmt.Errorf("want function: %q, got %q", w.Func, g.Function.Name) } if g.Function.Filename != w.File { return fmt.Errorf("want filename: %q, got %q", w.File, g.Function.Filename) } if g.Line != int64(w.Line) { return fmt.Errorf("want lineno: %d, got %d", w.Line, g.Line) } } return nil } var mockAddresses = map[uint64][]plugin.Frame{ 1000: {frame("fun11", "file11.src", 10)}, 2000: {frame("fun21", "file21.src", 20), frame("fun22", "file22.src", 20)}, 3000: {frame("fun31", "file31.src", 30), frame("fun32", "file32.src", 30), frame("fun33", "file33.src", 30)}, 4000: {frame("fun41", "file41.src", 40), frame("fun42", "file42.src", 40), frame("fun43", "file43.src", 40), frame("fun44", "file44.src", 40)}, 5000: {frame("fun51", "file51.src", 50), frame("fun52", "file52.src", 50), frame("fun53", "file53.src", 50), frame("fun54", "file54.src", 50), frame("fun55", "file55.src", 50)}, } func frame(fname, file string, line int) plugin.Frame { return plugin.Frame{ Func: fname, File: file, Line: line} } type mockObjTool struct{} func (mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) { return mockObjFile{frames: mockAddresses}, nil } func (mockObjTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) { return nil, fmt.Errorf("disassembly not supported") } type mockObjFile struct { frames map[uint64][]plugin.Frame } func (mockObjFile) Name() string { return "" } func (mockObjFile) Base() uint64 { return 0 } func (mockObjFile) BuildID() string { return "" } func (mf mockObjFile) SourceLine(addr uint64) ([]plugin.Frame, error) { return mf.frames[addr], nil } func (mockObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { return []*plugin.Sym{}, nil } func (mockObjFile) Close() error { return nil }