Source file
src/runtime/mprof.go
1
2
3
4
5
6
7
8 package runtime
9
10 import (
11 "internal/abi"
12 "internal/goarch"
13 "internal/profilerecord"
14 "internal/runtime/atomic"
15 "internal/runtime/sys"
16 "unsafe"
17 )
18
19
20 var (
21
22 profInsertLock mutex
23
24 profBlockLock mutex
25
26 profMemActiveLock mutex
27
28
29 profMemFutureLock [len(memRecord{}.future)]mutex
30 )
31
32
33
34
35 const (
36
37 memProfile bucketType = 1 + iota
38 blockProfile
39 mutexProfile
40
41
42 buckHashSize = 179999
43
44
45
46
47
48
49
50
51
52 maxSkip = 6
53
54
55
56
57 maxProfStackDepth = 1024
58 )
59
60 type bucketType int
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 type bucket struct {
76 _ sys.NotInHeap
77 next *bucket
78 allnext *bucket
79 typ bucketType
80 hash uintptr
81 size uintptr
82 nstk uintptr
83 }
84
85
86
87 type memRecord struct {
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 active memRecordCycle
133
134
135
136
137
138
139
140
141
142
143
144 future [3]memRecordCycle
145 }
146
147
148 type memRecordCycle struct {
149 allocs, frees uintptr
150 alloc_bytes, free_bytes uintptr
151 }
152
153
154 func (a *memRecordCycle) add(b *memRecordCycle) {
155 a.allocs += b.allocs
156 a.frees += b.frees
157 a.alloc_bytes += b.alloc_bytes
158 a.free_bytes += b.free_bytes
159 }
160
161
162
163 type blockRecord struct {
164 count float64
165 cycles int64
166 }
167
168 var (
169 mbuckets atomic.UnsafePointer
170 bbuckets atomic.UnsafePointer
171 xbuckets atomic.UnsafePointer
172 buckhash atomic.UnsafePointer
173
174 mProfCycle mProfCycleHolder
175 )
176
177 type buckhashArray [buckHashSize]atomic.UnsafePointer
178
179 const mProfCycleWrap = uint32(len(memRecord{}.future)) * (2 << 24)
180
181
182
183
184
185 type mProfCycleHolder struct {
186 value atomic.Uint32
187 }
188
189
190 func (c *mProfCycleHolder) read() (cycle uint32) {
191 v := c.value.Load()
192 cycle = v >> 1
193 return cycle
194 }
195
196
197
198 func (c *mProfCycleHolder) setFlushed() (cycle uint32, alreadyFlushed bool) {
199 for {
200 prev := c.value.Load()
201 cycle = prev >> 1
202 alreadyFlushed = (prev & 0x1) != 0
203 next := prev | 0x1
204 if c.value.CompareAndSwap(prev, next) {
205 return cycle, alreadyFlushed
206 }
207 }
208 }
209
210
211
212 func (c *mProfCycleHolder) increment() {
213
214
215
216 for {
217 prev := c.value.Load()
218 cycle := prev >> 1
219 cycle = (cycle + 1) % mProfCycleWrap
220 next := cycle << 1
221 if c.value.CompareAndSwap(prev, next) {
222 break
223 }
224 }
225 }
226
227
228 func newBucket(typ bucketType, nstk int) *bucket {
229 size := unsafe.Sizeof(bucket{}) + uintptr(nstk)*unsafe.Sizeof(uintptr(0))
230 switch typ {
231 default:
232 throw("invalid profile bucket type")
233 case memProfile:
234 size += unsafe.Sizeof(memRecord{})
235 case blockProfile, mutexProfile:
236 size += unsafe.Sizeof(blockRecord{})
237 }
238
239 b := (*bucket)(persistentalloc(size, 0, &memstats.buckhash_sys))
240 b.typ = typ
241 b.nstk = uintptr(nstk)
242 return b
243 }
244
245
246
247 func (b *bucket) stk() []uintptr {
248 stk := (*[maxProfStackDepth]uintptr)(add(unsafe.Pointer(b), unsafe.Sizeof(*b)))
249 if b.nstk > maxProfStackDepth {
250
251 throw("bad profile stack count")
252 }
253 return stk[:b.nstk:b.nstk]
254 }
255
256
257 func (b *bucket) mp() *memRecord {
258 if b.typ != memProfile {
259 throw("bad use of bucket.mp")
260 }
261 data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(uintptr(0)))
262 return (*memRecord)(data)
263 }
264
265
266 func (b *bucket) bp() *blockRecord {
267 if b.typ != blockProfile && b.typ != mutexProfile {
268 throw("bad use of bucket.bp")
269 }
270 data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(uintptr(0)))
271 return (*blockRecord)(data)
272 }
273
274
275 func stkbucket(typ bucketType, size uintptr, stk []uintptr, alloc bool) *bucket {
276 bh := (*buckhashArray)(buckhash.Load())
277 if bh == nil {
278 lock(&profInsertLock)
279
280 bh = (*buckhashArray)(buckhash.Load())
281 if bh == nil {
282 bh = (*buckhashArray)(sysAlloc(unsafe.Sizeof(buckhashArray{}), &memstats.buckhash_sys, "profiler hash buckets"))
283 if bh == nil {
284 throw("runtime: cannot allocate memory")
285 }
286 buckhash.StoreNoWB(unsafe.Pointer(bh))
287 }
288 unlock(&profInsertLock)
289 }
290
291
292 var h uintptr
293 for _, pc := range stk {
294 h += pc
295 h += h << 10
296 h ^= h >> 6
297 }
298
299 h += size
300 h += h << 10
301 h ^= h >> 6
302
303 h += h << 3
304 h ^= h >> 11
305
306 i := int(h % buckHashSize)
307
308 for b := (*bucket)(bh[i].Load()); b != nil; b = b.next {
309 if b.typ == typ && b.hash == h && b.size == size && eqslice(b.stk(), stk) {
310 return b
311 }
312 }
313
314 if !alloc {
315 return nil
316 }
317
318 lock(&profInsertLock)
319
320 for b := (*bucket)(bh[i].Load()); b != nil; b = b.next {
321 if b.typ == typ && b.hash == h && b.size == size && eqslice(b.stk(), stk) {
322 unlock(&profInsertLock)
323 return b
324 }
325 }
326
327
328 b := newBucket(typ, len(stk))
329 copy(b.stk(), stk)
330 b.hash = h
331 b.size = size
332
333 var allnext *atomic.UnsafePointer
334 if typ == memProfile {
335 allnext = &mbuckets
336 } else if typ == mutexProfile {
337 allnext = &xbuckets
338 } else {
339 allnext = &bbuckets
340 }
341
342 b.next = (*bucket)(bh[i].Load())
343 b.allnext = (*bucket)(allnext.Load())
344
345 bh[i].StoreNoWB(unsafe.Pointer(b))
346 allnext.StoreNoWB(unsafe.Pointer(b))
347
348 unlock(&profInsertLock)
349 return b
350 }
351
352 func eqslice(x, y []uintptr) bool {
353 if len(x) != len(y) {
354 return false
355 }
356 for i, xi := range x {
357 if xi != y[i] {
358 return false
359 }
360 }
361 return true
362 }
363
364
365
366
367
368
369
370
371
372 func mProf_NextCycle() {
373 mProfCycle.increment()
374 }
375
376
377
378
379
380
381
382
383 func mProf_Flush() {
384 cycle, alreadyFlushed := mProfCycle.setFlushed()
385 if alreadyFlushed {
386 return
387 }
388
389 index := cycle % uint32(len(memRecord{}.future))
390 lock(&profMemActiveLock)
391 lock(&profMemFutureLock[index])
392 mProf_FlushLocked(index)
393 unlock(&profMemFutureLock[index])
394 unlock(&profMemActiveLock)
395 }
396
397
398
399
400
401 func mProf_FlushLocked(index uint32) {
402 assertLockHeld(&profMemActiveLock)
403 assertLockHeld(&profMemFutureLock[index])
404 head := (*bucket)(mbuckets.Load())
405 for b := head; b != nil; b = b.allnext {
406 mp := b.mp()
407
408
409
410 mpc := &mp.future[index]
411 mp.active.add(mpc)
412 *mpc = memRecordCycle{}
413 }
414 }
415
416
417
418
419
420 func mProf_PostSweep() {
421
422
423
424
425
426 cycle := mProfCycle.read() + 1
427
428 index := cycle % uint32(len(memRecord{}.future))
429 lock(&profMemActiveLock)
430 lock(&profMemFutureLock[index])
431 mProf_FlushLocked(index)
432 unlock(&profMemFutureLock[index])
433 unlock(&profMemActiveLock)
434 }
435
436
437 func mProf_Malloc(mp *m, p unsafe.Pointer, size uintptr) {
438 if mp.profStack == nil {
439
440
441
442
443 return
444 }
445
446
447 nstk := callers(3, mp.profStack[:debug.profstackdepth+2])
448 index := (mProfCycle.read() + 2) % uint32(len(memRecord{}.future))
449
450 b := stkbucket(memProfile, size, mp.profStack[:nstk], true)
451 mr := b.mp()
452 mpc := &mr.future[index]
453
454 lock(&profMemFutureLock[index])
455 mpc.allocs++
456 mpc.alloc_bytes += size
457 unlock(&profMemFutureLock[index])
458
459
460
461
462
463 systemstack(func() {
464 setprofilebucket(p, b)
465 })
466 }
467
468
469 func mProf_Free(b *bucket, size uintptr) {
470 index := (mProfCycle.read() + 1) % uint32(len(memRecord{}.future))
471
472 mp := b.mp()
473 mpc := &mp.future[index]
474
475 lock(&profMemFutureLock[index])
476 mpc.frees++
477 mpc.free_bytes += size
478 unlock(&profMemFutureLock[index])
479 }
480
481 var blockprofilerate uint64
482
483
484
485
486
487
488
489 func SetBlockProfileRate(rate int) {
490 var r int64
491 if rate <= 0 {
492 r = 0
493 } else if rate == 1 {
494 r = 1
495 } else {
496
497 r = int64(float64(rate) * float64(ticksPerSecond()) / (1000 * 1000 * 1000))
498 if r == 0 {
499 r = 1
500 }
501 }
502
503 atomic.Store64(&blockprofilerate, uint64(r))
504 }
505
506 func blockevent(cycles int64, skip int) {
507 if cycles <= 0 {
508 cycles = 1
509 }
510
511 rate := int64(atomic.Load64(&blockprofilerate))
512 if blocksampled(cycles, rate) {
513 saveblockevent(cycles, rate, skip+1, blockProfile)
514 }
515 }
516
517
518
519 func blocksampled(cycles, rate int64) bool {
520 if rate <= 0 || (rate > cycles && cheaprand64()%rate > cycles) {
521 return false
522 }
523 return true
524 }
525
526
527
528
529
530
531
532
533 func saveblockevent(cycles, rate int64, skip int, which bucketType) {
534 if debug.profstackdepth == 0 {
535
536
537 return
538 }
539 if skip > maxSkip {
540 print("requested skip=", skip)
541 throw("invalid skip value")
542 }
543 gp := getg()
544 mp := acquirem()
545
546 var nstk int
547 if tracefpunwindoff() || gp.m.hasCgoOnStack() {
548 if gp.m.curg == nil || gp.m.curg == gp {
549 nstk = callers(skip, mp.profStack)
550 } else {
551 nstk = gcallers(gp.m.curg, skip, mp.profStack)
552 }
553 } else {
554 if gp.m.curg == nil || gp.m.curg == gp {
555 if skip > 0 {
556
557
558
559
560
561 skip -= 1
562 }
563 nstk = fpTracebackPartialExpand(skip, unsafe.Pointer(getfp()), mp.profStack)
564 } else {
565 mp.profStack[0] = gp.m.curg.sched.pc
566 nstk = 1 + fpTracebackPartialExpand(skip, unsafe.Pointer(gp.m.curg.sched.bp), mp.profStack[1:])
567 }
568 }
569
570 saveBlockEventStack(cycles, rate, mp.profStack[:nstk], which)
571 releasem(mp)
572 }
573
574
575
576
577
578 func fpTracebackPartialExpand(skip int, fp unsafe.Pointer, pcBuf []uintptr) int {
579 var n int
580 lastFuncID := abi.FuncIDNormal
581 skipOrAdd := func(retPC uintptr) bool {
582 if skip > 0 {
583 skip--
584 } else if n < len(pcBuf) {
585 pcBuf[n] = retPC
586 n++
587 }
588 return n < len(pcBuf)
589 }
590 for n < len(pcBuf) && fp != nil {
591
592 pc := *(*uintptr)(unsafe.Pointer(uintptr(fp) + goarch.PtrSize))
593
594 if skip > 0 {
595 callPC := pc - 1
596 fi := findfunc(callPC)
597 u, uf := newInlineUnwinder(fi, callPC)
598 for ; uf.valid(); uf = u.next(uf) {
599 sf := u.srcFunc(uf)
600 if sf.funcID == abi.FuncIDWrapper && elideWrapperCalling(lastFuncID) {
601
602 } else if more := skipOrAdd(uf.pc + 1); !more {
603 return n
604 }
605 lastFuncID = sf.funcID
606 }
607 } else {
608
609
610 pcBuf[n] = pc
611 n++
612 }
613
614
615 fp = unsafe.Pointer(*(*uintptr)(fp))
616 }
617 return n
618 }
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651 type mLockProfile struct {
652 waitTime atomic.Int64
653 stack []uintptr
654 cycles int64
655 cyclesLost int64
656 haveStack bool
657 disabled bool
658 }
659
660 func (prof *mLockProfile) start() int64 {
661 if cheaprandn(gTrackingPeriod) == 0 {
662 return nanotime()
663 }
664 return 0
665 }
666
667 func (prof *mLockProfile) end(start int64) {
668 if start != 0 {
669 prof.waitTime.Add((nanotime() - start) * gTrackingPeriod)
670 }
671 }
672
673
674
675
676
677
678
679 func (prof *mLockProfile) recordUnlock(cycles int64) {
680 if cycles < 0 {
681 cycles = 0
682 }
683
684 if prof.disabled {
685
686
687
688 prof.cyclesLost += cycles
689 return
690 }
691
692 if prev := prof.cycles; prev > 0 {
693
694
695
696 if cycles == 0 {
697 return
698 }
699 prevScore := cheaprandu64() % uint64(prev)
700 thisScore := cheaprandu64() % uint64(cycles)
701 if prevScore > thisScore {
702 prof.cyclesLost += cycles
703 return
704 } else {
705 prof.cyclesLost += prev
706 }
707 }
708 prof.captureStack()
709 prof.cycles = cycles
710 }
711
712 func (prof *mLockProfile) captureStack() {
713 if debug.profstackdepth == 0 {
714
715
716 return
717 }
718
719 skip := 4
720 if staticLockRanking {
721
722
723
724
725
726
727
728
729
730 skip += 1
731 }
732 prof.haveStack = true
733
734 var nstk int
735 gp := getg()
736 sp := sys.GetCallerSP()
737 pc := sys.GetCallerPC()
738 systemstack(func() {
739 var u unwinder
740 u.initAt(pc, sp, 0, gp, unwindSilentErrors|unwindJumpStack)
741 nstk = tracebackPCs(&u, skip, prof.stack)
742 })
743 if nstk < len(prof.stack) {
744 prof.stack[nstk] = 0
745 }
746 }
747
748
749
750
751
752
753 func (prof *mLockProfile) store() {
754 if gp := getg(); gp.m.locks == 1 && gp.m.mLockProfile.haveStack {
755 prof.storeSlow()
756 }
757 }
758
759 func (prof *mLockProfile) storeSlow() {
760
761
762
763
764 mp := acquirem()
765 prof.disabled = true
766
767 nstk := int(debug.profstackdepth)
768 for i := 0; i < nstk; i++ {
769 if pc := prof.stack[i]; pc == 0 {
770 nstk = i
771 break
772 }
773 }
774
775 cycles, lost := prof.cycles, prof.cyclesLost
776 prof.cycles, prof.cyclesLost = 0, 0
777 prof.haveStack = false
778
779 rate := int64(atomic.Load64(&mutexprofilerate))
780 saveBlockEventStack(cycles, rate, prof.stack[:nstk], mutexProfile)
781 if lost > 0 {
782 lostStk := [...]uintptr{
783 abi.FuncPCABIInternal(_LostContendedRuntimeLock) + sys.PCQuantum,
784 }
785 saveBlockEventStack(lost, rate, lostStk[:], mutexProfile)
786 }
787
788 prof.disabled = false
789 releasem(mp)
790 }
791
792 func saveBlockEventStack(cycles, rate int64, stk []uintptr, which bucketType) {
793 b := stkbucket(which, 0, stk, true)
794 bp := b.bp()
795
796 lock(&profBlockLock)
797
798
799
800
801
802 if which == blockProfile && cycles < rate {
803
804 bp.count += float64(rate) / float64(cycles)
805 bp.cycles += rate
806 } else if which == mutexProfile {
807 bp.count += float64(rate)
808 bp.cycles += rate * cycles
809 } else {
810 bp.count++
811 bp.cycles += cycles
812 }
813 unlock(&profBlockLock)
814 }
815
816 var mutexprofilerate uint64
817
818
819
820
821
822
823
824
825 func SetMutexProfileFraction(rate int) int {
826 if rate < 0 {
827 return int(mutexprofilerate)
828 }
829 old := mutexprofilerate
830 atomic.Store64(&mutexprofilerate, uint64(rate))
831 return int(old)
832 }
833
834 func mutexevent(cycles int64, skip int) {
835 if cycles < 0 {
836 cycles = 0
837 }
838 rate := int64(atomic.Load64(&mutexprofilerate))
839 if rate > 0 && cheaprand64()%rate == 0 {
840 saveblockevent(cycles, rate, skip+1, mutexProfile)
841 }
842 }
843
844
845
846
847 type StackRecord struct {
848 Stack0 [32]uintptr
849 }
850
851
852
853 func (r *StackRecord) Stack() []uintptr {
854 for i, v := range r.Stack0 {
855 if v == 0 {
856 return r.Stack0[0:i]
857 }
858 }
859 return r.Stack0[0:]
860 }
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876 var MemProfileRate int = 512 * 1024
877
878
879
880
881
882 var disableMemoryProfiling bool
883
884
885
886 type MemProfileRecord struct {
887 AllocBytes, FreeBytes int64
888 AllocObjects, FreeObjects int64
889 Stack0 [32]uintptr
890 }
891
892
893 func (r *MemProfileRecord) InUseBytes() int64 { return r.AllocBytes - r.FreeBytes }
894
895
896 func (r *MemProfileRecord) InUseObjects() int64 {
897 return r.AllocObjects - r.FreeObjects
898 }
899
900
901
902 func (r *MemProfileRecord) Stack() []uintptr {
903 for i, v := range r.Stack0 {
904 if v == 0 {
905 return r.Stack0[0:i]
906 }
907 }
908 return r.Stack0[0:]
909 }
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932 func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
933 return memProfileInternal(len(p), inuseZero, func(r profilerecord.MemProfileRecord) {
934 copyMemProfileRecord(&p[0], r)
935 p = p[1:]
936 })
937 }
938
939
940
941
942
943
944
945
946
947
948
949 func memProfileInternal(size int, inuseZero bool, copyFn func(profilerecord.MemProfileRecord)) (n int, ok bool) {
950 cycle := mProfCycle.read()
951
952
953
954 index := cycle % uint32(len(memRecord{}.future))
955 lock(&profMemActiveLock)
956 lock(&profMemFutureLock[index])
957 mProf_FlushLocked(index)
958 unlock(&profMemFutureLock[index])
959 clear := true
960 head := (*bucket)(mbuckets.Load())
961 for b := head; b != nil; b = b.allnext {
962 mp := b.mp()
963 if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes {
964 n++
965 }
966 if mp.active.allocs != 0 || mp.active.frees != 0 {
967 clear = false
968 }
969 }
970 if clear {
971
972
973
974
975 n = 0
976 for b := head; b != nil; b = b.allnext {
977 mp := b.mp()
978 for c := range mp.future {
979 lock(&profMemFutureLock[c])
980 mp.active.add(&mp.future[c])
981 mp.future[c] = memRecordCycle{}
982 unlock(&profMemFutureLock[c])
983 }
984 if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes {
985 n++
986 }
987 }
988 }
989 if n <= size {
990 ok = true
991 for b := head; b != nil; b = b.allnext {
992 mp := b.mp()
993 if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes {
994 r := profilerecord.MemProfileRecord{
995 AllocBytes: int64(mp.active.alloc_bytes),
996 FreeBytes: int64(mp.active.free_bytes),
997 AllocObjects: int64(mp.active.allocs),
998 FreeObjects: int64(mp.active.frees),
999 Stack: b.stk(),
1000 }
1001 copyFn(r)
1002 }
1003 }
1004 }
1005 unlock(&profMemActiveLock)
1006 return
1007 }
1008
1009 func copyMemProfileRecord(dst *MemProfileRecord, src profilerecord.MemProfileRecord) {
1010 dst.AllocBytes = src.AllocBytes
1011 dst.FreeBytes = src.FreeBytes
1012 dst.AllocObjects = src.AllocObjects
1013 dst.FreeObjects = src.FreeObjects
1014 if raceenabled {
1015 racewriterangepc(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0), sys.GetCallerPC(), abi.FuncPCABIInternal(MemProfile))
1016 }
1017 if msanenabled {
1018 msanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1019 }
1020 if asanenabled {
1021 asanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1022 }
1023 i := copy(dst.Stack0[:], src.Stack)
1024 clear(dst.Stack0[i:])
1025 }
1026
1027
1028 func pprof_memProfileInternal(p []profilerecord.MemProfileRecord, inuseZero bool) (n int, ok bool) {
1029 return memProfileInternal(len(p), inuseZero, func(r profilerecord.MemProfileRecord) {
1030 p[0] = r
1031 p = p[1:]
1032 })
1033 }
1034
1035 func iterate_memprof(fn func(*bucket, uintptr, *uintptr, uintptr, uintptr, uintptr)) {
1036 lock(&profMemActiveLock)
1037 head := (*bucket)(mbuckets.Load())
1038 for b := head; b != nil; b = b.allnext {
1039 mp := b.mp()
1040 fn(b, b.nstk, &b.stk()[0], b.size, mp.active.allocs, mp.active.frees)
1041 }
1042 unlock(&profMemActiveLock)
1043 }
1044
1045
1046
1047 type BlockProfileRecord struct {
1048 Count int64
1049 Cycles int64
1050 StackRecord
1051 }
1052
1053
1054
1055
1056
1057
1058
1059
1060 func BlockProfile(p []BlockProfileRecord) (n int, ok bool) {
1061 var m int
1062 n, ok = blockProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1063 copyBlockProfileRecord(&p[m], r)
1064 m++
1065 })
1066 if ok {
1067 expandFrames(p[:n])
1068 }
1069 return
1070 }
1071
1072 func expandFrames(p []BlockProfileRecord) {
1073 expandedStack := makeProfStack()
1074 for i := range p {
1075 cf := CallersFrames(p[i].Stack())
1076 j := 0
1077 for j < len(expandedStack) {
1078 f, more := cf.Next()
1079
1080
1081 expandedStack[j] = f.PC + 1
1082 j++
1083 if !more {
1084 break
1085 }
1086 }
1087 k := copy(p[i].Stack0[:], expandedStack[:j])
1088 clear(p[i].Stack0[k:])
1089 }
1090 }
1091
1092
1093
1094
1095 func blockProfileInternal(size int, copyFn func(profilerecord.BlockProfileRecord)) (n int, ok bool) {
1096 lock(&profBlockLock)
1097 head := (*bucket)(bbuckets.Load())
1098 for b := head; b != nil; b = b.allnext {
1099 n++
1100 }
1101 if n <= size {
1102 ok = true
1103 for b := head; b != nil; b = b.allnext {
1104 bp := b.bp()
1105 r := profilerecord.BlockProfileRecord{
1106 Count: int64(bp.count),
1107 Cycles: bp.cycles,
1108 Stack: b.stk(),
1109 }
1110
1111
1112 if r.Count == 0 {
1113 r.Count = 1
1114 }
1115 copyFn(r)
1116 }
1117 }
1118 unlock(&profBlockLock)
1119 return
1120 }
1121
1122
1123
1124
1125 func copyBlockProfileRecord(dst *BlockProfileRecord, src profilerecord.BlockProfileRecord) {
1126 dst.Count = src.Count
1127 dst.Cycles = src.Cycles
1128 if raceenabled {
1129 racewriterangepc(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0), sys.GetCallerPC(), abi.FuncPCABIInternal(BlockProfile))
1130 }
1131 if msanenabled {
1132 msanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1133 }
1134 if asanenabled {
1135 asanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1136 }
1137
1138
1139
1140
1141 i := copy(dst.Stack0[:], src.Stack)
1142 clear(dst.Stack0[i:])
1143 }
1144
1145
1146 func pprof_blockProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok bool) {
1147 return blockProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1148 p[0] = r
1149 p = p[1:]
1150 })
1151 }
1152
1153
1154
1155
1156
1157
1158
1159 func MutexProfile(p []BlockProfileRecord) (n int, ok bool) {
1160 var m int
1161 n, ok = mutexProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1162 copyBlockProfileRecord(&p[m], r)
1163 m++
1164 })
1165 if ok {
1166 expandFrames(p[:n])
1167 }
1168 return
1169 }
1170
1171
1172
1173
1174 func mutexProfileInternal(size int, copyFn func(profilerecord.BlockProfileRecord)) (n int, ok bool) {
1175 lock(&profBlockLock)
1176 head := (*bucket)(xbuckets.Load())
1177 for b := head; b != nil; b = b.allnext {
1178 n++
1179 }
1180 if n <= size {
1181 ok = true
1182 for b := head; b != nil; b = b.allnext {
1183 bp := b.bp()
1184 r := profilerecord.BlockProfileRecord{
1185 Count: int64(bp.count),
1186 Cycles: bp.cycles,
1187 Stack: b.stk(),
1188 }
1189 copyFn(r)
1190 }
1191 }
1192 unlock(&profBlockLock)
1193 return
1194 }
1195
1196
1197 func pprof_mutexProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok bool) {
1198 return mutexProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1199 p[0] = r
1200 p = p[1:]
1201 })
1202 }
1203
1204
1205
1206
1207
1208
1209
1210 func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
1211 return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
1212 i := copy(p[0].Stack0[:], r.Stack)
1213 clear(p[0].Stack0[i:])
1214 p = p[1:]
1215 })
1216 }
1217
1218
1219
1220
1221 func threadCreateProfileInternal(size int, copyFn func(profilerecord.StackRecord)) (n int, ok bool) {
1222 first := (*m)(atomic.Loadp(unsafe.Pointer(&allm)))
1223 for mp := first; mp != nil; mp = mp.alllink {
1224 n++
1225 }
1226 if n <= size {
1227 ok = true
1228 for mp := first; mp != nil; mp = mp.alllink {
1229 r := profilerecord.StackRecord{Stack: mp.createstack[:]}
1230 copyFn(r)
1231 }
1232 }
1233 return
1234 }
1235
1236
1237 func pprof_threadCreateInternal(p []profilerecord.StackRecord) (n int, ok bool) {
1238 return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
1239 p[0] = r
1240 p = p[1:]
1241 })
1242 }
1243
1244
1245 func pprof_goroutineProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1246 return goroutineProfileWithLabels(p, labels)
1247 }
1248
1249
1250 func goroutineProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1251 if labels != nil && len(labels) != len(p) {
1252 labels = nil
1253 }
1254
1255 return goroutineProfileWithLabelsConcurrent(p, labels)
1256 }
1257
1258
1259 func pprof_goroutineLeakProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1260 return goroutineLeakProfileWithLabels(p, labels)
1261 }
1262
1263
1264 func goroutineLeakProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1265 if labels != nil && len(labels) != len(p) {
1266 labels = nil
1267 }
1268
1269 return goroutineLeakProfileWithLabelsConcurrent(p, labels)
1270 }
1271
1272 var goroutineProfile = struct {
1273 sema uint32
1274 active bool
1275 offset atomic.Int64
1276 records []profilerecord.StackRecord
1277 labels []unsafe.Pointer
1278 }{
1279 sema: 1,
1280 }
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293 type goroutineProfileState uint32
1294
1295 const (
1296 goroutineProfileAbsent goroutineProfileState = iota
1297 goroutineProfileInProgress
1298 goroutineProfileSatisfied
1299 )
1300
1301 type goroutineProfileStateHolder atomic.Uint32
1302
1303 func (p *goroutineProfileStateHolder) Load() goroutineProfileState {
1304 return goroutineProfileState((*atomic.Uint32)(p).Load())
1305 }
1306
1307 func (p *goroutineProfileStateHolder) Store(value goroutineProfileState) {
1308 (*atomic.Uint32)(p).Store(uint32(value))
1309 }
1310
1311 func (p *goroutineProfileStateHolder) CompareAndSwap(old, new goroutineProfileState) bool {
1312 return (*atomic.Uint32)(p).CompareAndSwap(uint32(old), uint32(new))
1313 }
1314
1315 func goroutineLeakProfileWithLabelsConcurrent(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1316 if len(p) == 0 {
1317
1318
1319 return work.goroutineLeak.count, false
1320 }
1321
1322 pcbuf := makeProfStack()
1323
1324
1325 n = work.goroutineLeak.count
1326
1327 if n > len(p) {
1328
1329
1330 return n, false
1331 }
1332
1333
1334 var offset int
1335 forEachGRace(func(gp1 *g) {
1336 if readgstatus(gp1)&^_Gscan == _Gleaked {
1337 systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &p[offset], pcbuf) })
1338 if labels != nil {
1339 labels[offset] = gp1.labels
1340 }
1341 offset++
1342 }
1343 })
1344
1345 if raceenabled {
1346 raceacquire(unsafe.Pointer(&labelSync))
1347 }
1348
1349 return n, true
1350 }
1351
1352 func goroutineProfileWithLabelsConcurrent(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1353 if len(p) == 0 {
1354
1355
1356
1357
1358 return int(gcount(false)), false
1359 }
1360
1361 semacquire(&goroutineProfile.sema)
1362
1363 ourg := getg()
1364
1365 pcbuf := makeProfStack()
1366 stw := stopTheWorld(stwGoroutineProfile)
1367
1368
1369
1370
1371
1372
1373
1374 n = int(gcount(false))
1375 if fingStatus.Load()&fingRunningFinalizer != 0 {
1376 n++
1377 }
1378 n += int(gcCleanups.running.Load())
1379
1380 if n > len(p) {
1381
1382
1383
1384 startTheWorld(stw)
1385 semrelease(&goroutineProfile.sema)
1386 return n, false
1387 }
1388
1389
1390 sp := sys.GetCallerSP()
1391 pc := sys.GetCallerPC()
1392 systemstack(func() {
1393 saveg(pc, sp, ourg, &p[0], pcbuf)
1394 })
1395 if labels != nil {
1396 labels[0] = ourg.labels
1397 }
1398 ourg.goroutineProfiled.Store(goroutineProfileSatisfied)
1399 goroutineProfile.offset.Store(1)
1400
1401
1402
1403
1404
1405
1406 goroutineProfile.active = true
1407 goroutineProfile.records = p
1408 goroutineProfile.labels = labels
1409 startTheWorld(stw)
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422 forEachGRace(func(gp1 *g) {
1423 tryRecordGoroutineProfile(gp1, pcbuf, Gosched)
1424 })
1425
1426 stw = stopTheWorld(stwGoroutineProfileCleanup)
1427 endOffset := goroutineProfile.offset.Swap(0)
1428 goroutineProfile.active = false
1429 goroutineProfile.records = nil
1430 goroutineProfile.labels = nil
1431 startTheWorld(stw)
1432
1433
1434
1435 forEachGRace(func(gp1 *g) {
1436 gp1.goroutineProfiled.Store(goroutineProfileAbsent)
1437 })
1438
1439 if raceenabled {
1440 raceacquire(unsafe.Pointer(&labelSync))
1441 }
1442
1443 if n != int(endOffset) {
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453 }
1454
1455 semrelease(&goroutineProfile.sema)
1456 return n, true
1457 }
1458
1459
1460
1461
1462
1463 func tryRecordGoroutineProfileWB(gp1 *g) {
1464 if getg().m.p.ptr() == nil {
1465 throw("no P available, write barriers are forbidden")
1466 }
1467 tryRecordGoroutineProfile(gp1, nil, osyield)
1468 }
1469
1470
1471
1472
1473 func tryRecordGoroutineProfile(gp1 *g, pcbuf []uintptr, yield func()) {
1474 if status := readgstatus(gp1); status == _Gdead || status == _Gdeadextra {
1475
1476
1477
1478
1479 return
1480 }
1481
1482 for {
1483 prev := gp1.goroutineProfiled.Load()
1484 if prev == goroutineProfileSatisfied {
1485
1486
1487 break
1488 }
1489 if prev == goroutineProfileInProgress {
1490
1491
1492 yield()
1493 continue
1494 }
1495
1496
1497
1498
1499
1500
1501 mp := acquirem()
1502 if gp1.goroutineProfiled.CompareAndSwap(goroutineProfileAbsent, goroutineProfileInProgress) {
1503 doRecordGoroutineProfile(gp1, pcbuf)
1504 gp1.goroutineProfiled.Store(goroutineProfileSatisfied)
1505 }
1506 releasem(mp)
1507 }
1508 }
1509
1510
1511
1512
1513
1514
1515
1516
1517 func doRecordGoroutineProfile(gp1 *g, pcbuf []uintptr) {
1518 if isSystemGoroutine(gp1, false) {
1519
1520
1521
1522
1523
1524
1525
1526
1527 return
1528 }
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540 if readgstatus(gp1) == _Grunning && gp1.syscallsp == 0 {
1541 print("doRecordGoroutineProfile gp1=", gp1.goid, "\n")
1542 throw("cannot read stack of running goroutine")
1543 }
1544
1545 offset := int(goroutineProfile.offset.Add(1)) - 1
1546
1547 if offset >= len(goroutineProfile.records) {
1548
1549
1550
1551 return
1552 }
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562 systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &goroutineProfile.records[offset], pcbuf) })
1563
1564 if goroutineProfile.labels != nil {
1565 goroutineProfile.labels[offset] = gp1.labels
1566 }
1567 }
1568
1569 func goroutineProfileWithLabelsSync(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1570 gp := getg()
1571
1572 isOK := func(gp1 *g) bool {
1573
1574
1575 if gp1 == gp {
1576 return false
1577 }
1578 if status := readgstatus(gp1); status == _Gdead || status == _Gdeadextra {
1579 return false
1580 }
1581 if isSystemGoroutine(gp1, false) {
1582 return false
1583 }
1584 return true
1585 }
1586
1587 pcbuf := makeProfStack()
1588 stw := stopTheWorld(stwGoroutineProfile)
1589
1590
1591 n = 1
1592 forEachGRace(func(gp1 *g) {
1593 if isOK(gp1) {
1594 n++
1595 }
1596 })
1597
1598 if n <= len(p) {
1599 ok = true
1600 r, lbl := p, labels
1601
1602
1603 sp := sys.GetCallerSP()
1604 pc := sys.GetCallerPC()
1605 systemstack(func() {
1606 saveg(pc, sp, gp, &r[0], pcbuf)
1607 })
1608 r = r[1:]
1609
1610
1611 if labels != nil {
1612 lbl[0] = gp.labels
1613 lbl = lbl[1:]
1614 }
1615
1616
1617 forEachGRace(func(gp1 *g) {
1618 if !isOK(gp1) {
1619 return
1620 }
1621
1622 if len(r) == 0 {
1623
1624
1625 return
1626 }
1627
1628
1629
1630
1631 systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &r[0], pcbuf) })
1632 if labels != nil {
1633 lbl[0] = gp1.labels
1634 lbl = lbl[1:]
1635 }
1636 r = r[1:]
1637 })
1638 }
1639
1640 if raceenabled {
1641 raceacquire(unsafe.Pointer(&labelSync))
1642 }
1643
1644 startTheWorld(stw)
1645 return n, ok
1646 }
1647
1648
1649
1650
1651
1652
1653
1654 func GoroutineProfile(p []StackRecord) (n int, ok bool) {
1655 records := make([]profilerecord.StackRecord, len(p))
1656 n, ok = goroutineProfileInternal(records)
1657 if !ok {
1658 return
1659 }
1660 for i, mr := range records[0:n] {
1661 l := copy(p[i].Stack0[:], mr.Stack)
1662 clear(p[i].Stack0[l:])
1663 }
1664 return
1665 }
1666
1667 func goroutineProfileInternal(p []profilerecord.StackRecord) (n int, ok bool) {
1668 return goroutineProfileWithLabels(p, nil)
1669 }
1670
1671 func saveg(pc, sp uintptr, gp *g, r *profilerecord.StackRecord, pcbuf []uintptr) {
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682 if pcbuf == nil {
1683 pcbuf = makeProfStack()
1684 }
1685
1686 var u unwinder
1687 u.initAt(pc, sp, 0, gp, unwindSilentErrors)
1688 n := tracebackPCs(&u, 0, pcbuf)
1689 r.Stack = make([]uintptr, n)
1690 copy(r.Stack, pcbuf)
1691 }
1692
1693
1694
1695
1696
1697 func Stack(buf []byte, all bool) int {
1698 var stw worldStop
1699 if all {
1700 stw = stopTheWorld(stwAllGoroutinesStack)
1701 }
1702
1703 n := 0
1704 if len(buf) > 0 {
1705 gp := getg()
1706 sp := sys.GetCallerSP()
1707 pc := sys.GetCallerPC()
1708 systemstack(func() {
1709 g0 := getg()
1710
1711
1712
1713 g0.m.traceback = 1
1714 g0.writebuf = buf[0:0:len(buf)]
1715 goroutineheader(gp)
1716 traceback(pc, sp, 0, gp)
1717 if all {
1718 tracebackothers(gp)
1719 }
1720 g0.m.traceback = 0
1721 n = len(g0.writebuf)
1722 g0.writebuf = nil
1723 })
1724 }
1725
1726 if all {
1727 startTheWorld(stw)
1728 }
1729 return n
1730 }
1731
View as plain text