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