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
122
123
124
125
126
127
128
129
130
131
132 type Profile struct {
133 name string
134 mu sync.Mutex
135 m map[interface{}][]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[interface{}][]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
278 func (p *Profile) Add(value interface{}, 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{funcPC(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 interface{}) {
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 func (p *Profile) WriteTo(w io.Writer, debug int) error {
324 if p.name == "" {
325 panic("pprof: use of zero Profile")
326 }
327 if p.write != nil {
328 return p.write(w, debug)
329 }
330
331
332 p.mu.Lock()
333 all := make([][]uintptr, 0, len(p.m))
334 for _, stk := range p.m {
335 all = append(all, stk)
336 }
337 p.mu.Unlock()
338
339
340 sort.Slice(all, func(i, j int) bool {
341 t, u := all[i], all[j]
342 for k := 0; k < len(t) && k < len(u); k++ {
343 if t[k] != u[k] {
344 return t[k] < u[k]
345 }
346 }
347 return len(t) < len(u)
348 })
349
350 return printCountProfile(w, debug, p.name, stackProfile(all))
351 }
352
353 type stackProfile [][]uintptr
354
355 func (x stackProfile) Len() int { return len(x) }
356 func (x stackProfile) Stack(i int) []uintptr { return x[i] }
357
358
359
360
361
362 type countProfile interface {
363 Len() int
364 Stack(i int) []uintptr
365 }
366
367
368
369
370
371
372 func printCountCycleProfile(w io.Writer, countName, cycleName string, scaler func(int64, float64) (int64, float64), records []runtime.BlockProfileRecord) error {
373
374 b := newProfileBuilder(w)
375 b.pbValueType(tagProfile_PeriodType, countName, "count")
376 b.pb.int64Opt(tagProfile_Period, 1)
377 b.pbValueType(tagProfile_SampleType, countName, "count")
378 b.pbValueType(tagProfile_SampleType, cycleName, "nanoseconds")
379
380 cpuGHz := float64(runtime_cyclesPerSecond()) / 1e9
381
382 values := []int64{0, 0}
383 var locs []uint64
384 for _, r := range records {
385 count, nanosec := scaler(r.Count, float64(r.Cycles)/cpuGHz)
386 values[0] = count
387 values[1] = int64(nanosec)
388 locs = locs[:0]
389 for _, addr := range r.Stack() {
390
391
392 l := b.locForPC(addr)
393 if l == 0 {
394 continue
395 }
396 locs = append(locs, l)
397 }
398 b.pbSample(values, locs, nil)
399 }
400 b.build()
401 return nil
402 }
403
404
405
406 func printCountProfile(w io.Writer, debug int, name string, p countProfile) error {
407
408 var buf bytes.Buffer
409 key := func(stk []uintptr) string {
410 buf.Reset()
411 fmt.Fprintf(&buf, "@")
412 for _, pc := range stk {
413 fmt.Fprintf(&buf, " %#x", pc)
414 }
415 return buf.String()
416 }
417 count := map[string]int{}
418 index := map[string]int{}
419 var keys []string
420 n := p.Len()
421 for i := 0; i < n; i++ {
422 k := key(p.Stack(i))
423 if count[k] == 0 {
424 index[k] = i
425 keys = append(keys, k)
426 }
427 count[k]++
428 }
429
430 sort.Sort(&keysByCount{keys, count})
431
432 if debug > 0 {
433
434 tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
435 fmt.Fprintf(tw, "%s profile: total %d\n", name, p.Len())
436 for _, k := range keys {
437 fmt.Fprintf(tw, "%d %s\n", count[k], k)
438 printStackRecord(tw, p.Stack(index[k]), false)
439 }
440 return tw.Flush()
441 }
442
443
444 b := newProfileBuilder(w)
445 b.pbValueType(tagProfile_PeriodType, name, "count")
446 b.pb.int64Opt(tagProfile_Period, 1)
447 b.pbValueType(tagProfile_SampleType, name, "count")
448
449 values := []int64{0}
450 var locs []uint64
451 for _, k := range keys {
452 values[0] = int64(count[k])
453 locs = locs[:0]
454 for _, addr := range p.Stack(index[k]) {
455
456
457 l := b.locForPC(addr)
458 if l == 0 {
459 continue
460 }
461 locs = append(locs, l)
462 }
463 b.pbSample(values, locs, nil)
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 fmt.Fprintf(w, "heap profile: %d: %d [%d: %d] @ heap/%d\n",
594 total.InUseObjects(), total.InUseBytes(),
595 total.AllocObjects, total.AllocBytes,
596 2*runtime.MemProfileRate)
597
598 for i := range p {
599 r := &p[i]
600 fmt.Fprintf(w, "%d: %d [%d: %d] @",
601 r.InUseObjects(), r.InUseBytes(),
602 r.AllocObjects, r.AllocBytes)
603 for _, pc := range r.Stack() {
604 fmt.Fprintf(w, " %#x", pc)
605 }
606 fmt.Fprintf(w, "\n")
607 printStackRecord(w, r.Stack(), false)
608 }
609
610
611
612 s := memStats
613 fmt.Fprintf(w, "\n# runtime.MemStats\n")
614 fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
615 fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
616 fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
617 fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
618 fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
619 fmt.Fprintf(w, "# Frees = %d\n", s.Frees)
620
621 fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
622 fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
623 fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
624 fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
625 fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
626 fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)
627
628 fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
629 fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
630 fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
631 fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
632 fmt.Fprintf(w, "# GCSys = %d\n", s.GCSys)
633 fmt.Fprintf(w, "# OtherSys = %d\n", s.OtherSys)
634
635 fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
636 fmt.Fprintf(w, "# LastGC = %d\n", s.LastGC)
637 fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
638 fmt.Fprintf(w, "# PauseEnd = %d\n", s.PauseEnd)
639 fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
640 fmt.Fprintf(w, "# NumForcedGC = %d\n", s.NumForcedGC)
641 fmt.Fprintf(w, "# GCCPUFraction = %v\n", s.GCCPUFraction)
642 fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
643
644 tw.Flush()
645 return b.Flush()
646 }
647
648
649 func countThreadCreate() int {
650 n, _ := runtime.ThreadCreateProfile(nil)
651 return n
652 }
653
654
655 func writeThreadCreate(w io.Writer, debug int) error {
656 return writeRuntimeProfile(w, debug, "threadcreate", runtime.ThreadCreateProfile)
657 }
658
659
660 func countGoroutine() int {
661 return runtime.NumGoroutine()
662 }
663
664
665 func writeGoroutine(w io.Writer, debug int) error {
666 if debug >= 2 {
667 return writeGoroutineStacks(w)
668 }
669 return writeRuntimeProfile(w, debug, "goroutine", runtime.GoroutineProfile)
670 }
671
672 func writeGoroutineStacks(w io.Writer) error {
673
674
675
676 buf := make([]byte, 1<<20)
677 for i := 0; ; i++ {
678 n := runtime.Stack(buf, true)
679 if n < len(buf) {
680 buf = buf[:n]
681 break
682 }
683 if len(buf) >= 64<<20 {
684
685 break
686 }
687 buf = make([]byte, 2*len(buf))
688 }
689 _, err := w.Write(buf)
690 return err
691 }
692
693 func writeRuntimeProfile(w io.Writer, debug int, name string, fetch func([]runtime.StackRecord) (int, bool)) error {
694
695
696
697
698
699
700 var p []runtime.StackRecord
701 n, ok := fetch(nil)
702 for {
703
704
705
706 p = make([]runtime.StackRecord, n+10)
707 n, ok = fetch(p)
708 if ok {
709 p = p[0:n]
710 break
711 }
712
713 }
714
715 return printCountProfile(w, debug, name, runtimeProfile(p))
716 }
717
718 type runtimeProfile []runtime.StackRecord
719
720 func (p runtimeProfile) Len() int { return len(p) }
721 func (p runtimeProfile) Stack(i int) []uintptr { return p[i].Stack() }
722
723 var cpu struct {
724 sync.Mutex
725 profiling bool
726 done chan bool
727 }
728
729
730
731
732
733
734
735
736
737
738
739
740 func StartCPUProfile(w io.Writer) error {
741
742
743
744
745
746
747
748
749
750 const hz = 100
751
752 cpu.Lock()
753 defer cpu.Unlock()
754 if cpu.done == nil {
755 cpu.done = make(chan bool)
756 }
757
758 if cpu.profiling {
759 return fmt.Errorf("cpu profiling already in use")
760 }
761 cpu.profiling = true
762 runtime.SetCPUProfileRate(hz)
763 go profileWriter(w)
764 return nil
765 }
766
767
768
769
770
771
772 func readProfile() (data []uint64, tags []unsafe.Pointer, eof bool)
773
774 func profileWriter(w io.Writer) {
775 b := newProfileBuilder(w)
776 var err error
777 for {
778 time.Sleep(100 * time.Millisecond)
779 data, tags, eof := readProfile()
780 if e := b.addCPUData(data, tags); e != nil && err == nil {
781 err = e
782 }
783 if eof {
784 break
785 }
786 }
787 if err != nil {
788
789
790 panic("runtime/pprof: converting profile: " + err.Error())
791 }
792 b.build()
793 cpu.done <- true
794 }
795
796
797
798
799 func StopCPUProfile() {
800 cpu.Lock()
801 defer cpu.Unlock()
802
803 if !cpu.profiling {
804 return
805 }
806 cpu.profiling = false
807 runtime.SetCPUProfileRate(0)
808 <-cpu.done
809 }
810
811
812 func countBlock() int {
813 n, _ := runtime.BlockProfile(nil)
814 return n
815 }
816
817
818 func countMutex() int {
819 n, _ := runtime.MutexProfile(nil)
820 return n
821 }
822
823
824 func writeBlock(w io.Writer, debug int) error {
825 var p []runtime.BlockProfileRecord
826 n, ok := runtime.BlockProfile(nil)
827 for {
828 p = make([]runtime.BlockProfileRecord, n+50)
829 n, ok = runtime.BlockProfile(p)
830 if ok {
831 p = p[:n]
832 break
833 }
834 }
835
836 sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })
837
838 if debug <= 0 {
839 return printCountCycleProfile(w, "contentions", "delay", scaleBlockProfile, p)
840 }
841
842 b := bufio.NewWriter(w)
843 tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
844 w = tw
845
846 fmt.Fprintf(w, "--- contention:\n")
847 fmt.Fprintf(w, "cycles/second=%v\n", runtime_cyclesPerSecond())
848 for i := range p {
849 r := &p[i]
850 fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count)
851 for _, pc := range r.Stack() {
852 fmt.Fprintf(w, " %#x", pc)
853 }
854 fmt.Fprint(w, "\n")
855 if debug > 0 {
856 printStackRecord(w, r.Stack(), true)
857 }
858 }
859
860 if tw != nil {
861 tw.Flush()
862 }
863 return b.Flush()
864 }
865
866 func scaleBlockProfile(cnt int64, ns float64) (int64, float64) {
867
868
869
870
871 return cnt, ns
872 }
873
874
875 func writeMutex(w io.Writer, debug int) error {
876
877 var p []runtime.BlockProfileRecord
878 n, ok := runtime.MutexProfile(nil)
879 for {
880 p = make([]runtime.BlockProfileRecord, n+50)
881 n, ok = runtime.MutexProfile(p)
882 if ok {
883 p = p[:n]
884 break
885 }
886 }
887
888 sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })
889
890 if debug <= 0 {
891 return printCountCycleProfile(w, "contentions", "delay", scaleMutexProfile, p)
892 }
893
894 b := bufio.NewWriter(w)
895 tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
896 w = tw
897
898 fmt.Fprintf(w, "--- mutex:\n")
899 fmt.Fprintf(w, "cycles/second=%v\n", runtime_cyclesPerSecond())
900 fmt.Fprintf(w, "sampling period=%d\n", runtime.SetMutexProfileFraction(-1))
901 for i := range p {
902 r := &p[i]
903 fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count)
904 for _, pc := range r.Stack() {
905 fmt.Fprintf(w, " %#x", pc)
906 }
907 fmt.Fprint(w, "\n")
908 if debug > 0 {
909 printStackRecord(w, r.Stack(), true)
910 }
911 }
912
913 if tw != nil {
914 tw.Flush()
915 }
916 return b.Flush()
917 }
918
919 func scaleMutexProfile(cnt int64, ns float64) (int64, float64) {
920 period := runtime.SetMutexProfileFraction(-1)
921 return cnt * int64(period), ns * float64(period)
922 }
923
924 func runtime_cyclesPerSecond() int64
925
View as plain text