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 = 5
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))
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(5, mp.profStack[:debug.profstackdepth])
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
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678 type lockTimer struct {
679 lock *mutex
680 timeRate int64
681 timeStart int64
682 tickStart int64
683 }
684
685 func (lt *lockTimer) begin() {
686 rate := int64(atomic.Load64(&mutexprofilerate))
687
688 lt.timeRate = gTrackingPeriod
689 if rate != 0 && rate < lt.timeRate {
690 lt.timeRate = rate
691 }
692 if int64(cheaprand())%lt.timeRate == 0 {
693 lt.timeStart = nanotime()
694 }
695
696 if rate > 0 && int64(cheaprand())%rate == 0 {
697 lt.tickStart = cputicks()
698 }
699 }
700
701 func (lt *lockTimer) end() {
702 gp := getg()
703
704 if lt.timeStart != 0 {
705 nowTime := nanotime()
706 gp.m.mLockProfile.waitTime.Add((nowTime - lt.timeStart) * lt.timeRate)
707 }
708
709 if lt.tickStart != 0 {
710 nowTick := cputicks()
711 gp.m.mLockProfile.recordLock(nowTick-lt.tickStart, lt.lock)
712 }
713 }
714
715 type mLockProfile struct {
716 waitTime atomic.Int64
717 stack []uintptr
718 pending uintptr
719 cycles int64
720 cyclesLost int64
721 haveStack bool
722 disabled bool
723 }
724
725 func (prof *mLockProfile) recordLock(cycles int64, l *mutex) {
726 if cycles < 0 {
727 cycles = 0
728 }
729
730 if prof.disabled {
731
732
733
734 prof.cyclesLost += cycles
735 return
736 }
737
738 if uintptr(unsafe.Pointer(l)) == prof.pending {
739
740
741 prof.cycles += cycles
742 return
743 }
744
745 if prev := prof.cycles; prev > 0 {
746
747
748
749 if cycles == 0 {
750 return
751 }
752 prevScore := uint64(cheaprand64()) % uint64(prev)
753 thisScore := uint64(cheaprand64()) % uint64(cycles)
754 if prevScore > thisScore {
755 prof.cyclesLost += cycles
756 return
757 } else {
758 prof.cyclesLost += prev
759 }
760 }
761
762
763
764
765 prof.pending = uintptr(unsafe.Pointer(l))
766 prof.cycles = cycles
767 }
768
769
770
771
772 func (prof *mLockProfile) recordUnlock(l *mutex) {
773 if uintptr(unsafe.Pointer(l)) == prof.pending {
774 prof.captureStack()
775 }
776 if gp := getg(); gp.m.locks == 1 && gp.m.mLockProfile.haveStack {
777 prof.store()
778 }
779 }
780
781 func (prof *mLockProfile) captureStack() {
782 if debug.profstackdepth == 0 {
783
784
785 return
786 }
787
788 skip := 3
789 if staticLockRanking {
790
791
792
793
794
795
796
797
798
799 skip += 1
800 }
801 prof.pending = 0
802 prof.haveStack = true
803
804 prof.stack[0] = logicalStackSentinel
805 if debug.runtimeContentionStacks.Load() == 0 {
806 prof.stack[1] = abi.FuncPCABIInternal(_LostContendedRuntimeLock) + sys.PCQuantum
807 prof.stack[2] = 0
808 return
809 }
810
811 var nstk int
812 gp := getg()
813 sp := sys.GetCallerSP()
814 pc := sys.GetCallerPC()
815 systemstack(func() {
816 var u unwinder
817 u.initAt(pc, sp, 0, gp, unwindSilentErrors|unwindJumpStack)
818 nstk = 1 + tracebackPCs(&u, skip, prof.stack[1:])
819 })
820 if nstk < len(prof.stack) {
821 prof.stack[nstk] = 0
822 }
823 }
824
825 func (prof *mLockProfile) store() {
826
827
828
829
830 mp := acquirem()
831 prof.disabled = true
832
833 nstk := int(debug.profstackdepth)
834 for i := 0; i < nstk; i++ {
835 if pc := prof.stack[i]; pc == 0 {
836 nstk = i
837 break
838 }
839 }
840
841 cycles, lost := prof.cycles, prof.cyclesLost
842 prof.cycles, prof.cyclesLost = 0, 0
843 prof.haveStack = false
844
845 rate := int64(atomic.Load64(&mutexprofilerate))
846 saveBlockEventStack(cycles, rate, prof.stack[:nstk], mutexProfile)
847 if lost > 0 {
848 lostStk := [...]uintptr{
849 logicalStackSentinel,
850 abi.FuncPCABIInternal(_LostContendedRuntimeLock) + sys.PCQuantum,
851 }
852 saveBlockEventStack(lost, rate, lostStk[:], mutexProfile)
853 }
854
855 prof.disabled = false
856 releasem(mp)
857 }
858
859 func saveBlockEventStack(cycles, rate int64, stk []uintptr, which bucketType) {
860 b := stkbucket(which, 0, stk, true)
861 bp := b.bp()
862
863 lock(&profBlockLock)
864
865
866
867
868
869 if which == blockProfile && cycles < rate {
870
871 bp.count += float64(rate) / float64(cycles)
872 bp.cycles += rate
873 } else if which == mutexProfile {
874 bp.count += float64(rate)
875 bp.cycles += rate * cycles
876 } else {
877 bp.count++
878 bp.cycles += cycles
879 }
880 unlock(&profBlockLock)
881 }
882
883 var mutexprofilerate uint64
884
885
886
887
888
889
890
891
892 func SetMutexProfileFraction(rate int) int {
893 if rate < 0 {
894 return int(mutexprofilerate)
895 }
896 old := mutexprofilerate
897 atomic.Store64(&mutexprofilerate, uint64(rate))
898 return int(old)
899 }
900
901
902 func mutexevent(cycles int64, skip int) {
903 if cycles < 0 {
904 cycles = 0
905 }
906 rate := int64(atomic.Load64(&mutexprofilerate))
907 if rate > 0 && cheaprand64()%rate == 0 {
908 saveblockevent(cycles, rate, skip+1, mutexProfile)
909 }
910 }
911
912
913
914
915 type StackRecord struct {
916 Stack0 [32]uintptr
917 }
918
919
920
921 func (r *StackRecord) Stack() []uintptr {
922 for i, v := range r.Stack0 {
923 if v == 0 {
924 return r.Stack0[0:i]
925 }
926 }
927 return r.Stack0[0:]
928 }
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944 var MemProfileRate int = 512 * 1024
945
946
947
948
949
950 var disableMemoryProfiling bool
951
952
953
954 type MemProfileRecord struct {
955 AllocBytes, FreeBytes int64
956 AllocObjects, FreeObjects int64
957 Stack0 [32]uintptr
958 }
959
960
961 func (r *MemProfileRecord) InUseBytes() int64 { return r.AllocBytes - r.FreeBytes }
962
963
964 func (r *MemProfileRecord) InUseObjects() int64 {
965 return r.AllocObjects - r.FreeObjects
966 }
967
968
969
970 func (r *MemProfileRecord) Stack() []uintptr {
971 for i, v := range r.Stack0 {
972 if v == 0 {
973 return r.Stack0[0:i]
974 }
975 }
976 return r.Stack0[0:]
977 }
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000 func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
1001 return memProfileInternal(len(p), inuseZero, func(r profilerecord.MemProfileRecord) {
1002 copyMemProfileRecord(&p[0], r)
1003 p = p[1:]
1004 })
1005 }
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017 func memProfileInternal(size int, inuseZero bool, copyFn func(profilerecord.MemProfileRecord)) (n int, ok bool) {
1018 cycle := mProfCycle.read()
1019
1020
1021
1022 index := cycle % uint32(len(memRecord{}.future))
1023 lock(&profMemActiveLock)
1024 lock(&profMemFutureLock[index])
1025 mProf_FlushLocked(index)
1026 unlock(&profMemFutureLock[index])
1027 clear := true
1028 head := (*bucket)(mbuckets.Load())
1029 for b := head; b != nil; b = b.allnext {
1030 mp := b.mp()
1031 if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes {
1032 n++
1033 }
1034 if mp.active.allocs != 0 || mp.active.frees != 0 {
1035 clear = false
1036 }
1037 }
1038 if clear {
1039
1040
1041
1042
1043 n = 0
1044 for b := head; b != nil; b = b.allnext {
1045 mp := b.mp()
1046 for c := range mp.future {
1047 lock(&profMemFutureLock[c])
1048 mp.active.add(&mp.future[c])
1049 mp.future[c] = memRecordCycle{}
1050 unlock(&profMemFutureLock[c])
1051 }
1052 if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes {
1053 n++
1054 }
1055 }
1056 }
1057 if n <= size {
1058 ok = true
1059 for b := head; b != nil; b = b.allnext {
1060 mp := b.mp()
1061 if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes {
1062 r := profilerecord.MemProfileRecord{
1063 AllocBytes: int64(mp.active.alloc_bytes),
1064 FreeBytes: int64(mp.active.free_bytes),
1065 AllocObjects: int64(mp.active.allocs),
1066 FreeObjects: int64(mp.active.frees),
1067 Stack: b.stk(),
1068 }
1069 copyFn(r)
1070 }
1071 }
1072 }
1073 unlock(&profMemActiveLock)
1074 return
1075 }
1076
1077 func copyMemProfileRecord(dst *MemProfileRecord, src profilerecord.MemProfileRecord) {
1078 dst.AllocBytes = src.AllocBytes
1079 dst.FreeBytes = src.FreeBytes
1080 dst.AllocObjects = src.AllocObjects
1081 dst.FreeObjects = src.FreeObjects
1082 if raceenabled {
1083 racewriterangepc(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0), sys.GetCallerPC(), abi.FuncPCABIInternal(MemProfile))
1084 }
1085 if msanenabled {
1086 msanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1087 }
1088 if asanenabled {
1089 asanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1090 }
1091 i := copy(dst.Stack0[:], src.Stack)
1092 clear(dst.Stack0[i:])
1093 }
1094
1095
1096 func pprof_memProfileInternal(p []profilerecord.MemProfileRecord, inuseZero bool) (n int, ok bool) {
1097 return memProfileInternal(len(p), inuseZero, func(r profilerecord.MemProfileRecord) {
1098 p[0] = r
1099 p = p[1:]
1100 })
1101 }
1102
1103 func iterate_memprof(fn func(*bucket, uintptr, *uintptr, uintptr, uintptr, uintptr)) {
1104 lock(&profMemActiveLock)
1105 head := (*bucket)(mbuckets.Load())
1106 for b := head; b != nil; b = b.allnext {
1107 mp := b.mp()
1108 fn(b, b.nstk, &b.stk()[0], b.size, mp.active.allocs, mp.active.frees)
1109 }
1110 unlock(&profMemActiveLock)
1111 }
1112
1113
1114
1115 type BlockProfileRecord struct {
1116 Count int64
1117 Cycles int64
1118 StackRecord
1119 }
1120
1121
1122
1123
1124
1125
1126
1127
1128 func BlockProfile(p []BlockProfileRecord) (n int, ok bool) {
1129 var m int
1130 n, ok = blockProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1131 copyBlockProfileRecord(&p[m], r)
1132 m++
1133 })
1134 if ok {
1135 expandFrames(p[:n])
1136 }
1137 return
1138 }
1139
1140 func expandFrames(p []BlockProfileRecord) {
1141 expandedStack := makeProfStack()
1142 for i := range p {
1143 cf := CallersFrames(p[i].Stack())
1144 j := 0
1145 for j < len(expandedStack) {
1146 f, more := cf.Next()
1147
1148
1149 expandedStack[j] = f.PC + 1
1150 j++
1151 if !more {
1152 break
1153 }
1154 }
1155 k := copy(p[i].Stack0[:], expandedStack[:j])
1156 clear(p[i].Stack0[k:])
1157 }
1158 }
1159
1160
1161
1162
1163 func blockProfileInternal(size int, copyFn func(profilerecord.BlockProfileRecord)) (n int, ok bool) {
1164 lock(&profBlockLock)
1165 head := (*bucket)(bbuckets.Load())
1166 for b := head; b != nil; b = b.allnext {
1167 n++
1168 }
1169 if n <= size {
1170 ok = true
1171 for b := head; b != nil; b = b.allnext {
1172 bp := b.bp()
1173 r := profilerecord.BlockProfileRecord{
1174 Count: int64(bp.count),
1175 Cycles: bp.cycles,
1176 Stack: b.stk(),
1177 }
1178
1179
1180 if r.Count == 0 {
1181 r.Count = 1
1182 }
1183 copyFn(r)
1184 }
1185 }
1186 unlock(&profBlockLock)
1187 return
1188 }
1189
1190
1191
1192
1193 func copyBlockProfileRecord(dst *BlockProfileRecord, src profilerecord.BlockProfileRecord) {
1194 dst.Count = src.Count
1195 dst.Cycles = src.Cycles
1196 if raceenabled {
1197 racewriterangepc(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0), sys.GetCallerPC(), abi.FuncPCABIInternal(BlockProfile))
1198 }
1199 if msanenabled {
1200 msanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1201 }
1202 if asanenabled {
1203 asanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0))
1204 }
1205
1206
1207
1208
1209 i := copy(dst.Stack0[:], src.Stack)
1210 clear(dst.Stack0[i:])
1211 }
1212
1213
1214 func pprof_blockProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok bool) {
1215 return blockProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1216 p[0] = r
1217 p = p[1:]
1218 })
1219 }
1220
1221
1222
1223
1224
1225
1226
1227 func MutexProfile(p []BlockProfileRecord) (n int, ok bool) {
1228 var m int
1229 n, ok = mutexProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1230 copyBlockProfileRecord(&p[m], r)
1231 m++
1232 })
1233 if ok {
1234 expandFrames(p[:n])
1235 }
1236 return
1237 }
1238
1239
1240
1241
1242 func mutexProfileInternal(size int, copyFn func(profilerecord.BlockProfileRecord)) (n int, ok bool) {
1243 lock(&profBlockLock)
1244 head := (*bucket)(xbuckets.Load())
1245 for b := head; b != nil; b = b.allnext {
1246 n++
1247 }
1248 if n <= size {
1249 ok = true
1250 for b := head; b != nil; b = b.allnext {
1251 bp := b.bp()
1252 r := profilerecord.BlockProfileRecord{
1253 Count: int64(bp.count),
1254 Cycles: bp.cycles,
1255 Stack: b.stk(),
1256 }
1257 copyFn(r)
1258 }
1259 }
1260 unlock(&profBlockLock)
1261 return
1262 }
1263
1264
1265 func pprof_mutexProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok bool) {
1266 return mutexProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) {
1267 p[0] = r
1268 p = p[1:]
1269 })
1270 }
1271
1272
1273
1274
1275
1276
1277
1278 func ThreadCreateProfile(p []StackRecord) (n int, ok bool) {
1279 return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
1280 i := copy(p[0].Stack0[:], r.Stack)
1281 clear(p[0].Stack0[i:])
1282 p = p[1:]
1283 })
1284 }
1285
1286
1287
1288
1289 func threadCreateProfileInternal(size int, copyFn func(profilerecord.StackRecord)) (n int, ok bool) {
1290 first := (*m)(atomic.Loadp(unsafe.Pointer(&allm)))
1291 for mp := first; mp != nil; mp = mp.alllink {
1292 n++
1293 }
1294 if n <= size {
1295 ok = true
1296 for mp := first; mp != nil; mp = mp.alllink {
1297 r := profilerecord.StackRecord{Stack: mp.createstack[:]}
1298 copyFn(r)
1299 }
1300 }
1301 return
1302 }
1303
1304
1305 func pprof_threadCreateInternal(p []profilerecord.StackRecord) (n int, ok bool) {
1306 return threadCreateProfileInternal(len(p), func(r profilerecord.StackRecord) {
1307 p[0] = r
1308 p = p[1:]
1309 })
1310 }
1311
1312
1313 func pprof_goroutineProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1314 return goroutineProfileWithLabels(p, labels)
1315 }
1316
1317
1318 func goroutineProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1319 if labels != nil && len(labels) != len(p) {
1320 labels = nil
1321 }
1322
1323 return goroutineProfileWithLabelsConcurrent(p, labels)
1324 }
1325
1326 var goroutineProfile = struct {
1327 sema uint32
1328 active bool
1329 offset atomic.Int64
1330 records []profilerecord.StackRecord
1331 labels []unsafe.Pointer
1332 }{
1333 sema: 1,
1334 }
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347 type goroutineProfileState uint32
1348
1349 const (
1350 goroutineProfileAbsent goroutineProfileState = iota
1351 goroutineProfileInProgress
1352 goroutineProfileSatisfied
1353 )
1354
1355 type goroutineProfileStateHolder atomic.Uint32
1356
1357 func (p *goroutineProfileStateHolder) Load() goroutineProfileState {
1358 return goroutineProfileState((*atomic.Uint32)(p).Load())
1359 }
1360
1361 func (p *goroutineProfileStateHolder) Store(value goroutineProfileState) {
1362 (*atomic.Uint32)(p).Store(uint32(value))
1363 }
1364
1365 func (p *goroutineProfileStateHolder) CompareAndSwap(old, new goroutineProfileState) bool {
1366 return (*atomic.Uint32)(p).CompareAndSwap(uint32(old), uint32(new))
1367 }
1368
1369 func goroutineProfileWithLabelsConcurrent(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1370 if len(p) == 0 {
1371
1372
1373
1374
1375 return int(gcount()), false
1376 }
1377
1378 semacquire(&goroutineProfile.sema)
1379
1380 ourg := getg()
1381
1382 pcbuf := makeProfStack()
1383 stw := stopTheWorld(stwGoroutineProfile)
1384
1385
1386
1387
1388
1389
1390
1391 n = int(gcount())
1392 if fingStatus.Load()&fingRunningFinalizer != 0 {
1393 n++
1394 }
1395
1396 if n > len(p) {
1397
1398
1399
1400 startTheWorld(stw)
1401 semrelease(&goroutineProfile.sema)
1402 return n, false
1403 }
1404
1405
1406 sp := sys.GetCallerSP()
1407 pc := sys.GetCallerPC()
1408 systemstack(func() {
1409 saveg(pc, sp, ourg, &p[0], pcbuf)
1410 })
1411 if labels != nil {
1412 labels[0] = ourg.labels
1413 }
1414 ourg.goroutineProfiled.Store(goroutineProfileSatisfied)
1415 goroutineProfile.offset.Store(1)
1416
1417
1418
1419
1420
1421
1422 goroutineProfile.active = true
1423 goroutineProfile.records = p
1424 goroutineProfile.labels = labels
1425
1426
1427
1428 if fing != nil {
1429 fing.goroutineProfiled.Store(goroutineProfileSatisfied)
1430 if readgstatus(fing) != _Gdead && !isSystemGoroutine(fing, false) {
1431 doRecordGoroutineProfile(fing, pcbuf)
1432 }
1433 }
1434 startTheWorld(stw)
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447 forEachGRace(func(gp1 *g) {
1448 tryRecordGoroutineProfile(gp1, pcbuf, Gosched)
1449 })
1450
1451 stw = stopTheWorld(stwGoroutineProfileCleanup)
1452 endOffset := goroutineProfile.offset.Swap(0)
1453 goroutineProfile.active = false
1454 goroutineProfile.records = nil
1455 goroutineProfile.labels = nil
1456 startTheWorld(stw)
1457
1458
1459
1460 forEachGRace(func(gp1 *g) {
1461 gp1.goroutineProfiled.Store(goroutineProfileAbsent)
1462 })
1463
1464 if raceenabled {
1465 raceacquire(unsafe.Pointer(&labelSync))
1466 }
1467
1468 if n != int(endOffset) {
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478 }
1479
1480 semrelease(&goroutineProfile.sema)
1481 return n, true
1482 }
1483
1484
1485
1486
1487
1488 func tryRecordGoroutineProfileWB(gp1 *g) {
1489 if getg().m.p.ptr() == nil {
1490 throw("no P available, write barriers are forbidden")
1491 }
1492 tryRecordGoroutineProfile(gp1, nil, osyield)
1493 }
1494
1495
1496
1497
1498 func tryRecordGoroutineProfile(gp1 *g, pcbuf []uintptr, yield func()) {
1499 if readgstatus(gp1) == _Gdead {
1500
1501
1502
1503
1504 return
1505 }
1506 if isSystemGoroutine(gp1, true) {
1507
1508
1509 return
1510 }
1511
1512 for {
1513 prev := gp1.goroutineProfiled.Load()
1514 if prev == goroutineProfileSatisfied {
1515
1516
1517 break
1518 }
1519 if prev == goroutineProfileInProgress {
1520
1521
1522 yield()
1523 continue
1524 }
1525
1526
1527
1528
1529
1530
1531 mp := acquirem()
1532 if gp1.goroutineProfiled.CompareAndSwap(goroutineProfileAbsent, goroutineProfileInProgress) {
1533 doRecordGoroutineProfile(gp1, pcbuf)
1534 gp1.goroutineProfiled.Store(goroutineProfileSatisfied)
1535 }
1536 releasem(mp)
1537 }
1538 }
1539
1540
1541
1542
1543
1544
1545
1546
1547 func doRecordGoroutineProfile(gp1 *g, pcbuf []uintptr) {
1548 if readgstatus(gp1) == _Grunning {
1549 print("doRecordGoroutineProfile gp1=", gp1.goid, "\n")
1550 throw("cannot read stack of running goroutine")
1551 }
1552
1553 offset := int(goroutineProfile.offset.Add(1)) - 1
1554
1555 if offset >= len(goroutineProfile.records) {
1556
1557
1558
1559 return
1560 }
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570 systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &goroutineProfile.records[offset], pcbuf) })
1571
1572 if goroutineProfile.labels != nil {
1573 goroutineProfile.labels[offset] = gp1.labels
1574 }
1575 }
1576
1577 func goroutineProfileWithLabelsSync(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1578 gp := getg()
1579
1580 isOK := func(gp1 *g) bool {
1581
1582
1583 return gp1 != gp && readgstatus(gp1) != _Gdead && !isSystemGoroutine(gp1, false)
1584 }
1585
1586 pcbuf := makeProfStack()
1587 stw := stopTheWorld(stwGoroutineProfile)
1588
1589
1590 n = 1
1591 forEachGRace(func(gp1 *g) {
1592 if isOK(gp1) {
1593 n++
1594 }
1595 })
1596
1597 if n <= len(p) {
1598 ok = true
1599 r, lbl := p, labels
1600
1601
1602 sp := sys.GetCallerSP()
1603 pc := sys.GetCallerPC()
1604 systemstack(func() {
1605 saveg(pc, sp, gp, &r[0], pcbuf)
1606 })
1607 r = r[1:]
1608
1609
1610 if labels != nil {
1611 lbl[0] = gp.labels
1612 lbl = lbl[1:]
1613 }
1614
1615
1616 forEachGRace(func(gp1 *g) {
1617 if !isOK(gp1) {
1618 return
1619 }
1620
1621 if len(r) == 0 {
1622
1623
1624 return
1625 }
1626
1627
1628
1629
1630 systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &r[0], pcbuf) })
1631 if labels != nil {
1632 lbl[0] = gp1.labels
1633 lbl = lbl[1:]
1634 }
1635 r = r[1:]
1636 })
1637 }
1638
1639 if raceenabled {
1640 raceacquire(unsafe.Pointer(&labelSync))
1641 }
1642
1643 startTheWorld(stw)
1644 return n, ok
1645 }
1646
1647
1648
1649
1650
1651
1652
1653 func GoroutineProfile(p []StackRecord) (n int, ok bool) {
1654 records := make([]profilerecord.StackRecord, len(p))
1655 n, ok = goroutineProfileInternal(records)
1656 if !ok {
1657 return
1658 }
1659 for i, mr := range records[0:n] {
1660 l := copy(p[i].Stack0[:], mr.Stack)
1661 clear(p[i].Stack0[l:])
1662 }
1663 return
1664 }
1665
1666 func goroutineProfileInternal(p []profilerecord.StackRecord) (n int, ok bool) {
1667 return goroutineProfileWithLabels(p, nil)
1668 }
1669
1670 func saveg(pc, sp uintptr, gp *g, r *profilerecord.StackRecord, pcbuf []uintptr) {
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681 if pcbuf == nil {
1682 pcbuf = makeProfStack()
1683 }
1684
1685 var u unwinder
1686 u.initAt(pc, sp, 0, gp, unwindSilentErrors)
1687 n := tracebackPCs(&u, 0, pcbuf)
1688 r.Stack = make([]uintptr, n)
1689 copy(r.Stack, pcbuf)
1690 }
1691
1692
1693
1694
1695
1696 func Stack(buf []byte, all bool) int {
1697 var stw worldStop
1698 if all {
1699 stw = stopTheWorld(stwAllGoroutinesStack)
1700 }
1701
1702 n := 0
1703 if len(buf) > 0 {
1704 gp := getg()
1705 sp := sys.GetCallerSP()
1706 pc := sys.GetCallerPC()
1707 systemstack(func() {
1708 g0 := getg()
1709
1710
1711
1712 g0.m.traceback = 1
1713 g0.writebuf = buf[0:0:len(buf)]
1714 goroutineheader(gp)
1715 traceback(pc, sp, 0, gp)
1716 if all {
1717 tracebackothers(gp)
1718 }
1719 g0.m.traceback = 0
1720 n = len(g0.writebuf)
1721 g0.writebuf = nil
1722 })
1723 }
1724
1725 if all {
1726 startTheWorld(stw)
1727 }
1728 return n
1729 }
1730
View as plain text