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