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 isSigPanic := frame.fn.valid() && frame.fn.funcID == abi.FuncID_sigpanic
1098 if usesLR && (isSigPanic || isAsyncPreempt || isDebugCall) {
1099
1100
1101
1102
1103
1104 size += alignUp(sys.MinFrameSize, sys.StackAlign)
1105 }
1106 scanConservative(frame.sp, size, nil, gcw, state)
1107 }
1108 }
1109
1110
1111 if n := frame.argBytes(); n != 0 {
1112
1113
1114 scanConservative(frame.argp, n, nil, gcw, state)
1115 }
1116
1117 if isAsyncPreempt || isDebugCall {
1118
1119
1120
1121
1122 state.conservative = true
1123 } else {
1124
1125
1126
1127 state.conservative = false
1128 }
1129 return
1130 }
1131
1132 locals, args, objs := frame.getStackMap(false)
1133
1134
1135 if locals.n > 0 {
1136 size := uintptr(locals.n) * goarch.PtrSize
1137 scanblock(frame.varp-size, size, locals.bytedata, gcw, state)
1138 }
1139
1140
1141 if args.n > 0 {
1142 scanblock(frame.argp, uintptr(args.n)*goarch.PtrSize, args.bytedata, gcw, state)
1143 }
1144
1145
1146 if frame.varp != 0 {
1147
1148
1149
1150 for i := range objs {
1151 obj := &objs[i]
1152 off := obj.off
1153 base := frame.varp
1154 if off >= 0 {
1155 base = frame.argp
1156 }
1157 ptr := base + uintptr(off)
1158 if ptr < frame.sp {
1159
1160 continue
1161 }
1162 if stackTraceDebug {
1163 println("stkobj at", hex(ptr), "of size", obj.size)
1164 }
1165 state.addObject(ptr, obj)
1166 }
1167 }
1168 }
1169
1170 type gcDrainFlags int
1171
1172 const (
1173 gcDrainUntilPreempt gcDrainFlags = 1 << iota
1174 gcDrainFlushBgCredit
1175 gcDrainIdle
1176 gcDrainFractional
1177 )
1178
1179
1180
1181 func gcDrainMarkWorkerIdle(gcw *gcWork) {
1182 gcDrain(gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit)
1183 }
1184
1185
1186
1187 func gcDrainMarkWorkerDedicated(gcw *gcWork, untilPreempt bool) {
1188 flags := gcDrainFlushBgCredit
1189 if untilPreempt {
1190 flags |= gcDrainUntilPreempt
1191 }
1192 gcDrain(gcw, flags)
1193 }
1194
1195
1196
1197 func gcDrainMarkWorkerFractional(gcw *gcWork) {
1198 gcDrain(gcw, gcDrainFractional|gcDrainUntilPreempt|gcDrainFlushBgCredit)
1199 }
1200
1201
1202
1203
1204
1205 func gcNextMarkRoot() (uint32, bool) {
1206 if !work.goroutineLeak.enabled {
1207
1208 job := work.markrootNext.Add(1) - 1
1209 return job, job < work.markrootJobs.Load()
1210 }
1211
1212
1213 for next, jobs := work.markrootNext.Load(), work.markrootJobs.Load(); next < jobs; next = work.markrootNext.Load() {
1214
1215 if work.markrootNext.CompareAndSwap(next, next+1) {
1216
1217 return next, true
1218 }
1219 }
1220 return 0, false
1221 }
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253 func gcDrain(gcw *gcWork, flags gcDrainFlags) {
1254 if !writeBarrier.enabled {
1255 throw("gcDrain phase incorrect")
1256 }
1257
1258
1259
1260 gp := getg().m.curg
1261 pp := gp.m.p.ptr()
1262 preemptible := flags&gcDrainUntilPreempt != 0
1263 flushBgCredit := flags&gcDrainFlushBgCredit != 0
1264 idle := flags&gcDrainIdle != 0
1265
1266 initScanWork := gcw.heapScanWork
1267
1268
1269
1270 checkWork := int64(1<<63 - 1)
1271 var check func() bool
1272 if flags&(gcDrainIdle|gcDrainFractional) != 0 {
1273 checkWork = initScanWork + drainCheckThreshold
1274 if idle {
1275 check = pollWork
1276 } else if flags&gcDrainFractional != 0 {
1277 check = pollFractionalWorkerExit
1278 }
1279 }
1280
1281 if work.markrootNext.Load() < work.markrootJobs.Load() {
1282
1283
1284 for !(gp.preempt && (preemptible || sched.gcwaiting.Load() || pp.runSafePointFn != 0)) {
1285 job, ok := gcNextMarkRoot()
1286 if !ok {
1287 break
1288 }
1289 markroot(gcw, job, flushBgCredit)
1290 if check != nil && check() {
1291 goto done
1292 }
1293
1294
1295 if goexperiment.GreenTeaGC && gcw.mayNeedWorker {
1296 gcw.mayNeedWorker = false
1297 if gcphase == _GCmark {
1298 gcController.enlistWorker()
1299 }
1300 }
1301 }
1302 }
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314 for !(gp.preempt && (preemptible || sched.gcwaiting.Load() || pp.runSafePointFn != 0)) {
1315
1316
1317
1318
1319
1320 if work.full == 0 {
1321 gcw.balance()
1322 }
1323
1324
1325 var b uintptr
1326 var s objptr
1327 if b = gcw.tryGetObjFast(); b == 0 {
1328 if s = gcw.tryGetSpanFast(); s == 0 {
1329 if b = gcw.tryGetObj(); b == 0 {
1330 if s = gcw.tryGetSpan(); s == 0 {
1331
1332
1333
1334 wbBufFlush()
1335 if b = gcw.tryGetObj(); b == 0 {
1336 if s = gcw.tryGetSpan(); s == 0 {
1337 s = gcw.tryStealSpan()
1338 }
1339 }
1340 }
1341 }
1342 }
1343 }
1344 if b != 0 {
1345 scanObject(b, gcw)
1346 } else if s != 0 {
1347 scanSpan(s, gcw)
1348 } else {
1349
1350 break
1351 }
1352
1353
1354 if goexperiment.GreenTeaGC && gcw.mayNeedWorker {
1355 gcw.mayNeedWorker = false
1356 if gcphase == _GCmark {
1357 gcController.enlistWorker()
1358 }
1359 }
1360
1361
1362
1363
1364 if gcw.heapScanWork >= gcCreditSlack {
1365 gcController.heapScanWork.Add(gcw.heapScanWork)
1366 if flushBgCredit {
1367 gcFlushBgCredit(gcw.heapScanWork - initScanWork)
1368 initScanWork = 0
1369 }
1370 checkWork -= gcw.heapScanWork
1371 gcw.heapScanWork = 0
1372
1373 if checkWork <= 0 {
1374 checkWork += drainCheckThreshold
1375 if check != nil && check() {
1376 break
1377 }
1378 }
1379 }
1380 }
1381
1382 done:
1383
1384 if gcw.heapScanWork > 0 {
1385 gcController.heapScanWork.Add(gcw.heapScanWork)
1386 if flushBgCredit {
1387 gcFlushBgCredit(gcw.heapScanWork - initScanWork)
1388 }
1389 gcw.heapScanWork = 0
1390 }
1391 }
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406 func gcDrainN(gcw *gcWork, scanWork int64) int64 {
1407 if !writeBarrier.enabled {
1408 throw("gcDrainN phase incorrect")
1409 }
1410
1411
1412
1413 workFlushed := -gcw.heapScanWork
1414
1415
1416
1417 gp := getg().m.curg
1418 for !gp.preempt && !gcCPULimiter.limiting() && workFlushed+gcw.heapScanWork < scanWork {
1419
1420 if work.full == 0 {
1421 gcw.balance()
1422 }
1423
1424
1425 var b uintptr
1426 var s objptr
1427 if b = gcw.tryGetObjFast(); b == 0 {
1428 if s = gcw.tryGetSpanFast(); s == 0 {
1429 if b = gcw.tryGetObj(); b == 0 {
1430 if s = gcw.tryGetSpan(); s == 0 {
1431
1432
1433
1434 wbBufFlush()
1435 if b = gcw.tryGetObj(); b == 0 {
1436 if s = gcw.tryGetSpan(); s == 0 {
1437
1438 if work.markrootNext.Load() < work.markrootJobs.Load() {
1439 job, ok := gcNextMarkRoot()
1440 if ok {
1441 workFlushed += markroot(gcw, job, false)
1442 continue
1443 }
1444 }
1445 s = gcw.tryStealSpan()
1446 }
1447 }
1448 }
1449 }
1450 }
1451 }
1452 if b != 0 {
1453 scanObject(b, gcw)
1454 } else if s != 0 {
1455 scanSpan(s, gcw)
1456 } else {
1457
1458 break
1459 }
1460
1461
1462 if gcw.heapScanWork >= gcCreditSlack {
1463 gcController.heapScanWork.Add(gcw.heapScanWork)
1464 workFlushed += gcw.heapScanWork
1465 gcw.heapScanWork = 0
1466 }
1467
1468
1469 if goexperiment.GreenTeaGC && gcw.mayNeedWorker {
1470 gcw.mayNeedWorker = false
1471 if gcphase == _GCmark {
1472 gcController.enlistWorker()
1473 }
1474 }
1475 }
1476
1477
1478
1479
1480
1481 return workFlushed + gcw.heapScanWork
1482 }
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493 func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) {
1494
1495
1496
1497 b := b0
1498 n := n0
1499
1500 for i := uintptr(0); i < n; {
1501
1502 bits := uint32(*addb(ptrmask, i/(goarch.PtrSize*8)))
1503 if bits == 0 {
1504 i += goarch.PtrSize * 8
1505 continue
1506 }
1507 for j := 0; j < 8 && i < n; j++ {
1508 if bits&1 != 0 {
1509
1510 p := *(*uintptr)(unsafe.Pointer(b + i))
1511 if p != 0 {
1512 if stk != nil && p >= stk.stack.lo && p < stk.stack.hi {
1513 stk.putPtr(p, false)
1514 } else {
1515 if !tryDeferToSpanScan(p, gcw) {
1516 if obj, span, objIndex := findObject(p, b, i); obj != 0 {
1517 greyobject(obj, b, i, span, gcw, objIndex)
1518 }
1519 }
1520 }
1521 }
1522 }
1523 bits >>= 1
1524 i += goarch.PtrSize
1525 }
1526 }
1527 }
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537 func scanConservative(b, n uintptr, ptrmask *uint8, gcw *gcWork, state *stackScanState) {
1538 if debugScanConservative {
1539 printlock()
1540 print("conservatively scanning [", hex(b), ",", hex(b+n), ")\n")
1541 hexdumpWords(b, n, func(p uintptr, m hexdumpMarker) {
1542 if ptrmask != nil {
1543 word := (p - b) / goarch.PtrSize
1544 bits := *addb(ptrmask, word/8)
1545 if (bits>>(word%8))&1 == 0 {
1546 return
1547 }
1548 }
1549
1550 val := *(*uintptr)(unsafe.Pointer(p))
1551 if state != nil && state.stack.lo <= val && val < state.stack.hi {
1552 m.start()
1553 println("ptr to stack")
1554 return
1555 }
1556
1557 span := spanOfHeap(val)
1558 if span == nil {
1559 return
1560 }
1561 idx := span.objIndex(val)
1562 if span.isFreeOrNewlyAllocated(idx) {
1563 return
1564 }
1565 m.start()
1566 println("ptr to heap")
1567 })
1568 printunlock()
1569 }
1570
1571 for i := uintptr(0); i < n; i += goarch.PtrSize {
1572 if ptrmask != nil {
1573 word := i / goarch.PtrSize
1574 bits := *addb(ptrmask, word/8)
1575 if bits == 0 {
1576
1577
1578
1579
1580
1581
1582 if i%(goarch.PtrSize*8) != 0 {
1583 throw("misaligned mask")
1584 }
1585 i += goarch.PtrSize*8 - goarch.PtrSize
1586 continue
1587 }
1588 if (bits>>(word%8))&1 == 0 {
1589 continue
1590 }
1591 }
1592
1593 val := *(*uintptr)(unsafe.Pointer(b + i))
1594
1595
1596 if state != nil && state.stack.lo <= val && val < state.stack.hi {
1597
1598
1599
1600
1601
1602
1603
1604
1605 state.putPtr(val, true)
1606 continue
1607 }
1608
1609
1610 span := spanOfHeap(val)
1611 if span == nil {
1612 continue
1613 }
1614
1615
1616
1617
1618
1619 idx := span.objIndex(val)
1620 if span.isFreeOrNewlyAllocated(idx) {
1621 continue
1622 }
1623
1624
1625 obj := span.base() + idx*span.elemsize
1626 if !tryDeferToSpanScan(obj, gcw) {
1627 greyobject(obj, b, i, span, gcw, idx)
1628 }
1629 }
1630 }
1631
1632
1633
1634
1635
1636
1637 func shade(b uintptr) {
1638 gcw := &getg().m.p.ptr().gcw
1639 if !tryDeferToSpanScan(b, gcw) {
1640 if obj, span, objIndex := findObject(b, 0, 0); obj != 0 {
1641 greyobject(obj, 0, 0, span, gcw, objIndex)
1642 }
1643 }
1644 }
1645
1646
1647
1648
1649
1650
1651
1652
1653 func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintptr) {
1654
1655 if obj&(goarch.PtrSize-1) != 0 {
1656 throw("greyobject: obj not pointer-aligned")
1657 }
1658 mbits := span.markBitsForIndex(objIndex)
1659
1660 if useCheckmark {
1661 if setCheckmark(obj, base, off, mbits) {
1662
1663 return
1664 }
1665 if debug.checkfinalizers > 1 {
1666 print(" mark ", hex(obj), " found at *(", hex(base), "+", hex(off), ")\n")
1667 }
1668 } else {
1669 if debug.gccheckmark > 0 && span.isFree(objIndex) {
1670 print("runtime: marking free object ", hex(obj), " found at *(", hex(base), "+", hex(off), ")\n")
1671 gcDumpObject("base", base, off)
1672 gcDumpObject("obj", obj, ^uintptr(0))
1673 getg().m.traceback = 2
1674 throw("marking free object")
1675 }
1676
1677
1678 if mbits.isMarked() {
1679 return
1680 }
1681 mbits.setMarked()
1682
1683
1684 arena, pageIdx, pageMask := pageIndexOf(span.base())
1685 if arena.pageMarks[pageIdx]&pageMask == 0 {
1686 atomic.Or8(&arena.pageMarks[pageIdx], pageMask)
1687 }
1688 }
1689
1690
1691
1692 if span.spanclass.noscan() {
1693 gcw.bytesMarked += uint64(span.elemsize)
1694 return
1695 }
1696
1697
1698
1699
1700
1701 sys.Prefetch(obj)
1702
1703 if !gcw.putObjFast(obj) {
1704 gcw.putObj(obj)
1705 }
1706 }
1707
1708
1709
1710 func gcDumpObject(label string, obj, off uintptr) {
1711 s := spanOf(obj)
1712 print(label, "=", hex(obj))
1713 if s == nil {
1714 print(" s=nil\n")
1715 return
1716 }
1717 print(" s.base()=", hex(s.base()), " s.limit=", hex(s.limit), " s.spanclass=", s.spanclass, " s.elemsize=", s.elemsize, " s.state=")
1718 if state := s.state.get(); 0 <= state && int(state) < len(mSpanStateNames) {
1719 print(mSpanStateNames[state], "\n")
1720 } else {
1721 print("unknown(", state, ")\n")
1722 }
1723
1724 skipped := false
1725 size := s.elemsize
1726 if s.state.get() == mSpanManual && size == 0 {
1727
1728
1729
1730 size = off + goarch.PtrSize
1731 }
1732 for i := uintptr(0); i < size; i += goarch.PtrSize {
1733
1734
1735
1736 if !(i < 128*goarch.PtrSize || off-16*goarch.PtrSize < i && i < off+16*goarch.PtrSize) {
1737 skipped = true
1738 continue
1739 }
1740 if skipped {
1741 print(" ...\n")
1742 skipped = false
1743 }
1744 print(" *(", label, "+", i, ") = ", hex(*(*uintptr)(unsafe.Pointer(obj + i))))
1745 if i == off {
1746 print(" <==")
1747 }
1748 print("\n")
1749 }
1750 if skipped {
1751 print(" ...\n")
1752 }
1753 }
1754
1755
1756
1757
1758
1759
1760
1761
1762 func gcmarknewobject(span *mspan, obj uintptr) {
1763 if useCheckmark {
1764 throw("gcmarknewobject called while doing checkmark")
1765 }
1766 if gcphase == _GCmarktermination {
1767
1768 throw("mallocgc called with gcphase == _GCmarktermination")
1769 }
1770
1771
1772 objIndex := span.objIndex(obj)
1773 span.markBitsForIndex(objIndex).setMarked()
1774 if goexperiment.GreenTeaGC && gcUsesSpanInlineMarkBits(span.elemsize) {
1775
1776 span.scannedBitsForIndex(objIndex).setMarked()
1777 }
1778
1779
1780 arena, pageIdx, pageMask := pageIndexOf(span.base())
1781 if arena.pageMarks[pageIdx]&pageMask == 0 {
1782 atomic.Or8(&arena.pageMarks[pageIdx], pageMask)
1783 }
1784
1785 gcw := &getg().m.p.ptr().gcw
1786 gcw.bytesMarked += uint64(span.elemsize)
1787 }
1788
1789
1790
1791
1792 func gcMarkTinyAllocs() {
1793 assertWorldStopped()
1794
1795 for _, p := range allp {
1796 c := p.mcache
1797 if c == nil || c.tiny == 0 {
1798 continue
1799 }
1800 gcw := &p.gcw
1801 if !tryDeferToSpanScan(c.tiny, gcw) {
1802 _, span, objIndex := findObject(c.tiny, 0, 0)
1803 greyobject(c.tiny, 0, 0, span, gcw, objIndex)
1804 }
1805 }
1806 }
1807
View as plain text