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  }