github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/runtime/pprof/proto.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pprof 6 7 import ( 8 "bytes" 9 "compress/gzip" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "runtime" 14 "strconv" 15 "time" 16 "unsafe" 17 ) 18 19 // lostProfileEvent is the function to which lost profiling 20 // events are attributed. 21 // (The name shows up in the pprof graphs.) 22 func lostProfileEvent() { lostProfileEvent() } 23 24 // funcPC returns the PC for the func value f. 25 func funcPC(f interface{}) uintptr { 26 return *(*[2]*uintptr)(unsafe.Pointer(&f))[1] 27 } 28 29 // A profileBuilder writes a profile incrementally from a 30 // stream of profile samples delivered by the runtime. 31 type profileBuilder struct { 32 start time.Time 33 end time.Time 34 havePeriod bool 35 period int64 36 m profMap 37 38 // encoding state 39 w io.Writer 40 zw *gzip.Writer 41 pb protobuf 42 strings []string 43 stringMap map[string]int 44 locs map[uintptr]int 45 funcs map[string]int // Package path-qualified function name to Function.ID 46 mem []memMap 47 } 48 49 type memMap struct { 50 // initialized as reading mapping 51 start uintptr 52 end uintptr 53 offset uint64 54 file, buildID string 55 56 funcs symbolizeFlag 57 fake bool // map entry was faked; /proc/self/maps wasn't available 58 } 59 60 // symbolizeFlag keeps track of symbolization result. 61 // 0 : no symbol lookup was performed 62 // 1<<0 (lookupTried) : symbol lookup was performed 63 // 1<<1 (lookupFailed): symbol lookup was performed but failed 64 type symbolizeFlag uint8 65 66 const ( 67 lookupTried symbolizeFlag = 1 << iota 68 lookupFailed symbolizeFlag = 1 << iota 69 ) 70 71 const ( 72 // message Profile 73 tagProfile_SampleType = 1 // repeated ValueType 74 tagProfile_Sample = 2 // repeated Sample 75 tagProfile_Mapping = 3 // repeated Mapping 76 tagProfile_Location = 4 // repeated Location 77 tagProfile_Function = 5 // repeated Function 78 tagProfile_StringTable = 6 // repeated string 79 tagProfile_DropFrames = 7 // int64 (string table index) 80 tagProfile_KeepFrames = 8 // int64 (string table index) 81 tagProfile_TimeNanos = 9 // int64 82 tagProfile_DurationNanos = 10 // int64 83 tagProfile_PeriodType = 11 // ValueType (really optional string???) 84 tagProfile_Period = 12 // int64 85 tagProfile_Comment = 13 // repeated int64 86 tagProfile_DefaultSampleType = 14 // int64 87 88 // message ValueType 89 tagValueType_Type = 1 // int64 (string table index) 90 tagValueType_Unit = 2 // int64 (string table index) 91 92 // message Sample 93 tagSample_Location = 1 // repeated uint64 94 tagSample_Value = 2 // repeated int64 95 tagSample_Label = 3 // repeated Label 96 97 // message Label 98 tagLabel_Key = 1 // int64 (string table index) 99 tagLabel_Str = 2 // int64 (string table index) 100 tagLabel_Num = 3 // int64 101 102 // message Mapping 103 tagMapping_ID = 1 // uint64 104 tagMapping_Start = 2 // uint64 105 tagMapping_Limit = 3 // uint64 106 tagMapping_Offset = 4 // uint64 107 tagMapping_Filename = 5 // int64 (string table index) 108 tagMapping_BuildID = 6 // int64 (string table index) 109 tagMapping_HasFunctions = 7 // bool 110 tagMapping_HasFilenames = 8 // bool 111 tagMapping_HasLineNumbers = 9 // bool 112 tagMapping_HasInlineFrames = 10 // bool 113 114 // message Location 115 tagLocation_ID = 1 // uint64 116 tagLocation_MappingID = 2 // uint64 117 tagLocation_Address = 3 // uint64 118 tagLocation_Line = 4 // repeated Line 119 120 // message Line 121 tagLine_FunctionID = 1 // uint64 122 tagLine_Line = 2 // int64 123 124 // message Function 125 tagFunction_ID = 1 // uint64 126 tagFunction_Name = 2 // int64 (string table index) 127 tagFunction_SystemName = 3 // int64 (string table index) 128 tagFunction_Filename = 4 // int64 (string table index) 129 tagFunction_StartLine = 5 // int64 130 ) 131 132 // stringIndex adds s to the string table if not already present 133 // and returns the index of s in the string table. 134 func (b *profileBuilder) stringIndex(s string) int64 { 135 id, ok := b.stringMap[s] 136 if !ok { 137 id = len(b.strings) 138 b.strings = append(b.strings, s) 139 b.stringMap[s] = id 140 } 141 return int64(id) 142 } 143 144 func (b *profileBuilder) flush() { 145 const dataFlush = 4096 146 if b.pb.nest == 0 && len(b.pb.data) > dataFlush { 147 b.zw.Write(b.pb.data) 148 b.pb.data = b.pb.data[:0] 149 } 150 } 151 152 // pbValueType encodes a ValueType message to b.pb. 153 func (b *profileBuilder) pbValueType(tag int, typ, unit string) { 154 start := b.pb.startMessage() 155 b.pb.int64(tagValueType_Type, b.stringIndex(typ)) 156 b.pb.int64(tagValueType_Unit, b.stringIndex(unit)) 157 b.pb.endMessage(tag, start) 158 } 159 160 // pbSample encodes a Sample message to b.pb. 161 func (b *profileBuilder) pbSample(values []int64, locs []uint64, labels func()) { 162 start := b.pb.startMessage() 163 b.pb.int64s(tagSample_Value, values) 164 b.pb.uint64s(tagSample_Location, locs) 165 if labels != nil { 166 labels() 167 } 168 b.pb.endMessage(tagProfile_Sample, start) 169 b.flush() 170 } 171 172 // pbLabel encodes a Label message to b.pb. 173 func (b *profileBuilder) pbLabel(tag int, key, str string, num int64) { 174 start := b.pb.startMessage() 175 b.pb.int64Opt(tagLabel_Key, b.stringIndex(key)) 176 b.pb.int64Opt(tagLabel_Str, b.stringIndex(str)) 177 b.pb.int64Opt(tagLabel_Num, num) 178 b.pb.endMessage(tag, start) 179 } 180 181 // pbLine encodes a Line message to b.pb. 182 func (b *profileBuilder) pbLine(tag int, funcID uint64, line int64) { 183 start := b.pb.startMessage() 184 b.pb.uint64Opt(tagLine_FunctionID, funcID) 185 b.pb.int64Opt(tagLine_Line, line) 186 b.pb.endMessage(tag, start) 187 } 188 189 // pbMapping encodes a Mapping message to b.pb. 190 func (b *profileBuilder) pbMapping(tag int, id, base, limit, offset uint64, file, buildID string, hasFuncs bool) { 191 start := b.pb.startMessage() 192 b.pb.uint64Opt(tagMapping_ID, id) 193 b.pb.uint64Opt(tagMapping_Start, base) 194 b.pb.uint64Opt(tagMapping_Limit, limit) 195 b.pb.uint64Opt(tagMapping_Offset, offset) 196 b.pb.int64Opt(tagMapping_Filename, b.stringIndex(file)) 197 b.pb.int64Opt(tagMapping_BuildID, b.stringIndex(buildID)) 198 // TODO: we set HasFunctions if all symbols from samples were symbolized (hasFuncs). 199 // Decide what to do about HasInlineFrames and HasLineNumbers. 200 // Also, another approach to handle the mapping entry with 201 // incomplete symbolization results is to dupliace the mapping 202 // entry (but with different Has* fields values) and use 203 // different entries for symbolized locations and unsymbolized locations. 204 if hasFuncs { 205 b.pb.bool(tagMapping_HasFunctions, true) 206 } 207 b.pb.endMessage(tag, start) 208 } 209 210 // locForPC returns the location ID for addr. 211 // addr must a return PC or 1 + the PC of an inline marker. This returns the location of the corresponding call. 212 // It may emit to b.pb, so there must be no message encoding in progress. 213 func (b *profileBuilder) locForPC(addr uintptr) uint64 { 214 id := uint64(b.locs[addr]) 215 if id != 0 { 216 return id 217 } 218 219 // Expand this one address using CallersFrames so we can cache 220 // each expansion. In general, CallersFrames takes a whole 221 // stack, but in this case we know there will be no skips in 222 // the stack and we have return PCs anyway. 223 frames := runtime.CallersFrames([]uintptr{addr}) 224 frame, more := frames.Next() 225 if frame.Function == "runtime.goexit" { 226 // Short-circuit if we see runtime.goexit so the loop 227 // below doesn't allocate a useless empty location. 228 return 0 229 } 230 231 symbolizeResult := lookupTried 232 if frame.PC == 0 || frame.Function == "" || frame.File == "" || frame.Line == 0 { 233 symbolizeResult |= lookupFailed 234 } 235 236 if frame.PC == 0 { 237 // If we failed to resolve the frame, at least make up 238 // a reasonable call PC. This mostly happens in tests. 239 frame.PC = addr - 1 240 } 241 242 // We can't write out functions while in the middle of the 243 // Location message, so record new functions we encounter and 244 // write them out after the Location. 245 type newFunc struct { 246 id uint64 247 name, file string 248 } 249 newFuncs := make([]newFunc, 0, 8) 250 251 id = uint64(len(b.locs)) + 1 252 b.locs[addr] = int(id) 253 start := b.pb.startMessage() 254 b.pb.uint64Opt(tagLocation_ID, id) 255 b.pb.uint64Opt(tagLocation_Address, uint64(frame.PC)) 256 for frame.Function != "runtime.goexit" { 257 // Write out each line in frame expansion. 258 funcID := uint64(b.funcs[frame.Function]) 259 if funcID == 0 { 260 funcID = uint64(len(b.funcs)) + 1 261 b.funcs[frame.Function] = int(funcID) 262 newFuncs = append(newFuncs, newFunc{funcID, frame.Function, frame.File}) 263 } 264 b.pbLine(tagLocation_Line, funcID, int64(frame.Line)) 265 if !more { 266 break 267 } 268 frame, more = frames.Next() 269 } 270 for i := range b.mem { 271 if b.mem[i].start <= addr && addr < b.mem[i].end || b.mem[i].fake { 272 b.pb.uint64Opt(tagLocation_MappingID, uint64(i+1)) 273 274 m := b.mem[i] 275 m.funcs |= symbolizeResult 276 b.mem[i] = m 277 break 278 } 279 } 280 b.pb.endMessage(tagProfile_Location, start) 281 282 // Write out functions we found during frame expansion. 283 for _, fn := range newFuncs { 284 start := b.pb.startMessage() 285 b.pb.uint64Opt(tagFunction_ID, fn.id) 286 b.pb.int64Opt(tagFunction_Name, b.stringIndex(fn.name)) 287 b.pb.int64Opt(tagFunction_SystemName, b.stringIndex(fn.name)) 288 b.pb.int64Opt(tagFunction_Filename, b.stringIndex(fn.file)) 289 b.pb.endMessage(tagProfile_Function, start) 290 } 291 292 b.flush() 293 return id 294 } 295 296 // newProfileBuilder returns a new profileBuilder. 297 // CPU profiling data obtained from the runtime can be added 298 // by calling b.addCPUData, and then the eventual profile 299 // can be obtained by calling b.finish. 300 func newProfileBuilder(w io.Writer) *profileBuilder { 301 zw, _ := gzip.NewWriterLevel(w, gzip.BestSpeed) 302 b := &profileBuilder{ 303 w: w, 304 zw: zw, 305 start: time.Now(), 306 strings: []string{""}, 307 stringMap: map[string]int{"": 0}, 308 locs: map[uintptr]int{}, 309 funcs: map[string]int{}, 310 } 311 b.readMapping() 312 return b 313 } 314 315 // addCPUData adds the CPU profiling data to the profile. 316 // The data must be a whole number of records, 317 // as delivered by the runtime. 318 func (b *profileBuilder) addCPUData(data []uint64, tags []unsafe.Pointer) error { 319 if !b.havePeriod { 320 // first record is period 321 if len(data) < 3 { 322 return fmt.Errorf("truncated profile") 323 } 324 if data[0] != 3 || data[2] == 0 { 325 return fmt.Errorf("malformed profile") 326 } 327 // data[2] is sampling rate in Hz. Convert to sampling 328 // period in nanoseconds. 329 b.period = 1e9 / int64(data[2]) 330 b.havePeriod = true 331 data = data[3:] 332 } 333 334 // Parse CPU samples from the profile. 335 // Each sample is 3+n uint64s: 336 // data[0] = 3+n 337 // data[1] = time stamp (ignored) 338 // data[2] = count 339 // data[3:3+n] = stack 340 // If the count is 0 and the stack has length 1, 341 // that's an overflow record inserted by the runtime 342 // to indicate that stack[0] samples were lost. 343 // Otherwise the count is usually 1, 344 // but in a few special cases like lost non-Go samples 345 // there can be larger counts. 346 // Because many samples with the same stack arrive, 347 // we want to deduplicate immediately, which we do 348 // using the b.m profMap. 349 for len(data) > 0 { 350 if len(data) < 3 || data[0] > uint64(len(data)) { 351 return fmt.Errorf("truncated profile") 352 } 353 if data[0] < 3 || tags != nil && len(tags) < 1 { 354 return fmt.Errorf("malformed profile") 355 } 356 count := data[2] 357 stk := data[3:data[0]] 358 data = data[data[0]:] 359 var tag unsafe.Pointer 360 if tags != nil { 361 tag = tags[0] 362 tags = tags[1:] 363 } 364 365 if count == 0 && len(stk) == 1 { 366 // overflow record 367 count = uint64(stk[0]) 368 stk = []uint64{ 369 uint64(funcPC(lostProfileEvent)), 370 } 371 } 372 b.m.lookup(stk, tag).count += int64(count) 373 } 374 return nil 375 } 376 377 // build completes and returns the constructed profile. 378 func (b *profileBuilder) build() { 379 b.end = time.Now() 380 381 b.pb.int64Opt(tagProfile_TimeNanos, b.start.UnixNano()) 382 if b.havePeriod { // must be CPU profile 383 b.pbValueType(tagProfile_SampleType, "samples", "count") 384 b.pbValueType(tagProfile_SampleType, "cpu", "nanoseconds") 385 b.pb.int64Opt(tagProfile_DurationNanos, b.end.Sub(b.start).Nanoseconds()) 386 b.pbValueType(tagProfile_PeriodType, "cpu", "nanoseconds") 387 b.pb.int64Opt(tagProfile_Period, b.period) 388 } 389 390 values := []int64{0, 0} 391 var locs []uint64 392 for e := b.m.all; e != nil; e = e.nextAll { 393 values[0] = e.count 394 values[1] = e.count * b.period 395 396 var labels func() 397 if e.tag != nil { 398 labels = func() { 399 for k, v := range *(*labelMap)(e.tag) { 400 b.pbLabel(tagSample_Label, k, v, 0) 401 } 402 } 403 } 404 405 locs = locs[:0] 406 for i, addr := range e.stk { 407 // Addresses from stack traces point to the 408 // next instruction after each call, except 409 // for the leaf, which points to where the 410 // signal occurred. locForPC expects return 411 // PCs, so increment the leaf address to look 412 // like a return PC. 413 if i == 0 { 414 addr++ 415 } 416 l := b.locForPC(addr) 417 if l == 0 { // runtime.goexit 418 continue 419 } 420 locs = append(locs, l) 421 } 422 b.pbSample(values, locs, labels) 423 } 424 425 for i, m := range b.mem { 426 hasFunctions := m.funcs == lookupTried // lookupTried but not lookupFailed 427 b.pbMapping(tagProfile_Mapping, uint64(i+1), uint64(m.start), uint64(m.end), m.offset, m.file, m.buildID, hasFunctions) 428 } 429 430 // TODO: Anything for tagProfile_DropFrames? 431 // TODO: Anything for tagProfile_KeepFrames? 432 433 b.pb.strings(tagProfile_StringTable, b.strings) 434 b.zw.Write(b.pb.data) 435 b.zw.Close() 436 } 437 438 // readMapping reads /proc/self/maps and writes mappings to b.pb. 439 // It saves the address ranges of the mappings in b.mem for use 440 // when emitting locations. 441 func (b *profileBuilder) readMapping() { 442 data, _ := ioutil.ReadFile("/proc/self/maps") 443 parseProcSelfMaps(data, b.addMapping) 444 if len(b.mem) == 0 { // pprof expects a map entry, so fake one. 445 b.addMappingEntry(0, 0, 0, "", "", true) 446 // TODO(hyangah): make addMapping return *memMap or 447 // take a memMap struct, and get rid of addMappingEntry 448 // that takes a bunch of positional arguments. 449 } 450 } 451 452 func parseProcSelfMaps(data []byte, addMapping func(lo, hi, offset uint64, file, buildID string)) { 453 // $ cat /proc/self/maps 454 // 00400000-0040b000 r-xp 00000000 fc:01 787766 /bin/cat 455 // 0060a000-0060b000 r--p 0000a000 fc:01 787766 /bin/cat 456 // 0060b000-0060c000 rw-p 0000b000 fc:01 787766 /bin/cat 457 // 014ab000-014cc000 rw-p 00000000 00:00 0 [heap] 458 // 7f7d76af8000-7f7d7797c000 r--p 00000000 fc:01 1318064 /usr/lib/locale/locale-archive 459 // 7f7d7797c000-7f7d77b36000 r-xp 00000000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so 460 // 7f7d77b36000-7f7d77d36000 ---p 001ba000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so 461 // 7f7d77d36000-7f7d77d3a000 r--p 001ba000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so 462 // 7f7d77d3a000-7f7d77d3c000 rw-p 001be000 fc:01 1180226 /lib/x86_64-linux-gnu/libc-2.19.so 463 // 7f7d77d3c000-7f7d77d41000 rw-p 00000000 00:00 0 464 // 7f7d77d41000-7f7d77d64000 r-xp 00000000 fc:01 1180217 /lib/x86_64-linux-gnu/ld-2.19.so 465 // 7f7d77f3f000-7f7d77f42000 rw-p 00000000 00:00 0 466 // 7f7d77f61000-7f7d77f63000 rw-p 00000000 00:00 0 467 // 7f7d77f63000-7f7d77f64000 r--p 00022000 fc:01 1180217 /lib/x86_64-linux-gnu/ld-2.19.so 468 // 7f7d77f64000-7f7d77f65000 rw-p 00023000 fc:01 1180217 /lib/x86_64-linux-gnu/ld-2.19.so 469 // 7f7d77f65000-7f7d77f66000 rw-p 00000000 00:00 0 470 // 7ffc342a2000-7ffc342c3000 rw-p 00000000 00:00 0 [stack] 471 // 7ffc34343000-7ffc34345000 r-xp 00000000 00:00 0 [vdso] 472 // ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 473 474 var line []byte 475 // next removes and returns the next field in the line. 476 // It also removes from line any spaces following the field. 477 next := func() []byte { 478 j := bytes.IndexByte(line, ' ') 479 if j < 0 { 480 f := line 481 line = nil 482 return f 483 } 484 f := line[:j] 485 line = line[j+1:] 486 for len(line) > 0 && line[0] == ' ' { 487 line = line[1:] 488 } 489 return f 490 } 491 492 for len(data) > 0 { 493 i := bytes.IndexByte(data, '\n') 494 if i < 0 { 495 line, data = data, nil 496 } else { 497 line, data = data[:i], data[i+1:] 498 } 499 addr := next() 500 i = bytes.IndexByte(addr, '-') 501 if i < 0 { 502 continue 503 } 504 lo, err := strconv.ParseUint(string(addr[:i]), 16, 64) 505 if err != nil { 506 continue 507 } 508 hi, err := strconv.ParseUint(string(addr[i+1:]), 16, 64) 509 if err != nil { 510 continue 511 } 512 perm := next() 513 if len(perm) < 4 || perm[2] != 'x' { 514 // Only interested in executable mappings. 515 continue 516 } 517 offset, err := strconv.ParseUint(string(next()), 16, 64) 518 if err != nil { 519 continue 520 } 521 next() // dev 522 inode := next() // inode 523 if line == nil { 524 continue 525 } 526 file := string(line) 527 528 // Trim deleted file marker. 529 deletedStr := " (deleted)" 530 deletedLen := len(deletedStr) 531 if len(file) >= deletedLen && file[len(file)-deletedLen:] == deletedStr { 532 file = file[:len(file)-deletedLen] 533 } 534 535 if len(inode) == 1 && inode[0] == '0' && file == "" { 536 // Huge-page text mappings list the initial fragment of 537 // mapped but unpopulated memory as being inode 0. 538 // Don't report that part. 539 // But [vdso] and [vsyscall] are inode 0, so let non-empty file names through. 540 continue 541 } 542 543 // TODO: pprof's remapMappingIDs makes two adjustments: 544 // 1. If there is an /anon_hugepage mapping first and it is 545 // consecutive to a next mapping, drop the /anon_hugepage. 546 // 2. If start-offset = 0x400000, change start to 0x400000 and offset to 0. 547 // There's no indication why either of these is needed. 548 // Let's try not doing these and see what breaks. 549 // If we do need them, they would go here, before we 550 // enter the mappings into b.mem in the first place. 551 552 buildID, _ := elfBuildID(file) 553 addMapping(lo, hi, offset, file, buildID) 554 } 555 } 556 557 func (b *profileBuilder) addMapping(lo, hi, offset uint64, file, buildID string) { 558 b.addMappingEntry(lo, hi, offset, file, buildID, false) 559 } 560 561 func (b *profileBuilder) addMappingEntry(lo, hi, offset uint64, file, buildID string, fake bool) { 562 b.mem = append(b.mem, memMap{ 563 start: uintptr(lo), 564 end: uintptr(hi), 565 offset: offset, 566 file: file, 567 buildID: buildID, 568 fake: fake, 569 }) 570 }