github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/runtime/pprof/internal/protopprof/protopprof.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 protopprof converts the runtime's raw profile logs
     6  // to Profile structs containing a representation of the pprof
     7  // protocol buffer profile format.
     8  package protopprof
     9  
    10  import (
    11  	"fmt"
    12  	"os"
    13  	"runtime"
    14  	"time"
    15  	"unsafe"
    16  
    17  	"internal/pprof/profile"
    18  )
    19  
    20  // TranslateCPUProfile parses binary CPU profiling stack trace data
    21  // generated by runtime.CPUProfile() into a profile struct.
    22  func TranslateCPUProfile(b []byte, startTime time.Time) (*profile.Profile, error) {
    23  	const wordSize = unsafe.Sizeof(uintptr(0))
    24  	const minRawProfile = 5 * wordSize // Need a minimum of 5 words.
    25  	if uintptr(len(b)) < minRawProfile {
    26  		return nil, fmt.Errorf("truncated profile")
    27  	}
    28  	n := int(uintptr(len(b)) / wordSize)
    29  	data := ((*[1 << 28]uintptr)(unsafe.Pointer(&b[0])))[:n:n]
    30  	period := data[3]
    31  	data = data[5:] // skip header
    32  
    33  	// profile initialization taken from pprof tool
    34  	p := &profile.Profile{
    35  		Period:     int64(period) * 1000,
    36  		PeriodType: &profile.ValueType{Type: "cpu", Unit: "nanoseconds"},
    37  		SampleType: []*profile.ValueType{
    38  			{Type: "samples", Unit: "count"},
    39  			{Type: "cpu", Unit: "nanoseconds"},
    40  		},
    41  		TimeNanos:     int64(startTime.UnixNano()),
    42  		DurationNanos: time.Since(startTime).Nanoseconds(),
    43  	}
    44  	// Parse CPU samples from the profile.
    45  	locs := make(map[uint64]*profile.Location)
    46  	for len(b) > 0 {
    47  		if len(data) < 2 || uintptr(len(data)) < 2+data[1] {
    48  			return nil, fmt.Errorf("truncated profile")
    49  		}
    50  		count := data[0]
    51  		nstk := data[1]
    52  		if uintptr(len(data)) < 2+nstk {
    53  			return nil, fmt.Errorf("truncated profile")
    54  		}
    55  		stk := data[2 : 2+nstk]
    56  		data = data[2+nstk:]
    57  
    58  		if count == 0 && nstk == 1 && stk[0] == 0 {
    59  			// end of data marker
    60  			break
    61  		}
    62  
    63  		sloc := make([]*profile.Location, len(stk))
    64  		for i, addr := range stk {
    65  			addr := uint64(addr)
    66  			// Addresses from stack traces point to the next instruction after
    67  			// each call.  Adjust by -1 to land somewhere on the actual call
    68  			// (except for the leaf, which is not a call).
    69  			if i > 0 {
    70  				addr--
    71  			}
    72  			loc := locs[addr]
    73  			if loc == nil {
    74  				loc = &profile.Location{
    75  					ID:      uint64(len(p.Location) + 1),
    76  					Address: addr,
    77  				}
    78  				locs[addr] = loc
    79  				p.Location = append(p.Location, loc)
    80  			}
    81  			sloc[i] = loc
    82  		}
    83  		p.Sample = append(p.Sample, &profile.Sample{
    84  			Value:    []int64{int64(count), int64(count) * int64(p.Period)},
    85  			Location: sloc,
    86  		})
    87  	}
    88  
    89  	if runtime.GOOS == "linux" {
    90  		if err := addMappings(p); err != nil {
    91  			return nil, err
    92  		}
    93  	}
    94  	return p, nil
    95  }
    96  
    97  func addMappings(p *profile.Profile) error {
    98  	// Parse memory map from /proc/self/maps
    99  	f, err := os.Open("/proc/self/maps")
   100  	if err != nil {
   101  		return err
   102  	}
   103  	defer f.Close()
   104  	return p.ParseMemoryMap(f)
   105  }