1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 package pprof
77
78 import (
79 "bufio"
80 "cmp"
81 "fmt"
82 "internal/abi"
83 "internal/goexperiment"
84 "internal/profilerecord"
85 "io"
86 "runtime"
87 "slices"
88 "sort"
89 "strings"
90 "sync"
91 "text/tabwriter"
92 "time"
93 "unsafe"
94 )
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175 type Profile struct {
176 name string
177 mu sync.Mutex
178 m map[any][]uintptr
179 count func() int
180 write func(io.Writer, int) error
181 }
182
183
184 var profiles struct {
185 mu sync.Mutex
186 m map[string]*Profile
187 }
188
189 var goroutineProfile = &Profile{
190 name: "goroutine",
191 count: countGoroutine,
192 write: writeGoroutine,
193 }
194
195 var goroutineLeakProfile = &Profile{
196 name: "goroutineleak",
197 count: runtime_goroutineleakcount,
198 write: writeGoroutineLeak,
199 }
200
201 var threadcreateProfile = &Profile{
202 name: "threadcreate",
203 count: countThreadCreate,
204 write: writeThreadCreate,
205 }
206
207 var heapProfile = &Profile{
208 name: "heap",
209 count: countHeap,
210 write: writeHeap,
211 }
212
213 var allocsProfile = &Profile{
214 name: "allocs",
215 count: countHeap,
216 write: writeAlloc,
217 }
218
219 var blockProfile = &Profile{
220 name: "block",
221 count: countBlock,
222 write: writeBlock,
223 }
224
225 var mutexProfile = &Profile{
226 name: "mutex",
227 count: countMutex,
228 write: writeMutex,
229 }
230
231 func lockProfiles() {
232 profiles.mu.Lock()
233 if profiles.m == nil {
234
235 profiles.m = map[string]*Profile{
236 "goroutine": goroutineProfile,
237 "threadcreate": threadcreateProfile,
238 "heap": heapProfile,
239 "allocs": allocsProfile,
240 "block": blockProfile,
241 "mutex": mutexProfile,
242 }
243 if goexperiment.GoroutineLeakProfile {
244 profiles.m["goroutineleak"] = goroutineLeakProfile
245 }
246 }
247 }
248
249 func unlockProfiles() {
250 profiles.mu.Unlock()
251 }
252
253
254
255
256
257
258
259 func NewProfile(name string) *Profile {
260 lockProfiles()
261 defer unlockProfiles()
262 if name == "" {
263 panic("pprof: NewProfile with empty name")
264 }
265 if profiles.m[name] != nil {
266 panic("pprof: NewProfile name already in use: " + name)
267 }
268 p := &Profile{
269 name: name,
270 m: map[any][]uintptr{},
271 }
272 profiles.m[name] = p
273 return p
274 }
275
276
277 func Lookup(name string) *Profile {
278 lockProfiles()
279 defer unlockProfiles()
280 return profiles.m[name]
281 }
282
283
284 func Profiles() []*Profile {
285 lockProfiles()
286 defer unlockProfiles()
287
288 all := make([]*Profile, 0, len(profiles.m))
289 for _, p := range profiles.m {
290
291 all = append(all, p)
292 }
293
294 slices.SortFunc(all, func(a, b *Profile) int {
295 return strings.Compare(a.name, b.name)
296 })
297 return all
298 }
299
300
301 func (p *Profile) Name() string {
302 return p.name
303 }
304
305
306 func (p *Profile) Count() int {
307 p.mu.Lock()
308 defer p.mu.Unlock()
309 if p.count != nil {
310 return p.count()
311 }
312 return len(p.m)
313 }
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 func (p *Profile) Add(value any, skip int) {
333 if p.name == "" {
334 panic("pprof: use of uninitialized Profile")
335 }
336 if p.write != nil {
337 panic("pprof: Add called on built-in Profile " + p.name)
338 }
339
340 stk := make([]uintptr, 32)
341 n := runtime.Callers(skip+1, stk[:])
342 stk = stk[:n]
343 if len(stk) == 0 {
344
345 stk = []uintptr{abi.FuncPCABIInternal(lostProfileEvent)}
346 }
347
348 p.mu.Lock()
349 defer p.mu.Unlock()
350 if p.m[value] != nil {
351 panic("pprof: Profile.Add of duplicate value")
352 }
353 p.m[value] = stk
354 }
355
356
357
358 func (p *Profile) Remove(value any) {
359 p.mu.Lock()
360 defer p.mu.Unlock()
361 delete(p.m, value)
362 }
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379 func (p *Profile) WriteTo(w io.Writer, debug int) error {
380 if p.name == "" {
381 panic("pprof: use of zero Profile")
382 }
383 if p.write != nil {
384 return p.write(w, debug)
385 }
386
387
388 p.mu.Lock()
389 all := make([][]uintptr, 0, len(p.m))
390 for _, stk := range p.m {
391 all = append(all, stk)
392 }
393 p.mu.Unlock()
394
395
396 slices.SortFunc(all, slices.Compare)
397
398 return printCountProfile(w, debug, p.name, stackProfile(all))
399 }
400
401 type stackProfile [][]uintptr
402
403 func (x stackProfile) Len() int { return len(x) }
404 func (x stackProfile) Stack(i int) []uintptr { return x[i] }
405 func (x stackProfile) Label(i int) *labelMap { return nil }
406
407
408
409
410
411 type countProfile interface {
412 Len() int
413 Stack(i int) []uintptr
414 Label(i int) *labelMap
415 }
416
417
418
419
420 func expandInlinedFrames(dst, pcs []uintptr) int {
421 cf := runtime.CallersFrames(pcs)
422 var n int
423 for n < len(dst) {
424 f, more := cf.Next()
425
426
427 dst[n] = f.PC + 1
428 n++
429 if !more {
430 break
431 }
432 }
433 return n
434 }
435
436
437
438
439
440 func printCountCycleProfile(w io.Writer, countName, cycleName string, records []profilerecord.BlockProfileRecord) error {
441
442 b := newProfileBuilder(w)
443 b.pbValueType(tagProfile_PeriodType, countName, "count")
444 b.pb.int64Opt(tagProfile_Period, 1)
445 b.pbValueType(tagProfile_SampleType, countName, "count")
446 b.pbValueType(tagProfile_SampleType, cycleName, "nanoseconds")
447
448 cpuGHz := float64(pprof_cyclesPerSecond()) / 1e9
449
450 values := []int64{0, 0}
451 var locs []uint64
452 expandedStack := pprof_makeProfStack()
453 for _, r := range records {
454 values[0] = r.Count
455 values[1] = int64(float64(r.Cycles) / cpuGHz)
456
457
458 n := expandInlinedFrames(expandedStack, r.Stack)
459 locs = b.appendLocsForStack(locs[:0], expandedStack[:n])
460 b.pbSample(values, locs, nil)
461 }
462 return b.build()
463 }
464
465
466
467 func printCountProfile(w io.Writer, debug int, name string, p countProfile) error {
468
469 var buf strings.Builder
470 key := func(stk []uintptr, lbls *labelMap) string {
471 buf.Reset()
472 fmt.Fprintf(&buf, "@")
473 for _, pc := range stk {
474 fmt.Fprintf(&buf, " %#x", pc)
475 }
476 if lbls != nil {
477 buf.WriteString("\n# labels: ")
478 buf.WriteString(lbls.String())
479 }
480 return buf.String()
481 }
482 count := map[string]int{}
483 index := map[string]int{}
484 var keys []string
485 n := p.Len()
486 for i := 0; i < n; i++ {
487 k := key(p.Stack(i), p.Label(i))
488 if count[k] == 0 {
489 index[k] = i
490 keys = append(keys, k)
491 }
492 count[k]++
493 }
494
495 sort.Sort(&keysByCount{keys, count})
496
497 if debug > 0 {
498
499 tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
500 fmt.Fprintf(tw, "%s profile: total %d\n", name, p.Len())
501 for _, k := range keys {
502 fmt.Fprintf(tw, "%d %s\n", count[k], k)
503 printStackRecord(tw, p.Stack(index[k]), false)
504 }
505 return tw.Flush()
506 }
507
508
509 b := newProfileBuilder(w)
510 b.pbValueType(tagProfile_PeriodType, name, "count")
511 b.pb.int64Opt(tagProfile_Period, 1)
512 b.pbValueType(tagProfile_SampleType, name, "count")
513
514 values := []int64{0}
515 var locs []uint64
516 for _, k := range keys {
517 values[0] = int64(count[k])
518
519
520 locs = b.appendLocsForStack(locs[:0], p.Stack(index[k]))
521 idx := index[k]
522 var labels func()
523 if p.Label(idx) != nil {
524 labels = func() {
525 for _, lbl := range p.Label(idx).list {
526 b.pbLabel(tagSample_Label, lbl.key, lbl.value, 0)
527 }
528 }
529 }
530 b.pbSample(values, locs, labels)
531 }
532 return b.build()
533 }
534
535
536 type keysByCount struct {
537 keys []string
538 count map[string]int
539 }
540
541 func (x *keysByCount) Len() int { return len(x.keys) }
542 func (x *keysByCount) Swap(i, j int) { x.keys[i], x.keys[j] = x.keys[j], x.keys[i] }
543 func (x *keysByCount) Less(i, j int) bool {
544 ki, kj := x.keys[i], x.keys[j]
545 ci, cj := x.count[ki], x.count[kj]
546 if ci != cj {
547 return ci > cj
548 }
549 return ki < kj
550 }
551
552
553
554 func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
555 show := allFrames
556 frames := runtime.CallersFrames(stk)
557 for {
558 frame, more := frames.Next()
559 name := frame.Function
560 if name == "" {
561 show = true
562 fmt.Fprintf(w, "#\t%#x\n", frame.PC)
563 } else if name != "runtime.goexit" && (show || !(strings.HasPrefix(name, "runtime.") || strings.HasPrefix(name, "internal/runtime/"))) {
564
565
566 show = true
567 fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", frame.PC, name, frame.PC-frame.Entry, frame.File, frame.Line)
568 }
569 if !more {
570 break
571 }
572 }
573 if !show {
574
575
576 printStackRecord(w, stk, true)
577 return
578 }
579 fmt.Fprintf(w, "\n")
580 }
581
582
583
584
585
586 func WriteHeapProfile(w io.Writer) error {
587 return writeHeap(w, 0)
588 }
589
590
591 func countHeap() int {
592 n, _ := runtime.MemProfile(nil, true)
593 return n
594 }
595
596
597 func writeHeap(w io.Writer, debug int) error {
598 return writeHeapInternal(w, debug, "")
599 }
600
601
602
603 func writeAlloc(w io.Writer, debug int) error {
604 return writeHeapInternal(w, debug, "alloc_space")
605 }
606
607 func writeHeapInternal(w io.Writer, debug int, defaultSampleType string) error {
608 var memStats *runtime.MemStats
609 if debug != 0 {
610
611
612 memStats = new(runtime.MemStats)
613 runtime.ReadMemStats(memStats)
614 }
615
616
617
618
619
620
621
622
623 var p []profilerecord.MemProfileRecord
624 n, ok := pprof_memProfileInternal(nil, true)
625 for {
626
627
628
629 p = make([]profilerecord.MemProfileRecord, n+50)
630 n, ok = pprof_memProfileInternal(p, true)
631 if ok {
632 p = p[0:n]
633 break
634 }
635
636 }
637
638 if debug == 0 {
639 return writeHeapProto(w, p, int64(runtime.MemProfileRate), defaultSampleType)
640 }
641
642 slices.SortFunc(p, func(a, b profilerecord.MemProfileRecord) int {
643 return cmp.Compare(a.InUseBytes(), b.InUseBytes())
644 })
645
646 b := bufio.NewWriter(w)
647 tw := tabwriter.NewWriter(b, 1, 8, 1, '\t', 0)
648 w = tw
649
650 var total runtime.MemProfileRecord
651 for i := range p {
652 r := &p[i]
653 total.AllocBytes += r.AllocBytes
654 total.AllocObjects += r.AllocObjects
655 total.FreeBytes += r.FreeBytes
656 total.FreeObjects += r.FreeObjects
657 }
658
659
660
661
662 rate := 2 * runtime.MemProfileRate
663
664
665
666
667
668
669
670 inUseBytes := total.InUseBytes()
671 allocBytes := total.AllocBytes
672 if inUseBytes == allocBytes {
673 allocBytes++
674 }
675
676 fmt.Fprintf(w, "heap profile: %d: %d [%d: %d] @ heap/%d\n",
677 total.InUseObjects(), inUseBytes,
678 total.AllocObjects, allocBytes,
679 rate)
680
681 for i := range p {
682 r := &p[i]
683 fmt.Fprintf(w, "%d: %d [%d: %d] @",
684 r.InUseObjects(), r.InUseBytes(),
685 r.AllocObjects, r.AllocBytes)
686 for _, pc := range r.Stack {
687 fmt.Fprintf(w, " %#x", pc)
688 }
689 fmt.Fprintf(w, "\n")
690 printStackRecord(w, r.Stack, false)
691 }
692
693
694
695 s := memStats
696 fmt.Fprintf(w, "\n# runtime.MemStats\n")
697 fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
698 fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
699 fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
700 fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
701 fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
702 fmt.Fprintf(w, "# Frees = %d\n", s.Frees)
703
704 fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
705 fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
706 fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
707 fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
708 fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
709 fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)
710
711 fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
712 fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
713 fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
714 fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
715 fmt.Fprintf(w, "# GCSys = %d\n", s.GCSys)
716 fmt.Fprintf(w, "# OtherSys = %d\n", s.OtherSys)
717
718 fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
719 fmt.Fprintf(w, "# LastGC = %d\n", s.LastGC)
720 fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
721 fmt.Fprintf(w, "# PauseEnd = %d\n", s.PauseEnd)
722 fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
723 fmt.Fprintf(w, "# NumForcedGC = %d\n", s.NumForcedGC)
724 fmt.Fprintf(w, "# GCCPUFraction = %v\n", s.GCCPUFraction)
725 fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
726
727
728 addMaxRSS(w)
729
730 tw.Flush()
731 return b.Flush()
732 }
733
734
735 func countThreadCreate() int {
736 n, _ := runtime.ThreadCreateProfile(nil)
737 return n
738 }
739
740
741 func writeThreadCreate(w io.Writer, debug int) error {
742
743
744
745 return writeRuntimeProfile(w, debug, "threadcreate", func(p []profilerecord.StackRecord, _ []unsafe.Pointer) (n int, ok bool) {
746 return pprof_threadCreateInternal(p)
747 })
748 }
749
750
751 func countGoroutine() int {
752 return runtime.NumGoroutine()
753 }
754
755
756 func writeGoroutine(w io.Writer, debug int) error {
757 if debug >= 2 {
758 return writeGoroutineStacks(w)
759 }
760 return writeRuntimeProfile(w, debug, "goroutine", pprof_goroutineProfileWithLabels)
761 }
762
763
764
765 func writeGoroutineLeak(w io.Writer, debug int) error {
766
767
768 runtime_goroutineLeakGC()
769
770
771
772 if debug >= 2 {
773 return writeGoroutineStacks(w)
774 }
775
776
777 return writeRuntimeProfile(w, debug, "goroutineleak", pprof_goroutineLeakProfileWithLabels)
778 }
779
780 func writeGoroutineStacks(w io.Writer) error {
781
782
783
784 buf := make([]byte, 1<<20)
785 for i := 0; ; i++ {
786 n := runtime.Stack(buf, true)
787 if n < len(buf) {
788 buf = buf[:n]
789 break
790 }
791 if len(buf) >= 64<<20 {
792
793 break
794 }
795 buf = make([]byte, 2*len(buf))
796 }
797 _, err := w.Write(buf)
798 return err
799 }
800
801 func writeRuntimeProfile(w io.Writer, debug int, name string, fetch func([]profilerecord.StackRecord, []unsafe.Pointer) (int, bool)) error {
802
803
804
805
806
807
808 var p []profilerecord.StackRecord
809 var labels []unsafe.Pointer
810 n, ok := fetch(nil, nil)
811
812 for {
813
814
815
816 p = make([]profilerecord.StackRecord, n+10)
817 labels = make([]unsafe.Pointer, n+10)
818 n, ok = fetch(p, labels)
819 if ok {
820 p = p[0:n]
821 break
822 }
823
824 }
825
826 return printCountProfile(w, debug, name, &runtimeProfile{p, labels})
827 }
828
829 type runtimeProfile struct {
830 stk []profilerecord.StackRecord
831 labels []unsafe.Pointer
832 }
833
834 func (p *runtimeProfile) Len() int { return len(p.stk) }
835 func (p *runtimeProfile) Stack(i int) []uintptr { return p.stk[i].Stack }
836 func (p *runtimeProfile) Label(i int) *labelMap { return (*labelMap)(p.labels[i]) }
837
838 var cpu struct {
839 sync.Mutex
840 profiling bool
841 done chan bool
842 }
843
844
845
846
847
848
849
850
851
852
853
854
855 func StartCPUProfile(w io.Writer) error {
856
857
858
859
860
861
862
863
864
865 const hz = 100
866
867 cpu.Lock()
868 defer cpu.Unlock()
869 if cpu.done == nil {
870 cpu.done = make(chan bool)
871 }
872
873 if cpu.profiling {
874 return fmt.Errorf("cpu profiling already in use")
875 }
876 cpu.profiling = true
877 runtime.SetCPUProfileRate(hz)
878 go profileWriter(w)
879 return nil
880 }
881
882
883
884
885
886
887 func readProfile() (data []uint64, tags []unsafe.Pointer, eof bool)
888
889 func profileWriter(w io.Writer) {
890 b := newProfileBuilder(w)
891 var err error
892 for {
893 time.Sleep(100 * time.Millisecond)
894 data, tags, eof := readProfile()
895 if e := b.addCPUData(data, tags); e != nil && err == nil {
896 err = e
897 }
898 if eof {
899 break
900 }
901 }
902 if err != nil {
903
904
905 panic("runtime/pprof: converting profile: " + err.Error())
906 }
907 b.build()
908 cpu.done <- true
909 }
910
911
912
913
914 func StopCPUProfile() {
915 cpu.Lock()
916 defer cpu.Unlock()
917
918 if !cpu.profiling {
919 return
920 }
921 cpu.profiling = false
922 runtime.SetCPUProfileRate(0)
923 <-cpu.done
924 }
925
926
927 func countBlock() int {
928 n, _ := runtime.BlockProfile(nil)
929 return n
930 }
931
932
933 func countMutex() int {
934 n, _ := runtime.MutexProfile(nil)
935 return n
936 }
937
938
939 func writeBlock(w io.Writer, debug int) error {
940 return writeProfileInternal(w, debug, "contention", pprof_blockProfileInternal)
941 }
942
943
944 func writeMutex(w io.Writer, debug int) error {
945 return writeProfileInternal(w, debug, "mutex", pprof_mutexProfileInternal)
946 }
947
948
949 func writeProfileInternal(w io.Writer, debug int, name string, runtimeProfile func([]profilerecord.BlockProfileRecord) (int, bool)) error {
950 var p []profilerecord.BlockProfileRecord
951 n, ok := runtimeProfile(nil)
952 for {
953 p = make([]profilerecord.BlockProfileRecord, n+50)
954 n, ok = runtimeProfile(p)
955 if ok {
956 p = p[:n]
957 break
958 }
959 }
960
961 slices.SortFunc(p, func(a, b profilerecord.BlockProfileRecord) int {
962 return cmp.Compare(b.Cycles, a.Cycles)
963 })
964
965 if debug <= 0 {
966 return printCountCycleProfile(w, "contentions", "delay", p)
967 }
968
969 b := bufio.NewWriter(w)
970 tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
971 w = tw
972
973 fmt.Fprintf(w, "--- %v:\n", name)
974 fmt.Fprintf(w, "cycles/second=%v\n", pprof_cyclesPerSecond())
975 if name == "mutex" {
976 fmt.Fprintf(w, "sampling period=%d\n", runtime.SetMutexProfileFraction(-1))
977 }
978 expandedStack := pprof_makeProfStack()
979 for i := range p {
980 r := &p[i]
981 fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count)
982 n := expandInlinedFrames(expandedStack, r.Stack)
983 stack := expandedStack[:n]
984 for _, pc := range stack {
985 fmt.Fprintf(w, " %#x", pc)
986 }
987 fmt.Fprint(w, "\n")
988 if debug > 0 {
989 printStackRecord(w, stack, true)
990 }
991 }
992
993 if tw != nil {
994 tw.Flush()
995 }
996 return b.Flush()
997 }
998
999
1000 func pprof_goroutineProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool)
1001
1002
1003 func pprof_goroutineLeakProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool)
1004
1005
1006 func pprof_cyclesPerSecond() int64
1007
1008
1009 func pprof_memProfileInternal(p []profilerecord.MemProfileRecord, inuseZero bool) (n int, ok bool)
1010
1011
1012 func pprof_blockProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok bool)
1013
1014
1015 func pprof_mutexProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok bool)
1016
1017
1018 func pprof_threadCreateInternal(p []profilerecord.StackRecord) (n int, ok bool)
1019
1020
1021 func pprof_fpunwindExpand(dst, src []uintptr) int
1022
1023
1024 func pprof_makeProfStack() []uintptr
1025
View as plain text