blob: ec5bbd681626ac3fe50d74dc914dae7bddfa0602 [file] [edit]
// Copyright 2025 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 main
import (
"bytes"
"net/http"
"os"
"runtime/trace"
"strings"
"testing"
"testing/synctest"
"time"
itrace "internal/trace"
"internal/trace/testtrace"
)
// Regression test for go.dev/issue/74850.
func TestSyscallProfile74850(t *testing.T) {
testtrace.MustHaveSyscallEvents(t)
var buf bytes.Buffer
err := trace.Start(&buf)
if err != nil {
t.Fatalf("start tracing: %v", err)
}
synctest.Test(t, func(t *testing.T) {
go hidden1(t)
go hidden2(t)
go visible(t)
synctest.Wait()
time.Sleep(1 * time.Millisecond)
synctest.Wait()
})
trace.Stop()
if t.Failed() {
return
}
parsed, err := parseTrace(&buf, int64(buf.Len()))
if err != nil {
t.Fatalf("parsing trace: %v", err)
}
records, err := pprofByGoroutine(computePprofSyscall(), parsed)(&http.Request{})
if err != nil {
t.Fatalf("failed to generate pprof: %v\n", err)
}
for _, r := range records {
t.Logf("Record: n=%d, total=%v", r.Count, r.Time)
for _, f := range r.Stack {
t.Logf("\t%s", f.Func)
t.Logf("\t\t%s:%d @ 0x%x", f.File, f.Line, f.PC)
}
}
if len(records) == 0 {
t.Error("empty profile")
}
// Make sure we see the right frames.
wantSymbols := []string{"cmd/trace.visible", "cmd/trace.hidden1", "cmd/trace.hidden2"}
haveSymbols := make([]bool, len(wantSymbols))
for _, r := range records {
for _, f := range r.Stack {
for i, s := range wantSymbols {
if strings.Contains(f.Func, s) {
haveSymbols[i] = true
}
}
}
}
for i, have := range haveSymbols {
if !have {
t.Errorf("expected %s in syscall profile", wantSymbols[i])
}
}
}
// Regression test: pcsForStack must not panic when the stack has more than
// pprofMaxStack (128) frames. Before the fix, pcs[i] was written before
// checking i >= len(pcs), causing an index-out-of-range panic.
func TestFillPCsOverflow(t *testing.T) {
var pcs [pprofMaxStack]uint64
// Build a stack with more frames than pprofMaxStack.
n := pprofMaxStack + 10
frames := make([]itrace.StackFrame, n)
for i := range frames {
frames[i].PC = uint64(0x1000 + i)
}
stack := itrace.MakeStack(frames)
// This must not panic.
pcsForStack(stack, &pcs)
// Verify the first pprofMaxStack entries were written correctly.
for i := 0; i < pprofMaxStack; i++ {
if pcs[i] != uint64(0x1000+i) {
t.Errorf("pcs[%d] = %#x, want %#x", i, pcs[i], 0x1000+i)
}
}
}
func stat(t *testing.T) {
_, err := os.Stat(".")
if err != nil {
t.Errorf("os.Stat: %v", err)
}
}
func hidden1(t *testing.T) {
stat(t)
}
func hidden2(t *testing.T) {
stat(t)
stat(t)
}
func visible(t *testing.T) {
stat(t)
time.Sleep(1 * time.Millisecond)
}