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