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