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