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 //go:build !plan9 && !windows 6 // +build !plan9,!windows 7 8 package main 9 10 // Run a slow C function saving a CPU profile. 11 12 /* 13 #include <stdint.h> 14 #include <time.h> 15 #include <pthread.h> 16 17 int threadSalt1; 18 int threadSalt2; 19 20 static pthread_t tid; 21 22 void cpuHogThread() { 23 int foo = threadSalt1; 24 int i; 25 26 for (i = 0; i < 100000; i++) { 27 if (foo > 0) { 28 foo *= foo; 29 } else { 30 foo *= foo + 1; 31 } 32 } 33 threadSalt2 = foo; 34 } 35 36 void cpuHogThread2() { 37 } 38 39 struct cgoTracebackArg { 40 uintptr_t context; 41 uintptr_t sigContext; 42 uintptr_t* buf; 43 uintptr_t max; 44 }; 45 46 // pprofCgoThreadTraceback is passed to runtime.SetCgoTraceback. 47 // For testing purposes it pretends that all CPU hits on the cpuHog 48 // C thread are in cpuHog. 49 void pprofCgoThreadTraceback(void* parg) { 50 struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg); 51 if (pthread_self() == tid) { 52 arg->buf[0] = (uintptr_t)(cpuHogThread) + 0x10; 53 arg->buf[1] = (uintptr_t)(cpuHogThread2) + 0x4; 54 arg->buf[2] = 0; 55 } else 56 arg->buf[0] = 0; 57 } 58 59 static void* cpuHogDriver(void* arg __attribute__ ((unused))) { 60 while (1) { 61 cpuHogThread(); 62 } 63 return 0; 64 } 65 66 void runCPUHogThread(void) { 67 pthread_create(&tid, 0, cpuHogDriver, 0); 68 } 69 */ 70 import "C" 71 72 import ( 73 "context" 74 "fmt" 75 "os" 76 "runtime" 77 "runtime/pprof" 78 "time" 79 "unsafe" 80 ) 81 82 func init() { 83 register("CgoPprofThread", CgoPprofThread) 84 register("CgoPprofThreadNoTraceback", CgoPprofThreadNoTraceback) 85 } 86 87 func CgoPprofThread() { 88 runtime.SetCgoTraceback(0, unsafe.Pointer(C.pprofCgoThreadTraceback), nil, nil) 89 pprofThread() 90 } 91 92 func CgoPprofThreadNoTraceback() { 93 pprofThread() 94 } 95 96 func pprofThread() { 97 f, err := os.CreateTemp("", "prof") 98 if err != nil { 99 fmt.Fprintln(os.Stderr, err) 100 os.Exit(2) 101 } 102 103 if err := pprof.StartCPUProfile(f); err != nil { 104 fmt.Fprintln(os.Stderr, err) 105 os.Exit(2) 106 } 107 108 // This goroutine may receive a profiling signal while creating the C-owned 109 // thread. If it does, the SetCgoTraceback handler will make the leaf end of 110 // the stack look almost (but not exactly) like the stacks the test case is 111 // trying to find. Attach a profiler label so the test can filter out those 112 // confusing samples. 113 pprof.Do(context.Background(), pprof.Labels("ignore", "ignore"), func(ctx context.Context) { 114 C.runCPUHogThread() 115 }) 116 117 time.Sleep(1 * time.Second) 118 119 pprof.StopCPUProfile() 120 121 name := f.Name() 122 if err := f.Close(); err != nil { 123 fmt.Fprintln(os.Stderr, err) 124 os.Exit(2) 125 } 126 127 fmt.Println(name) 128 } 129