Açıklama Yok

driver_test.go 28KB

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