You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

301 lines
6.7 KiB

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