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