blob: f3edcc67c40454cfdebe2dabd430895232ab8e2b [file] [log] [blame]
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package pprof provides minimalistic routines for extracting
// information from profiles.
package pprof
import (
"fmt"
"time"
)
// TotalTime parses the profile data and returns the accumulated time.
// The input should not be gzipped.
func TotalTime(data []byte) (total time.Duration, err error) {
defer func() {
if x := recover(); x != nil {
err = fmt.Errorf("error parsing pprof profile: %v", x)
}
}()
decode(&total, data, msgProfile)
return
}
// All errors are handled by panicking.
// Constants are copied below to avoid dependency on protobufs or pprof.
// protobuf wire types, from https://developers.google.com/protocol-buffers/docs/encoding
const (
wireVarint = 0
wireBytes = 2
)
// pprof field numbers, from https://github.com/google/pprof/blob/master/proto/profile.proto
const (
fldProfileSample = 2 // repeated Sample
fldSampleValue = 2 // repeated int64
)
// arbitrary numbering of message types
const (
msgProfile = 0
msgSample = 1
)
func decode(total *time.Duration, data []byte, msg int) {
for len(data) > 0 {
// Read tag (wire type and field number).
tag := varint(&data)
// Read wire value (int or bytes).
wire := tag & 7
var ival uint64
var sval []byte
switch wire {
case wireVarint:
ival = varint(&data)
case wireBytes:
n := varint(&data)
sval, data = data[:n], data[n:]
default:
panic(fmt.Sprintf("unexpected wire type: %d", wire))
}
// Process field of msg.
fld := tag >> 3
switch {
case msg == msgProfile && fld == fldProfileSample:
decode(total, sval, msgSample) // recursively decode Sample message
case msg == msgSample, fld == fldSampleValue:
*total += time.Duration(ival) // accumulate time
}
}
}
func varint(data *[]byte) (v uint64) {
for i := 0; ; i++ {
b := uint64((*data)[i])
v += (b & 0x7f) << (7 * i)
if b < 0x80 {
*data = (*data)[i+1:]
return v
}
}
}