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
5package main
6
7// Run a slow C function saving a CPU profile.
8
9/*
10#include <stdint.h>
11
12int salt1;
13int salt2;
14
15void cpuHog() {
16	int foo = salt1;
17	int i;
18
19	for (i = 0; i < 100000; i++) {
20		if (foo > 0) {
21			foo *= foo;
22		} else {
23			foo *= foo + 1;
24		}
25	}
26	salt2 = foo;
27}
28
29void cpuHog2() {
30}
31
32struct cgoTracebackArg {
33	uintptr_t  context;
34	uintptr_t  sigContext;
35	uintptr_t* buf;
36	uintptr_t  max;
37};
38
39// pprofCgoTraceback is passed to runtime.SetCgoTraceback.
40// For testing purposes it pretends that all CPU hits in C code are in cpuHog.
41// Issue #29034: At least 2 frames are required to verify all frames are captured
42// since runtime/pprof ignores the runtime.goexit base frame if it exists.
43void pprofCgoTraceback(void* parg) {
44	struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
45	arg->buf[0] = (uintptr_t)(cpuHog) + 0x10;
46	arg->buf[1] = (uintptr_t)(cpuHog2) + 0x4;
47	arg->buf[2] = 0;
48}
49*/
50import "C"
51
52import (
53	"fmt"
54	"os"
55	"runtime"
56	"runtime/pprof"
57	"time"
58	"unsafe"
59)
60
61func init() {
62	register("CgoPprof", CgoPprof)
63}
64
65func CgoPprof() {
66	runtime.SetCgoTraceback(0, unsafe.Pointer(C.pprofCgoTraceback), nil, nil)
67
68	f, err := os.CreateTemp("", "prof")
69	if err != nil {
70		fmt.Fprintln(os.Stderr, err)
71		os.Exit(2)
72	}
73
74	if err := pprof.StartCPUProfile(f); err != nil {
75		fmt.Fprintln(os.Stderr, err)
76		os.Exit(2)
77	}
78
79	t0 := time.Now()
80	for time.Since(t0) < time.Second {
81		C.cpuHog()
82	}
83
84	pprof.StopCPUProfile()
85
86	name := f.Name()
87	if err := f.Close(); err != nil {
88		fmt.Fprintln(os.Stderr, err)
89		os.Exit(2)
90	}
91
92	fmt.Println(name)
93}
94