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 := 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
1263 func pprof_goroutineLeakProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1264 return goroutineLeakProfileWithLabelsConcurrent(p, labels)
1265 }
1266
1267
1268 func goroutineLeakProfileWithLabels(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1269 if labels != nil && len(labels) != len(p) {
1270 labels = nil
1271 }
1272
1273 return goroutineLeakProfileWithLabelsConcurrent(p, labels)
1274 }
1275
1276 var goroutineProfile = struct {
1277 sema uint32
1278 active bool
1279 offset atomic.Int64
1280 records []profilerecord.StackRecord
1281 labels []unsafe.Pointer
1282 }{
1283 sema: 1,
1284 }
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297 type goroutineProfileState uint32
1298
1299 const (
1300 goroutineProfileAbsent goroutineProfileState = iota
1301 goroutineProfileInProgress
1302 goroutineProfileSatisfied
1303 )
1304
1305 type goroutineProfileStateHolder atomic.Uint32
1306
1307 func (p *goroutineProfileStateHolder) Load() goroutineProfileState {
1308 return goroutineProfileState((*atomic.Uint32)(p).Load())
1309 }
1310
1311 func (p *goroutineProfileStateHolder) Store(value goroutineProfileState) {
1312 (*atomic.Uint32)(p).Store(uint32(value))
1313 }
1314
1315 func (p *goroutineProfileStateHolder) CompareAndSwap(old, new goroutineProfileState) bool {
1316 return (*atomic.Uint32)(p).CompareAndSwap(uint32(old), uint32(new))
1317 }
1318
1319 func goroutineLeakProfileWithLabelsConcurrent(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1320 if len(p) == 0 {
1321
1322
1323 return work.goroutineLeak.count, false
1324 }
1325
1326
1327
1328 semacquire(&goroutineProfile.sema)
1329
1330
1331
1332
1333 pcbuf := makeProfStack()
1334
1335
1336 n = work.goroutineLeak.count
1337
1338 if n > len(p) {
1339
1340
1341
1342 semrelease(&goroutineProfile.sema)
1343 return n, false
1344 }
1345
1346
1347 forEachGRace(func(gp1 *g) {
1348 if readgstatus(gp1) == _Gleaked {
1349 doRecordGoroutineProfile(gp1, pcbuf)
1350 }
1351 })
1352
1353 if raceenabled {
1354 raceacquire(unsafe.Pointer(&labelSync))
1355 }
1356
1357 semrelease(&goroutineProfile.sema)
1358 return n, true
1359 }
1360
1361 func goroutineProfileWithLabelsConcurrent(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1362 if len(p) == 0 {
1363
1364
1365
1366
1367 return int(gcount(false)), false
1368 }
1369
1370 semacquire(&goroutineProfile.sema)
1371
1372 ourg := getg()
1373
1374 pcbuf := makeProfStack()
1375 stw := stopTheWorld(stwGoroutineProfile)
1376
1377
1378
1379
1380
1381
1382
1383 n = int(gcount(false))
1384 if fingStatus.Load()&fingRunningFinalizer != 0 {
1385 n++
1386 }
1387 n += int(gcCleanups.running.Load())
1388
1389 if n > len(p) {
1390
1391
1392
1393 startTheWorld(stw)
1394 semrelease(&goroutineProfile.sema)
1395 return n, false
1396 }
1397
1398
1399 sp := sys.GetCallerSP()
1400 pc := sys.GetCallerPC()
1401 systemstack(func() {
1402 saveg(pc, sp, ourg, &p[0], pcbuf)
1403 })
1404 if labels != nil {
1405 labels[0] = ourg.labels
1406 }
1407 ourg.goroutineProfiled.Store(goroutineProfileSatisfied)
1408 goroutineProfile.offset.Store(1)
1409
1410
1411
1412
1413
1414
1415 goroutineProfile.active = true
1416 goroutineProfile.records = p
1417 goroutineProfile.labels = labels
1418 startTheWorld(stw)
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431 forEachGRace(func(gp1 *g) {
1432 tryRecordGoroutineProfile(gp1, pcbuf, Gosched)
1433 })
1434
1435 stw = stopTheWorld(stwGoroutineProfileCleanup)
1436 endOffset := goroutineProfile.offset.Swap(0)
1437 goroutineProfile.active = false
1438 goroutineProfile.records = nil
1439 goroutineProfile.labels = nil
1440 startTheWorld(stw)
1441
1442
1443
1444 forEachGRace(func(gp1 *g) {
1445 gp1.goroutineProfiled.Store(goroutineProfileAbsent)
1446 })
1447
1448 if raceenabled {
1449 raceacquire(unsafe.Pointer(&labelSync))
1450 }
1451
1452 if n != int(endOffset) {
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462 }
1463
1464 semrelease(&goroutineProfile.sema)
1465 return n, true
1466 }
1467
1468
1469
1470
1471
1472 func tryRecordGoroutineProfileWB(gp1 *g) {
1473 if getg().m.p.ptr() == nil {
1474 throw("no P available, write barriers are forbidden")
1475 }
1476 tryRecordGoroutineProfile(gp1, nil, osyield)
1477 }
1478
1479
1480
1481
1482 func tryRecordGoroutineProfile(gp1 *g, pcbuf []uintptr, yield func()) {
1483 if readgstatus(gp1) == _Gdead {
1484
1485
1486
1487
1488 return
1489 }
1490
1491 for {
1492 prev := gp1.goroutineProfiled.Load()
1493 if prev == goroutineProfileSatisfied {
1494
1495
1496 break
1497 }
1498 if prev == goroutineProfileInProgress {
1499
1500
1501 yield()
1502 continue
1503 }
1504
1505
1506
1507
1508
1509
1510 mp := acquirem()
1511 if gp1.goroutineProfiled.CompareAndSwap(goroutineProfileAbsent, goroutineProfileInProgress) {
1512 doRecordGoroutineProfile(gp1, pcbuf)
1513 gp1.goroutineProfiled.Store(goroutineProfileSatisfied)
1514 }
1515 releasem(mp)
1516 }
1517 }
1518
1519
1520
1521
1522
1523
1524
1525
1526 func doRecordGoroutineProfile(gp1 *g, pcbuf []uintptr) {
1527 if isSystemGoroutine(gp1, false) {
1528
1529
1530
1531
1532
1533
1534
1535
1536 return
1537 }
1538 if readgstatus(gp1) == _Grunning {
1539 print("doRecordGoroutineProfile gp1=", gp1.goid, "\n")
1540 throw("cannot read stack of running goroutine")
1541 }
1542
1543 offset := int(goroutineProfile.offset.Add(1)) - 1
1544
1545 if offset >= len(goroutineProfile.records) {
1546
1547
1548
1549 return
1550 }
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560 systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &goroutineProfile.records[offset], pcbuf) })
1561
1562 if goroutineProfile.labels != nil {
1563 goroutineProfile.labels[offset] = gp1.labels
1564 }
1565 }
1566
1567 func goroutineProfileWithLabelsSync(p []profilerecord.StackRecord, labels []unsafe.Pointer) (n int, ok bool) {
1568 gp := getg()
1569
1570 isOK := func(gp1 *g) bool {
1571
1572
1573 return gp1 != gp && readgstatus(gp1) != _Gdead && !isSystemGoroutine(gp1, false)
1574 }
1575
1576 pcbuf := makeProfStack()
1577 stw := stopTheWorld(stwGoroutineProfile)
1578
1579
1580 n = 1
1581 forEachGRace(func(gp1 *g) {
1582 if isOK(gp1) {
1583 n++
1584 }
1585 })
1586
1587 if n <= len(p) {
1588 ok = true
1589 r, lbl := p, labels
1590
1591
1592 sp := sys.GetCallerSP()
1593 pc := sys.GetCallerPC()
1594 systemstack(func() {
1595 saveg(pc, sp, gp, &r[0], pcbuf)
1596 })
1597 r = r[1:]
1598
1599
1600 if labels != nil {
1601 lbl[0] = gp.labels
1602 lbl = lbl[1:]
1603 }
1604
1605
1606 forEachGRace(func(gp1 *g) {
1607 if !isOK(gp1) {
1608 return
1609 }
1610
1611 if len(r) == 0 {
1612
1613
1614 return
1615 }
1616
1617
1618
1619
1620 systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &r[0], pcbuf) })
1621 if labels != nil {
1622 lbl[0] = gp1.labels
1623 lbl = lbl[1:]
1624 }
1625 r = r[1:]
1626 })
1627 }
1628
1629 if raceenabled {
1630 raceacquire(unsafe.Pointer(&labelSync))
1631 }
1632
1633 startTheWorld(stw)
1634 return n, ok
1635 }
1636
1637
1638
1639
1640
1641
1642
1643 func GoroutineProfile(p []StackRecord) (n int, ok bool) {
1644 records := make([]profilerecord.StackRecord, len(p))
1645 n, ok = goroutineProfileInternal(records)
1646 if !ok {
1647 return
1648 }
1649 for i, mr := range records[0:n] {
1650 l := copy(p[i].Stack0[:], mr.Stack)
1651 clear(p[i].Stack0[l:])
1652 }
1653 return
1654 }
1655
1656 func goroutineProfileInternal(p []profilerecord.StackRecord) (n int, ok bool) {
1657 return goroutineProfileWithLabels(p, nil)
1658 }
1659
1660 func saveg(pc, sp uintptr, gp *g, r *profilerecord.StackRecord, pcbuf []uintptr) {
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671 if pcbuf == nil {
1672 pcbuf = makeProfStack()
1673 }
1674
1675 var u unwinder
1676 u.initAt(pc, sp, 0, gp, unwindSilentErrors)
1677 n := tracebackPCs(&u, 0, pcbuf)
1678 r.Stack = make([]uintptr, n)
1679 copy(r.Stack, pcbuf)
1680 }
1681
1682
1683
1684
1685
1686 func Stack(buf []byte, all bool) int {
1687 var stw worldStop
1688 if all {
1689 stw = stopTheWorld(stwAllGoroutinesStack)
1690 }
1691
1692 n := 0
1693 if len(buf) > 0 {
1694 gp := getg()
1695 sp := sys.GetCallerSP()
1696 pc := sys.GetCallerPC()
1697 systemstack(func() {
1698 g0 := getg()
1699
1700
1701
1702 g0.m.traceback = 1
1703 g0.writebuf = buf[0:0:len(buf)]
1704 goroutineheader(gp)
1705 traceback(pc, sp, 0, gp)
1706 if all {
1707 tracebackothers(gp)
1708 }
1709 g0.m.traceback = 0
1710 n = len(g0.writebuf)
1711 g0.writebuf = nil
1712 })
1713 }
1714
1715 if all {
1716 startTheWorld(stw)
1717 }
1718 return n
1719 }
1720
View as plain text