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