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