1// Copyright 2014 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package symbolizer 16 17import ( 18 "fmt" 19 "regexp" 20 "sort" 21 "strings" 22 "testing" 23 24 "github.com/google/pprof/internal/plugin" 25 "github.com/google/pprof/internal/proftest" 26 "github.com/google/pprof/profile" 27) 28 29var testM = []*profile.Mapping{ 30 { 31 ID: 1, 32 Start: 0x1000, 33 Limit: 0x5000, 34 File: "mapping", 35 }, 36} 37 38var testL = []*profile.Location{ 39 { 40 ID: 1, 41 Mapping: testM[0], 42 Address: 1000, 43 }, 44 { 45 ID: 2, 46 Mapping: testM[0], 47 Address: 2000, 48 }, 49 { 50 ID: 3, 51 Mapping: testM[0], 52 Address: 3000, 53 }, 54 { 55 ID: 4, 56 Mapping: testM[0], 57 Address: 4000, 58 }, 59 { 60 ID: 5, 61 Mapping: testM[0], 62 Address: 5000, 63 }, 64} 65 66var testProfile = profile.Profile{ 67 DurationNanos: 10e9, 68 SampleType: []*profile.ValueType{ 69 {Type: "cpu", Unit: "cycles"}, 70 }, 71 Sample: []*profile.Sample{ 72 { 73 Location: []*profile.Location{testL[0]}, 74 Value: []int64{1}, 75 }, 76 { 77 Location: []*profile.Location{testL[1], testL[0]}, 78 Value: []int64{10}, 79 }, 80 { 81 Location: []*profile.Location{testL[2], testL[0]}, 82 Value: []int64{100}, 83 }, 84 { 85 Location: []*profile.Location{testL[3], testL[0]}, 86 Value: []int64{1}, 87 }, 88 { 89 Location: []*profile.Location{testL[4], testL[3], testL[0]}, 90 Value: []int64{10000}, 91 }, 92 }, 93 Location: testL, 94 Mapping: testM, 95 PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"}, 96 Period: 10, 97} 98 99func TestSymbolization(t *testing.T) { 100 sSym := symbolzSymbolize 101 lSym := localSymbolize 102 defer func() { 103 symbolzSymbolize = sSym 104 localSymbolize = lSym 105 demangleFunction = Demangle 106 }() 107 symbolzSymbolize = symbolzMock 108 localSymbolize = localMock 109 demangleFunction = demangleMock 110 111 type testcase struct { 112 mode string 113 wantComment string 114 } 115 116 s := Symbolizer{ 117 Obj: mockObjTool{}, 118 UI: &proftest.TestUI{T: t}, 119 } 120 for i, tc := range []testcase{ 121 { 122 "local", 123 "local=[]", 124 }, 125 { 126 "fastlocal", 127 "local=[fast]", 128 }, 129 { 130 "remote", 131 "symbolz=[]", 132 }, 133 { 134 "", 135 "local=[]:symbolz=[]", 136 }, 137 { 138 "demangle=none", 139 "demangle=[none]:force:local=[force]:symbolz=[force]", 140 }, 141 { 142 "remote:demangle=full", 143 "demangle=[full]:force:symbolz=[force]", 144 }, 145 { 146 "local:demangle=templates", 147 "demangle=[templates]:force:local=[force]", 148 }, 149 { 150 "force:remote", 151 "force:symbolz=[force]", 152 }, 153 } { 154 prof := testProfile.Copy() 155 if err := s.Symbolize(tc.mode, nil, prof); err != nil { 156 t.Errorf("symbolize #%d: %v", i, err) 157 continue 158 } 159 sort.Strings(prof.Comments) 160 if got, want := strings.Join(prof.Comments, ":"), tc.wantComment; got != want { 161 t.Errorf("%q: got %s, want %s", tc.mode, got, want) 162 continue 163 } 164 } 165} 166 167func symbolzMock(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error { 168 var args []string 169 if force { 170 args = append(args, "force") 171 } 172 p.Comments = append(p.Comments, "symbolz=["+strings.Join(args, ",")+"]") 173 return nil 174} 175 176func localMock(p *profile.Profile, fast, force bool, obj plugin.ObjTool, ui plugin.UI) error { 177 var args []string 178 if fast { 179 args = append(args, "fast") 180 } 181 if force { 182 args = append(args, "force") 183 } 184 p.Comments = append(p.Comments, "local=["+strings.Join(args, ",")+"]") 185 return nil 186} 187 188func demangleMock(p *profile.Profile, force bool, mode string) { 189 if force { 190 p.Comments = append(p.Comments, "force") 191 } 192 if mode != "" { 193 p.Comments = append(p.Comments, "demangle=["+mode+"]") 194 } 195} 196 197func TestLocalSymbolization(t *testing.T) { 198 prof := testProfile.Copy() 199 200 if prof.HasFunctions() { 201 t.Error("unexpected function names") 202 } 203 if prof.HasFileLines() { 204 t.Error("unexpected filenames or line numbers") 205 } 206 207 b := mockObjTool{} 208 if err := localSymbolize(prof, false, false, b, &proftest.TestUI{T: t}); err != nil { 209 t.Fatalf("localSymbolize(): %v", err) 210 } 211 212 for _, loc := range prof.Location { 213 if err := checkSymbolizedLocation(loc.Address, loc.Line); err != nil { 214 t.Errorf("location %d: %v", loc.Address, err) 215 } 216 } 217 if !prof.HasFunctions() { 218 t.Error("missing function names") 219 } 220 if !prof.HasFileLines() { 221 t.Error("missing filenames or line numbers") 222 } 223} 224 225func checkSymbolizedLocation(a uint64, got []profile.Line) error { 226 want, ok := mockAddresses[a] 227 if !ok { 228 return fmt.Errorf("unexpected address") 229 } 230 if len(want) != len(got) { 231 return fmt.Errorf("want len %d, got %d", len(want), len(got)) 232 } 233 234 for i, w := range want { 235 g := got[i] 236 if g.Function.Name != w.Func { 237 return fmt.Errorf("want function: %q, got %q", w.Func, g.Function.Name) 238 } 239 if g.Function.Filename != w.File { 240 return fmt.Errorf("want filename: %q, got %q", w.File, g.Function.Filename) 241 } 242 if g.Line != int64(w.Line) { 243 return fmt.Errorf("want lineno: %d, got %d", w.Line, g.Line) 244 } 245 } 246 return nil 247} 248 249var mockAddresses = map[uint64][]plugin.Frame{ 250 1000: {frame("fun11", "file11.src", 10)}, 251 2000: {frame("fun21", "file21.src", 20), frame("fun22", "file22.src", 20)}, 252 3000: {frame("fun31", "file31.src", 30), frame("fun32", "file32.src", 30), frame("fun33", "file33.src", 30)}, 253 4000: {frame("fun41", "file41.src", 40), frame("fun42", "file42.src", 40), frame("fun43", "file43.src", 40), frame("fun44", "file44.src", 40)}, 254 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)}, 255} 256 257func frame(fname, file string, line int) plugin.Frame { 258 return plugin.Frame{ 259 Func: fname, 260 File: file, 261 Line: line} 262} 263 264type mockObjTool struct{} 265 266func (mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) { 267 return mockObjFile{frames: mockAddresses}, nil 268} 269 270func (mockObjTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) { 271 return nil, fmt.Errorf("disassembly not supported") 272} 273 274type mockObjFile struct { 275 frames map[uint64][]plugin.Frame 276} 277 278func (mockObjFile) Name() string { 279 return "" 280} 281 282func (mockObjFile) Base() uint64 { 283 return 0 284} 285 286func (mockObjFile) BuildID() string { 287 return "" 288} 289 290func (mf mockObjFile) SourceLine(addr uint64) ([]plugin.Frame, error) { 291 return mf.frames[addr], nil 292} 293 294func (mockObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) { 295 return []*plugin.Sym{}, nil 296} 297 298func (mockObjFile) Close() error { 299 return nil 300} 301