Ei kuvausta

driver_test.go 29KB

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