Aucune description

legacy_profile.go 31KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252
  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. // This file implements parsers to convert legacy profiles into the
  15. // profile.proto format.
  16. package profile
  17. import (
  18. "bufio"
  19. "bytes"
  20. "fmt"
  21. "io"
  22. "math"
  23. "regexp"
  24. "strconv"
  25. "strings"
  26. )
  27. var (
  28. countStartRE = regexp.MustCompile(`\A(\w+) profile: total \d+\n\z`)
  29. countRE = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\n\z`)
  30. heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`)
  31. heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`)
  32. contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`)
  33. hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`)
  34. growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz?`)
  35. fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz?`)
  36. threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`)
  37. threadStartRE = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`)
  38. procMapsRE = regexp.MustCompile(`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?`)
  39. briefMapsRE = regexp.MustCompile(`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?`)
  40. )
  41. func isSpaceOrComment(line string) bool {
  42. trimmed := strings.TrimSpace(line)
  43. return len(trimmed) == 0 || trimmed[0] == '#'
  44. }
  45. // parseGoCount parses a Go count profile (e.g., threadcreate or
  46. // goroutine) and returns a new Profile.
  47. func parseGoCount(b []byte) (*Profile, error) {
  48. r := bytes.NewBuffer(b)
  49. var line string
  50. var err error
  51. for {
  52. // Skip past comments and empty lines seeking a real header.
  53. line, err = r.ReadString('\n')
  54. if err != nil {
  55. return nil, err
  56. }
  57. if !isSpaceOrComment(line) {
  58. break
  59. }
  60. }
  61. m := countStartRE.FindStringSubmatch(line)
  62. if m == nil {
  63. return nil, errUnrecognized
  64. }
  65. profileType := string(m[1])
  66. p := &Profile{
  67. PeriodType: &ValueType{Type: profileType, Unit: "count"},
  68. Period: 1,
  69. SampleType: []*ValueType{{Type: profileType, Unit: "count"}},
  70. }
  71. locations := make(map[uint64]*Location)
  72. for {
  73. line, err = r.ReadString('\n')
  74. if err != nil {
  75. if err == io.EOF {
  76. break
  77. }
  78. return nil, err
  79. }
  80. if isSpaceOrComment(line) {
  81. continue
  82. }
  83. if strings.HasPrefix(line, "---") {
  84. break
  85. }
  86. m := countRE.FindStringSubmatch(line)
  87. if m == nil {
  88. return nil, errMalformed
  89. }
  90. n, err := strconv.ParseInt(string(m[1]), 0, 64)
  91. if err != nil {
  92. return nil, errMalformed
  93. }
  94. fields := strings.Fields(string(m[2]))
  95. locs := make([]*Location, 0, len(fields))
  96. for _, stk := range fields {
  97. addr, err := strconv.ParseUint(stk, 0, 64)
  98. if err != nil {
  99. return nil, errMalformed
  100. }
  101. // Adjust all frames by -1 to land on top of the call instruction.
  102. addr--
  103. loc := locations[addr]
  104. if loc == nil {
  105. loc = &Location{
  106. Address: addr,
  107. }
  108. locations[addr] = loc
  109. p.Location = append(p.Location, loc)
  110. }
  111. locs = append(locs, loc)
  112. }
  113. p.Sample = append(p.Sample, &Sample{
  114. Location: locs,
  115. Value: []int64{n},
  116. })
  117. }
  118. if err = parseAdditionalSections(strings.TrimSpace(line), r, p); err != nil {
  119. return nil, err
  120. }
  121. return p, nil
  122. }
  123. // remapLocationIDs ensures there is a location for each address
  124. // referenced by a sample, and remaps the samples to point to the new
  125. // location ids.
  126. func (p *Profile) remapLocationIDs() {
  127. seen := make(map[*Location]bool, len(p.Location))
  128. var locs []*Location
  129. for _, s := range p.Sample {
  130. for _, l := range s.Location {
  131. if seen[l] {
  132. continue
  133. }
  134. l.ID = uint64(len(locs) + 1)
  135. locs = append(locs, l)
  136. seen[l] = true
  137. }
  138. }
  139. p.Location = locs
  140. }
  141. func (p *Profile) remapFunctionIDs() {
  142. seen := make(map[*Function]bool, len(p.Function))
  143. var fns []*Function
  144. for _, l := range p.Location {
  145. for _, ln := range l.Line {
  146. fn := ln.Function
  147. if fn == nil || seen[fn] {
  148. continue
  149. }
  150. fn.ID = uint64(len(fns) + 1)
  151. fns = append(fns, fn)
  152. seen[fn] = true
  153. }
  154. }
  155. p.Function = fns
  156. }
  157. // remapMappingIDs matches location addresses with existing mappings
  158. // and updates them appropriately. This is O(N*M), if this ever shows
  159. // up as a bottleneck, evaluate sorting the mappings and doing a
  160. // binary search, which would make it O(N*log(M)).
  161. func (p *Profile) remapMappingIDs() {
  162. // Some profile handlers will incorrectly set regions for the main
  163. // executable if its section is remapped. Fix them through heuristics.
  164. if len(p.Mapping) > 0 {
  165. // Remove the initial mapping if named '/anon_hugepage' and has a
  166. // consecutive adjacent mapping.
  167. if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") {
  168. if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start {
  169. p.Mapping = p.Mapping[1:]
  170. }
  171. }
  172. }
  173. // Subtract the offset from the start of the main mapping if it
  174. // ends up at a recognizable start address.
  175. if len(p.Mapping) > 0 {
  176. const expectedStart = 0x400000
  177. if m := p.Mapping[0]; m.Start-m.Offset == expectedStart {
  178. m.Start = expectedStart
  179. m.Offset = 0
  180. }
  181. }
  182. // Associate each location with an address to the corresponding
  183. // mapping. Create fake mapping if a suitable one isn't found.
  184. var fake *Mapping
  185. nextLocation:
  186. for _, l := range p.Location {
  187. a := l.Address
  188. if l.Mapping != nil || a == 0 {
  189. continue
  190. }
  191. for _, m := range p.Mapping {
  192. if m.Start <= a && a < m.Limit {
  193. l.Mapping = m
  194. continue nextLocation
  195. }
  196. }
  197. // Work around legacy handlers failing to encode the first
  198. // part of mappings split into adjacent ranges.
  199. for _, m := range p.Mapping {
  200. if m.Offset != 0 && m.Start-m.Offset <= a && a < m.Start {
  201. m.Start -= m.Offset
  202. m.Offset = 0
  203. l.Mapping = m
  204. continue nextLocation
  205. }
  206. }
  207. // If there is still no mapping, create a fake one.
  208. // This is important for the Go legacy handler, which produced
  209. // no mappings.
  210. if fake == nil {
  211. fake = &Mapping{
  212. ID: 1,
  213. Limit: ^uint64(0),
  214. }
  215. p.Mapping = append(p.Mapping, fake)
  216. }
  217. l.Mapping = fake
  218. }
  219. // Reset all mapping IDs.
  220. for i, m := range p.Mapping {
  221. m.ID = uint64(i + 1)
  222. }
  223. }
  224. var cpuInts = []func([]byte) (uint64, []byte){
  225. get32l,
  226. get32b,
  227. get64l,
  228. get64b,
  229. }
  230. func get32l(b []byte) (uint64, []byte) {
  231. if len(b) < 4 {
  232. return 0, nil
  233. }
  234. return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:]
  235. }
  236. func get32b(b []byte) (uint64, []byte) {
  237. if len(b) < 4 {
  238. return 0, nil
  239. }
  240. return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:]
  241. }
  242. func get64l(b []byte) (uint64, []byte) {
  243. if len(b) < 8 {
  244. return 0, nil
  245. }
  246. return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:]
  247. }
  248. func get64b(b []byte) (uint64, []byte) {
  249. if len(b) < 8 {
  250. return 0, nil
  251. }
  252. return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:]
  253. }
  254. // parseCPU parses a profilez legacy profile and returns a newly
  255. // populated Profile.
  256. //
  257. // The general format for profilez samples is a sequence of words in
  258. // binary format. The first words are a header with the following data:
  259. // 1st word -- 0
  260. // 2nd word -- 3
  261. // 3rd word -- 0 if a c++ application, 1 if a java application.
  262. // 4th word -- Sampling period (in microseconds).
  263. // 5th word -- Padding.
  264. func parseCPU(b []byte) (*Profile, error) {
  265. var parse func([]byte) (uint64, []byte)
  266. var n1, n2, n3, n4, n5 uint64
  267. for _, parse = range cpuInts {
  268. var tmp []byte
  269. n1, tmp = parse(b)
  270. n2, tmp = parse(tmp)
  271. n3, tmp = parse(tmp)
  272. n4, tmp = parse(tmp)
  273. n5, tmp = parse(tmp)
  274. if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 {
  275. b = tmp
  276. return cpuProfile(b, int64(n4), parse)
  277. }
  278. if tmp != nil && n1 == 0 && n2 == 3 && n3 == 1 && n4 > 0 && n5 == 0 {
  279. b = tmp
  280. return javaCPUProfile(b, int64(n4), parse)
  281. }
  282. }
  283. return nil, errUnrecognized
  284. }
  285. // cpuProfile returns a new Profile from C++ profilez data.
  286. // b is the profile bytes after the header, period is the profiling
  287. // period, and parse is a function to parse 8-byte chunks from the
  288. // profile in its native endianness.
  289. func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) {
  290. p := &Profile{
  291. Period: period * 1000,
  292. PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"},
  293. SampleType: []*ValueType{
  294. {Type: "samples", Unit: "count"},
  295. {Type: "cpu", Unit: "nanoseconds"},
  296. },
  297. }
  298. var err error
  299. if b, _, err = parseCPUSamples(b, parse, true, p); err != nil {
  300. return nil, err
  301. }
  302. // If *most* samples have the same second-to-the-bottom frame, it
  303. // strongly suggests that it is an uninteresting artifact of
  304. // measurement -- a stack frame pushed by the signal handler. The
  305. // bottom frame is always correct as it is picked up from the signal
  306. // structure, not the stack. Check if this is the case and if so,
  307. // remove.
  308. // Remove up to two frames.
  309. maxiter := 2
  310. // Allow one different sample for this many samples with the same
  311. // second-to-last frame.
  312. similarSamples := 32
  313. margin := len(p.Sample) / similarSamples
  314. for iter := 0; iter < maxiter; iter++ {
  315. addr1 := make(map[uint64]int)
  316. for _, s := range p.Sample {
  317. if len(s.Location) > 1 {
  318. a := s.Location[1].Address
  319. addr1[a] = addr1[a] + 1
  320. }
  321. }
  322. for id1, count := range addr1 {
  323. if count >= len(p.Sample)-margin {
  324. // Found uninteresting frame, strip it out from all samples
  325. for _, s := range p.Sample {
  326. if len(s.Location) > 1 && s.Location[1].Address == id1 {
  327. s.Location = append(s.Location[:1], s.Location[2:]...)
  328. }
  329. }
  330. break
  331. }
  332. }
  333. }
  334. if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil {
  335. return nil, err
  336. }
  337. cleanupDuplicateLocations(p)
  338. return p, nil
  339. }
  340. func cleanupDuplicateLocations(p *Profile) {
  341. // The profile handler may duplicate the leaf frame, because it gets
  342. // its address both from stack unwinding and from the signal
  343. // context. Detect this and delete the duplicate, which has been
  344. // adjusted by -1. The leaf address should not be adjusted as it is
  345. // not a call.
  346. for _, s := range p.Sample {
  347. if len(s.Location) > 1 && s.Location[0].Address == s.Location[1].Address+1 {
  348. s.Location = append(s.Location[:1], s.Location[2:]...)
  349. }
  350. }
  351. }
  352. // parseCPUSamples parses a collection of profilez samples from a
  353. // profile.
  354. //
  355. // profilez samples are a repeated sequence of stack frames of the
  356. // form:
  357. // 1st word -- The number of times this stack was encountered.
  358. // 2nd word -- The size of the stack (StackSize).
  359. // 3rd word -- The first address on the stack.
  360. // ...
  361. // StackSize + 2 -- The last address on the stack
  362. // The last stack trace is of the form:
  363. // 1st word -- 0
  364. // 2nd word -- 1
  365. // 3rd word -- 0
  366. //
  367. // Addresses from stack traces may point to the next instruction after
  368. // each call. Optionally adjust by -1 to land somewhere on the actual
  369. // call (except for the leaf, which is not a call).
  370. func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) {
  371. locs := make(map[uint64]*Location)
  372. for len(b) > 0 {
  373. var count, nstk uint64
  374. count, b = parse(b)
  375. nstk, b = parse(b)
  376. if b == nil || nstk > uint64(len(b)/4) {
  377. return nil, nil, errUnrecognized
  378. }
  379. var sloc []*Location
  380. addrs := make([]uint64, nstk)
  381. for i := 0; i < int(nstk); i++ {
  382. addrs[i], b = parse(b)
  383. }
  384. if count == 0 && nstk == 1 && addrs[0] == 0 {
  385. // End of data marker
  386. break
  387. }
  388. for i, addr := range addrs {
  389. if adjust && i > 0 {
  390. addr--
  391. }
  392. loc := locs[addr]
  393. if loc == nil {
  394. loc = &Location{
  395. Address: addr,
  396. }
  397. locs[addr] = loc
  398. p.Location = append(p.Location, loc)
  399. }
  400. sloc = append(sloc, loc)
  401. }
  402. p.Sample = append(p.Sample,
  403. &Sample{
  404. Value: []int64{int64(count), int64(count) * int64(p.Period)},
  405. Location: sloc,
  406. })
  407. }
  408. // Reached the end without finding the EOD marker.
  409. return b, locs, nil
  410. }
  411. // parseHeap parses a heapz legacy or a growthz profile and
  412. // returns a newly populated Profile.
  413. func parseHeap(b []byte) (p *Profile, err error) {
  414. r := bytes.NewBuffer(b)
  415. l, err := r.ReadString('\n')
  416. if err != nil {
  417. return nil, errUnrecognized
  418. }
  419. p = &Profile{}
  420. sampling := ""
  421. hasAlloc := false
  422. p.PeriodType = &ValueType{Type: "space", Unit: "bytes"}
  423. if header := heapHeaderRE.FindStringSubmatch(l); header != nil {
  424. sampling, p.Period, hasAlloc, err = parseHeapHeader(l)
  425. if err != nil {
  426. return nil, err
  427. }
  428. } else if header = growthHeaderRE.FindStringSubmatch(l); header != nil {
  429. p.Period = 1
  430. } else if header = fragmentationHeaderRE.FindStringSubmatch(l); header != nil {
  431. p.Period = 1
  432. } else {
  433. return nil, errUnrecognized
  434. }
  435. if hasAlloc {
  436. // Put alloc before inuse so that default pprof selection
  437. // will prefer inuse_space.
  438. p.SampleType = []*ValueType{
  439. {Type: "alloc_objects", Unit: "count"},
  440. {Type: "alloc_space", Unit: "bytes"},
  441. {Type: "inuse_objects", Unit: "count"},
  442. {Type: "inuse_space", Unit: "bytes"},
  443. }
  444. } else {
  445. p.SampleType = []*ValueType{
  446. {Type: "objects", Unit: "count"},
  447. {Type: "space", Unit: "bytes"},
  448. }
  449. }
  450. locs := make(map[uint64]*Location)
  451. for {
  452. l, err = r.ReadString('\n')
  453. if err != nil {
  454. if err != io.EOF {
  455. return nil, err
  456. }
  457. if l == "" {
  458. break
  459. }
  460. }
  461. if isSpaceOrComment(l) {
  462. continue
  463. }
  464. l = strings.TrimSpace(l)
  465. if sectionTrigger(l) != unrecognizedSection {
  466. break
  467. }
  468. value, blocksize, addrs, err := parseHeapSample(l, p.Period, sampling, hasAlloc)
  469. if err != nil {
  470. return nil, err
  471. }
  472. var sloc []*Location
  473. for _, addr := range addrs {
  474. // Addresses from stack traces point to the next instruction after
  475. // each call. Adjust by -1 to land somewhere on the actual call.
  476. addr--
  477. loc := locs[addr]
  478. if locs[addr] == nil {
  479. loc = &Location{
  480. Address: addr,
  481. }
  482. p.Location = append(p.Location, loc)
  483. locs[addr] = loc
  484. }
  485. sloc = append(sloc, loc)
  486. }
  487. p.Sample = append(p.Sample, &Sample{
  488. Value: value,
  489. Location: sloc,
  490. NumLabel: map[string][]int64{"bytes": []int64{blocksize}},
  491. })
  492. }
  493. if err = parseAdditionalSections(l, r, p); err != nil {
  494. return nil, err
  495. }
  496. return p, nil
  497. }
  498. func parseHeapHeader(line string) (sampling string, period int64, hasAlloc bool, err error) {
  499. header := heapHeaderRE.FindStringSubmatch(line)
  500. if header == nil {
  501. return "", 0, false, errUnrecognized
  502. }
  503. if len(header[6]) > 0 {
  504. if period, err = strconv.ParseInt(string(header[6]), 10, 64); err != nil {
  505. return "", 0, false, errUnrecognized
  506. }
  507. }
  508. if (header[3] != header[1] && header[3] != "0") || (header[4] != header[2] && header[4] != "0") {
  509. hasAlloc = true
  510. }
  511. switch header[5] {
  512. case "heapz_v2", "heap_v2":
  513. return "v2", period, hasAlloc, nil
  514. case "heapprofile":
  515. return "", 1, hasAlloc, nil
  516. case "heap":
  517. return "v2", period / 2, hasAlloc, nil
  518. default:
  519. return "", 0, false, errUnrecognized
  520. }
  521. }
  522. // parseHeapSample parses a single row from a heap profile into a new Sample.
  523. func parseHeapSample(line string, rate int64, sampling string, includeAlloc bool) (value []int64, blocksize int64, addrs []uint64, err error) {
  524. sampleData := heapSampleRE.FindStringSubmatch(line)
  525. if len(sampleData) != 6 {
  526. return nil, 0, nil, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData))
  527. }
  528. // This is a local-scoped helper function to avoid needing to pass
  529. // around rate, sampling and many return parameters.
  530. addValues := func(countString, sizeString string, label string) error {
  531. count, err := strconv.ParseInt(countString, 10, 64)
  532. if err != nil {
  533. return fmt.Errorf("malformed sample: %s: %v", line, err)
  534. }
  535. size, err := strconv.ParseInt(sizeString, 10, 64)
  536. if err != nil {
  537. return fmt.Errorf("malformed sample: %s: %v", line, err)
  538. }
  539. if count == 0 && size != 0 {
  540. return fmt.Errorf("%s count was 0 but %s bytes was %d", label, label, size)
  541. }
  542. if count != 0 {
  543. blocksize = size / count
  544. if sampling == "v2" {
  545. count, size = scaleHeapSample(count, size, rate)
  546. }
  547. }
  548. value = append(value, count, size)
  549. return nil
  550. }
  551. if includeAlloc {
  552. if err := addValues(sampleData[3], sampleData[4], "allocation"); err != nil {
  553. return nil, 0, nil, err
  554. }
  555. }
  556. if err := addValues(sampleData[1], sampleData[2], "inuse"); err != nil {
  557. return nil, 0, nil, err
  558. }
  559. addrs = parseHexAddresses(sampleData[5])
  560. return value, blocksize, addrs, nil
  561. }
  562. // extractHexAddresses extracts hex numbers from a string and returns
  563. // them, together with their numeric value, in a slice.
  564. func extractHexAddresses(s string) ([]string, []uint64) {
  565. hexStrings := hexNumberRE.FindAllString(s, -1)
  566. var ids []uint64
  567. for _, s := range hexStrings {
  568. if id, err := strconv.ParseUint(s, 0, 64); err == nil {
  569. ids = append(ids, id)
  570. } else {
  571. // Do not expect any parsing failures due to the regexp matching.
  572. panic("failed to parse hex value:" + s)
  573. }
  574. }
  575. return hexStrings, ids
  576. }
  577. // parseHexAddresses parses hex numbers from a string and returns them
  578. // in a slice.
  579. func parseHexAddresses(s string) []uint64 {
  580. _, ids := extractHexAddresses(s)
  581. return ids
  582. }
  583. // scaleHeapSample adjusts the data from a heapz Sample to
  584. // account for its probability of appearing in the collected
  585. // data. heapz profiles are a sampling of the memory allocations
  586. // requests in a program. We estimate the unsampled value by dividing
  587. // each collected sample by its probability of appearing in the
  588. // profile. heapz v2 profiles rely on a poisson process to determine
  589. // which samples to collect, based on the desired average collection
  590. // rate R. The probability of a sample of size S to appear in that
  591. // profile is 1-exp(-S/R).
  592. func scaleHeapSample(count, size, rate int64) (int64, int64) {
  593. if count == 0 || size == 0 {
  594. return 0, 0
  595. }
  596. if rate <= 1 {
  597. // if rate==1 all samples were collected so no adjustment is needed.
  598. // if rate<1 treat as unknown and skip scaling.
  599. return count, size
  600. }
  601. avgSize := float64(size) / float64(count)
  602. scale := 1 / (1 - math.Exp(-avgSize/float64(rate)))
  603. return int64(float64(count) * scale), int64(float64(size) * scale)
  604. }
  605. // parseContention parses a contentionz profile and returns a newly
  606. // populated Profile.
  607. func parseContention(b []byte) (p *Profile, err error) {
  608. r := bytes.NewBuffer(b)
  609. l, err := r.ReadString('\n')
  610. if err != nil {
  611. return nil, errUnrecognized
  612. }
  613. if !strings.HasPrefix(l, "--- contention") {
  614. return nil, errUnrecognized
  615. }
  616. p = &Profile{
  617. PeriodType: &ValueType{Type: "contentions", Unit: "count"},
  618. Period: 1,
  619. SampleType: []*ValueType{
  620. {Type: "contentions", Unit: "count"},
  621. {Type: "delay", Unit: "nanoseconds"},
  622. },
  623. }
  624. var cpuHz int64
  625. // Parse text of the form "attribute = value" before the samples.
  626. const delimiter = "="
  627. for {
  628. l, err = r.ReadString('\n')
  629. if err != nil {
  630. if err != io.EOF {
  631. return nil, err
  632. }
  633. if l == "" {
  634. break
  635. }
  636. }
  637. if l = strings.TrimSpace(l); l == "" {
  638. continue
  639. }
  640. if strings.HasPrefix(l, "---") {
  641. break
  642. }
  643. attr := strings.SplitN(l, delimiter, 2)
  644. if len(attr) != 2 {
  645. break
  646. }
  647. key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])
  648. var err error
  649. switch key {
  650. case "cycles/second":
  651. if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil {
  652. return nil, errUnrecognized
  653. }
  654. case "sampling period":
  655. if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil {
  656. return nil, errUnrecognized
  657. }
  658. case "ms since reset":
  659. ms, err := strconv.ParseInt(val, 0, 64)
  660. if err != nil {
  661. return nil, errUnrecognized
  662. }
  663. p.DurationNanos = ms * 1000 * 1000
  664. case "format":
  665. // CPP contentionz profiles don't have format.
  666. return nil, errUnrecognized
  667. case "resolution":
  668. // CPP contentionz profiles don't have resolution.
  669. return nil, errUnrecognized
  670. case "discarded samples":
  671. default:
  672. return nil, errUnrecognized
  673. }
  674. }
  675. locs := make(map[uint64]*Location)
  676. for {
  677. if l = strings.TrimSpace(l); strings.HasPrefix(l, "---") {
  678. break
  679. }
  680. value, addrs, err := parseContentionSample(l, p.Period, cpuHz)
  681. if err != nil {
  682. return nil, err
  683. }
  684. var sloc []*Location
  685. for _, addr := range addrs {
  686. // Addresses from stack traces point to the next instruction after
  687. // each call. Adjust by -1 to land somewhere on the actual call.
  688. addr--
  689. loc := locs[addr]
  690. if locs[addr] == nil {
  691. loc = &Location{
  692. Address: addr,
  693. }
  694. p.Location = append(p.Location, loc)
  695. locs[addr] = loc
  696. }
  697. sloc = append(sloc, loc)
  698. }
  699. p.Sample = append(p.Sample, &Sample{
  700. Value: value,
  701. Location: sloc,
  702. })
  703. if l, err = r.ReadString('\n'); err != nil {
  704. if err != io.EOF {
  705. return nil, err
  706. }
  707. if l == "" {
  708. break
  709. }
  710. }
  711. }
  712. if err = parseAdditionalSections(l, r, p); err != nil {
  713. return nil, err
  714. }
  715. return p, nil
  716. }
  717. // parseContentionSample parses a single row from a contention profile
  718. // into a new Sample.
  719. func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) {
  720. sampleData := contentionSampleRE.FindStringSubmatch(line)
  721. if sampleData == nil {
  722. return value, addrs, errUnrecognized
  723. }
  724. v1, err := strconv.ParseInt(sampleData[1], 10, 64)
  725. if err != nil {
  726. return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
  727. }
  728. v2, err := strconv.ParseInt(sampleData[2], 10, 64)
  729. if err != nil {
  730. return value, addrs, fmt.Errorf("malformed sample: %s: %v", line, err)
  731. }
  732. // Unsample values if period and cpuHz are available.
  733. // - Delays are scaled to cycles and then to nanoseconds.
  734. // - Contentions are scaled to cycles.
  735. if period > 0 {
  736. if cpuHz > 0 {
  737. cpuGHz := float64(cpuHz) / 1e9
  738. v1 = int64(float64(v1) * float64(period) / cpuGHz)
  739. }
  740. v2 = v2 * period
  741. }
  742. value = []int64{v2, v1}
  743. addrs = parseHexAddresses(sampleData[3])
  744. return value, addrs, nil
  745. }
  746. // parseThread parses a Threadz profile and returns a new Profile.
  747. func parseThread(b []byte) (*Profile, error) {
  748. r := bytes.NewBuffer(b)
  749. var line string
  750. var err error
  751. for {
  752. // Skip past comments and empty lines seeking a real header.
  753. line, err = r.ReadString('\n')
  754. if err != nil {
  755. return nil, err
  756. }
  757. if !isSpaceOrComment(line) {
  758. break
  759. }
  760. }
  761. if m := threadzStartRE.FindStringSubmatch(line); m != nil {
  762. // Advance over initial comments until first stack trace.
  763. for {
  764. line, err = r.ReadString('\n')
  765. if err != nil {
  766. if err != io.EOF {
  767. return nil, err
  768. }
  769. if line == "" {
  770. break
  771. }
  772. }
  773. if sectionTrigger(line) != unrecognizedSection || line[0] == '-' {
  774. break
  775. }
  776. }
  777. } else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 {
  778. return nil, errUnrecognized
  779. }
  780. p := &Profile{
  781. SampleType: []*ValueType{{Type: "thread", Unit: "count"}},
  782. PeriodType: &ValueType{Type: "thread", Unit: "count"},
  783. Period: 1,
  784. }
  785. locs := make(map[uint64]*Location)
  786. // Recognize each thread and populate profile samples.
  787. for sectionTrigger(line) == unrecognizedSection {
  788. if strings.HasPrefix(line, "---- no stack trace for") {
  789. line = ""
  790. break
  791. }
  792. if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 {
  793. return nil, errUnrecognized
  794. }
  795. var addrs []uint64
  796. line, addrs, err = parseThreadSample(r)
  797. if err != nil {
  798. return nil, errUnrecognized
  799. }
  800. if len(addrs) == 0 {
  801. // We got a --same as previous threads--. Bump counters.
  802. if len(p.Sample) > 0 {
  803. s := p.Sample[len(p.Sample)-1]
  804. s.Value[0]++
  805. }
  806. continue
  807. }
  808. var sloc []*Location
  809. for i, addr := range addrs {
  810. // Addresses from stack traces point to the next instruction after
  811. // each call. Adjust by -1 to land somewhere on the actual call
  812. // (except for the leaf, which is not a call).
  813. if i > 0 {
  814. addr--
  815. }
  816. loc := locs[addr]
  817. if locs[addr] == nil {
  818. loc = &Location{
  819. Address: addr,
  820. }
  821. p.Location = append(p.Location, loc)
  822. locs[addr] = loc
  823. }
  824. sloc = append(sloc, loc)
  825. }
  826. p.Sample = append(p.Sample, &Sample{
  827. Value: []int64{1},
  828. Location: sloc,
  829. })
  830. }
  831. if err = parseAdditionalSections(line, r, p); err != nil {
  832. return nil, err
  833. }
  834. cleanupDuplicateLocations(p)
  835. return p, nil
  836. }
  837. // parseThreadSample parses a symbolized or unsymbolized stack trace.
  838. // Returns the first line after the traceback, the sample (or nil if
  839. // it hits a 'same-as-previous' marker) and an error.
  840. func parseThreadSample(b *bytes.Buffer) (nextl string, addrs []uint64, err error) {
  841. var l string
  842. sameAsPrevious := false
  843. for {
  844. if l, err = b.ReadString('\n'); err != nil {
  845. if err != io.EOF {
  846. return "", nil, err
  847. }
  848. if l == "" {
  849. break
  850. }
  851. }
  852. if l = strings.TrimSpace(l); l == "" {
  853. continue
  854. }
  855. if strings.HasPrefix(l, "---") {
  856. break
  857. }
  858. if strings.Contains(l, "same as previous thread") {
  859. sameAsPrevious = true
  860. continue
  861. }
  862. addrs = append(addrs, parseHexAddresses(l)...)
  863. }
  864. if sameAsPrevious {
  865. return l, nil, nil
  866. }
  867. return l, addrs, nil
  868. }
  869. // parseAdditionalSections parses any additional sections in the
  870. // profile, ignoring any unrecognized sections.
  871. func parseAdditionalSections(l string, b *bytes.Buffer, p *Profile) error {
  872. for {
  873. if sectionTrigger(l) == memoryMapSection {
  874. break
  875. }
  876. // Ignore any unrecognized sections.
  877. var err error
  878. if l, err = b.ReadString('\n'); err != nil {
  879. if err != io.EOF {
  880. return err
  881. }
  882. if l == "" {
  883. break
  884. }
  885. }
  886. }
  887. return p.ParseMemoryMap(b)
  888. }
  889. // ParseProcMaps parses a memory map in the format of /proc/self/maps.
  890. // ParseMemoryMap should be called after setting on a profile to
  891. // associate locations to the corresponding mapping based on their
  892. // address.
  893. func ParseProcMaps(rd io.Reader) ([]*Mapping, error) {
  894. var mapping []*Mapping
  895. b := bufio.NewReader(rd)
  896. var attrs []string
  897. var r *strings.Replacer
  898. const delimiter = "="
  899. for {
  900. l, err := b.ReadString('\n')
  901. if err != nil {
  902. if err != io.EOF {
  903. return nil, err
  904. }
  905. if l == "" {
  906. break
  907. }
  908. }
  909. if l = strings.TrimSpace(l); l == "" {
  910. continue
  911. }
  912. if r != nil {
  913. l = r.Replace(l)
  914. }
  915. m, err := parseMappingEntry(l)
  916. if err != nil {
  917. if err == errUnrecognized {
  918. // Recognize assignments of the form: attr=value, and replace
  919. // $attr with value on subsequent mappings.
  920. if attr := strings.SplitN(l, delimiter, 2); len(attr) == 2 {
  921. attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]))
  922. r = strings.NewReplacer(attrs...)
  923. }
  924. // Ignore any unrecognized entries
  925. continue
  926. }
  927. return nil, err
  928. }
  929. if m == nil {
  930. continue
  931. }
  932. mapping = append(mapping, m)
  933. }
  934. return mapping, nil
  935. }
  936. // ParseMemoryMap parses a memory map in the format of
  937. // /proc/self/maps, and overrides the mappings in the current profile.
  938. // It renumbers the samples and locations in the profile correspondingly.
  939. func (p *Profile) ParseMemoryMap(rd io.Reader) error {
  940. mapping, err := ParseProcMaps(rd)
  941. if err != nil {
  942. return err
  943. }
  944. p.Mapping = append(p.Mapping, mapping...)
  945. p.massageMappings()
  946. p.remapLocationIDs()
  947. p.remapFunctionIDs()
  948. p.remapMappingIDs()
  949. return nil
  950. }
  951. func parseMappingEntry(l string) (*Mapping, error) {
  952. mapping := &Mapping{}
  953. var err error
  954. if me := procMapsRE.FindStringSubmatch(l); len(me) == 9 {
  955. if !strings.Contains(me[3], "x") {
  956. // Skip non-executable entries.
  957. return nil, nil
  958. }
  959. if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
  960. return nil, errUnrecognized
  961. }
  962. if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
  963. return nil, errUnrecognized
  964. }
  965. if me[4] != "" {
  966. if mapping.Offset, err = strconv.ParseUint(me[4], 16, 64); err != nil {
  967. return nil, errUnrecognized
  968. }
  969. }
  970. mapping.File = me[8]
  971. return mapping, nil
  972. }
  973. if me := briefMapsRE.FindStringSubmatch(l); len(me) == 6 {
  974. if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
  975. return nil, errUnrecognized
  976. }
  977. if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
  978. return nil, errUnrecognized
  979. }
  980. mapping.File = me[3]
  981. if me[5] != "" {
  982. if mapping.Offset, err = strconv.ParseUint(me[5], 16, 64); err != nil {
  983. return nil, errUnrecognized
  984. }
  985. }
  986. return mapping, nil
  987. }
  988. return nil, errUnrecognized
  989. }
  990. type sectionType int
  991. const (
  992. unrecognizedSection sectionType = iota
  993. memoryMapSection
  994. )
  995. var memoryMapTriggers = []string{
  996. "--- Memory map: ---",
  997. "MAPPED_LIBRARIES:",
  998. }
  999. func sectionTrigger(line string) sectionType {
  1000. for _, trigger := range memoryMapTriggers {
  1001. if strings.Contains(line, trigger) {
  1002. return memoryMapSection
  1003. }
  1004. }
  1005. return unrecognizedSection
  1006. }
  1007. func (p *Profile) addLegacyFrameInfo() {
  1008. switch {
  1009. case isProfileType(p, heapzSampleTypes):
  1010. p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr
  1011. case isProfileType(p, contentionzSampleTypes):
  1012. p.DropFrames, p.KeepFrames = lockRxStr, ""
  1013. default:
  1014. p.DropFrames, p.KeepFrames = cpuProfilerRxStr, ""
  1015. }
  1016. }
  1017. var heapzSampleTypes = [][]string{
  1018. {"allocations", "size"}, // early Go pprof profiles
  1019. {"objects", "space"},
  1020. {"inuse_objects", "inuse_space"},
  1021. {"alloc_objects", "alloc_space"},
  1022. }
  1023. var contentionzSampleTypes = [][]string{
  1024. {"contentions", "delay"},
  1025. }
  1026. func isProfileType(p *Profile, types [][]string) bool {
  1027. st := p.SampleType
  1028. nextType:
  1029. for _, t := range types {
  1030. if len(st) != len(t) {
  1031. continue
  1032. }
  1033. for i := range st {
  1034. if st[i].Type != t[i] {
  1035. continue nextType
  1036. }
  1037. }
  1038. return true
  1039. }
  1040. return false
  1041. }
  1042. var allocRxStr = strings.Join([]string{
  1043. // POSIX entry points.
  1044. `calloc`,
  1045. `cfree`,
  1046. `malloc`,
  1047. `free`,
  1048. `memalign`,
  1049. `do_memalign`,
  1050. `(__)?posix_memalign`,
  1051. `pvalloc`,
  1052. `valloc`,
  1053. `realloc`,
  1054. // TC malloc.
  1055. `tcmalloc::.*`,
  1056. `tc_calloc`,
  1057. `tc_cfree`,
  1058. `tc_malloc`,
  1059. `tc_free`,
  1060. `tc_memalign`,
  1061. `tc_posix_memalign`,
  1062. `tc_pvalloc`,
  1063. `tc_valloc`,
  1064. `tc_realloc`,
  1065. `tc_new`,
  1066. `tc_delete`,
  1067. `tc_newarray`,
  1068. `tc_deletearray`,
  1069. `tc_new_nothrow`,
  1070. `tc_newarray_nothrow`,
  1071. // Memory-allocation routines on OS X.
  1072. `malloc_zone_malloc`,
  1073. `malloc_zone_calloc`,
  1074. `malloc_zone_valloc`,
  1075. `malloc_zone_realloc`,
  1076. `malloc_zone_memalign`,
  1077. `malloc_zone_free`,
  1078. // Go runtime
  1079. `runtime\..*`,
  1080. // Other misc. memory allocation routines
  1081. `BaseArena::.*`,
  1082. `(::)?do_malloc_no_errno`,
  1083. `(::)?do_malloc_pages`,
  1084. `(::)?do_malloc`,
  1085. `DoSampledAllocation`,
  1086. `MallocedMemBlock::MallocedMemBlock`,
  1087. `_M_allocate`,
  1088. `__builtin_(vec_)?delete`,
  1089. `__builtin_(vec_)?new`,
  1090. `__gnu_cxx::new_allocator::allocate`,
  1091. `__libc_malloc`,
  1092. `__malloc_alloc_template::allocate`,
  1093. `allocate`,
  1094. `cpp_alloc`,
  1095. `operator new(\[\])?`,
  1096. `simple_alloc::allocate`,
  1097. }, `|`)
  1098. var allocSkipRxStr = strings.Join([]string{
  1099. // Preserve Go runtime frames that appear in the middle/bottom of
  1100. // the stack.
  1101. `runtime\.panic`,
  1102. }, `|`)
  1103. var cpuProfilerRxStr = strings.Join([]string{
  1104. `ProfileData::Add`,
  1105. `ProfileData::prof_handler`,
  1106. `CpuProfiler::prof_handler`,
  1107. `__pthread_sighandler`,
  1108. `__restore`,
  1109. }, `|`)
  1110. var lockRxStr = strings.Join([]string{
  1111. `RecordLockProfileData`,
  1112. `(base::)?RecordLockProfileData.*`,
  1113. `(base::)?SubmitMutexProfileData.*`,
  1114. `(base::)?SubmitSpinLockProfileData.*`,
  1115. `(Mutex::)?AwaitCommon.*`,
  1116. `(Mutex::)?Unlock.*`,
  1117. `(Mutex::)?UnlockSlow.*`,
  1118. `(Mutex::)?ReaderUnlock.*`,
  1119. `(MutexLock::)?~MutexLock.*`,
  1120. `(SpinLock::)?Unlock.*`,
  1121. `(SpinLock::)?SlowUnlock.*`,
  1122. `(SpinLockHolder::)?~SpinLockHolder.*`,
  1123. }, `|`)