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