설명 없음

legacy_profile.go 31KB

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