暂无描述

driver_test.go 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  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 driver
  15. import (
  16. "bytes"
  17. "fmt"
  18. "io/ioutil"
  19. "os"
  20. "regexp"
  21. "strconv"
  22. "strings"
  23. "testing"
  24. "time"
  25. "github.com/google/pprof/internal/plugin"
  26. "github.com/google/pprof/internal/proftest"
  27. "github.com/google/pprof/internal/symbolz"
  28. "github.com/google/pprof/profile"
  29. )
  30. func TestParse(t *testing.T) {
  31. // Override weblist command to collect output in buffer
  32. pprofCommands["weblist"].postProcess = nil
  33. // Our mockObjTool.Open will always return success, causing
  34. // driver.locateBinaries to "find" the binaries below in a non-existant
  35. // directory. As a workaround, point the search path to the fake
  36. // directory containing out fake binaries.
  37. savePath := os.Getenv("PPROF_BINARY_PATH")
  38. os.Setenv("PPROF_BINARY_PATH", "/path/to")
  39. defer os.Setenv("PPROF_BINARY_PATH", savePath)
  40. testcase := []struct {
  41. flags, source string
  42. }{
  43. {"text,functions,flat", "cpu"},
  44. {"tree,addresses,flat,nodecount=4", "cpusmall"},
  45. {"text,functions,flat", "unknown"},
  46. {"text,alloc_objects,flat", "heap_alloc"},
  47. {"text,files,flat", "heap"},
  48. {"text,inuse_objects,flat", "heap"},
  49. {"text,lines,cum,hide=line[X3]0", "cpu"},
  50. {"text,lines,cum,show=[12]00", "cpu"},
  51. {"topproto,lines,cum,hide=mangled[X3]0", "cpu"},
  52. {"tree,lines,cum,focus=[24]00", "heap"},
  53. {"tree,relative_percentages,cum,focus=[24]00", "heap"},
  54. {"callgrind", "cpu"},
  55. {"callgrind", "heap"},
  56. {"dot,functions,flat", "cpu"},
  57. {"dot,lines,flat,focus=[12]00", "heap"},
  58. {"dot,addresses,flat,ignore=[X3]002,focus=[X1]000", "contention"},
  59. {"dot,files,cum", "contention"},
  60. {"tags", "cpu"},
  61. {"tags,tagignore=tag[13],tagfocus=key[12]", "cpu"},
  62. {"traces", "cpu"},
  63. {"dot,alloc_space,flat,focus=[234]00", "heap_alloc"},
  64. {"dot,alloc_space,flat,hide=line.*1?23?", "heap_alloc"},
  65. {"dot,inuse_space,flat,tagfocus=1mb:2gb", "heap"},
  66. {"dot,inuse_space,flat,tagfocus=30kb:,tagignore=1mb:2mb", "heap"},
  67. {"disasm=line[13],addresses,flat", "cpu"},
  68. {"peek=line.*01", "cpu"},
  69. {"weblist=line[13],addresses,flat", "cpu"},
  70. }
  71. baseVars := pprofVariables
  72. defer func() { pprofVariables = baseVars }()
  73. for _, tc := range testcase {
  74. // Reset the pprof variables before processing
  75. pprofVariables = baseVars.makeCopy()
  76. f := baseFlags()
  77. f.args = []string{tc.source}
  78. flags := strings.Split(tc.flags, ",")
  79. // Skip the output format in the first flag, to output to a proto
  80. addFlags(&f, flags[1:])
  81. // Encode profile into a protobuf and decode it again.
  82. protoTempFile, err := ioutil.TempFile("", "profile_proto")
  83. if err != nil {
  84. t.Errorf("cannot create tempfile: %v", err)
  85. }
  86. defer protoTempFile.Close()
  87. f.strings["output"] = protoTempFile.Name()
  88. if flags[0] == "topproto" {
  89. f.bools["proto"] = false
  90. f.bools["topproto"] = true
  91. }
  92. // First pprof invocation to save the profile into a profile.proto.
  93. o1 := setDefaults(nil)
  94. o1.Flagset = f
  95. o1.Fetch = testFetcher{}
  96. o1.Sym = testSymbolizer{}
  97. if err := PProf(o1); err != nil {
  98. t.Errorf("%s %q: %v", tc.source, tc.flags, err)
  99. continue
  100. }
  101. // Reset the pprof variables after the proto invocation
  102. pprofVariables = baseVars.makeCopy()
  103. // Read the profile from the encoded protobuf
  104. outputTempFile, err := ioutil.TempFile("", "profile_output")
  105. if err != nil {
  106. t.Errorf("cannot create tempfile: %v", err)
  107. }
  108. defer outputTempFile.Close()
  109. f.strings["output"] = outputTempFile.Name()
  110. f.args = []string{protoTempFile.Name()}
  111. var solution string
  112. // Apply the flags for the second pprof run, and identify name of
  113. // the file containing expected results
  114. if flags[0] == "topproto" {
  115. solution = solutionFilename(tc.source, &f)
  116. delete(f.bools, "topproto")
  117. f.bools["text"] = true
  118. } else {
  119. delete(f.bools, "proto")
  120. addFlags(&f, flags[:1])
  121. solution = solutionFilename(tc.source, &f)
  122. }
  123. // Second pprof invocation to read the profile from profile.proto
  124. // and generate a report.
  125. o2 := setDefaults(nil)
  126. o2.Flagset = f
  127. o2.Sym = testSymbolizeDemangler{}
  128. o2.Obj = new(mockObjTool)
  129. if err := PProf(o2); err != nil {
  130. t.Errorf("%s: %v", tc.source, err)
  131. }
  132. b, err := ioutil.ReadFile(outputTempFile.Name())
  133. if err != nil {
  134. t.Errorf("Failed to read profile %s: %v", outputTempFile.Name(), err)
  135. }
  136. // Read data file with expected solution
  137. solution = "testdata/" + solution
  138. sbuf, err := ioutil.ReadFile(solution)
  139. if err != nil {
  140. t.Errorf("reading solution file %s: %v", solution, err)
  141. continue
  142. }
  143. if flags[0] == "svg" {
  144. b = removeScripts(b)
  145. sbuf = removeScripts(sbuf)
  146. }
  147. if string(b) != string(sbuf) {
  148. t.Errorf("diff %s %s", solution, tc.source)
  149. d, err := proftest.Diff(sbuf, b)
  150. if err != nil {
  151. t.Fatalf("diff %s %v", solution, err)
  152. }
  153. t.Errorf("%s\n%s\n", solution, d)
  154. }
  155. }
  156. }
  157. // removeScripts removes <script > .. </script> pairs from its input
  158. func removeScripts(in []byte) []byte {
  159. beginMarker := []byte("<script")
  160. endMarker := []byte("</script>")
  161. if begin := bytes.Index(in, beginMarker); begin > 0 {
  162. if end := bytes.Index(in[begin:], endMarker); end > 0 {
  163. in = append(in[:begin], removeScripts(in[begin+end+len(endMarker):])...)
  164. }
  165. }
  166. return in
  167. }
  168. // addFlags parses flag descriptions and adds them to the testFlags
  169. func addFlags(f *testFlags, flags []string) {
  170. for _, flag := range flags {
  171. fields := strings.SplitN(flag, "=", 2)
  172. switch len(fields) {
  173. case 1:
  174. f.bools[fields[0]] = true
  175. case 2:
  176. if i, err := strconv.Atoi(fields[1]); err == nil {
  177. f.ints[fields[0]] = i
  178. } else {
  179. f.strings[fields[0]] = fields[1]
  180. }
  181. }
  182. }
  183. }
  184. // solutionFilename returns the name of the solution file for the test
  185. func solutionFilename(source string, f *testFlags) string {
  186. name := []string{"pprof", strings.TrimPrefix(source, "http://host:8000/")}
  187. name = addString(name, f, []string{"flat", "cum"})
  188. name = addString(name, f, []string{"functions", "files", "lines", "addresses"})
  189. name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
  190. name = addString(name, f, []string{"relative_percentages"})
  191. name = addString(name, f, []string{"seconds"})
  192. name = addString(name, f, []string{"text", "tree", "callgrind", "dot", "svg", "tags", "dot", "traces", "disasm", "peek", "weblist", "topproto"})
  193. if f.strings["focus"] != "" || f.strings["tagfocus"] != "" {
  194. name = append(name, "focus")
  195. }
  196. if f.strings["ignore"] != "" || f.strings["tagignore"] != "" {
  197. name = append(name, "ignore")
  198. }
  199. name = addString(name, f, []string{"hide", "show"})
  200. return strings.Join(name, ".")
  201. }
  202. func addString(name []string, f *testFlags, components []string) []string {
  203. for _, c := range components {
  204. if f.bools[c] || f.strings[c] != "" || f.ints[c] != 0 {
  205. return append(name, c)
  206. }
  207. }
  208. return name
  209. }
  210. // testFlags implements the plugin.FlagSet interface.
  211. type testFlags struct {
  212. bools map[string]bool
  213. ints map[string]int
  214. floats map[string]float64
  215. strings map[string]string
  216. args []string
  217. }
  218. func (testFlags) ExtraUsage() string { return "" }
  219. func (f testFlags) Bool(s string, d bool, c string) *bool {
  220. if b, ok := f.bools[s]; ok {
  221. return &b
  222. }
  223. return &d
  224. }
  225. func (f testFlags) Int(s string, d int, c string) *int {
  226. if i, ok := f.ints[s]; ok {
  227. return &i
  228. }
  229. return &d
  230. }
  231. func (f testFlags) Float64(s string, d float64, c string) *float64 {
  232. if g, ok := f.floats[s]; ok {
  233. return &g
  234. }
  235. return &d
  236. }
  237. func (f testFlags) String(s, d, c string) *string {
  238. if t, ok := f.strings[s]; ok {
  239. return &t
  240. }
  241. return &d
  242. }
  243. func (f testFlags) BoolVar(p *bool, s string, d bool, c string) {
  244. if b, ok := f.bools[s]; ok {
  245. *p = b
  246. } else {
  247. *p = d
  248. }
  249. }
  250. func (f testFlags) IntVar(p *int, s string, d int, c string) {
  251. if i, ok := f.ints[s]; ok {
  252. *p = i
  253. } else {
  254. *p = d
  255. }
  256. }
  257. func (f testFlags) Float64Var(p *float64, s string, d float64, c string) {
  258. if g, ok := f.floats[s]; ok {
  259. *p = g
  260. } else {
  261. *p = d
  262. }
  263. }
  264. func (f testFlags) StringVar(p *string, s, d, c string) {
  265. if t, ok := f.strings[s]; ok {
  266. *p = t
  267. } else {
  268. *p = d
  269. }
  270. }
  271. func (f testFlags) StringList(s, d, c string) *[]*string {
  272. return &[]*string{}
  273. }
  274. func (f testFlags) Parse(func()) []string {
  275. return f.args
  276. }
  277. func baseFlags() testFlags {
  278. return testFlags{
  279. bools: map[string]bool{
  280. "proto": true,
  281. "trim": true,
  282. "compact_labels": true,
  283. },
  284. ints: map[string]int{
  285. "nodecount": 20,
  286. },
  287. floats: map[string]float64{
  288. "nodefraction": 0.05,
  289. "edgefraction": 0.01,
  290. "divide_by": 1.0,
  291. },
  292. strings: map[string]string{
  293. "unit": "minimum",
  294. },
  295. }
  296. }
  297. type testProfile struct {
  298. }
  299. const testStart = 0x1000
  300. const testOffset = 0x5000
  301. type testFetcher struct{}
  302. func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {
  303. var p *profile.Profile
  304. s = strings.TrimPrefix(s, "http://host:8000/")
  305. switch s {
  306. case "cpu", "unknown":
  307. p = cpuProfile()
  308. case "cpusmall":
  309. p = cpuProfileSmall()
  310. case "heap":
  311. p = heapProfile()
  312. case "heap_alloc":
  313. p = heapProfile()
  314. p.SampleType = []*profile.ValueType{
  315. {Type: "alloc_objects", Unit: "count"},
  316. {Type: "alloc_space", Unit: "bytes"},
  317. }
  318. case "contention":
  319. p = contentionProfile()
  320. case "symbolz":
  321. p = symzProfile()
  322. case "http://host2/symbolz":
  323. p = symzProfile()
  324. p.Mapping[0].Start += testOffset
  325. p.Mapping[0].Limit += testOffset
  326. for i := range p.Location {
  327. p.Location[i].Address += testOffset
  328. }
  329. default:
  330. return nil, "", fmt.Errorf("unexpected source: %s", s)
  331. }
  332. return p, s, nil
  333. }
  334. type testSymbolizer struct{}
  335. func (testSymbolizer) Symbolize(_ string, _ plugin.MappingSources, _ *profile.Profile) error {
  336. return nil
  337. }
  338. type testSymbolizeDemangler struct{}
  339. func (testSymbolizeDemangler) Symbolize(_ string, _ plugin.MappingSources, p *profile.Profile) error {
  340. for _, fn := range p.Function {
  341. if fn.Name == "" || fn.SystemName == fn.Name {
  342. fn.Name = fakeDemangler(fn.SystemName)
  343. }
  344. }
  345. return nil
  346. }
  347. func testFetchSymbols(source, post string) ([]byte, error) {
  348. var buf bytes.Buffer
  349. if source == "http://host2/symbolz" {
  350. for _, address := range strings.Split(post, "+") {
  351. a, _ := strconv.ParseInt(address, 0, 64)
  352. fmt.Fprintf(&buf, "%v\t", address)
  353. if a-testStart < testOffset {
  354. fmt.Fprintf(&buf, "wrong_source_%v_", address)
  355. continue
  356. }
  357. fmt.Fprintf(&buf, "%#x\n", a-testStart-testOffset)
  358. }
  359. return buf.Bytes(), nil
  360. }
  361. for _, address := range strings.Split(post, "+") {
  362. a, _ := strconv.ParseInt(address, 0, 64)
  363. fmt.Fprintf(&buf, "%v\t", address)
  364. if a-testStart > testOffset {
  365. fmt.Fprintf(&buf, "wrong_source_%v_", address)
  366. continue
  367. }
  368. fmt.Fprintf(&buf, "%#x\n", a-testStart)
  369. }
  370. return buf.Bytes(), nil
  371. }
  372. type testSymbolzSymbolizer struct{}
  373. func (testSymbolzSymbolizer) Symbolize(variables string, sources plugin.MappingSources, p *profile.Profile) error {
  374. return symbolz.Symbolize(sources, testFetchSymbols, p, nil)
  375. }
  376. func fakeDemangler(name string) string {
  377. switch name {
  378. case "mangled1000":
  379. return "line1000"
  380. case "mangled2000":
  381. return "line2000"
  382. case "mangled2001":
  383. return "line2001"
  384. case "mangled3000":
  385. return "line3000"
  386. case "mangled3001":
  387. return "line3001"
  388. case "mangled3002":
  389. return "line3002"
  390. case "mangledNEW":
  391. return "operator new"
  392. case "mangledMALLOC":
  393. return "malloc"
  394. default:
  395. return name
  396. }
  397. }
  398. func cpuProfile() *profile.Profile {
  399. var cpuM = []*profile.Mapping{
  400. {
  401. ID: 1,
  402. Start: 0x1000,
  403. Limit: 0x4000,
  404. File: "/path/to/testbinary",
  405. HasFunctions: true,
  406. HasFilenames: true,
  407. HasLineNumbers: true,
  408. HasInlineFrames: true,
  409. },
  410. }
  411. var cpuF = []*profile.Function{
  412. {ID: 1, Name: "mangled1000", SystemName: "mangled1000", Filename: "testdata/file1000.src"},
  413. {ID: 2, Name: "mangled2000", SystemName: "mangled2000", Filename: "testdata/file2000.src"},
  414. {ID: 3, Name: "mangled2001", SystemName: "mangled2001", Filename: "testdata/file2000.src"},
  415. {ID: 4, Name: "mangled3000", SystemName: "mangled3000", Filename: "testdata/file3000.src"},
  416. {ID: 5, Name: "mangled3001", SystemName: "mangled3001", Filename: "testdata/file3000.src"},
  417. {ID: 6, Name: "mangled3002", SystemName: "mangled3002", Filename: "testdata/file3000.src"},
  418. }
  419. var cpuL = []*profile.Location{
  420. {
  421. ID: 1000,
  422. Mapping: cpuM[0],
  423. Address: 0x1000,
  424. Line: []profile.Line{
  425. {Function: cpuF[0], Line: 1},
  426. },
  427. },
  428. {
  429. ID: 2000,
  430. Mapping: cpuM[0],
  431. Address: 0x2000,
  432. Line: []profile.Line{
  433. {Function: cpuF[2], Line: 9},
  434. {Function: cpuF[1], Line: 4},
  435. },
  436. },
  437. {
  438. ID: 3000,
  439. Mapping: cpuM[0],
  440. Address: 0x3000,
  441. Line: []profile.Line{
  442. {Function: cpuF[5], Line: 2},
  443. {Function: cpuF[4], Line: 5},
  444. {Function: cpuF[3], Line: 6},
  445. },
  446. },
  447. {
  448. ID: 3001,
  449. Mapping: cpuM[0],
  450. Address: 0x3001,
  451. Line: []profile.Line{
  452. {Function: cpuF[4], Line: 8},
  453. {Function: cpuF[3], Line: 9},
  454. },
  455. },
  456. {
  457. ID: 3002,
  458. Mapping: cpuM[0],
  459. Address: 0x3002,
  460. Line: []profile.Line{
  461. {Function: cpuF[5], Line: 5},
  462. {Function: cpuF[3], Line: 7},
  463. },
  464. },
  465. }
  466. return &profile.Profile{
  467. PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
  468. Period: 1,
  469. DurationNanos: 10e9,
  470. SampleType: []*profile.ValueType{
  471. {Type: "samples", Unit: "count"},
  472. {Type: "cpu", Unit: "milliseconds"},
  473. },
  474. Sample: []*profile.Sample{
  475. {
  476. Location: []*profile.Location{cpuL[0], cpuL[1], cpuL[2]},
  477. Value: []int64{1000, 1000},
  478. Label: map[string][]string{
  479. "key1": []string{"tag1"},
  480. "key2": []string{"tag1"},
  481. },
  482. },
  483. {
  484. Location: []*profile.Location{cpuL[0], cpuL[3]},
  485. Value: []int64{100, 100},
  486. Label: map[string][]string{
  487. "key1": []string{"tag2"},
  488. "key3": []string{"tag2"},
  489. },
  490. },
  491. {
  492. Location: []*profile.Location{cpuL[1], cpuL[4]},
  493. Value: []int64{10, 10},
  494. Label: map[string][]string{
  495. "key1": []string{"tag3"},
  496. "key2": []string{"tag2"},
  497. },
  498. },
  499. {
  500. Location: []*profile.Location{cpuL[2]},
  501. Value: []int64{10, 10},
  502. Label: map[string][]string{
  503. "key1": []string{"tag4"},
  504. "key2": []string{"tag1"},
  505. },
  506. },
  507. },
  508. Location: cpuL,
  509. Function: cpuF,
  510. Mapping: cpuM,
  511. }
  512. }
  513. func cpuProfileSmall() *profile.Profile {
  514. var cpuM = []*profile.Mapping{
  515. {
  516. ID: 1,
  517. Start: 0x1000,
  518. Limit: 0x4000,
  519. File: "/path/to/testbinary",
  520. HasFunctions: true,
  521. HasFilenames: true,
  522. HasLineNumbers: true,
  523. HasInlineFrames: true,
  524. },
  525. }
  526. var cpuL = []*profile.Location{
  527. {
  528. ID: 1000,
  529. Mapping: cpuM[0],
  530. Address: 0x1000,
  531. },
  532. {
  533. ID: 2000,
  534. Mapping: cpuM[0],
  535. Address: 0x2000,
  536. },
  537. {
  538. ID: 3000,
  539. Mapping: cpuM[0],
  540. Address: 0x3000,
  541. },
  542. {
  543. ID: 4000,
  544. Mapping: cpuM[0],
  545. Address: 0x4000,
  546. },
  547. {
  548. ID: 5000,
  549. Mapping: cpuM[0],
  550. Address: 0x5000,
  551. },
  552. }
  553. return &profile.Profile{
  554. PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
  555. Period: 1,
  556. DurationNanos: 10e9,
  557. SampleType: []*profile.ValueType{
  558. {Type: "samples", Unit: "count"},
  559. {Type: "cpu", Unit: "milliseconds"},
  560. },
  561. Sample: []*profile.Sample{
  562. {
  563. Location: []*profile.Location{cpuL[0], cpuL[1], cpuL[2]},
  564. Value: []int64{1000, 1000},
  565. },
  566. {
  567. Location: []*profile.Location{cpuL[3], cpuL[1], cpuL[4]},
  568. Value: []int64{1000, 1000},
  569. },
  570. {
  571. Location: []*profile.Location{cpuL[2]},
  572. Value: []int64{1000, 1000},
  573. },
  574. {
  575. Location: []*profile.Location{cpuL[4]},
  576. Value: []int64{1000, 1000},
  577. },
  578. },
  579. Location: cpuL,
  580. Function: nil,
  581. Mapping: cpuM,
  582. }
  583. }
  584. func heapProfile() *profile.Profile {
  585. var heapM = []*profile.Mapping{
  586. {
  587. ID: 1,
  588. BuildID: "buildid",
  589. Start: 0x1000,
  590. Limit: 0x4000,
  591. HasFunctions: true,
  592. HasFilenames: true,
  593. HasLineNumbers: true,
  594. HasInlineFrames: true,
  595. },
  596. }
  597. var heapF = []*profile.Function{
  598. {ID: 1, Name: "pruneme", SystemName: "pruneme", Filename: "prune.h"},
  599. {ID: 2, Name: "mangled1000", SystemName: "mangled1000", Filename: "testdata/file1000.src"},
  600. {ID: 3, Name: "mangled2000", SystemName: "mangled2000", Filename: "testdata/file2000.src"},
  601. {ID: 4, Name: "mangled2001", SystemName: "mangled2001", Filename: "testdata/file2000.src"},
  602. {ID: 5, Name: "mangled3000", SystemName: "mangled3000", Filename: "testdata/file3000.src"},
  603. {ID: 6, Name: "mangled3001", SystemName: "mangled3001", Filename: "testdata/file3000.src"},
  604. {ID: 7, Name: "mangled3002", SystemName: "mangled3002", Filename: "testdata/file3000.src"},
  605. {ID: 8, Name: "mangledMALLOC", SystemName: "mangledMALLOC", Filename: "malloc.h"},
  606. {ID: 9, Name: "mangledNEW", SystemName: "mangledNEW", Filename: "new.h"},
  607. }
  608. var heapL = []*profile.Location{
  609. {
  610. ID: 1000,
  611. Mapping: heapM[0],
  612. Address: 0x1000,
  613. Line: []profile.Line{
  614. {Function: heapF[0], Line: 100},
  615. {Function: heapF[7], Line: 100},
  616. {Function: heapF[1], Line: 1},
  617. },
  618. },
  619. {
  620. ID: 2000,
  621. Mapping: heapM[0],
  622. Address: 0x2000,
  623. Line: []profile.Line{
  624. {Function: heapF[8], Line: 100},
  625. {Function: heapF[3], Line: 2},
  626. {Function: heapF[2], Line: 3},
  627. },
  628. },
  629. {
  630. ID: 3000,
  631. Mapping: heapM[0],
  632. Address: 0x3000,
  633. Line: []profile.Line{
  634. {Function: heapF[8], Line: 100},
  635. {Function: heapF[6], Line: 3},
  636. {Function: heapF[5], Line: 2},
  637. {Function: heapF[4], Line: 4},
  638. },
  639. },
  640. {
  641. ID: 3001,
  642. Mapping: heapM[0],
  643. Address: 0x3001,
  644. Line: []profile.Line{
  645. {Function: heapF[0], Line: 100},
  646. {Function: heapF[8], Line: 100},
  647. {Function: heapF[5], Line: 2},
  648. {Function: heapF[4], Line: 4},
  649. },
  650. },
  651. {
  652. ID: 3002,
  653. Mapping: heapM[0],
  654. Address: 0x3002,
  655. Line: []profile.Line{
  656. {Function: heapF[6], Line: 3},
  657. {Function: heapF[4], Line: 4},
  658. },
  659. },
  660. }
  661. return &profile.Profile{
  662. PeriodType: &profile.ValueType{Type: "allocations", Unit: "bytes"},
  663. Period: 524288,
  664. SampleType: []*profile.ValueType{
  665. {Type: "inuse_objects", Unit: "count"},
  666. {Type: "inuse_space", Unit: "bytes"},
  667. },
  668. Sample: []*profile.Sample{
  669. {
  670. Location: []*profile.Location{heapL[0], heapL[1], heapL[2]},
  671. Value: []int64{10, 1024000},
  672. NumLabel: map[string][]int64{
  673. "bytes": []int64{102400},
  674. },
  675. },
  676. {
  677. Location: []*profile.Location{heapL[0], heapL[3]},
  678. Value: []int64{20, 4096000},
  679. NumLabel: map[string][]int64{
  680. "bytes": []int64{204800},
  681. },
  682. },
  683. {
  684. Location: []*profile.Location{heapL[1], heapL[4]},
  685. Value: []int64{40, 65536000},
  686. NumLabel: map[string][]int64{
  687. "bytes": []int64{1638400},
  688. },
  689. },
  690. {
  691. Location: []*profile.Location{heapL[2]},
  692. Value: []int64{80, 32768000},
  693. NumLabel: map[string][]int64{
  694. "bytes": []int64{409600},
  695. },
  696. },
  697. },
  698. DropFrames: ".*operator new.*|malloc",
  699. Location: heapL,
  700. Function: heapF,
  701. Mapping: heapM,
  702. }
  703. }
  704. func contentionProfile() *profile.Profile {
  705. var contentionM = []*profile.Mapping{
  706. {
  707. ID: 1,
  708. BuildID: "buildid-contention",
  709. Start: 0x1000,
  710. Limit: 0x4000,
  711. HasFunctions: true,
  712. HasFilenames: true,
  713. HasLineNumbers: true,
  714. HasInlineFrames: true,
  715. },
  716. }
  717. var contentionF = []*profile.Function{
  718. {ID: 1, Name: "mangled1000", SystemName: "mangled1000", Filename: "testdata/file1000.src"},
  719. {ID: 2, Name: "mangled2000", SystemName: "mangled2000", Filename: "testdata/file2000.src"},
  720. {ID: 3, Name: "mangled2001", SystemName: "mangled2001", Filename: "testdata/file2000.src"},
  721. {ID: 4, Name: "mangled3000", SystemName: "mangled3000", Filename: "testdata/file3000.src"},
  722. {ID: 5, Name: "mangled3001", SystemName: "mangled3001", Filename: "testdata/file3000.src"},
  723. {ID: 6, Name: "mangled3002", SystemName: "mangled3002", Filename: "testdata/file3000.src"},
  724. }
  725. var contentionL = []*profile.Location{
  726. {
  727. ID: 1000,
  728. Mapping: contentionM[0],
  729. Address: 0x1000,
  730. Line: []profile.Line{
  731. {Function: contentionF[0], Line: 1},
  732. },
  733. },
  734. {
  735. ID: 2000,
  736. Mapping: contentionM[0],
  737. Address: 0x2000,
  738. Line: []profile.Line{
  739. {Function: contentionF[2], Line: 2},
  740. {Function: contentionF[1], Line: 3},
  741. },
  742. },
  743. {
  744. ID: 3000,
  745. Mapping: contentionM[0],
  746. Address: 0x3000,
  747. Line: []profile.Line{
  748. {Function: contentionF[5], Line: 2},
  749. {Function: contentionF[4], Line: 3},
  750. {Function: contentionF[3], Line: 5},
  751. },
  752. },
  753. {
  754. ID: 3001,
  755. Mapping: contentionM[0],
  756. Address: 0x3001,
  757. Line: []profile.Line{
  758. {Function: contentionF[4], Line: 3},
  759. {Function: contentionF[3], Line: 5},
  760. },
  761. },
  762. {
  763. ID: 3002,
  764. Mapping: contentionM[0],
  765. Address: 0x3002,
  766. Line: []profile.Line{
  767. {Function: contentionF[5], Line: 4},
  768. {Function: contentionF[3], Line: 3},
  769. },
  770. },
  771. }
  772. return &profile.Profile{
  773. PeriodType: &profile.ValueType{Type: "contentions", Unit: "count"},
  774. Period: 524288,
  775. SampleType: []*profile.ValueType{
  776. {Type: "contentions", Unit: "count"},
  777. {Type: "delay", Unit: "nanoseconds"},
  778. },
  779. Sample: []*profile.Sample{
  780. {
  781. Location: []*profile.Location{contentionL[0], contentionL[1], contentionL[2]},
  782. Value: []int64{10, 10240000},
  783. },
  784. {
  785. Location: []*profile.Location{contentionL[0], contentionL[3]},
  786. Value: []int64{20, 40960000},
  787. },
  788. {
  789. Location: []*profile.Location{contentionL[1], contentionL[4]},
  790. Value: []int64{40, 65536000},
  791. },
  792. {
  793. Location: []*profile.Location{contentionL[2]},
  794. Value: []int64{80, 32768000},
  795. },
  796. },
  797. Location: contentionL,
  798. Function: contentionF,
  799. Mapping: contentionM,
  800. Comments: []string{"Comment #1", "Comment #2"},
  801. }
  802. }
  803. func symzProfile() *profile.Profile {
  804. var symzM = []*profile.Mapping{
  805. {
  806. ID: 1,
  807. Start: testStart,
  808. Limit: 0x4000,
  809. File: "/path/to/testbinary",
  810. },
  811. }
  812. var symzL = []*profile.Location{
  813. {ID: 1, Mapping: symzM[0], Address: testStart},
  814. {ID: 2, Mapping: symzM[0], Address: testStart + 0x1000},
  815. {ID: 3, Mapping: symzM[0], Address: testStart + 0x2000},
  816. }
  817. return &profile.Profile{
  818. PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
  819. Period: 1,
  820. DurationNanos: 10e9,
  821. SampleType: []*profile.ValueType{
  822. {Type: "samples", Unit: "count"},
  823. {Type: "cpu", Unit: "milliseconds"},
  824. },
  825. Sample: []*profile.Sample{
  826. {
  827. Location: []*profile.Location{symzL[0], symzL[1], symzL[2]},
  828. Value: []int64{1, 1},
  829. },
  830. },
  831. Location: symzL,
  832. Mapping: symzM,
  833. }
  834. }
  835. var autoCompleteTests = []struct {
  836. in string
  837. out string
  838. }{
  839. {"", ""},
  840. {"xyz", "xyz"}, // no match
  841. {"dis", "disasm"}, // single match
  842. {"t", "t"}, // many matches
  843. {"top abc", "top abc"}, // no function name match
  844. {"top mangledM", "top mangledMALLOC"}, // single function name match
  845. {"top cmd cmd mangledM", "top cmd cmd mangledMALLOC"},
  846. {"top mangled", "top mangled"}, // many function name matches
  847. {"cmd mangledM", "cmd mangledM"}, // invalid command
  848. {"top mangledM cmd", "top mangledM cmd"}, // cursor misplaced
  849. {"top edMA", "top mangledMALLOC"}, // single infix function name match
  850. {"top -mangledM", "top -mangledMALLOC"}, // ignore sign handled
  851. {"lin", "lines"}, // single variable match
  852. {"EdGeF", "edgefraction"}, // single capitalized match
  853. {"help dis", "help disasm"}, // help command match
  854. {"help relative_perc", "help relative_percentages"}, // help variable match
  855. {"help coMpa", "help compact_labels"}, // help variable capitalized match
  856. }
  857. func TestAutoComplete(t *testing.T) {
  858. complete := newCompleter(functionNames(heapProfile()))
  859. for _, test := range autoCompleteTests {
  860. if out := complete(test.in); out != test.out {
  861. t.Errorf("autoComplete(%s) = %s; want %s", test.in, out, test.out)
  862. }
  863. }
  864. }
  865. func TestTagFilter(t *testing.T) {
  866. var tagFilterTests = []struct {
  867. name, value string
  868. tags map[string][]string
  869. want bool
  870. }{
  871. {"test1", "tag2", map[string][]string{"value1": {"tag1", "tag2"}}, true},
  872. {"test2", "tag3", map[string][]string{"value1": {"tag1", "tag2"}}, false},
  873. {"test3", "tag1,tag3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
  874. {"test4", "t..[12],t..3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
  875. {"test5", "tag2,tag3", map[string][]string{"value1": {"tag1", "tag2"}}, false},
  876. }
  877. for _, test := range tagFilterTests {
  878. filter, err := compileTagFilter(test.name, test.value, &proftest.TestUI{T: t}, nil)
  879. if err != nil {
  880. t.Errorf("tagFilter %s:%v", test.name, err)
  881. continue
  882. }
  883. s := profile.Sample{
  884. Label: test.tags,
  885. }
  886. if got := filter(&s); got != test.want {
  887. t.Errorf("tagFilter %s: got %v, want %v", test.name, got, test.want)
  888. }
  889. }
  890. }
  891. func TestSymbolzAfterMerge(t *testing.T) {
  892. baseVars := pprofVariables
  893. pprofVariables = baseVars.makeCopy()
  894. defer func() { pprofVariables = baseVars }()
  895. f := baseFlags()
  896. f.args = []string{"symbolz", "http://host2/symbolz"}
  897. o := setDefaults(nil)
  898. o.Flagset = f
  899. o.Obj = new(mockObjTool)
  900. src, cmd, err := parseFlags(o)
  901. if err != nil {
  902. t.Fatalf("parseFlags: %v", err)
  903. }
  904. if len(cmd) != 1 || cmd[0] != "proto" {
  905. t.Fatalf("parseFlags returned command %v, want [proto]", cmd)
  906. }
  907. o.Fetch = testFetcher{}
  908. o.Sym = testSymbolzSymbolizer{}
  909. p, err := fetchProfiles(src, o)
  910. if err != nil {
  911. t.Fatalf("fetchProfiles: %v", err)
  912. }
  913. if len(p.Location) != 3 {
  914. t.Errorf("Got %d locations after merge, want %d", len(p.Location), 3)
  915. }
  916. for i, l := range p.Location {
  917. if len(l.Line) != 1 {
  918. t.Errorf("Number of lines for symbolz %#x in iteration %d, got %d, want %d", l.Address, i, len(l.Line), 1)
  919. continue
  920. }
  921. address := l.Address - l.Mapping.Start
  922. if got, want := l.Line[0].Function.Name, fmt.Sprintf("%#x", address); got != want {
  923. t.Errorf("symbolz %#x, got %s, want %s", address, got, want)
  924. }
  925. }
  926. }
  927. type mockObjTool struct{}
  928. func (*mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
  929. return &mockFile{file, "abcdef", 0}, nil
  930. }
  931. func (m *mockObjTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
  932. switch start {
  933. case 0x1000:
  934. return []plugin.Inst{
  935. {0x1000, "instruction one", "", 0},
  936. {0x1001, "instruction two", "", 0},
  937. {0x1002, "instruction three", "", 0},
  938. {0x1003, "instruction four", "", 0},
  939. }, nil
  940. case 0x3000:
  941. return []plugin.Inst{
  942. {0x3000, "instruction one", "", 0},
  943. {0x3001, "instruction two", "", 0},
  944. {0x3002, "instruction three", "", 0},
  945. {0x3003, "instruction four", "", 0},
  946. {0x3004, "instruction five", "", 0},
  947. }, nil
  948. }
  949. return nil, fmt.Errorf("unimplemented")
  950. }
  951. type mockFile struct {
  952. name, buildId string
  953. base uint64
  954. }
  955. // Name returns the underlyinf file name, if available
  956. func (m *mockFile) Name() string {
  957. return m.name
  958. }
  959. // Base returns the base address to use when looking up symbols in the file.
  960. func (m *mockFile) Base() uint64 {
  961. return m.base
  962. }
  963. // BuildID returns the GNU build ID of the file, or an empty string.
  964. func (m *mockFile) BuildID() string {
  965. return m.buildId
  966. }
  967. // SourceLine reports the source line information for a given
  968. // address in the file. Due to inlining, the source line information
  969. // is in general a list of positions representing a call stack,
  970. // with the leaf function first.
  971. func (*mockFile) SourceLine(addr uint64) ([]plugin.Frame, error) {
  972. return nil, fmt.Errorf("unimplemented")
  973. }
  974. // Symbols returns a list of symbols in the object file.
  975. // If r is not nil, Symbols restricts the list to symbols
  976. // with names matching the regular expression.
  977. // If addr is not zero, Symbols restricts the list to symbols
  978. // containing that address.
  979. func (m *mockFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
  980. switch r.String() {
  981. case "line[13]":
  982. return []*plugin.Sym{
  983. {[]string{"line1000"}, m.name, 0x1000, 0x1003},
  984. {[]string{"line3000"}, m.name, 0x3000, 0x3004},
  985. }, nil
  986. }
  987. return nil, fmt.Errorf("unimplemented")
  988. }
  989. // Close closes the file, releasing associated resources.
  990. func (*mockFile) Close() error {
  991. return nil
  992. }