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