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