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