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