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