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