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