golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/pprof/pprof.go (about) 1 // Copyright 2023 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 provides minimalistic routines for extracting 6 // information from profiles. 7 package pprof 8 9 import ( 10 "fmt" 11 "time" 12 ) 13 14 // TotalTime parses the profile data and returns the accumulated time. 15 // The input should not be gzipped. 16 func TotalTime(data []byte) (total time.Duration, err error) { 17 defer func() { 18 if x := recover(); x != nil { 19 err = fmt.Errorf("error parsing pprof profile: %v", x) 20 } 21 }() 22 decode(&total, data, msgProfile) 23 return 24 } 25 26 // All errors are handled by panicking. 27 // Constants are copied below to avoid dependency on protobufs or pprof. 28 29 // protobuf wire types, from https://developers.google.com/protocol-buffers/docs/encoding 30 const ( 31 wireVarint = 0 32 wireBytes = 2 33 ) 34 35 // pprof field numbers, from https://github.com/google/pprof/blob/master/proto/profile.proto 36 const ( 37 fldProfileSample = 2 // repeated Sample 38 fldSampleValue = 2 // repeated int64 39 ) 40 41 // arbitrary numbering of message types 42 const ( 43 msgProfile = 0 44 msgSample = 1 45 ) 46 47 func decode(total *time.Duration, data []byte, msg int) { 48 for len(data) > 0 { 49 // Read tag (wire type and field number). 50 tag := varint(&data) 51 52 // Read wire value (int or bytes). 53 wire := tag & 7 54 var ival uint64 55 var sval []byte 56 switch wire { 57 case wireVarint: 58 ival = varint(&data) 59 60 case wireBytes: 61 n := varint(&data) 62 sval, data = data[:n], data[n:] 63 64 default: 65 panic(fmt.Sprintf("unexpected wire type: %d", wire)) 66 } 67 68 // Process field of msg. 69 fld := tag >> 3 70 switch { 71 case msg == msgProfile && fld == fldProfileSample: 72 decode(total, sval, msgSample) // recursively decode Sample message 73 74 case msg == msgSample, fld == fldSampleValue: 75 *total += time.Duration(ival) // accumulate time 76 } 77 } 78 } 79 80 func varint(data *[]byte) (v uint64) { 81 for i := 0; ; i++ { 82 b := uint64((*data)[i]) 83 v += (b & 0x7f) << (7 * i) 84 if b < 0x80 { 85 *data = (*data)[i+1:] 86 return v 87 } 88 } 89 }