"runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"},
"runtime/pprof/internal/gzip0": {"L2"},
"runtime/pprof/internal/protopprof": {"L2", "fmt", "internal/pprof/profile", "os", "time"},
- "runtime/pprof": {"L2", "fmt", "os", "text/tabwriter"},
+ "runtime/pprof": {"L2", "fmt", "os", "runtime/pprof/internal/protopprof", "text/tabwriter", "time"},
"runtime/trace": {"L0"},
"text/tabwriter": {"L2"},
}
count := data[0]
nstk := data[1]
- fmt.Printf("count:%v nstk: %v\n", count, nstk)
if uintptr(len(data)) < 2+nstk {
return nil, fmt.Errorf("truncated profile")
}
"bytes"
"fmt"
"internal/pprof/profile"
- "io"
"io/ioutil"
"reflect"
"runtime"
- "runtime/pprof"
"testing"
"time"
"unsafe"
)
-// Profile collects a CPU utilization profile and
-// writes it to w as a compressed profile.proto. It's used by
-// TestProfileParse.
-func Profile(w io.Writer, seconds int) error {
- var buf bytes.Buffer
- // Collect the CPU profile in legacy format in buf.
- startTime := time.Now()
- if err := pprof.StartCPUProfile(&buf); err != nil {
- return fmt.Errorf("Could not enable CPU profiling: %s\n", err)
- }
- time.Sleep(time.Duration(seconds) * time.Second)
- pprof.StopCPUProfile()
-
- const untagged = false
- p, err := TranslateCPUProfile(buf.Bytes(), startTime)
- if err != nil {
- return err
- }
- return p.Write(w)
-}
-
// Helper function to initialize empty cpu profile with sampling period provided.
func createEmptyProfileWithPeriod(t *testing.T, periodMs uint64) bytes.Buffer {
// Mock the sample header produced by cpu profiler. Write a sample
return *buf
}
-// Tests that server creates a cpu profile handler that outputs a parsable Profile profile.
-func TestCPUProfileParse(t *testing.T) {
- var before, after runtime.MemStats
- runtime.ReadMemStats(&before)
- var buf bytes.Buffer
- if err := Profile(&buf, 30); err != nil {
- t.Fatalf("Profile failed: %v", err)
- }
- runtime.ReadMemStats(&after)
- _, err := profile.Parse(&buf)
- if err != nil {
- t.Fatalf("Could not parse Profile profile: %v", err)
- }
-}
-
// Tests TranslateCPUProfile parses correct sampling period in an otherwise empty cpu profile.
func TestTranlateCPUProfileSamplingPeriod(t *testing.T) {
// A test server with mock cpu profile data.
"bytes"
"fmt"
"io"
- "os"
"runtime"
+ "runtime/pprof/internal/protopprof"
"sort"
"strings"
"sync"
"text/tabwriter"
+ "time"
)
// BUG(rsc): Profiles are only as good as the kernel support used to generate them.
}
func profileWriter(w io.Writer) {
+ startTime := time.Now()
+ // This will buffer the entire profile into buf and then
+ // translate it into a profile.Profile structure. This will
+ // create two copies of all the data in the profile in memory.
+ // TODO(matloob): Convert each chunk of the proto output and
+ // stream it out instead of converting the entire profile.
+ var buf bytes.Buffer
for {
data := runtime.CPUProfile()
if data == nil {
break
}
- w.Write(data)
- }
-
- // We are emitting the legacy profiling format, which permits
- // a memory map following the CPU samples. The memory map is
- // simply a copy of the GNU/Linux /proc/self/maps file. The
- // profiler uses the memory map to map PC values in shared
- // libraries to a shared library in the filesystem, in order
- // to report the correct function and, if the shared library
- // has debug info, file/line. This is particularly useful for
- // PIE (position independent executables) as on ELF systems a
- // PIE is simply an executable shared library.
- //
- // Because the profiling format expects the memory map in
- // GNU/Linux format, we only do this on GNU/Linux for now. To
- // add support for profiling PIE on other ELF-based systems,
- // it may be necessary to map the system-specific mapping
- // information to the GNU/Linux format. For a reasonably
- // portable C++ version, see the FillProcSelfMaps function in
- // https://github.com/gperftools/gperftools/blob/master/src/base/sysinfo.cc
- //
- // The code that parses this mapping for the pprof tool is
- // ParseMemoryMap in cmd/internal/pprof/legacy_profile.go, but
- // don't change that code, as similar code exists in other
- // (non-Go) pprof readers. Change this code so that that code works.
- //
- // We ignore errors reading or copying the memory map; the
- // profile is likely usable without it, and we have no good way
- // to report errors.
- if runtime.GOOS == "linux" {
- f, err := os.Open("/proc/self/maps")
- if err == nil {
- io.WriteString(w, "\nMAPPED_LIBRARIES:\n")
- io.Copy(w, f)
- f.Close()
- }
+ buf.Write(data)
+ }
+
+ profile, err := protopprof.TranslateCPUProfile(buf.Bytes(), startTime)
+ if err != nil {
+ // The runtime should never produce an invalid or truncated profile.
+ // It drops records that can't fit into its log buffers.
+ panic(fmt.Errorf("could not translate binary profile to proto format: %v", err))
}
+ profile.Write(w)
cpu.done <- true
}
import (
"bytes"
"fmt"
+ "internal/pprof/profile"
"internal/testenv"
"math/big"
"os"
"sync"
"testing"
"time"
- "unsafe"
)
func cpuHogger(f func(), dur time.Duration) {
}
func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []uintptr)) {
- // Convert []byte to []uintptr.
- l := len(valBytes)
- if i := bytes.Index(valBytes, []byte("\nMAPPED_LIBRARIES:\n")); i >= 0 {
- l = i
- }
- l /= int(unsafe.Sizeof(uintptr(0)))
- val := *(*[]uintptr)(unsafe.Pointer(&valBytes))
- val = val[:l]
-
- // 5 for the header, 3 for the trailer.
- if l < 5+3 {
- t.Logf("profile too short: %#x", val)
- if badOS[runtime.GOOS] {
- t.Skipf("ignoring failure on %s; see golang.org/issue/13841", runtime.GOOS)
- return
- }
- t.FailNow()
- }
-
- hd, val, tl := val[:5], val[5:l-3], val[l-3:]
- if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
- t.Fatalf("unexpected header %#x", hd)
- }
-
- if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
- t.Fatalf("malformed end-of-data marker %#x", tl)
+ p, err := profile.Parse(bytes.NewReader(valBytes))
+ if err != nil {
+ t.Fatal(err)
}
-
- for len(val) > 0 {
- if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
- t.Fatalf("malformed profile. leftover: %#x", val)
+ for _, sample := range p.Sample {
+ count := uintptr(sample.Value[0])
+ stk := make([]uintptr, len(sample.Location))
+ for i := range sample.Location {
+ stk[i] = uintptr(sample.Location[i].Address)
}
- f(val[0], val[2:2+val[1]])
- val = val[2+val[1]:]
+ f(count, stk)
}
}
}
return true
}
+
+// Tests that the profiler outputs a parsable protobuf Profile profile.
+func TestCPUProfileParse(t *testing.T) {
+
+ var before, after runtime.MemStats
+ runtime.ReadMemStats(&before)
+ var buf bytes.Buffer
+
+ if err := StartCPUProfile(&buf); err != nil {
+ t.Fatalf("Profile failed: Could not enable CPU profiling: %s\n", err)
+ }
+ time.Sleep(5 * time.Second)
+ StopCPUProfile()
+
+ runtime.ReadMemStats(&after)
+ _, err := profile.Parse(&buf)
+ if err != nil {
+ t.Fatalf("Could not parse Profile profile: %v", err)
+ }
+}