Source file
src/runtime/mgcmark.go
1
2
3
4
5
6
7 package runtime
8
9 import (
10 "internal/abi"
11 "internal/goarch"
12 "internal/goexperiment"
13 "internal/runtime/atomic"
14 "internal/runtime/sys"
15 "unsafe"
16 )
17
18 const (
19 fixedRootFinalizers = iota
20 fixedRootFreeGStacks
21 fixedRootCleanups
22 fixedRootCount
23
24
25
26 rootBlockBytes = 256 << 10
27
28
29
30
31
32
33
34
35 maxObletBytes = 128 << 10
36
37
38
39
40
41
42
43 drainCheckThreshold = 100000
44
45
46
47
48
49
50
51
52
53 pagesPerSpanRoot = min(512, pagesPerArena)
54 )
55
56
57
58
59
60
61 func (gp *g) internalBlocked() bool {
62 reason := gp.waitreason
63 return reason < waitReasonChanReceiveNilChan || waitReasonSyncWaitGroupWait < reason
64 }
65
66
67
68
69
70
71
72 func allGsSnapshotSortedForGC() ([]*g, int) {
73 assertWorldStoppedOrLockHeld(&allglock)
74
75
76
77 for _, gp := range allgs {
78 gp.atomicstatus.CompareAndSwap(_Gleaked, _Gwaiting)
79 }
80
81 allgsSorted := make([]*g, len(allgs))
82
83
84 var currIndex, blockedIndex = 0, len(allgsSorted) - 1
85 for _, gp := range allgs {
86
87
88 if status := readgstatus(gp); status != _Gwaiting || gp.internalBlocked() {
89 allgsSorted[currIndex] = gp
90 currIndex++
91 } else {
92 allgsSorted[blockedIndex] = gp
93 blockedIndex--
94 }
95 }
96
97
98
99
100
101
102 return allgsSorted, blockedIndex + 1
103 }
104
105
106
107
108
109 func gcPrepareMarkRoots() {
110 assertWorldStopped()
111
112
113 nBlocks := func(bytes uintptr) int {
114 return int(divRoundUp(bytes, rootBlockBytes))
115 }
116
117 work.nDataRoots = 0
118 work.nBSSRoots = 0
119
120
121 for _, datap := range activeModules() {
122 nDataRoots := nBlocks(datap.edata - datap.data)
123 if nDataRoots > work.nDataRoots {
124 work.nDataRoots = nDataRoots
125 }
126
127 nBSSRoots := nBlocks(datap.ebss - datap.bss)
128 if nBSSRoots > work.nBSSRoots {
129 work.nBSSRoots = nBSSRoots
130 }
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145 mheap_.markArenas = mheap_.heapArenas[:len(mheap_.heapArenas):len(mheap_.heapArenas)]
146 work.nSpanRoots = len(mheap_.markArenas) * (pagesPerArena / pagesPerSpanRoot)
147
148
149
150
151
152
153
154 if work.goroutineLeak.enabled {
155
156
157 work.stackRoots, work.nMaybeRunnableStackRoots = allGsSnapshotSortedForGC()
158 } else {
159
160 work.stackRoots = allGsSnapshot()
161 work.nMaybeRunnableStackRoots = len(work.stackRoots)
162 }
163
164 work.nStackRoots = len(work.stackRoots)
165
166 work.markrootNext.Store(0)
167 work.markrootJobs.Store(uint32(fixedRootCount + work.nDataRoots + work.nBSSRoots + work.nSpanRoots + work.nMaybeRunnableStackRoots))
168
169
170 work.baseData = uint32(fixedRootCount)
171 work.baseBSS = work.baseData + uint32(work.nDataRoots)
172 work.baseSpans = work.baseBSS + uint32(work.nBSSRoots)
173 work.baseStacks = work.baseSpans + uint32(work.nSpanRoots)
174 work.baseEnd = work.baseStacks + uint32(work.nStackRoots)
175 }
176
177
178
179 func gcMarkRootCheck() {
180 if next, jobs := work.markrootNext.Load(), work.markrootJobs.Load(); next < jobs {
181 print(next, " of ", jobs, " markroot jobs done\n")
182 throw("left over markroot jobs")
183 }
184
185
186
187
188
189
190 i := 0
191 forEachGRace(func(gp *g) {
192 if i >= work.nStackRoots {
193 return
194 }
195
196 if !gp.gcscandone {
197 println("gp", gp, "goid", gp.goid,
198 "status", readgstatus(gp),
199 "gcscandone", gp.gcscandone)
200 throw("scan missed a g")
201 }
202
203 i++
204 })
205 }
206
207
208 var oneptrmask = [...]uint8{1}
209
210
211
212
213
214
215
216
217
218
219
220
221 func markroot(gcw *gcWork, i uint32, flushBgCredit bool) int64 {
222
223 var workDone int64
224 var workCounter *atomic.Int64
225 switch {
226 case work.baseData <= i && i < work.baseBSS:
227 workCounter = &gcController.globalsScanWork
228 for _, datap := range activeModules() {
229 workDone += markrootBlock(datap.data, datap.edata-datap.data, datap.gcdatamask.bytedata, gcw, int(i-work.baseData))
230 }
231
232 case work.baseBSS <= i && i < work.baseSpans:
233 workCounter = &gcController.globalsScanWork
234 for _, datap := range activeModules() {
235 workDone += markrootBlock(datap.bss, datap.ebss-datap.bss, datap.gcbssmask.bytedata, gcw, int(i-work.baseBSS))
236 }
237
238 case i == fixedRootFinalizers:
239 for fb := allfin; fb != nil; fb = fb.alllink {
240 cnt := uintptr(atomic.Load(&fb.cnt))
241 scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), cnt*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], gcw, nil)
242 }
243
244 case i == fixedRootFreeGStacks:
245
246
247 systemstack(markrootFreeGStacks)
248
249 case i == fixedRootCleanups:
250 for cb := (*cleanupBlock)(gcCleanups.all.Load()); cb != nil; cb = cb.alllink {
251
252
253 n := uintptr(atomic.Load(&cb.n))
254 scanblock(uintptr(unsafe.Pointer(&cb.cleanups[0])), n*unsafe.Sizeof(cleanupFn{}), &cleanupBlockPtrMask[0], gcw, nil)
255 }
256
257 case work.baseSpans <= i && i < work.baseStacks:
258
259 markrootSpans(gcw, int(i-work.baseSpans))
260
261 default:
262
263 workCounter = &gcController.stackScanWork
264 if i < work.baseStacks || work.baseEnd <= i {
265 printlock()
266 print("runtime: markroot index ", i, " not in stack roots range [", work.baseStacks, ", ", work.baseEnd, ")\n")
267 throw("markroot: bad index")
268 }
269 gp := work.stackRoots[i-work.baseStacks]
270
271
272
273 status := readgstatus(gp)
274 if (status == _Gwaiting || status == _Gsyscall) && gp.waitsince == 0 {
275 gp.waitsince = work.tstart
276 }
277
278
279
280 systemstack(func() {
281
282
283
284
285 userG := getg().m.curg
286 selfScan := gp == userG && readgstatus(userG) == _Grunning
287 if selfScan {
288 casGToWaitingForSuspendG(userG, _Grunning, waitReasonGarbageCollectionScan)
289 }
290
291
292
293
294
295
296
297
298 stopped := suspendG(gp)
299 if stopped.dead {
300 gp.gcscandone = true
301 return
302 }
303 if gp.gcscandone {
304 throw("g already scanned")
305 }
306 workDone += scanstack(gp, gcw)
307 gp.gcscandone = true
308 resumeG(stopped)
309
310 if selfScan {
311 casgstatus(userG, _Gwaiting, _Grunning)
312 }
313 })
314 }
315 if workCounter != nil && workDone != 0 {
316 workCounter.Add(workDone)
317 if flushBgCredit {
318 gcFlushBgCredit(workDone)
319 }
320 }
321 return workDone
322 }
323
324
325
326
327
328
329
330 func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) int64 {
331 if rootBlockBytes%(8*goarch.PtrSize) != 0 {
332
333 throw("rootBlockBytes must be a multiple of 8*ptrSize")
334 }
335
336
337
338
339 off := uintptr(shard) * rootBlockBytes
340 if off >= n0 {
341 return 0
342 }
343 b := b0 + off
344 ptrmask := (*uint8)(add(unsafe.Pointer(ptrmask0), uintptr(shard)*(rootBlockBytes/(8*goarch.PtrSize))))
345 n := uintptr(rootBlockBytes)
346 if off+n > n0 {
347 n = n0 - off
348 }
349
350
351 scanblock(b, n, ptrmask, gcw, nil)
352 return int64(n)
353 }
354
355
356
357
358
359 func markrootFreeGStacks() {
360
361 lock(&sched.gFree.lock)
362 list := sched.gFree.stack
363 sched.gFree.stack = gList{}
364 unlock(&sched.gFree.lock)
365 if list.empty() {
366 return
367 }
368
369
370 var tail *g
371 for gp := list.head.ptr(); gp != nil; gp = gp.schedlink.ptr() {
372 tail = gp
373 stackfree(gp.stack)
374 gp.stack.lo = 0
375 gp.stack.hi = 0
376 if valgrindenabled {
377 valgrindDeregisterStack(gp.valgrindStackID)
378 gp.valgrindStackID = 0
379 }
380 }
381
382 q := gQueue{list.head, tail.guintptr(), list.size}
383
384
385 lock(&sched.gFree.lock)
386 sched.gFree.noStack.pushAll(q)
387 unlock(&sched.gFree.lock)
388 }
389
390
391
392
393 func markrootSpans(gcw *gcWork, shard int) {
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410 sg := mheap_.sweepgen
411
412
413 ai := mheap_.markArenas[shard/(pagesPerArena/pagesPerSpanRoot)]
414 ha := mheap_.arenas[ai.l1()][ai.l2()]
415 arenaPage := uint(uintptr(shard) * pagesPerSpanRoot % pagesPerArena)
416
417
418 specialsbits := ha.pageSpecials[arenaPage/8:]
419 specialsbits = specialsbits[:pagesPerSpanRoot/8]
420 for i := range specialsbits {
421
422 specials := atomic.Load8(&specialsbits[i])
423 if specials == 0 {
424 continue
425 }
426 for j := uint(0); j < 8; j++ {
427 if specials&(1<<j) == 0 {
428 continue
429 }
430
431
432
433
434
435
436 s := ha.spans[arenaPage+uint(i)*8+j]
437
438
439
440 if state := s.state.get(); state != mSpanInUse {
441 print("s.state = ", state, "\n")
442 throw("non in-use span found with specials bit set")
443 }
444
445 if !useCheckmark && !(s.sweepgen == sg || s.sweepgen == sg+3) {
446
447 print("sweep ", s.sweepgen, " ", sg, "\n")
448 throw("gc: unswept span")
449 }
450
451
452
453 lock(&s.speciallock)
454 for sp := s.specials; sp != nil; sp = sp.next {
455 switch sp.kind {
456 case _KindSpecialFinalizer:
457 gcScanFinalizer((*specialfinalizer)(unsafe.Pointer(sp)), s, gcw)
458 case _KindSpecialWeakHandle:
459
460 spw := (*specialWeakHandle)(unsafe.Pointer(sp))
461 scanblock(uintptr(unsafe.Pointer(&spw.handle)), goarch.PtrSize, &oneptrmask[0], gcw, nil)
462 case _KindSpecialCleanup:
463 gcScanCleanup((*specialCleanup)(unsafe.Pointer(sp)), gcw)
464 }
465 }
466 unlock(&s.speciallock)
467 }
468 }
469 }
470
471
472 func gcScanFinalizer(spf *specialfinalizer, s *mspan, gcw *gcWork) {
473
474
475
476 p := s.base() + spf.special.offset/s.elemsize*s.elemsize
477
478
479
480
481 if !s.spanclass.noscan() {
482 scanObject(p, gcw)
483 }
484
485
486 scanblock(uintptr(unsafe.Pointer(&spf.fn)), goarch.PtrSize, &oneptrmask[0], gcw, nil)
487 }
488
489
490 func gcScanCleanup(spc *specialCleanup, gcw *gcWork) {
491
492 scanblock(uintptr(unsafe.Pointer(&spc.cleanup)), unsafe.Sizeof(cleanupFn{}), &cleanupFnPtrMask[0], gcw, nil)
493 }
494
495
496
497
498
499 func gcAssistAlloc(gp *g) {
500
501
502 if getg() == gp.m.g0 {
503 return
504 }
505 if mp := getg().m; mp.locks > 0 || mp.preemptoff != "" {
506 return
507 }
508
509 if gp := getg(); gp.bubble != nil {
510
511
512
513 bubble := gp.bubble
514 gp.bubble = nil
515 defer func() {
516 gp.bubble = bubble
517 }()
518 }
519
520
521
522
523
524
525
526
527
528
529
530
531
532 enteredMarkAssistForTracing := false
533 retry:
534 if gcCPULimiter.limiting() {
535
536
537 if enteredMarkAssistForTracing {
538 trace := traceAcquire()
539 if trace.ok() {
540 trace.GCMarkAssistDone()
541
542
543
544
545
546
547
548 gp.inMarkAssist = false
549 traceRelease(trace)
550 } else {
551
552
553
554 gp.inMarkAssist = false
555 }
556 }
557 return
558 }
559
560
561
562
563 assistWorkPerByte := gcController.assistWorkPerByte.Load()
564 assistBytesPerWork := gcController.assistBytesPerWork.Load()
565 debtBytes := -gp.gcAssistBytes
566 scanWork := int64(assistWorkPerByte * float64(debtBytes))
567 if scanWork < gcOverAssistWork {
568 scanWork = gcOverAssistWork
569 debtBytes = int64(assistBytesPerWork * float64(scanWork))
570 }
571
572
573
574
575
576
577
578 bgScanCredit := gcController.bgScanCredit.Load()
579 stolen := int64(0)
580 if bgScanCredit > 0 {
581 if bgScanCredit < scanWork {
582 stolen = bgScanCredit
583 gp.gcAssistBytes += 1 + int64(assistBytesPerWork*float64(stolen))
584 } else {
585 stolen = scanWork
586 gp.gcAssistBytes += debtBytes
587 }
588 gcController.bgScanCredit.Add(-stolen)
589
590 scanWork -= stolen
591
592 if scanWork == 0 {
593
594
595 if enteredMarkAssistForTracing {
596 trace := traceAcquire()
597 if trace.ok() {
598 trace.GCMarkAssistDone()
599
600
601
602
603
604
605
606 gp.inMarkAssist = false
607 traceRelease(trace)
608 } else {
609
610
611
612 gp.inMarkAssist = false
613 }
614 }
615 return
616 }
617 }
618 if !enteredMarkAssistForTracing {
619 trace := traceAcquire()
620 if trace.ok() {
621 trace.GCMarkAssistStart()
622
623
624 gp.inMarkAssist = true
625 traceRelease(trace)
626 } else {
627 gp.inMarkAssist = true
628 }
629
630
631
632
633
634 enteredMarkAssistForTracing = true
635 }
636
637
638 systemstack(func() {
639 gcAssistAlloc1(gp, scanWork)
640
641
642 })
643
644 completed := gp.param != nil
645 gp.param = nil
646 if completed {
647 gcMarkDone()
648 }
649
650 if gp.gcAssistBytes < 0 {
651
652
653
654
655
656
657
658 if gp.preempt {
659 Gosched()
660 goto retry
661 }
662
663
664
665
666
667
668
669
670
671
672 if !gcParkAssist() {
673 goto retry
674 }
675
676
677
678 }
679 if enteredMarkAssistForTracing {
680 trace := traceAcquire()
681 if trace.ok() {
682 trace.GCMarkAssistDone()
683
684
685
686
687
688
689
690 gp.inMarkAssist = false
691 traceRelease(trace)
692 } else {
693
694
695
696 gp.inMarkAssist = false
697 }
698 }
699 }
700
701
702
703
704
705
706
707
708
709
710
711 func gcAssistAlloc1(gp *g, scanWork int64) {
712
713
714 gp.param = nil
715
716 if atomic.Load(&gcBlackenEnabled) == 0 {
717
718
719
720
721
722
723
724 gp.gcAssistBytes = 0
725 return
726 }
727
728
729
730
731
732
733
734 startTime := nanotime()
735 trackLimiterEvent := gp.m.p.ptr().limiterEvent.start(limiterEventMarkAssist, startTime)
736
737 gcBeginWork()
738
739
740 casGToWaitingForSuspendG(gp, _Grunning, waitReasonGCAssistMarking)
741
742
743
744 gcw := &getg().m.p.ptr().gcw
745 workDone := gcDrainN(gcw, scanWork)
746
747 casgstatus(gp, _Gwaiting, _Grunning)
748
749
750
751
752
753
754
755 assistBytesPerWork := gcController.assistBytesPerWork.Load()
756 gp.gcAssistBytes += 1 + int64(assistBytesPerWork*float64(workDone))
757
758
759
760 if gcEndWork() {
761
762
763
764
765 gp.param = unsafe.Pointer(gp)
766 }
767 now := nanotime()
768 duration := now - startTime
769 pp := gp.m.p.ptr()
770 pp.gcAssistTime += duration
771 if trackLimiterEvent {
772 pp.limiterEvent.stop(limiterEventMarkAssist, now)
773 }
774 if pp.gcAssistTime > gcAssistTimeSlack {
775 gcController.assistTime.Add(pp.gcAssistTime)
776 gcCPULimiter.update(now)
777 pp.gcAssistTime = 0
778 }
779 }
780
781
782
783
784 func gcWakeAllAssists() {
785 lock(&work.assistQueue.lock)
786 list := work.assistQueue.q.popList()
787 injectglist(&list)
788 unlock(&work.assistQueue.lock)
789 }
790
791
792
793
794
795 func gcParkAssist() bool {
796 lock(&work.assistQueue.lock)
797
798
799
800 if atomic.Load(&gcBlackenEnabled) == 0 {
801 unlock(&work.assistQueue.lock)
802 return true
803 }
804
805 gp := getg()
806 oldList := work.assistQueue.q
807 work.assistQueue.q.pushBack(gp)
808
809
810
811
812
813 if gcController.bgScanCredit.Load() > 0 {
814 work.assistQueue.q = oldList
815 if oldList.tail != 0 {
816 oldList.tail.ptr().schedlink.set(nil)
817 }
818 unlock(&work.assistQueue.lock)
819 return false
820 }
821
822 goparkunlock(&work.assistQueue.lock, waitReasonGCAssistWait, traceBlockGCMarkAssist, 2)
823 return true
824 }
825
826
827
828
829
830
831
832
833
834
835
836 func gcFlushBgCredit(scanWork int64) {
837 if work.assistQueue.q.empty() {
838
839
840
841
842 gcController.bgScanCredit.Add(scanWork)
843 return
844 }
845
846 assistBytesPerWork := gcController.assistBytesPerWork.Load()
847 scanBytes := int64(float64(scanWork) * assistBytesPerWork)
848
849 lock(&work.assistQueue.lock)
850 for !work.assistQueue.q.empty() && scanBytes > 0 {
851 gp := work.assistQueue.q.pop()
852
853
854 if scanBytes+gp.gcAssistBytes >= 0 {
855
856 scanBytes += gp.gcAssistBytes
857 gp.gcAssistBytes = 0
858
859
860
861
862
863
864 ready(gp, 0, false)
865 } else {
866
867 gp.gcAssistBytes += scanBytes
868 scanBytes = 0
869
870
871
872
873 work.assistQueue.q.pushBack(gp)
874 break
875 }
876 }
877
878 if scanBytes > 0 {
879
880 assistWorkPerByte := gcController.assistWorkPerByte.Load()
881 scanWork = int64(float64(scanBytes) * assistWorkPerByte)
882 gcController.bgScanCredit.Add(scanWork)
883 }
884 unlock(&work.assistQueue.lock)
885 }
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904 func scanstack(gp *g, gcw *gcWork) int64 {
905 if readgstatus(gp)&_Gscan == 0 {
906 print("runtime:scanstack: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", hex(readgstatus(gp)), "\n")
907 throw("scanstack - bad status")
908 }
909
910 switch readgstatus(gp) &^ _Gscan {
911 default:
912 print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
913 throw("mark - bad status")
914 case _Gdead, _Gdeadextra:
915 return 0
916 case _Grunning:
917 print("runtime: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
918 throw("scanstack: goroutine not stopped")
919 case _Grunnable, _Gsyscall, _Gwaiting, _Gleaked:
920
921 }
922
923 if gp == getg() {
924 throw("can't scan our own stack")
925 }
926
927
928
929
930 var sp uintptr
931 if gp.syscallsp != 0 {
932 sp = gp.syscallsp
933 } else {
934 sp = gp.sched.sp
935 }
936 scannedSize := gp.stack.hi - sp
937
938
939
940 p := getg().m.p.ptr()
941 p.scannedStackSize += uint64(scannedSize)
942 p.scannedStacks++
943
944 if isShrinkStackSafe(gp) {
945
946 shrinkstack(gp)
947 } else {
948
949 gp.preemptShrink = true
950 }
951
952 var state stackScanState
953 state.stack = gp.stack
954
955 if stackTraceDebug {
956 println("stack trace goroutine", gp.goid)
957 }
958
959 if debugScanConservative && gp.asyncSafePoint {
960 print("scanning async preempted goroutine ", gp.goid, " stack [", hex(gp.stack.lo), ",", hex(gp.stack.hi), ")\n")
961 }
962
963
964
965
966 if gp.sched.ctxt != nil {
967 scanblock(uintptr(unsafe.Pointer(&gp.sched.ctxt)), goarch.PtrSize, &oneptrmask[0], gcw, &state)
968 }
969
970
971 if gp.asyncSafePoint {
972 xRegScan(gp, gcw, &state)
973 }
974
975
976 var u unwinder
977 for u.init(gp, 0); u.valid(); u.next() {
978 scanframeworker(&u.frame, &state, gcw)
979 }
980
981
982
983
984
985 for d := gp._defer; d != nil; d = d.link {
986 if d.fn != nil {
987
988
989 scanblock(uintptr(unsafe.Pointer(&d.fn)), goarch.PtrSize, &oneptrmask[0], gcw, &state)
990 }
991 if d.link != nil {
992
993
994 scanblock(uintptr(unsafe.Pointer(&d.link)), goarch.PtrSize, &oneptrmask[0], gcw, &state)
995 }
996
997
998
999 if d.heap {
1000 scanblock(uintptr(unsafe.Pointer(&d)), goarch.PtrSize, &oneptrmask[0], gcw, &state)
1001 }
1002 }
1003 if gp._panic != nil {
1004
1005 state.putPtr(uintptr(unsafe.Pointer(gp._panic)), false)
1006 }
1007
1008
1009
1010
1011
1012
1013 state.buildIndex()
1014 for {
1015 p, conservative := state.getPtr()
1016 if p == 0 {
1017 break
1018 }
1019 obj := state.findObject(p)
1020 if obj == nil {
1021 continue
1022 }
1023 r := obj.r
1024 if r == nil {
1025
1026 continue
1027 }
1028 obj.setRecord(nil)
1029 if stackTraceDebug {
1030 printlock()
1031 print(" live stkobj at", hex(state.stack.lo+uintptr(obj.off)), "of size", obj.size)
1032 if conservative {
1033 print(" (conservative)")
1034 }
1035 println()
1036 printunlock()
1037 }
1038 ptrBytes, gcData := r.gcdata()
1039 b := state.stack.lo + uintptr(obj.off)
1040 if conservative {
1041 scanConservative(b, ptrBytes, gcData, gcw, &state)
1042 } else {
1043 scanblock(b, ptrBytes, gcData, gcw, &state)
1044 }
1045 }
1046
1047
1048
1049 for state.head != nil {
1050 x := state.head
1051 state.head = x.next
1052 if stackTraceDebug {
1053 for i := 0; i < x.nobj; i++ {
1054 obj := &x.obj[i]
1055 if obj.r == nil {
1056 continue
1057 }
1058 println(" dead stkobj at", hex(gp.stack.lo+uintptr(obj.off)), "of size", obj.r.size)
1059
1060 }
1061 }
1062 x.nobj = 0
1063 putempty((*workbuf)(unsafe.Pointer(x)))
1064 }
1065 if state.buf != nil || state.cbuf != nil || state.freeBuf != nil {
1066 throw("remaining pointer buffers")
1067 }
1068 return int64(scannedSize)
1069 }
1070
1071
1072
1073
1074 func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) {
1075 if _DebugGC > 1 && frame.continpc != 0 {
1076 print("scanframe ", funcname(frame.fn), "\n")
1077 }
1078
1079 isAsyncPreempt := frame.fn.valid() && frame.fn.funcID == abi.FuncID_asyncPreempt
1080 isDebugCall := frame.fn.valid() && frame.fn.funcID == abi.FuncID_debugCallV2
1081 if state.conservative || isAsyncPreempt || isDebugCall {
1082 if debugScanConservative {
1083 println("conservatively scanning function", funcname(frame.fn), "at PC", hex(frame.continpc))
1084 }
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094 if frame.varp != 0 {
1095 size := frame.varp - frame.sp
1096 if size > 0 {
1097 scanConservative(frame.sp, size, nil, gcw, state)
1098 }
1099 }
1100
1101
1102 if n := frame.argBytes(); n != 0 {
1103
1104
1105 scanConservative(frame.argp, n, nil, gcw, state)
1106 }
1107
1108 if isAsyncPreempt || isDebugCall {
1109
1110
1111
1112
1113 state.conservative = true
1114 } else {
1115
1116
1117
1118 state.conservative = false
1119 }
1120 return
1121 }
1122
1123 locals, args, objs := frame.getStackMap(false)
1124
1125
1126 if locals.n > 0 {
1127 size := uintptr(locals.n) * goarch.PtrSize
1128 scanblock(frame.varp-size, size, locals.bytedata, gcw, state)
1129 }
1130
1131
1132 if args.n > 0 {
1133 scanblock(frame.argp, uintptr(args.n)*goarch.PtrSize, args.bytedata, gcw, state)
1134 }
1135
1136
1137 if frame.varp != 0 {
1138
1139
1140
1141 for i := range objs {
1142 obj := &objs[i]
1143 off := obj.off
1144 base := frame.varp
1145 if off >= 0 {
1146 base = frame.argp
1147 }
1148 ptr := base + uintptr(off)
1149 if ptr < frame.sp {
1150
1151 continue
1152 }
1153 if stackTraceDebug {
1154 println("stkobj at", hex(ptr), "of size", obj.size)
1155 }
1156 state.addObject(ptr, obj)
1157 }
1158 }
1159 }
1160
1161 type gcDrainFlags int
1162
1163 const (
1164 gcDrainUntilPreempt gcDrainFlags = 1 << iota
1165 gcDrainFlushBgCredit
1166 gcDrainIdle
1167 gcDrainFractional
1168 )
1169
1170
1171
1172 func gcDrainMarkWorkerIdle(gcw *gcWork) {
1173 gcDrain(gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit)
1174 }
1175
1176
1177
1178 func gcDrainMarkWorkerDedicated(gcw *gcWork, untilPreempt bool) {
1179 flags := gcDrainFlushBgCredit
1180 if untilPreempt {
1181 flags |= gcDrainUntilPreempt
1182 }
1183 gcDrain(gcw, flags)
1184 }
1185
1186
1187
1188 func gcDrainMarkWorkerFractional(gcw *gcWork) {
1189 gcDrain(gcw, gcDrainFractional|gcDrainUntilPreempt|gcDrainFlushBgCredit)
1190 }
1191
1192
1193
1194
1195
1196 func gcNextMarkRoot() (uint32, bool) {
1197 if !work.goroutineLeak.enabled {
1198
1199 job := work.markrootNext.Add(1) - 1
1200 return job, job < work.markrootJobs.Load()
1201 }
1202
1203
1204 for next, jobs := work.markrootNext.Load(), work.markrootJobs.Load(); next < jobs; next = work.markrootNext.Load() {
1205
1206 if work.markrootNext.CompareAndSwap(next, next+1) {
1207
1208 return next, true
1209 }
1210 }
1211 return 0, false
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244 func gcDrain(gcw *gcWork, flags gcDrainFlags) {
1245 if !writeBarrier.enabled {
1246 throw("gcDrain phase incorrect")
1247 }
1248
1249
1250
1251 gp := getg().m.curg
1252 pp := gp.m.p.ptr()
1253 preemptible := flags&gcDrainUntilPreempt != 0
1254 flushBgCredit := flags&gcDrainFlushBgCredit != 0
1255 idle := flags&gcDrainIdle != 0
1256
1257 initScanWork := gcw.heapScanWork
1258
1259
1260
1261 checkWork := int64(1<<63 - 1)
1262 var check func() bool
1263 if flags&(gcDrainIdle|gcDrainFractional) != 0 {
1264 checkWork = initScanWork + drainCheckThreshold
1265 if idle {
1266 check = pollWork
1267 } else if flags&gcDrainFractional != 0 {
1268 check = pollFractionalWorkerExit
1269 }
1270 }
1271
1272 if work.markrootNext.Load() < work.markrootJobs.Load() {
1273
1274
1275 for !(gp.preempt && (preemptible || sched.gcwaiting.Load() || pp.runSafePointFn != 0)) {
1276 job, ok := gcNextMarkRoot()
1277 if !ok {
1278 break
1279 }
1280 markroot(gcw, job, flushBgCredit)
1281 if check != nil && check() {
1282 goto done
1283 }
1284
1285
1286 if goexperiment.GreenTeaGC && gcw.mayNeedWorker {
1287 gcw.mayNeedWorker = false
1288 if gcphase == _GCmark {
1289 gcController.enlistWorker()
1290 }
1291 }
1292 }
1293 }
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305 for !(gp.preempt && (preemptible || sched.gcwaiting.Load() || pp.runSafePointFn != 0)) {
1306
1307
1308
1309
1310
1311 if work.full == 0 {
1312 gcw.balance()
1313 }
1314
1315
1316 var b uintptr
1317 var s objptr
1318 if b = gcw.tryGetObjFast(); b == 0 {
1319 if s = gcw.tryGetSpanFast(); s == 0 {
1320 if b = gcw.tryGetObj(); b == 0 {
1321 if s = gcw.tryGetSpan(); s == 0 {
1322
1323
1324
1325 wbBufFlush()
1326 if b = gcw.tryGetObj(); b == 0 {
1327 if s = gcw.tryGetSpan(); s == 0 {
1328 s = gcw.tryStealSpan()
1329 }
1330 }
1331 }
1332 }
1333 }
1334 }
1335 if b != 0 {
1336 scanObject(b, gcw)
1337 } else if s != 0 {
1338 scanSpan(s, gcw)
1339 } else {
1340
1341 break
1342 }
1343
1344
1345 if goexperiment.GreenTeaGC && gcw.mayNeedWorker {
1346 gcw.mayNeedWorker = false
1347 if gcphase == _GCmark {
1348 gcController.enlistWorker()
1349 }
1350 }
1351
1352
1353
1354
1355 if gcw.heapScanWork >= gcCreditSlack {
1356 gcController.heapScanWork.Add(gcw.heapScanWork)
1357 if flushBgCredit {
1358 gcFlushBgCredit(gcw.heapScanWork - initScanWork)
1359 initScanWork = 0
1360 }
1361 checkWork -= gcw.heapScanWork
1362 gcw.heapScanWork = 0
1363
1364 if checkWork <= 0 {
1365 checkWork += drainCheckThreshold
1366 if check != nil && check() {
1367 break
1368 }
1369 }
1370 }
1371 }
1372
1373 done:
1374
1375 if gcw.heapScanWork > 0 {
1376 gcController.heapScanWork.Add(gcw.heapScanWork)
1377 if flushBgCredit {
1378 gcFlushBgCredit(gcw.heapScanWork - initScanWork)
1379 }
1380 gcw.heapScanWork = 0
1381 }
1382 }
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397 func gcDrainN(gcw *gcWork, scanWork int64) int64 {
1398 if !writeBarrier.enabled {
1399 throw("gcDrainN phase incorrect")
1400 }
1401
1402
1403
1404 workFlushed := -gcw.heapScanWork
1405
1406
1407
1408 gp := getg().m.curg
1409 for !gp.preempt && !gcCPULimiter.limiting() && workFlushed+gcw.heapScanWork < scanWork {
1410
1411 if work.full == 0 {
1412 gcw.balance()
1413 }
1414
1415
1416 var b uintptr
1417 var s objptr
1418 if b = gcw.tryGetObjFast(); b == 0 {
1419 if s = gcw.tryGetSpanFast(); s == 0 {
1420 if b = gcw.tryGetObj(); b == 0 {
1421 if s = gcw.tryGetSpan(); s == 0 {
1422
1423
1424
1425 wbBufFlush()
1426 if b = gcw.tryGetObj(); b == 0 {
1427 if s = gcw.tryGetSpan(); s == 0 {
1428
1429 if work.markrootNext.Load() < work.markrootJobs.Load() {
1430 job, ok := gcNextMarkRoot()
1431 if ok {
1432 workFlushed += markroot(gcw, job, false)
1433 continue
1434 }
1435 }
1436 s = gcw.tryStealSpan()
1437 }
1438 }
1439 }
1440 }
1441 }
1442 }
1443 if b != 0 {
1444 scanObject(b, gcw)
1445 } else if s != 0 {
1446 scanSpan(s, gcw)
1447 } else {
1448
1449 break
1450 }
1451
1452
1453 if gcw.heapScanWork >= gcCreditSlack {
1454 gcController.heapScanWork.Add(gcw.heapScanWork)
1455 workFlushed += gcw.heapScanWork
1456 gcw.heapScanWork = 0
1457 }
1458
1459
1460 if goexperiment.GreenTeaGC && gcw.mayNeedWorker {
1461 gcw.mayNeedWorker = false
1462 if gcphase == _GCmark {
1463 gcController.enlistWorker()
1464 }
1465 }
1466 }
1467
1468
1469
1470
1471
1472 return workFlushed + gcw.heapScanWork
1473 }
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484 func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) {
1485
1486
1487
1488 b := b0
1489 n := n0
1490
1491 for i := uintptr(0); i < n; {
1492
1493 bits := uint32(*addb(ptrmask, i/(goarch.PtrSize*8)))
1494 if bits == 0 {
1495 i += goarch.PtrSize * 8
1496 continue
1497 }
1498 for j := 0; j < 8 && i < n; j++ {
1499 if bits&1 != 0 {
1500
1501 p := *(*uintptr)(unsafe.Pointer(b + i))
1502 if p != 0 {
1503 if stk != nil && p >= stk.stack.lo && p < stk.stack.hi {
1504 stk.putPtr(p, false)
1505 } else {
1506 if !tryDeferToSpanScan(p, gcw) {
1507 if obj, span, objIndex := findObject(p, b, i); obj != 0 {
1508 greyobject(obj, b, i, span, gcw, objIndex)
1509 }
1510 }
1511 }
1512 }
1513 }
1514 bits >>= 1
1515 i += goarch.PtrSize
1516 }
1517 }
1518 }
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528 func scanConservative(b, n uintptr, ptrmask *uint8, gcw *gcWork, state *stackScanState) {
1529 if debugScanConservative {
1530 printlock()
1531 print("conservatively scanning [", hex(b), ",", hex(b+n), ")\n")
1532 hexdumpWords(b, n, func(p uintptr, m hexdumpMarker) {
1533 if ptrmask != nil {
1534 word := (p - b) / goarch.PtrSize
1535 bits := *addb(ptrmask, word/8)
1536 if (bits>>(word%8))&1 == 0 {
1537 return
1538 }
1539 }
1540
1541 val := *(*uintptr)(unsafe.Pointer(p))
1542 if state != nil && state.stack.lo <= val && val < state.stack.hi {
1543 m.start()
1544 println("ptr to stack")
1545 return
1546 }
1547
1548 span := spanOfHeap(val)
1549 if span == nil {
1550 return
1551 }
1552 idx := span.objIndex(val)
1553 if span.isFreeOrNewlyAllocated(idx) {
1554 return
1555 }
1556 m.start()
1557 println("ptr to heap")
1558 })
1559 printunlock()
1560 }
1561
1562 for i := uintptr(0); i < n; i += goarch.PtrSize {
1563 if ptrmask != nil {
1564 word := i / goarch.PtrSize
1565 bits := *addb(ptrmask, word/8)
1566 if bits == 0 {
1567
1568
1569
1570
1571
1572
1573 if i%(goarch.PtrSize*8) != 0 {
1574 throw("misaligned mask")
1575 }
1576 i += goarch.PtrSize*8 - goarch.PtrSize
1577 continue
1578 }
1579 if (bits>>(word%8))&1 == 0 {
1580 continue
1581 }
1582 }
1583
1584 val := *(*uintptr)(unsafe.Pointer(b + i))
1585
1586
1587 if state != nil && state.stack.lo <= val && val < state.stack.hi {
1588
1589
1590
1591
1592
1593
1594
1595
1596 state.putPtr(val, true)
1597 continue
1598 }
1599
1600
1601 span := spanOfHeap(val)
1602 if span == nil {
1603 continue
1604 }
1605
1606
1607
1608
1609
1610 idx := span.objIndex(val)
1611 if span.isFreeOrNewlyAllocated(idx) {
1612 continue
1613 }
1614
1615
1616 obj := span.base() + idx*span.elemsize
1617 if !tryDeferToSpanScan(obj, gcw) {
1618 greyobject(obj, b, i, span, gcw, idx)
1619 }
1620 }
1621 }
1622
1623
1624
1625
1626
1627
1628 func shade(b uintptr) {
1629 gcw := &getg().m.p.ptr().gcw
1630 if !tryDeferToSpanScan(b, gcw) {
1631 if obj, span, objIndex := findObject(b, 0, 0); obj != 0 {
1632 greyobject(obj, 0, 0, span, gcw, objIndex)
1633 }
1634 }
1635 }
1636
1637
1638
1639
1640
1641
1642
1643
1644 func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintptr) {
1645
1646 if obj&(goarch.PtrSize-1) != 0 {
1647 throw("greyobject: obj not pointer-aligned")
1648 }
1649 mbits := span.markBitsForIndex(objIndex)
1650
1651 if useCheckmark {
1652 if setCheckmark(obj, base, off, mbits) {
1653
1654 return
1655 }
1656 if debug.checkfinalizers > 1 {
1657 print(" mark ", hex(obj), " found at *(", hex(base), "+", hex(off), ")\n")
1658 }
1659 } else {
1660 if debug.gccheckmark > 0 && span.isFree(objIndex) {
1661 print("runtime: marking free object ", hex(obj), " found at *(", hex(base), "+", hex(off), ")\n")
1662 gcDumpObject("base", base, off)
1663 gcDumpObject("obj", obj, ^uintptr(0))
1664 getg().m.traceback = 2
1665 throw("marking free object")
1666 }
1667
1668
1669 if mbits.isMarked() {
1670 return
1671 }
1672 mbits.setMarked()
1673
1674
1675 arena, pageIdx, pageMask := pageIndexOf(span.base())
1676 if arena.pageMarks[pageIdx]&pageMask == 0 {
1677 atomic.Or8(&arena.pageMarks[pageIdx], pageMask)
1678 }
1679 }
1680
1681
1682
1683 if span.spanclass.noscan() {
1684 gcw.bytesMarked += uint64(span.elemsize)
1685 return
1686 }
1687
1688
1689
1690
1691
1692 sys.Prefetch(obj)
1693
1694 if !gcw.putObjFast(obj) {
1695 gcw.putObj(obj)
1696 }
1697 }
1698
1699
1700
1701 func gcDumpObject(label string, obj, off uintptr) {
1702 s := spanOf(obj)
1703 print(label, "=", hex(obj))
1704 if s == nil {
1705 print(" s=nil\n")
1706 return
1707 }
1708 print(" s.base()=", hex(s.base()), " s.limit=", hex(s.limit), " s.spanclass=", s.spanclass, " s.elemsize=", s.elemsize, " s.state=")
1709 if state := s.state.get(); 0 <= state && int(state) < len(mSpanStateNames) {
1710 print(mSpanStateNames[state], "\n")
1711 } else {
1712 print("unknown(", state, ")\n")
1713 }
1714
1715 skipped := false
1716 size := s.elemsize
1717 if s.state.get() == mSpanManual && size == 0 {
1718
1719
1720
1721 size = off + goarch.PtrSize
1722 }
1723 for i := uintptr(0); i < size; i += goarch.PtrSize {
1724
1725
1726
1727 if !(i < 128*goarch.PtrSize || off-16*goarch.PtrSize < i && i < off+16*goarch.PtrSize) {
1728 skipped = true
1729 continue
1730 }
1731 if skipped {
1732 print(" ...\n")
1733 skipped = false
1734 }
1735 print(" *(", label, "+", i, ") = ", hex(*(*uintptr)(unsafe.Pointer(obj + i))))
1736 if i == off {
1737 print(" <==")
1738 }
1739 print("\n")
1740 }
1741 if skipped {
1742 print(" ...\n")
1743 }
1744 }
1745
1746
1747
1748
1749
1750
1751
1752
1753 func gcmarknewobject(span *mspan, obj uintptr) {
1754 if useCheckmark {
1755 throw("gcmarknewobject called while doing checkmark")
1756 }
1757 if gcphase == _GCmarktermination {
1758
1759 throw("mallocgc called with gcphase == _GCmarktermination")
1760 }
1761
1762
1763 objIndex := span.objIndex(obj)
1764 span.markBitsForIndex(objIndex).setMarked()
1765 if goexperiment.GreenTeaGC && gcUsesSpanInlineMarkBits(span.elemsize) {
1766
1767 span.scannedBitsForIndex(objIndex).setMarked()
1768 }
1769
1770
1771 arena, pageIdx, pageMask := pageIndexOf(span.base())
1772 if arena.pageMarks[pageIdx]&pageMask == 0 {
1773 atomic.Or8(&arena.pageMarks[pageIdx], pageMask)
1774 }
1775
1776 gcw := &getg().m.p.ptr().gcw
1777 gcw.bytesMarked += uint64(span.elemsize)
1778 }
1779
1780
1781
1782
1783 func gcMarkTinyAllocs() {
1784 assertWorldStopped()
1785
1786 for _, p := range allp {
1787 c := p.mcache
1788 if c == nil || c.tiny == 0 {
1789 continue
1790 }
1791 gcw := &p.gcw
1792 if !tryDeferToSpanScan(c.tiny, gcw) {
1793 _, span, objIndex := findObject(c.tiny, 0, 0)
1794 greyobject(c.tiny, 0, 0, span, gcw, objIndex)
1795 }
1796 }
1797 }
1798
View as plain text