1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2002-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23 
24 #define ERL_WANT_GC_INTERNALS__
25 
26 #include "sys.h"
27 #include "erl_vm.h"
28 #include "global.h"
29 #include "erl_process.h"
30 #include "erl_db.h"
31 #include "beam_catches.h"
32 #include "erl_binary.h"
33 #include "erl_bits.h"
34 #include "erl_map.h"
35 #include "error.h"
36 #include "big.h"
37 #include "erl_gc.h"
38 #include "dtrace-wrapper.h"
39 #include "erl_bif_unique.h"
40 #include "dist.h"
41 #include "erl_nfunc_sched.h"
42 #include "erl_proc_sig_queue.h"
43 #include "beam_common.h"
44 
45 #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1
46 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20
47 #define ERTS_INACT_WR_PB_LEAVE_LIMIT 10
48 #define ERTS_INACT_WR_PB_LEAVE_PERCENTAGE 10
49 
50 #if defined(DEBUG) || 0
51 #define ERTS_GC_DEBUG
52 #else
53 #undef ERTS_GC_DEBUG
54 #endif
55 #ifdef ERTS_GC_DEBUG
56 #  define ERTS_GC_ASSERT ASSERT
57 #else
58 #  define ERTS_GC_ASSERT(B) ((void) 1)
59 #endif
60 
61 #if defined(DEBUG) && 0
62 #  define HARDDEBUG 1
63 #endif
64 
65 /*
66  * Returns number of elements in an array.
67  */
68 #define ALENGTH(a) (sizeof(a)/sizeof(a[0]))
69 
70 /* Actual stack usage, note that this may include words in the redzone. */
71 # define STACK_SZ_ON_HEAP(p) (STACK_START(p) - STACK_TOP(p))
72 
73 # define OverRunCheck(P) \
74     if ((P)->stop < (P)->htop) { \
75         erts_fprintf(stderr, "hend=%p\n", (P)->hend); \
76         erts_fprintf(stderr, "stop=%p\n", (P)->stop); \
77         erts_fprintf(stderr, "htop=%p\n", (P)->htop); \
78         erts_fprintf(stderr, "heap=%p\n", (P)->heap); \
79         erts_exit(ERTS_ABORT_EXIT, "%s, line %d: %T: Overrun stack and heap\n", \
80 		 __FILE__,__LINE__,(P)->common.id); \
81     }
82 
83 #ifdef DEBUG
84 #define ErtsGcQuickSanityCheck(P)					\
85 do {									\
86     ASSERT((P)->heap < (P)->hend);					\
87     ASSERT((p)->abandoned_heap || (P)->heap_sz == (P)->hend - (P)->heap); \
88     ASSERT((P)->heap <= (P)->htop && (P)->htop <= (P)->hend);		\
89     ASSERT((P)->heap <= (P)->stop && (P)->stop <= (P)->hend);		\
90     ASSERT((p)->abandoned_heap || ((P)->heap <= (P)->high_water && (P)->high_water <= (P)->hend)); \
91     OverRunCheck((P));							\
92 } while (0)
93 #else
94 #define ErtsGcQuickSanityCheck(P)					\
95 do {									\
96     OverRunCheck((P));							\
97 } while (0)
98 #endif
99 /*
100  * This structure describes the rootset for the GC.
101  */
102 typedef struct roots {
103     Eterm* v;		/* Pointers to vectors with terms to GC
104 			 * (e.g. the stack).
105 			 */
106     Uint sz;		/* Size of each vector. */
107 } Roots;
108 
109 typedef struct {
110     Roots def[32];		/* Default storage. */
111     Roots* roots;		/* Pointer to root set array. */
112     Uint size;			/* Storage size. */
113     int num_roots;		/* Number of root arrays. */
114 } Rootset;
115 
116 static Uint setup_rootset(Process*, Eterm*, int, Rootset*);
117 static void cleanup_rootset(Rootset *rootset);
118 static Eterm *full_sweep_heaps(Process *p,
119                                ErlHeapFragment *live_hf_end,
120 			       int hibernate,
121 			       Eterm *n_heap, Eterm* n_htop,
122 			       char *oh, Uint oh_size,
123 			       Eterm *objv, int nobj);
124 static int garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
125 			   int need, Eterm* objv, int nobj, int fcalls,
126 			   Uint max_young_gen_usage);
127 static int major_collection(Process* p, ErlHeapFragment *live_hf_end,
128 			    int need, Eterm* objv, int nobj,
129 			    Uint ygen_usage, Uint *recl);
130 static int minor_collection(Process* p, ErlHeapFragment *live_hf_end,
131 			    int need, Eterm* objv, int nobj,
132 			    Uint ygen_usage, Uint *recl);
133 static void do_minor(Process *p, ErlHeapFragment *live_hf_end,
134 		     char *mature, Uint mature_size,
135 		     Uint new_sz, Eterm* objv, int nobj);
136 static Eterm *sweep_new_heap(Eterm *n_hp, Eterm *n_htop,
137 			     char* old_heap, Uint old_heap_size);
138 static Eterm *sweep_heaps(Eterm *n_hp, Eterm *n_htop,
139 			  char* old_heap, Uint old_heap_size);
140 static Eterm* sweep_literal_area(Eterm* n_hp, Eterm* n_htop,
141 				 char* old_heap, Uint old_heap_size,
142 				 char* src, Uint src_size);
143 static Eterm* sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
144 					 char* src, Uint src_size);
145 static Eterm* collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end,
146 				      Eterm* htop);
147 static int adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj);
148 static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj);
149 static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj);
150 static void sweep_off_heap(Process *p, int fullsweep);
151 static void offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size);
152 static void offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size);
153 static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size,
154 			   Eterm* objv, int nobj);
155 static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size);
156 static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size);
157 static int reached_max_heap_size(Process *p, Uint total_heap_size,
158                                  Uint extra_heap_size, Uint extra_old_heap_size);
159 static void init_gc_info(ErtsGCInfo *gcip);
160 static Uint64 next_vheap_size(Process* p, Uint64 vheap, Uint64 vheap_sz);
161 
162 #ifdef HARDDEBUG
163 static void disallow_heap_frag_ref_in_heap(Process *p, Eterm *heap, Eterm *htop);
164 static void disallow_heap_frag_ref_in_old_heap(Process* p);
165 #endif
166 
167 #if defined(ARCH_64)
168 # define MAX_HEAP_SIZES 154
169 #else
170 # define MAX_HEAP_SIZES 59
171 #endif
172 
173 static Sint heap_sizes[MAX_HEAP_SIZES];	/* Suitable heap sizes. */
174 static int num_heap_sizes;	/* Number of heap sizes. */
175 
176 Uint erts_test_long_gc_sleep; /* Only used for testing... */
177 
178 typedef struct {
179     Process *proc;
180     Eterm ref;
181     Eterm ref_heap[ERTS_REF_THING_SIZE];
182     Uint req_sched;
183     erts_atomic32_t refc;
184 } ErtsGCInfoReq;
185 
186 static struct {
187     erts_mtx_t mtx;
188     ErtsGCInfo info;
189 } dirty_gc;
190 
move_msgs_to_heap(Process * c_p)191 static void move_msgs_to_heap(Process *c_p)
192 {
193     Uint64 pre_oh, post_oh;
194 
195     pre_oh = c_p->off_heap.overhead;
196     erts_proc_sig_move_msgs_to_heap(c_p);
197     post_oh = c_p->off_heap.overhead;
198 
199     if (pre_oh != post_oh) {
200 	/* Got new binaries; update bin vheap size... */
201         c_p->bin_vheap_sz = next_vheap_size(c_p, post_oh,
202                                             c_p->bin_vheap_sz);
203     }
204 }
205 
206 static ERTS_INLINE int
gc_cost(Uint gc_moved_live_words,Uint resize_moved_words)207 gc_cost(Uint gc_moved_live_words, Uint resize_moved_words)
208 {
209     Sint reds;
210 
211     reds = gc_moved_live_words/10;
212     reds += resize_moved_words/100;
213     if (reds < 1)
214 	return 1;
215     if (reds > INT_MAX)
216 	return INT_MAX;
217     return (int) reds;
218 }
219 
220 ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(gcireq,
221                                  ErtsGCInfoReq,
222                                  5,
223                                  ERTS_ALC_T_GC_INFO_REQ)
224 
225 /*
226  * Initialize GC global data.
227  */
228 void
erts_init_gc(void)229 erts_init_gc(void)
230 {
231     int i = 0, ix;
232     Sint max_heap_size = 0;
233 
234     ERTS_CT_ASSERT(offsetof(ProcBin,thing_word) == offsetof(struct erl_off_heap_header,thing_word));
235     ERTS_CT_ASSERT(offsetof(ProcBin,thing_word) == offsetof(ErlFunThing,thing_word));
236     ERTS_CT_ASSERT(offsetof(ProcBin,thing_word) == offsetof(ExternalThing,header));
237     ERTS_CT_ASSERT(offsetof(ProcBin,size) == offsetof(struct erl_off_heap_header,size));
238     ERTS_CT_ASSERT(offsetof(ProcBin,size) == offsetof(ErlSubBin,size));
239     ERTS_CT_ASSERT(offsetof(ProcBin,size) == offsetof(ErlHeapBin,size));
240     ERTS_CT_ASSERT(offsetof(ProcBin,next) == offsetof(struct erl_off_heap_header,next));
241     ERTS_CT_ASSERT(offsetof(ProcBin,next) == offsetof(ErlFunThing,next));
242     ERTS_CT_ASSERT(offsetof(ProcBin,next) == offsetof(ExternalThing,next));
243 
244     erts_test_long_gc_sleep = 0;
245 
246     /*
247      * Heap sizes start growing in a Fibonacci sequence.
248      *
249      * Fib growth is not really ok for really large heaps, for
250      * example is fib(35) == 14meg, whereas fib(36) == 24meg;
251      * we really don't want that growth when the heaps are that big.
252      */
253 
254     /* Growth stage 1 - Fibonacci + 1*/
255     /* 12,38 will hit size 233, the old default */
256 
257     heap_sizes[0] = 12;
258     heap_sizes[1] = 38;
259 
260     for(i = 2; i < 23; i++) {
261         /* one extra word for block header */
262         heap_sizes[i] = heap_sizes[i-1] + heap_sizes[i-2] + 1;
263     }
264 
265 
266     /* for 32 bit we want max_heap_size to be MAX(32bit) / 4 [words]
267      * for 64 bit we want max_heap_size to be MAX(52bit) / 8 [words]
268      */
269 
270     max_heap_size = sizeof(Eterm) < 8 ? (Sint)((~(Uint)0)/(sizeof(Eterm))) :
271 					(Sint)(((Uint64)1 << 53)/sizeof(Eterm));
272 
273     /* Growth stage 2 - 20% growth */
274     /* At 1.3 mega words heap, we start to slow down. */
275     for (i = 23; i < ALENGTH(heap_sizes); i++) {
276 	heap_sizes[i] = heap_sizes[i-1] + heap_sizes[i-1]/5;
277 	if ((heap_sizes[i] < 0) || heap_sizes[i] > max_heap_size) {
278 	    /* Size turned negative. Discard this last size. */
279 	    i--;
280 	    break;
281 	}
282     }
283     num_heap_sizes = i;
284 
285     for (ix = 0; ix < erts_no_schedulers; ix++) {
286       ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(ix);
287       init_gc_info(&esdp->gc_info);
288     }
289 
290     erts_mtx_init(&dirty_gc.mtx, "dirty_gc_info", NIL,
291         ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
292     init_gc_info(&dirty_gc.info);
293 
294     init_gcireq_alloc();
295 }
296 
297 /*
298  * Find the next heap size equal to or greater than the given size (if offset == 0).
299  *
300  * If offset is 1, the next higher heap size is returned (always greater than size).
301  */
302 Uint
erts_next_heap_size(Uint size,Uint offset)303 erts_next_heap_size(Uint size, Uint offset)
304 {
305     if (size < heap_sizes[0]) {
306 	return heap_sizes[0];
307     } else {
308 	Sint* low = heap_sizes;
309 	Sint* high = heap_sizes + num_heap_sizes;
310 	Sint* mid;
311 
312 	while (low < high) {
313 	    mid = low + (high-low) / 2;
314 	    if (size < mid[0]) {
315 		high = mid;
316 	    } else if (size == mid[0]) {
317 		ASSERT(mid+offset-heap_sizes < num_heap_sizes);
318 		return mid[offset];
319 	    } else if (size < mid[1]) {
320 		ASSERT(mid[0] < size && size <= mid[1]);
321 		ASSERT(mid+offset-heap_sizes < num_heap_sizes);
322 		return mid[offset+1];
323 	    } else {
324 		low = mid + 1;
325 	    }
326 	}
327 	erts_exit(ERTS_ERROR_EXIT, "no next heap size found: %beu, offset %beu\n", size, offset);
328     }
329     return 0;
330 }
331 /*
332  * Return the next heap size to use. Make sure we never return
333  * a smaller heap size than the minimum heap size for the process.
334  * (Use of the erlang:hibernate/3 BIF could have shrinked the
335  * heap below the minimum heap size.)
336  */
337 static Uint
next_heap_size(Process * p,Uint size,Uint offset)338 next_heap_size(Process* p, Uint size, Uint offset)
339 {
340     size = erts_next_heap_size(size, offset);
341     return size < p->min_heap_size ? p->min_heap_size : size;
342 }
343 
344 Eterm
erts_heap_sizes(Process * p)345 erts_heap_sizes(Process* p)
346 {
347     int i;
348     int n = 0;
349     int big = 0;
350     Eterm res = NIL;
351     Eterm* hp;
352     Eterm* bigp;
353 
354     for (i = num_heap_sizes-1; i >= 0; i--) {
355 	n += 2;
356 	if (!IS_SSMALL(heap_sizes[i])) {
357 	    big += BIG_UINT_HEAP_SIZE;
358 	}
359     }
360 
361     /*
362      * We store all big numbers first on the heap, followed
363      * by all the cons cells.
364      */
365     bigp = HAlloc(p, n+big);
366     hp = bigp+big;
367     for (i = num_heap_sizes-1; i >= 0; i--) {
368 	Eterm num;
369 	Sint sz = heap_sizes[i];
370 
371 	if (IS_SSMALL(sz)) {
372 	    num = make_small(sz);
373 	} else {
374 	    num = uint_to_big(sz, bigp);
375 	    bigp += BIG_UINT_HEAP_SIZE;
376 	}
377         res = CONS(hp, num, res);
378         hp += 2;
379     }
380     return res;
381 }
382 
383 void
erts_offset_heap(Eterm * hp,Uint sz,Sint offs,Eterm * low,Eterm * high)384 erts_offset_heap(Eterm* hp, Uint sz, Sint offs, Eterm* low, Eterm* high)
385 {
386     offset_heap(hp, sz, offs, (char*) low, ((char *)high)-((char *)low));
387 }
388 
389 void
erts_offset_heap_ptr(Eterm * hp,Uint sz,Sint offs,Eterm * low,Eterm * high)390 erts_offset_heap_ptr(Eterm* hp, Uint sz, Sint offs,
391 		     Eterm* low, Eterm* high)
392 {
393     offset_heap_ptr(hp, sz, offs, (char *) low, ((char *)high)-((char *)low));
394 }
395 
396 
397 #define ptr_within(ptr, low, high) ((ptr) < (high) && (ptr) >= (low))
398 
399 void
erts_offset_off_heap(ErlOffHeap * ohp,Sint offs,Eterm * low,Eterm * high)400 erts_offset_off_heap(ErlOffHeap *ohp, Sint offs, Eterm* low, Eterm* high)
401 {
402     if (ohp->first && ptr_within((Eterm *)ohp->first, low, high)) {
403         Eterm** uptr = (Eterm**) (void *) &ohp->first;
404         *uptr += offs;
405     }
406 }
407 #undef ptr_within
408 
409 Eterm
erts_gc_after_bif_call_lhf(Process * p,ErlHeapFragment * live_hf_end,Eterm result,Eterm * regs,Uint arity)410 erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end,
411 			   Eterm result, Eterm* regs, Uint arity)
412 {
413     int cost;
414 
415     if (p->flags & F_HIBERNATE_SCHED) {
416 	/*
417 	 * We just hibernated. We do *not* want to mess
418 	 * up the hibernation by an ordinary GC...
419 	 */
420 	return result;
421     }
422 
423     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
424         erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
425         erts_proc_sig_fetch(p);
426 	erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
427     }
428 
429     if (!p->mbuf) {
430 	/* Must have GC:d in BIF call... invalidate live_hf_end */
431 	live_hf_end = ERTS_INVALID_HFRAG_PTR;
432     }
433 
434     if (is_non_value(result)) {
435 	if (p->freason == TRAP) {
436 	    cost = garbage_collect(p, live_hf_end, 0, regs, p->arity, p->fcalls, 0);
437 	} else {
438 	    cost = garbage_collect(p, live_hf_end, 0, regs, arity, p->fcalls, 0);
439 	}
440     } else {
441 	Eterm val[1];
442 
443 	val[0] = result;
444 	cost = garbage_collect(p, live_hf_end, 0, val, 1, p->fcalls, 0);
445 	result = val[0];
446     }
447     BUMP_REDS(p, cost);
448 
449     return result;
450 }
451 
452 Eterm
erts_gc_after_bif_call(Process * p,Eterm result,Eterm * regs,Uint arity)453 erts_gc_after_bif_call(Process* p, Eterm result, Eterm* regs, Uint arity)
454 {
455     return erts_gc_after_bif_call_lhf(p, ERTS_INVALID_HFRAG_PTR,
456                                       result, regs, arity);
457 }
458 
assert_no_active_writers(Process * p)459 static ERTS_INLINE void assert_no_active_writers(Process *p)
460 {
461 #ifdef DEBUG
462     struct erl_off_heap_header* ptr;
463     ptr = MSO(p).first;
464     while (ptr) {
465 	if (ptr->thing_word == HEADER_PROC_BIN) {
466 	    ProcBin *pbp = (ProcBin*) ptr;
467 	    ERTS_ASSERT(!(pbp->flags & PB_ACTIVE_WRITER));
468 	}
469 	ptr = ptr->next;
470     }
471 #endif
472 }
473 
474 #define ERTS_DELAY_GC_EXTRA_FREE 40
475 #define ERTS_ABANDON_HEAP_COST 10
476 
477 static int
delay_garbage_collection(Process * p,ErlHeapFragment * live_hf_end,int need,int fcalls)478 delay_garbage_collection(Process *p, ErlHeapFragment *live_hf_end, int need, int fcalls)
479 {
480     ErlHeapFragment *hfrag;
481     Eterm *orig_heap, *orig_hend, *orig_htop, *orig_stop;
482     Eterm *stop, *hend;
483     Uint hsz, ssz;
484     int reds_left;
485 
486     ERTS_HOLE_CHECK(p);
487 
488     if ((p->flags & F_DISABLE_GC)
489 	&& p->live_hf_end == ERTS_INVALID_HFRAG_PTR) {
490 	/*
491 	 * A BIF yielded with disabled GC. Remember
492 	 * heap fragments created by the BIF until we
493 	 * do next GC.
494 	 */
495 	p->live_hf_end = live_hf_end;
496     }
497 
498     if (need == 0) {
499         if (p->flags & (F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC)) {
500             ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(p)));
501             goto force_reschedule;
502         }
503 	return 1;
504     }
505     /*
506      * Satisfy need in a heap fragment...
507      */
508     ASSERT(need > 0);
509 
510     orig_heap = p->heap;
511     orig_hend = p->hend;
512     orig_htop = p->htop;
513     orig_stop = p->stop;
514 
515     ssz = orig_hend - orig_stop;
516     hsz = ssz + need + ERTS_DELAY_GC_EXTRA_FREE + S_RESERVED;
517 
518     hfrag = new_message_buffer(hsz);
519     p->heap = p->htop = &hfrag->mem[0];
520     p->hend = hend = &hfrag->mem[hsz];
521     p->stop = stop = hend - ssz;
522     sys_memcpy((void *) stop, (void *) orig_stop, ssz * sizeof(Eterm));
523 
524     if (p->abandoned_heap) {
525 	/*
526          * Active heap already in a fragment; adjust it and
527          * save it into mbuf list...
528          */
529         ErlHeapFragment *hfrag = ((ErlHeapFragment *)
530                                   (((char *) orig_heap)
531                                    - offsetof(ErlHeapFragment, mem)));
532         Uint used = orig_htop - orig_heap;
533         hfrag->used_size = used;
534         p->mbuf_sz += used;
535         ASSERT(hfrag->used_size <= hfrag->alloc_size);
536         ASSERT(!hfrag->off_heap.first && !hfrag->off_heap.overhead);
537         hfrag->next = p->mbuf;
538         p->mbuf = hfrag;
539     }
540     else {
541 	/* Do not leave a hole in the abandoned heap... */
542 	if (orig_htop < orig_hend) {
543 	    *orig_htop = make_pos_bignum_header(orig_hend-orig_htop-1);
544 	    if (orig_htop + 1 < orig_hend) {
545 		orig_hend[-1] = (Uint) (orig_htop - orig_heap);
546 		p->flags |= F_ABANDONED_HEAP_USE;
547 	    }
548 	}
549 	p->abandoned_heap = orig_heap;
550     }
551 
552 #ifdef CHECK_FOR_HOLES
553     p->last_htop = p->htop;
554     p->heap_hfrag = hfrag;
555 #endif
556 
557 force_reschedule:
558 
559     /* Make sure that we do a proper GC as soon as possible... */
560     p->flags |= F_FORCE_GC;
561     reds_left = ERTS_REDS_LEFT(p, fcalls);
562     ASSERT(CONTEXT_REDS - reds_left >= erts_proc_sched_data(p)->virtual_reds);
563 
564     if (reds_left > ERTS_ABANDON_HEAP_COST) {
565 	int vreds = reds_left - ERTS_ABANDON_HEAP_COST;
566 	erts_proc_sched_data((p))->virtual_reds += vreds;
567     }
568 
569     ERTS_CHK_MBUF_SZ(p);
570 
571     ASSERT(CONTEXT_REDS >= erts_proc_sched_data(p)->virtual_reds);
572     return reds_left;
573 }
574 
575 static ERTS_FORCE_INLINE Uint
young_gen_usage(Process * p,Uint * ext_msg_usage)576 young_gen_usage(Process *p, Uint *ext_msg_usage)
577 {
578     Uint hsz;
579     Eterm *aheap;
580 
581     ERTS_CHK_MBUF_SZ(p);
582 
583     hsz = p->mbuf_sz;
584 
585     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
586         ERTS_FOREACH_SIG_PRIVQS(
587             p, mp,
588             {
589                 /*
590                  * We leave not yet decoded distribution messages
591                  * as they are in the queue since it is not
592                  * possible to determine a maximum size until
593                  * actual decoding. However, we use their estimated
594                  * size when calculating need, and by this making
595                  * it more likely that they will fit on the heap
596                  * when actually decoded.
597                  *
598                  * We however ignore off heap messages...
599                  */
600                 if (ERTS_SIG_IS_MSG(mp)
601                     && mp->data.attached
602                     && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
603                     Uint sz = erts_msg_attached_data_size(mp);
604                     if (ERTS_SIG_IS_EXTERNAL_MSG(mp))
605                         *ext_msg_usage += sz;
606                     hsz += sz;
607                 }
608             });
609     }
610 
611     hsz += p->htop - p->heap;
612     aheap = p->abandoned_heap;
613     if (aheap) {
614 	/* used in orig heap */
615 	if (p->flags & F_ABANDONED_HEAP_USE)
616 	    hsz += aheap[p->heap_sz-1];
617 	else
618 	    hsz += p->heap_sz;
619     }
620     return hsz;
621 }
622 
623 #define ERTS_GET_ORIG_HEAP(Proc, Heap, HTop)			\
624     do {							\
625 	Eterm *aheap__ = (Proc)->abandoned_heap;		\
626 	if (!aheap__) {						\
627 	    (Heap) = (Proc)->heap;				\
628 	    (HTop) = (Proc)->htop;				\
629 	}							\
630 	else {							\
631 	    (Heap) = aheap__;					\
632 	    if ((Proc)->flags & F_ABANDONED_HEAP_USE)		\
633 		(HTop) = aheap__ + aheap__[(Proc)->heap_sz-1];	\
634 	    else						\
635 		(HTop) = aheap__ + (Proc)->heap_sz;		\
636 	}							\
637     } while (0)
638 
639 
640 static ERTS_INLINE void
check_for_possibly_long_gc(Process * p,Uint ygen_usage)641 check_for_possibly_long_gc(Process *p, Uint ygen_usage)
642 {
643     int major;
644     Uint sz;
645 
646     major = (p->flags & F_NEED_FULLSWEEP) || GEN_GCS(p) >= MAX_GEN_GCS(p);
647 
648     sz = ygen_usage;
649     sz += p->hend - p->stop;
650     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ)
651         sz += erts_proc_sig_privqs_len(p);
652     if (major)
653 	sz += p->old_htop - p->old_heap;
654 
655     if (sz >= ERTS_POTENTIALLY_LONG_GC_HSIZE) {
656         ASSERT(!(p->flags & (F_DISABLE_GC|F_DELAY_GC)));
657 	p->flags |= major ? F_DIRTY_MAJOR_GC : F_DIRTY_MINOR_GC;
658         erts_schedule_dirty_sys_execution(p);
659     }
660 }
661 
662 
663 /*
664  * Garbage collect a process.
665  *
666  * p: Pointer to the process structure.
667  * need: Number of Eterm words needed on the heap.
668  * objv: Array of terms to add to rootset; that is to preserve.
669  * nobj: Number of objects in objv.
670  */
671 static int
garbage_collect(Process * p,ErlHeapFragment * live_hf_end,int need,Eterm * objv,int nobj,int fcalls,Uint max_young_gen_usage)672 garbage_collect(Process* p, ErlHeapFragment *live_hf_end,
673 		int need, Eterm* objv, int nobj, int fcalls,
674 		Uint max_young_gen_usage)
675 {
676     Uint reclaimed_now = 0;
677     Uint ygen_usage;
678     Uint ext_msg_usage = 0;
679     Eterm gc_trace_end_tag;
680     int reds;
681     ErtsMonotonicTime start_time;
682     ErtsSchedulerData *esdp = erts_proc_sched_data(p);
683     erts_aint32_t state;
684 #ifdef USE_VM_PROBES
685     DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
686 #endif
687     ERTS_MSACC_PUSH_STATE();
688 
689     ERTS_UNDEF(start_time, 0);
690     ERTS_CHK_MBUF_SZ(p);
691 
692     ASSERT(CONTEXT_REDS - ERTS_REDS_LEFT(p, fcalls) >= esdp->virtual_reds);
693 
694     state = erts_atomic32_read_nob(&p->state);
695 
696     if ((p->flags & (F_DISABLE_GC|F_DELAY_GC)) || state & ERTS_PSFLG_EXITING) {
697     delay_gc_before_start:
698 	return delay_garbage_collection(p, live_hf_end, need, fcalls);
699     }
700 
701     ygen_usage = max_young_gen_usage ? max_young_gen_usage : young_gen_usage(p, &ext_msg_usage);
702 
703     if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
704 	check_for_possibly_long_gc(p, ygen_usage);
705 	if (p->flags & (F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC))
706 	    goto delay_gc_before_start;
707     }
708 
709     if (p->abandoned_heap)
710 	live_hf_end = ERTS_INVALID_HFRAG_PTR;
711     else if (p->live_hf_end != ERTS_INVALID_HFRAG_PTR)
712 	live_hf_end = p->live_hf_end;
713 
714     ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_GC);
715 
716     erts_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
717     if (erts_system_monitor_long_gc != 0)
718 	start_time = erts_get_monotonic_time(esdp);
719 
720     ERTS_CHK_OFFHEAP(p);
721 
722     ErtsGcQuickSanityCheck(p);
723 
724 #ifdef USE_VM_PROBES
725     *pidbuf = '\0';
726     if (DTRACE_ENABLED(gc_major_start)
727         || DTRACE_ENABLED(gc_major_end)
728         || DTRACE_ENABLED(gc_minor_start)
729         || DTRACE_ENABLED(gc_minor_end)) {
730         dtrace_proc_str(p, pidbuf);
731     }
732 #endif
733     /*
734      * Test which type of GC to do.
735      */
736 
737     if (GEN_GCS(p) < MAX_GEN_GCS(p) && !(FLAGS(p) & F_NEED_FULLSWEEP)) {
738         if (IS_TRACED_FL(p, F_TRACE_GC)) {
739             trace_gc(p, am_gc_minor_start, need, THE_NON_VALUE);
740         }
741         DTRACE2(gc_minor_start, pidbuf, need);
742         reds = minor_collection(p, live_hf_end, need + ext_msg_usage, objv, nobj,
743 				ygen_usage, &reclaimed_now);
744         DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
745         if (reds == -1) {
746             if (IS_TRACED_FL(p, F_TRACE_GC)) {
747                 trace_gc(p, am_gc_minor_end, reclaimed_now, THE_NON_VALUE);
748             }
749 	    if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
750 		p->flags |= F_NEED_FULLSWEEP;
751 		check_for_possibly_long_gc(p, ygen_usage);
752 		if (p->flags & F_DIRTY_MAJOR_GC)
753 		    goto delay_gc_after_start;
754 	    }
755             goto do_major_collection;
756         }
757         if (ERTS_SCHEDULER_IS_DIRTY(esdp))
758             p->flags &= ~F_DIRTY_MINOR_GC;
759         gc_trace_end_tag = am_gc_minor_end;
760     } else {
761 do_major_collection:
762         ERTS_MSACC_SET_STATE_CACHED_X(ERTS_MSACC_STATE_GC_FULL);
763         if (IS_TRACED_FL(p, F_TRACE_GC)) {
764             trace_gc(p, am_gc_major_start, need, THE_NON_VALUE);
765         }
766         DTRACE2(gc_major_start, pidbuf, need);
767         reds = major_collection(p, live_hf_end, need + ext_msg_usage, objv, nobj,
768 				ygen_usage, &reclaimed_now);
769         if (ERTS_SCHEDULER_IS_DIRTY(esdp))
770             p->flags &= ~(F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC);
771         DTRACE2(gc_major_end, pidbuf, reclaimed_now);
772         gc_trace_end_tag = am_gc_major_end;
773         ERTS_MSACC_SET_STATE_CACHED_X(ERTS_MSACC_STATE_GC);
774     }
775 
776     assert_no_active_writers(p);
777 
778     /*
779      * Finish.
780      */
781 
782     ERTS_CHK_OFFHEAP(p);
783 
784     ErtsGcQuickSanityCheck(p);
785 
786     /* Max heap size has been reached and the process was configured
787        to be killed, so we kill it and set it in a delayed garbage
788        collecting state. There should be no gc_end trace or
789        long_gc/large_gc triggers when this happens as process was
790        killed before a GC could be done. */
791     if (reds == -2) {
792         int res;
793 
794         erts_set_self_exiting(p, am_killed);
795 
796     delay_gc_after_start:
797         /* erts_send_exit_signal looks for ERTS_PSFLG_GC, so
798            we have to remove it after the signal is sent */
799         erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
800 
801         /* We have to make sure that we have space for need on the heap */
802         res = delay_garbage_collection(p, live_hf_end, need, fcalls);
803         ERTS_MSACC_POP_STATE();
804         return res;
805     }
806 
807     erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
808 
809     if (IS_TRACED_FL(p, F_TRACE_GC)) {
810         trace_gc(p, gc_trace_end_tag, reclaimed_now, THE_NON_VALUE);
811     }
812 
813     if (erts_system_monitor_long_gc != 0) {
814 	ErtsMonotonicTime end_time;
815 	Uint gc_time;
816 	if (erts_test_long_gc_sleep)
817 	    while (0 != erts_milli_sleep(erts_test_long_gc_sleep));
818 	end_time = erts_get_monotonic_time(esdp);
819 	gc_time = (Uint) ERTS_MONOTONIC_TO_MSEC(end_time - start_time);
820 	if (gc_time && gc_time > erts_system_monitor_long_gc) {
821 	    monitor_long_gc(p, gc_time);
822 	}
823     }
824     if (erts_system_monitor_large_heap != 0) {
825 	Uint size = HEAP_SIZE(p);
826 	size += OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) : 0;
827 	if (size >= erts_system_monitor_large_heap)
828 	    monitor_large_heap(p);
829     }
830 
831     if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
832 	erts_mtx_lock(&dirty_gc.mtx);
833 	dirty_gc.info.garbage_cols++;
834 	dirty_gc.info.reclaimed += reclaimed_now;
835 	erts_mtx_unlock(&dirty_gc.mtx);
836     }
837     else
838     {
839 	esdp->gc_info.garbage_cols++;
840 	esdp->gc_info.reclaimed += reclaimed_now;
841     }
842 
843     FLAGS(p) &= ~(F_FORCE_GC|F_HIBERNATED);
844     p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
845 
846     ERTS_MSACC_POP_STATE();
847 
848 #ifdef CHECK_FOR_HOLES
849     /*
850      * We intentionally do not rescan the areas copied by the GC.
851      * We trust the GC not to leave any holes.
852      */
853     p->last_htop = p->htop;
854     p->last_mbuf = 0;
855 #endif
856 
857 #ifdef DEBUG
858     /*
859      * The scanning for pointers from the old_heap into the new_heap or
860      * heap fragments turned out to be costly, so we remember how far we
861      * have scanned this time and will start scanning there next time.
862      * (We will not detect wild writes into the old heap, or modifications
863      * of the old heap in-between garbage collections.)
864      */
865     p->last_old_htop = p->old_htop;
866 #endif
867 
868     ASSERT(!p->mbuf);
869     ASSERT(!ERTS_IS_GC_DESIRED(p));
870     ASSERT(need <= HEAP_LIMIT(p) - HEAP_TOP(p));
871 
872     return reds;
873 }
874 
875 int
erts_garbage_collect_nobump(Process * p,int need,Eterm * objv,int nobj,int fcalls)876 erts_garbage_collect_nobump(Process* p, int need, Eterm* objv, int nobj, int fcalls)
877 {
878     int reds, reds_left;
879     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
880         erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
881         erts_proc_sig_fetch(p);
882 	erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
883     }
884     reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, fcalls, 0);
885     reds_left = ERTS_REDS_LEFT(p, fcalls);
886     if (reds > reds_left)
887 	reds = reds_left;
888     ASSERT(CONTEXT_REDS - (reds_left - reds) >= erts_proc_sched_data(p)->virtual_reds);
889     return reds;
890 }
891 
892 void
erts_garbage_collect(Process * p,int need,Eterm * objv,int nobj)893 erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
894 {
895     int reds;
896     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
897         erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
898         erts_proc_sig_fetch(p);
899 	erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
900     }
901     reds = garbage_collect(p, ERTS_INVALID_HFRAG_PTR, need, objv, nobj, p->fcalls, 0);
902     BUMP_REDS(p, reds);
903     ASSERT(CONTEXT_REDS - ERTS_BIF_REDS_LEFT(p)
904 	   >= erts_proc_sched_data(p)->virtual_reds);
905 }
906 
907 
908 /*
909  * Place all living data on a the new heap; deallocate any old heap.
910  * Meant to be used by hibernate/3.
911  */
912 static int
garbage_collect_hibernate(Process * p,int check_long_gc)913 garbage_collect_hibernate(Process* p, int check_long_gc)
914 {
915     Uint heap_size;
916     Eterm* heap;
917     Eterm* htop;
918     Uint actual_size;
919     char* area;
920     Uint area_size;
921     Sint offs;
922     int reds;
923 
924     if (p->flags & F_DISABLE_GC)
925 	ERTS_INTERNAL_ERROR("GC disabled");
926 
927     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
928         erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
929         erts_proc_sig_fetch(p);
930 	erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
931     }
932 
933     if (ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(p))) {
934         p->flags &= ~(F_DIRTY_GC_HIBERNATE|F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC);
935     } else if (check_long_gc) {
936         Uint flags = p->flags;
937 
938         p->flags |= F_NEED_FULLSWEEP;
939 
940         check_for_possibly_long_gc(p, ((p->htop - p->heap) +
941                                        p->mbuf_sz + S_RESERVED));
942 
943         if (p->flags & (F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC)) {
944             p->flags = flags|F_DIRTY_GC_HIBERNATE;
945             return 1;
946         }
947 
948         p->flags = flags;
949     }
950 
951     /*
952      * Preliminaries.
953      */
954     erts_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
955     ErtsGcQuickSanityCheck(p);
956 
957     /* Only allow one continuation pointer. */
958     ASSERT(p->stop == p->hend - CP_SIZE);
959     ASSERT(p->stop[0] == make_cp(beam_normal_exit));
960 
961     /*
962      * Do it.
963      */
964     heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz;
965 
966     /* Reserve place for continuation pointer and redzone */
967     heap_size += S_RESERVED;
968 
969     heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP,
970 				    sizeof(Eterm)*heap_size);
971     htop = heap;
972 
973     htop = full_sweep_heaps(p,
974                             ERTS_INVALID_HFRAG_PTR,
975 			    1,
976 			    heap,
977 			    htop,
978 			    (char *) p->old_heap,
979 			    (char *) p->old_htop - (char *) p->old_heap,
980 			    p->arg_reg,
981 			    p->arity);
982 
983 #ifdef HARDDEBUG
984     disallow_heap_frag_ref_in_heap(p, heap, htop);
985 #endif
986 
987     erts_deallocate_young_generation(p);
988 
989     p->heap = heap;
990     p->high_water = htop;
991     p->htop = htop;
992     p->hend = p->heap + heap_size;
993     p->stop = p->hend - CP_SIZE;
994     p->heap_sz = heap_size;
995 
996     heap_size = actual_size = p->htop - p->heap;
997 
998     /* Reserve place for continuation pointer and redzone */
999     heap_size += S_RESERVED;
1000 
1001     FLAGS(p) &= ~F_FORCE_GC;
1002     p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
1003 
1004     /*
1005      * Move the heap to its final destination.
1006      *
1007      * IMPORTANT: We have garbage collected to a temporary heap and
1008      * then copy the result to a newly allocated heap of exact size.
1009      * This is intentional and important! Garbage collecting as usual
1010      * and then shrinking the heap by reallocating it caused serious
1011      * fragmentation problems when large amounts of processes were
1012      * hibernated.
1013      */
1014 
1015     ASSERT(actual_size < p->heap_sz);
1016 
1017     heap = ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*heap_size);
1018     sys_memcpy((void *) heap, (void *) p->heap, actual_size*sizeof(Eterm));
1019     ERTS_HEAP_FREE(ERTS_ALC_T_TMP_HEAP, p->heap, p->heap_sz*sizeof(Eterm));
1020 
1021     p->hend = heap + heap_size;
1022     p->stop = p->hend - CP_SIZE;
1023     p->stop[0] = make_cp(beam_normal_exit);
1024 
1025     offs = heap - p->heap;
1026     area = (char *) p->heap;
1027     area_size = ((char *) p->htop) - area;
1028     offset_heap(heap, actual_size, offs, area, area_size);
1029     p->high_water = heap + (p->high_water - p->heap);
1030     offset_rootset(p, offs, area, area_size, p->arg_reg, p->arity);
1031     p->htop = heap + actual_size;
1032     p->heap = heap;
1033     p->heap_sz = heap_size;
1034 
1035 
1036 #ifdef CHECK_FOR_HOLES
1037     p->last_htop = p->htop;
1038     p->last_mbuf = 0;
1039 #endif
1040 #ifdef DEBUG
1041     p->last_old_htop = NULL;
1042 #endif
1043 
1044     /*
1045      * Finishing.
1046      */
1047 
1048     ErtsGcQuickSanityCheck(p);
1049 
1050     p->flags |= F_HIBERNATED;
1051 
1052     erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
1053 
1054     reds = gc_cost(actual_size, actual_size);
1055     return reds;
1056 }
1057 
1058 void
erts_garbage_collect_hibernate(Process * p)1059 erts_garbage_collect_hibernate(Process* p)
1060 {
1061     int reds = garbage_collect_hibernate(p, 1);
1062     BUMP_REDS(p, reds);
1063 }
1064 
1065 #define fullsweep_nstack(p,n_htop)		        	(n_htop)
1066 #define GENSWEEP_NSTACK(p,old_htop,n_htop)	        	do{}while(0)
1067 #define offset_nstack(p,offs,area,area_size)	        	do{}while(0)
1068 #define sweep_literals_nstack(p,old_htop,area,area_size)	(old_htop)
1069 
1070 int
erts_garbage_collect_literals(Process * p,Eterm * literals,Uint byte_lit_size,struct erl_off_heap_header * oh,int fcalls)1071 erts_garbage_collect_literals(Process* p, Eterm* literals,
1072 			      Uint byte_lit_size,
1073 			      struct erl_off_heap_header* oh,
1074 			      int fcalls)
1075 {
1076     Uint lit_size = byte_lit_size / sizeof(Eterm);
1077     Uint old_heap_size;
1078     Eterm* temp_lit;
1079     Sint offs;
1080     Rootset rootset;            /* Rootset for GC (stack, dictionary, etc). */
1081     Roots* roots;
1082     char* area;
1083     Uint area_size;
1084     Eterm* old_htop;
1085     Uint n;
1086     Uint ygen_usage = 0;
1087     Uint ext_msg_usage = 0;
1088     struct erl_off_heap_header** prev = NULL;
1089     Sint64 reds;
1090     int hibernated = !!(p->flags & F_HIBERNATED);
1091 
1092     if (p->flags & (F_DISABLE_GC|F_DELAY_GC))
1093 	ERTS_INTERNAL_ERROR("GC disabled");
1094 
1095     /*
1096      * First an ordinary major collection...
1097      */
1098 
1099     p->flags |= F_NEED_FULLSWEEP;
1100 
1101     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
1102         erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
1103         erts_proc_sig_fetch(p);
1104 	erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
1105     }
1106 
1107     if (ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(p)))
1108 	p->flags &= ~F_DIRTY_CLA;
1109     else {
1110         Uint size = byte_lit_size/sizeof(Uint);
1111 	ygen_usage = young_gen_usage(p, &ext_msg_usage);
1112         if (hibernated)
1113             size = size*2 + 3*ygen_usage;
1114         else
1115             size = size + 2*ygen_usage;
1116 	check_for_possibly_long_gc(p, size);
1117 	if (p->flags & F_DIRTY_MAJOR_GC) {
1118 	    p->flags |= F_DIRTY_CLA;
1119 	    return 10;
1120 	}
1121     }
1122 
1123     reds = (Sint64) garbage_collect(p, ERTS_INVALID_HFRAG_PTR, ext_msg_usage,
1124 				    p->arg_reg, p->arity, fcalls,
1125 				    ygen_usage);
1126     if (ERTS_PROC_IS_EXITING(p)) {
1127         return 0;
1128     }
1129 
1130     ASSERT(!(p->flags & (F_DIRTY_MAJOR_GC|F_DIRTY_MINOR_GC)));
1131 
1132     if (MAX_HEAP_SIZE_GET(p)) {
1133         Uint new_heap_size;
1134         Uint old_heap_size;
1135         Uint total_heap_size;
1136 
1137         new_heap_size = HEAP_END(p) - HEAP_START(p);
1138         old_heap_size = erts_next_heap_size(lit_size, 0);
1139         total_heap_size = new_heap_size + old_heap_size;
1140         if (MAX_HEAP_SIZE_GET(p) < total_heap_size &&
1141             reached_max_heap_size(p, total_heap_size,
1142                                   new_heap_size, old_heap_size)) {
1143             erts_set_self_exiting(p, am_killed);
1144             return 0;
1145         }
1146     }
1147 
1148     /*
1149      * Set GC state.
1150      */
1151     erts_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
1152 
1153     /*
1154      * Just did a major collection (which has discarded the old heap),
1155      * so that we don't have to cope with pointer to literals on the
1156      * old heap. We will now allocate an old heap to contain the literals.
1157      */
1158 
1159     ASSERT(p->old_heap == 0);	/* Must NOT have an old heap yet. */
1160     old_heap_size = erts_next_heap_size(lit_size, 0);
1161     p->old_heap = p->old_htop = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_OLD_HEAP,
1162 							 sizeof(Eterm)*old_heap_size);
1163     p->old_hend = p->old_heap + old_heap_size;
1164 
1165     /*
1166      * We soon want to garbage collect the literals. But since a GC is
1167      * destructive (MOVED markers are written), we must copy the literals
1168      * to a temporary area and change all references to literals.
1169      */
1170     temp_lit = (Eterm *) erts_alloc(ERTS_ALC_T_TMP, byte_lit_size);
1171     sys_memcpy(temp_lit, literals, byte_lit_size);
1172     offs = temp_lit - literals;
1173     offset_heap(temp_lit, lit_size, offs, (char *) literals, byte_lit_size);
1174     offset_heap(p->heap, p->htop - p->heap, offs, (char *) literals, byte_lit_size);
1175     offset_rootset(p, offs, (char *) literals, byte_lit_size, p->arg_reg, p->arity);
1176     if (oh) {
1177 	oh = (struct erl_off_heap_header *) ((Eterm *)(void *) oh + offs);
1178     }
1179 
1180     /*
1181      * Now the literals are placed in memory that is safe to write into,
1182      * so now we GC the literals into the old heap. First we go through the
1183      * rootset.
1184      */
1185 
1186     area = (char *) temp_lit;
1187     area_size = byte_lit_size;
1188     n = setup_rootset(p, p->arg_reg, p->arity, &rootset);
1189     roots = rootset.roots;
1190     old_htop = sweep_literals_nstack(p, p->old_htop, area, area_size);
1191     while (n--) {
1192         Eterm* g_ptr = roots->v;
1193         Uint g_sz = roots->sz;
1194 	Eterm* ptr;
1195 	Eterm val;
1196 
1197 	roots++;
1198 
1199         for ( ; g_sz--; g_ptr++) {
1200             Eterm gval = *g_ptr;
1201 
1202             switch (primary_tag(gval)) {
1203 	    case TAG_PRIMARY_BOXED:
1204 		ptr = boxed_val(gval);
1205 		val = *ptr;
1206                 if (IS_MOVED_BOXED(val)) {
1207 		    ASSERT(is_boxed(val));
1208                     *g_ptr = val;
1209 		} else if (ErtsInArea(ptr, area, area_size)) {
1210                     move_boxed(ptr,val,&old_htop,g_ptr);
1211 		}
1212 		break;
1213 	    case TAG_PRIMARY_LIST:
1214                 ptr = list_val(gval);
1215                 val = *ptr;
1216                 if (IS_MOVED_CONS(val)) { /* Moved */
1217                     *g_ptr = ptr[1];
1218 		} else if (ErtsInArea(ptr, area, area_size)) {
1219                     move_cons(ptr,val,&old_htop,g_ptr);
1220                 }
1221 		break;
1222 	    default:
1223 		break;
1224 	    }
1225 	}
1226     }
1227     ASSERT(p->old_htop <= old_htop && old_htop <= p->old_hend);
1228     cleanup_rootset(&rootset);
1229 
1230     /*
1231      * Now all references in the rootset to the literals have been updated.
1232      * Now we'll have to go through all heaps updating all other references.
1233      */
1234 
1235     old_htop = sweep_literals_to_old_heap(p->heap, p->htop, old_htop, area, area_size);
1236     old_htop = sweep_literal_area(p->old_heap, old_htop,
1237 				  (char *) p->old_heap, sizeof(Eterm)*old_heap_size,
1238 				  area, area_size);
1239     ASSERT(p->old_htop <= old_htop && old_htop <= p->old_hend);
1240     p->old_htop = old_htop;
1241 
1242     /*
1243      * Prepare to sweep off-heap objects. Since all MSOs on the new
1244      * heap must be come before MSOs on the old heap, find the end of
1245      * current MSO list and use that as a starting point.
1246      */
1247 
1248     if (oh) {
1249         prev = &MSO(p).first;
1250         while (*prev) {
1251             prev = &(*prev)->next;
1252         }
1253     }
1254 
1255     /*
1256      * Sweep through all off-heap objects in the temporary literal area.
1257      */
1258 
1259     while (oh) {
1260 	if (IS_MOVED_BOXED(oh->thing_word)) {
1261 	    struct erl_off_heap_header* ptr;
1262 
1263             /*
1264 	     * This off-heap object has been copied to the heap.
1265 	     * We must increment its reference count and
1266 	     * link it into the MSO list for the process.
1267 	     */
1268 
1269 	    ptr = (struct erl_off_heap_header*) boxed_val(oh->thing_word);
1270             switch (thing_subtag(ptr->thing_word)) {
1271             case REFC_BINARY_SUBTAG:
1272                 {
1273                     Binary* bptr = ((ProcBin*)ptr)->val;
1274                     erts_refc_inc(&bptr->intern.refc, 2);
1275                     break;
1276                 }
1277             case FUN_SUBTAG:
1278                 {
1279                     ErlFunEntry* fe = ((ErlFunThing*)ptr)->fe;
1280                     erts_refc_inc(&fe->refc, 2);
1281                     break;
1282                 }
1283             case REF_SUBTAG:
1284                 {
1285                     ErtsMagicBinary *bptr;
1286                     ASSERT(is_magic_ref_thing(ptr));
1287                     bptr = ((ErtsMRefThing *) ptr)->mb;
1288                     erts_refc_inc(&bptr->intern.refc, 2);
1289                     break;
1290                 }
1291             default:
1292                 {
1293                     ExternalThing *etp;
1294                     ASSERT(is_external_header(ptr->thing_word));
1295                     etp = (ExternalThing *) ptr;
1296                     erts_ref_node_entry(etp->node, 2,
1297                                         make_boxed(&oh->thing_word));
1298                     break;
1299                 }
1300             }
1301 	    *prev = ptr;
1302 	    prev = &ptr->next;
1303 	}
1304 	oh = oh->next;
1305     }
1306 
1307     if (prev) {
1308         *prev = NULL;
1309     }
1310 
1311     /*
1312      * We no longer need this temporary area.
1313      */
1314     erts_free(ERTS_ALC_T_TMP, (void *) temp_lit);
1315 
1316     /*
1317      * Restore status.
1318      */
1319     erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_GC);
1320 
1321     reds += (Sint64) gc_cost((p->htop - p->heap) + byte_lit_size/sizeof(Uint), 0);
1322 
1323     if (hibernated) {
1324         /* Restore the process into hibernated state... */
1325         reds += garbage_collect_hibernate(p, 0);
1326     }
1327 
1328     if (reds > INT_MAX)
1329 	return INT_MAX;
1330     return (int) reds;
1331 }
1332 
1333 static int
minor_collection(Process * p,ErlHeapFragment * live_hf_end,int need,Eterm * objv,int nobj,Uint ygen_usage,Uint * recl)1334 minor_collection(Process* p, ErlHeapFragment *live_hf_end,
1335 		 int need, Eterm* objv, int nobj,
1336 		 Uint ygen_usage, Uint *recl)
1337 {
1338     Eterm *mature = p->abandoned_heap ? p->abandoned_heap : p->heap;
1339     Uint mature_size = p->high_water - mature;
1340     Uint size_before = ygen_usage;
1341 #ifdef DEBUG
1342     Uint debug_tmp = 0;
1343 #endif
1344 
1345     /*
1346      * Check if we have gone past the max heap size limit
1347      */
1348 
1349     if (MAX_HEAP_SIZE_GET(p)) {
1350         Uint heap_size = size_before,
1351             /* Note that we also count the un-allocated area
1352                in between the stack and heap */
1353             stack_size = HEAP_END(p) - HEAP_TOP(p),
1354             extra_heap_size,
1355             extra_old_heap_size = 0;
1356 
1357         /* Add potential old heap size */
1358         if (OLD_HEAP(p) == NULL && mature_size != 0) {
1359             extra_old_heap_size = erts_next_heap_size(size_before, 1);
1360             heap_size += extra_old_heap_size;
1361         } else if (OLD_HEAP(p))
1362             heap_size += OLD_HEND(p) - OLD_HEAP(p);
1363 
1364         /* Add potential new young heap size */
1365         extra_heap_size = next_heap_size(p, stack_size + size_before, 0);
1366         heap_size += extra_heap_size;
1367 
1368         if (heap_size > MAX_HEAP_SIZE_GET(p))
1369             if (reached_max_heap_size(p, heap_size, extra_heap_size, extra_old_heap_size))
1370                 return -2;
1371     }
1372 
1373     /*
1374      * Allocate an old heap if we don't have one and if we'll need one.
1375      */
1376 
1377     if (OLD_HEAP(p) == NULL && mature_size != 0) {
1378         Eterm* n_old;
1379 
1380         /* Note: We choose a larger heap size than strictly needed,
1381          * which seems to reduce the number of fullsweeps.
1382          * This improved Estone by more than 1200 estones on my computer
1383          * (Ultra Sparc 10).
1384          */
1385         Uint new_sz = erts_next_heap_size(size_before, 1);
1386 
1387         /* Create new, empty old_heap */
1388         n_old = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_OLD_HEAP,
1389 					  sizeof(Eterm)*new_sz);
1390 
1391         OLD_HEND(p) = n_old + new_sz;
1392         OLD_HEAP(p) = OLD_HTOP(p) = n_old;
1393     }
1394 
1395     /*
1396      * Do a minor collection if there is an old heap and if it
1397      * is large enough.
1398      */
1399 
1400     if (OLD_HEAP(p) &&
1401 	((mature_size <= OLD_HEND(p) - OLD_HTOP(p)) &&
1402 	 ((BIN_OLD_VHEAP_SZ(p) > BIN_OLD_VHEAP(p))) ) ) {
1403 	Eterm *prev_old_htop;
1404 	Uint stack_size, size_after, adjust_size, need_after, new_sz, new_mature;
1405 
1406 	stack_size = STACK_START(p) - STACK_TOP(p);
1407 	new_sz = stack_size + size_before;
1408         new_sz = next_heap_size(p, new_sz, 0);
1409 
1410 	prev_old_htop = p->old_htop;
1411         do_minor(p, live_hf_end, (char *) mature, mature_size*sizeof(Eterm),
1412 		 new_sz, objv, nobj);
1413 
1414 	if (p->sig_qs.flags & FS_ON_HEAP_MSGQ)
1415 	    move_msgs_to_heap(p);
1416 
1417 	new_mature = p->old_htop - prev_old_htop;
1418 
1419 	size_after = new_mature;
1420         size_after += HEAP_TOP(p) - HEAP_START(p) + p->mbuf_sz;
1421         *recl += (size_before - size_after);
1422 
1423 	ErtsGcQuickSanityCheck(p);
1424 
1425         GEN_GCS(p)++;
1426         need_after = ((HEAP_TOP(p) - HEAP_START(p))
1427                       + need
1428                       + stack_size
1429                       + S_RESERVED);
1430 
1431         /*
1432          * Excessively large heaps should be shrunk, but
1433          * don't even bother on reasonable small heaps.
1434          *
1435          * The reason for this is that after tenuring, we often
1436          * use a really small portion of new heap, therefore, unless
1437          * the heap size is substantial, we don't want to shrink.
1438          */
1439 
1440 	adjust_size = 0;
1441 
1442         if ((HEAP_SIZE(p) > 3000) && (4 * need_after < HEAP_SIZE(p)) &&
1443             ((HEAP_SIZE(p) > 8000) ||
1444              (HEAP_SIZE(p) > (OLD_HEND(p) - OLD_HEAP(p))))) {
1445 	    Uint wanted = 3 * need_after;
1446 	    Uint old_heap_sz = OLD_HEND(p) - OLD_HEAP(p);
1447 
1448 	    /*
1449 	     * Additional test to make sure we don't make the heap too small
1450 	     * compared to the size of the older generation heap.
1451 	     */
1452 	    if (wanted*9 < old_heap_sz) {
1453 		Uint new_wanted = old_heap_sz / 8;
1454 		if (new_wanted > wanted) {
1455 		    wanted = new_wanted;
1456 		}
1457 	    }
1458 
1459 	    wanted = wanted < MIN_HEAP_SIZE(p) ? MIN_HEAP_SIZE(p)
1460 					       : next_heap_size(p, wanted, 0);
1461             if (wanted < HEAP_SIZE(p)) {
1462                 shrink_new_heap(p, wanted, objv, nobj);
1463 		adjust_size = p->htop - p->heap;
1464             }
1465 
1466         }
1467         else if (need_after > HEAP_SIZE(p)) {
1468             grow_new_heap(p, next_heap_size(p, need_after, 0), objv, nobj);
1469             adjust_size = p->htop - p->heap;
1470         }
1471 	/*else: The heap size turned out to be just right. We are done. */
1472 
1473 	ASSERT(HEAP_SIZE(p) == next_heap_size(p, HEAP_SIZE(p), 0));
1474 
1475         /* The heap usage during GC should be larger than what we end up
1476            after a GC, even if we grow it. If this assertion is not true
1477            we have to check size in grow_new_heap and potentially kill the
1478            process from there */
1479         ASSERT(!MAX_HEAP_SIZE_GET(p) ||
1480                !(MAX_HEAP_SIZE_FLAGS_GET(p) & MAX_HEAP_SIZE_KILL) ||
1481                MAX_HEAP_SIZE_GET(p) > (young_gen_usage(p, &debug_tmp) +
1482                                        (OLD_HEND(p) - OLD_HEAP(p)) +
1483                                        (HEAP_END(p) - HEAP_TOP(p))));
1484 
1485 	return gc_cost(size_after, adjust_size);
1486     }
1487 
1488     /*
1489      * Not enough room for a minor collection. Must force a major collection.
1490      */
1491     return -1;
1492 }
1493 
1494 static void
do_minor(Process * p,ErlHeapFragment * live_hf_end,char * mature,Uint mature_size,Uint new_sz,Eterm * objv,int nobj)1495 do_minor(Process *p, ErlHeapFragment *live_hf_end,
1496 	 char *mature, Uint mature_size,
1497 	 Uint new_sz, Eterm* objv, int nobj)
1498 {
1499     Rootset rootset;            /* Rootset for GC (stack, dictionary, etc). */
1500     Roots* roots;
1501     Eterm* n_htop;
1502     Uint n;
1503     Eterm* ptr;
1504     Eterm val;
1505     Eterm gval;
1506     Eterm* old_htop = OLD_HTOP(p);
1507     Eterm* n_heap;
1508     char* oh = (char *) OLD_HEAP(p);
1509     Uint oh_size = (char *) OLD_HTOP(p) - oh;
1510 
1511     VERBOSE(DEBUG_SHCOPY, ("[pid=%T] MINOR GC: %p %p %p %p\n", p->common.id,
1512                            HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)));
1513 
1514     n_htop = n_heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
1515 					       sizeof(Eterm)*new_sz);
1516 
1517     n = setup_rootset(p, objv, nobj, &rootset);
1518     roots = rootset.roots;
1519 
1520     /*
1521      * All allocations done. Start defile heap with move markers.
1522      * A crash dump due to allocation failure above will see a healthy heap.
1523      */
1524 
1525     if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
1526 	/*
1527 	 * Move heap frags that we know are completely live
1528 	 * directly into the new young heap generation.
1529 	 */
1530 	n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
1531     }
1532 
1533     GENSWEEP_NSTACK(p, old_htop, n_htop);
1534     while (n--) {
1535         Eterm* g_ptr = roots->v;
1536         Uint g_sz = roots->sz;
1537 
1538 	roots++;
1539         for ( ; g_sz--; g_ptr++) {
1540             gval = *g_ptr;
1541 
1542             switch (primary_tag(gval)) {
1543 
1544 	    case TAG_PRIMARY_BOXED: {
1545 		ptr = boxed_val(gval);
1546                 val = *ptr;
1547                 if (IS_MOVED_BOXED(val)) {
1548 		    ASSERT(is_boxed(val));
1549                     *g_ptr = val;
1550                 } else if (ErtsInArea(ptr, mature, mature_size)) {
1551                     move_boxed(ptr,val,&old_htop,g_ptr);
1552                 } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
1553                     move_boxed(ptr,val,&n_htop,g_ptr);
1554                 }
1555                 break;
1556 	    }
1557 
1558 	    case TAG_PRIMARY_LIST: {
1559                 ptr = list_val(gval);
1560                 val = *ptr;
1561                 if (IS_MOVED_CONS(val)) { /* Moved */
1562                     *g_ptr = ptr[1];
1563                 } else if (ErtsInArea(ptr, mature, mature_size)) {
1564                     move_cons(ptr,val,&old_htop,g_ptr);
1565                 } else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
1566                     move_cons(ptr,val,&n_htop,g_ptr);
1567                 }
1568 		break;
1569 	    }
1570 	    default:
1571 		break;
1572             }
1573         }
1574     }
1575 
1576     cleanup_rootset(&rootset);
1577 
1578     /*
1579      * Now all references in the rootset point to the new heap. However,
1580      * most references on the new heap point to the old heap so the next stage
1581      * is to scan through the new heap evacuating data from the old heap
1582      * until all is changed.
1583      */
1584 
1585     if (mature_size == 0) {
1586 	n_htop = sweep_new_heap(n_heap, n_htop, oh, oh_size);
1587     } else {
1588 	Eterm* n_hp = n_heap;
1589 	Eterm* ptr;
1590 	Eterm val;
1591 	Eterm gval;
1592 
1593 	while (n_hp != n_htop) {
1594 	    ASSERT(n_hp < n_htop);
1595 	    gval = *n_hp;
1596 	    switch (primary_tag(gval)) {
1597 	    case TAG_PRIMARY_BOXED: {
1598 		ptr = boxed_val(gval);
1599 		val = *ptr;
1600 		if (IS_MOVED_BOXED(val)) {
1601 		    ASSERT(is_boxed(val));
1602 		    *n_hp++ = val;
1603 		} else if (ErtsInArea(ptr, mature, mature_size)) {
1604 		    move_boxed(ptr,val,&old_htop,n_hp++);
1605 		} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
1606 		    move_boxed(ptr,val,&n_htop,n_hp++);
1607 		} else {
1608 		    n_hp++;
1609 		}
1610 		break;
1611 	    }
1612 	    case TAG_PRIMARY_LIST: {
1613 		ptr = list_val(gval);
1614 		val = *ptr;
1615 		if (IS_MOVED_CONS(val)) {
1616 		    *n_hp++ = ptr[1];
1617 		} else if (ErtsInArea(ptr, mature, mature_size)) {
1618 		    move_cons(ptr,val,&old_htop,n_hp++);
1619 		} else if (ErtsInYoungGen(gval, ptr, oh, oh_size)) {
1620 		    move_cons(ptr,val,&n_htop,n_hp++);
1621 		} else {
1622 		    n_hp++;
1623 		}
1624 		break;
1625 	    }
1626 	    case TAG_PRIMARY_HEADER: {
1627 		if (!header_is_thing(gval))
1628 		    n_hp++;
1629 		else {
1630 		    if (header_is_bin_matchstate(gval)) {
1631 			ErlBinMatchState *ms = (ErlBinMatchState*) n_hp;
1632 			ErlBinMatchBuffer *mb = &(ms->mb);
1633 			Eterm* origptr = &(mb->orig);
1634 			ptr = boxed_val(*origptr);
1635 			val = *ptr;
1636 			if (IS_MOVED_BOXED(val)) {
1637 			    *origptr = val;
1638 			    mb->base = binary_bytes(val);
1639 			} else if (ErtsInArea(ptr, mature, mature_size)) {
1640 			    move_boxed(ptr,val,&old_htop,origptr);
1641 			    mb->base = binary_bytes(mb->orig);
1642 			} else if (ErtsInYoungGen(*origptr, ptr, oh, oh_size)) {
1643 			    move_boxed(ptr,val,&n_htop,origptr);
1644 			    mb->base = binary_bytes(mb->orig);
1645 			}
1646 		    }
1647 		    n_hp += (thing_arityval(gval)+1);
1648 		}
1649 		break;
1650 	    }
1651 	    default:
1652 		n_hp++;
1653 		break;
1654 	    }
1655 	}
1656     }
1657 
1658     /*
1659      * And also if we have been tenuring, references on the second generation
1660      * may point to the old (soon to be deleted) new_heap.
1661      */
1662 
1663     if (OLD_HTOP(p) < old_htop)
1664 	old_htop = sweep_new_heap(OLD_HTOP(p), old_htop, oh, oh_size);
1665     OLD_HTOP(p) = old_htop;
1666     HIGH_WATER(p) = n_htop;
1667 
1668     if (MSO(p).first) {
1669 	sweep_off_heap(p, 0);
1670     }
1671 
1672 #ifdef HARDDEBUG
1673     /*
1674      * Go through the old_heap before, and try to find references from the old_heap
1675      * into the old new_heap that has just been evacuated and is about to be freed
1676      * (as well as looking for reference into heap fragments, of course).
1677      */
1678     disallow_heap_frag_ref_in_old_heap(p);
1679 #endif
1680 
1681     /* Copy stack to end of new heap */
1682     n = p->hend - p->stop;
1683     sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm));
1684     p->stop = n_heap + new_sz - n;
1685 
1686 #ifdef HARDDEBUG
1687     disallow_heap_frag_ref_in_heap(p, n_heap, n_htop);
1688 #endif
1689 
1690     erts_deallocate_young_generation(p);
1691 
1692     HEAP_START(p) = n_heap;
1693     HEAP_TOP(p) = n_htop;
1694     HEAP_END(p) = n_heap + new_sz;
1695 
1696 #ifdef USE_VM_PROBES
1697     if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) {
1698         DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
1699         Uint old_sz = HEAP_SIZE(p);
1700 
1701         HEAP_SIZE(p) = new_sz;
1702         dtrace_proc_str(p, pidbuf);
1703         DTRACE3(process_heap_grow, pidbuf, old_sz, new_sz);
1704     }
1705 #endif
1706     HEAP_SIZE(p) = new_sz;
1707 }
1708 
1709 /*
1710  * Major collection. DISCARD the old heap.
1711  */
1712 
1713 static int
major_collection(Process * p,ErlHeapFragment * live_hf_end,int need,Eterm * objv,int nobj,Uint ygen_usage,Uint * recl)1714 major_collection(Process* p, ErlHeapFragment *live_hf_end,
1715 		 int need, Eterm* objv, int nobj,
1716 		 Uint ygen_usage, Uint *recl)
1717 {
1718     Uint size_before, size_after, stack_size;
1719     Eterm* n_heap;
1720     Eterm* n_htop;
1721     char* oh = (char *) OLD_HEAP(p);
1722     Uint oh_size = (char *) OLD_HTOP(p) - oh;
1723     Uint new_sz, stk_sz;
1724     int adjusted;
1725 
1726     VERBOSE(DEBUG_SHCOPY, ("[pid=%T] MAJOR GC: %p %p %p %p\n", p->common.id,
1727                            HEAP_START(p), HEAP_END(p), OLD_HEAP(p), OLD_HEND(p)));
1728 
1729     /*
1730      * Do a fullsweep GC. First figure out the size of the heap
1731      * to receive all live data.
1732      */
1733 
1734     size_before = ygen_usage;
1735     size_before += p->old_htop - p->old_heap;
1736     stack_size = p->hend - p->stop;
1737 
1738     new_sz = stack_size + size_before + S_RESERVED;
1739     new_sz = next_heap_size(p, new_sz, 0);
1740 
1741     /*
1742      * Should we grow although we don't actually need to?
1743      */
1744 
1745     if (new_sz == HEAP_SIZE(p) && FLAGS(p) & F_HEAP_GROW) {
1746         new_sz = next_heap_size(p, HEAP_SIZE(p), 1);
1747     }
1748 
1749 
1750     if (MAX_HEAP_SIZE_GET(p)) {
1751         Uint heap_size = size_before;
1752 
1753         /* Add unused space in old heap */
1754         heap_size += OLD_HEND(p) - OLD_HTOP(p);
1755 
1756         /* Add stack + unused space in young heap */
1757         heap_size += HEAP_END(p) - HEAP_TOP(p);
1758 
1759         /* Add size of new young heap */
1760         heap_size += new_sz;
1761 
1762         if (MAX_HEAP_SIZE_GET(p) < heap_size)
1763             if (reached_max_heap_size(p, heap_size, new_sz, 0))
1764                 return -2;
1765     }
1766 
1767     FLAGS(p) &= ~(F_HEAP_GROW|F_NEED_FULLSWEEP);
1768     n_htop = n_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP,
1769 						sizeof(Eterm)*new_sz);
1770 
1771     n_htop = full_sweep_heaps(p, live_hf_end, 0, n_heap, n_htop, oh, oh_size,
1772                               objv, nobj);
1773 
1774     /* Move the stack to the end of the heap */
1775     stk_sz = HEAP_END(p) - p->stop;
1776     sys_memcpy(n_heap + new_sz - stk_sz, p->stop, stk_sz * sizeof(Eterm));
1777     p->stop = n_heap + new_sz - stk_sz;
1778 
1779 #ifdef HARDDEBUG
1780     disallow_heap_frag_ref_in_heap(p, n_heap, n_htop);
1781 #endif
1782 
1783     erts_deallocate_young_generation(p);
1784 
1785     HEAP_START(p) = n_heap;
1786     HEAP_TOP(p) = n_htop;
1787     HEAP_END(p) = n_heap + new_sz;
1788 
1789 #ifdef USE_VM_PROBES
1790     /* Fire process_heap_grow tracepoint after all heap references have
1791      * been updated. This allows to walk the stack. */
1792     if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) {
1793         DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
1794         Uint old_sz = HEAP_SIZE(p);
1795 
1796         /* Update the heap size before firing tracepoint */
1797         HEAP_SIZE(p) = new_sz;
1798 
1799         dtrace_proc_str(p, pidbuf);
1800         DTRACE3(process_heap_grow, pidbuf, old_sz, new_sz);
1801     }
1802 #endif
1803     HEAP_SIZE(p) = new_sz;
1804 
1805     GEN_GCS(p) = 0;
1806 
1807     HIGH_WATER(p) = HEAP_TOP(p);
1808 
1809     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ)
1810 	move_msgs_to_heap(p);
1811 
1812     ErtsGcQuickSanityCheck(p);
1813 
1814     size_after = HEAP_TOP(p) - HEAP_START(p) + p->mbuf_sz;
1815     *recl += size_before - size_after;
1816 
1817     adjusted = adjust_after_fullsweep(p, need, objv, nobj);
1818 
1819     ErtsGcQuickSanityCheck(p);
1820 
1821     return gc_cost(size_after, adjusted ? size_after : 0);
1822 }
1823 
1824 static Eterm *
full_sweep_heaps(Process * p,ErlHeapFragment * live_hf_end,int hibernate,Eterm * n_heap,Eterm * n_htop,char * oh,Uint oh_size,Eterm * objv,int nobj)1825 full_sweep_heaps(Process *p,
1826                  ErlHeapFragment *live_hf_end,
1827 		 int hibernate,
1828 		 Eterm *n_heap, Eterm* n_htop,
1829 		 char *oh, Uint oh_size,
1830 		 Eterm *objv, int nobj)
1831 {
1832     Rootset rootset;
1833     Roots *roots;
1834     Uint n;
1835 
1836     /*
1837      * Copy all top-level terms directly referenced by the rootset to
1838      * the new new_heap.
1839      */
1840 
1841     n = setup_rootset(p, objv, nobj, &rootset);
1842 
1843     /*
1844      * All allocations done. Start defile heap with move markers.
1845      * A crash dump due to allocation failure above will see a healthy heap.
1846      */
1847 
1848     if (live_hf_end != ERTS_INVALID_HFRAG_PTR) {
1849         /*
1850          * Move heap frags that we know are completely live
1851          * directly into the heap.
1852          */
1853         n_htop = collect_live_heap_frags(p, live_hf_end, n_htop);
1854     }
1855 
1856     roots = rootset.roots;
1857     while (n--) {
1858 	Eterm* g_ptr = roots->v;
1859 	Eterm g_sz = roots->sz;
1860 
1861 	roots++;
1862 	for ( ; g_sz--; g_ptr++) {
1863 	    Eterm* ptr;
1864 	    Eterm val;
1865 	    Eterm gval = *g_ptr;
1866 
1867 	    switch (primary_tag(gval)) {
1868 
1869 	    case TAG_PRIMARY_BOXED: {
1870 		ptr = boxed_val(gval);
1871 		val = *ptr;
1872 		if (IS_MOVED_BOXED(val)) {
1873 		    ASSERT(is_boxed(val));
1874 		    *g_ptr = val;
1875 		} else if (!erts_is_literal(gval, ptr)) {
1876 		    move_boxed(ptr,val,&n_htop,g_ptr);
1877 		}
1878                 break;
1879 	    }
1880 
1881 	    case TAG_PRIMARY_LIST: {
1882 		ptr = list_val(gval);
1883 		val = *ptr;
1884 		if (IS_MOVED_CONS(val)) {
1885 		    *g_ptr = ptr[1];
1886 		} else if (!erts_is_literal(gval, ptr)) {
1887 		    move_cons(ptr,val,&n_htop,g_ptr);
1888 		}
1889                 break;
1890 	    }
1891 
1892             default:
1893                 break;
1894 	    }
1895 	}
1896     }
1897 
1898     cleanup_rootset(&rootset);
1899 
1900     /*
1901      * Now all references on the stack point to the new heap. However,
1902      * most references on the new heap point to the old heap so the next stage
1903      * is to scan through the new heap evacuating data from the old heap
1904      * until all is copied.
1905      */
1906 
1907     n_htop = sweep_heaps(n_heap, n_htop, oh, oh_size);
1908 
1909     if (MSO(p).first) {
1910 	sweep_off_heap(p, 1);
1911     }
1912 
1913     if (OLD_HEAP(p) != NULL) {
1914 	ERTS_HEAP_FREE(ERTS_ALC_T_OLD_HEAP,
1915 		       OLD_HEAP(p),
1916 		       (OLD_HEND(p) - OLD_HEAP(p)) * sizeof(Eterm));
1917 	OLD_HEAP(p) = OLD_HTOP(p) = OLD_HEND(p) = NULL;
1918     }
1919 
1920     return n_htop;
1921 }
1922 
1923 static int
adjust_after_fullsweep(Process * p,int need,Eterm * objv,int nobj)1924 adjust_after_fullsweep(Process *p, int need, Eterm *objv, int nobj)
1925 {
1926     int adjusted = 0;
1927     Uint wanted, sz, need_after;
1928     Uint stack_size = STACK_SZ_ON_HEAP(p);
1929 
1930     /*
1931      * Resize the heap if needed.
1932      */
1933 
1934     need_after = (HEAP_TOP(p) - HEAP_START(p)) + need + stack_size + S_RESERVED;
1935     if (HEAP_SIZE(p) < need_after) {
1936         /* Too small - grow to match requested need */
1937         sz = next_heap_size(p, need_after, 0);
1938         grow_new_heap(p, sz, objv, nobj);
1939 	adjusted = 1;
1940     } else if (3 * HEAP_SIZE(p) < 4 * need_after){
1941         /* Need more than 75% of current, postpone to next GC.*/
1942         FLAGS(p) |= F_HEAP_GROW;
1943     } else if (4 * need_after < HEAP_SIZE(p) && HEAP_SIZE(p) > H_MIN_SIZE){
1944         /* We need less than 25% of the current heap, shrink.*/
1945         /* XXX - This is how it was done in the old GC:
1946            wanted = 4 * need_after;
1947            I think this is better as fullsweep is used mainly on
1948            small memory systems, but I could be wrong... */
1949         wanted = 2 * need_after;
1950 
1951 	sz = wanted < p->min_heap_size ? p->min_heap_size
1952 				       : next_heap_size(p, wanted, 0);
1953 
1954         if (sz < HEAP_SIZE(p)) {
1955             shrink_new_heap(p, sz, objv, nobj);
1956 	    adjusted = 1;
1957         }
1958     }
1959     return adjusted;
1960 }
1961 
1962 void
erts_deallocate_young_generation(Process * c_p)1963 erts_deallocate_young_generation(Process *c_p)
1964 {
1965     Eterm *orig_heap;
1966 
1967     if (!c_p->abandoned_heap) {
1968 	orig_heap = c_p->heap;
1969 	ASSERT(!(c_p->flags & F_ABANDONED_HEAP_USE));
1970     }
1971     else {
1972 	ErlHeapFragment *hfrag;
1973 
1974 	orig_heap = c_p->abandoned_heap;
1975 	c_p->abandoned_heap = NULL;
1976 	c_p->flags &= ~F_ABANDONED_HEAP_USE;
1977 
1978 	/*
1979 	 * Temporary heap located in heap fragment
1980 	 * only referred to by 'c_p->heap'. Add it to
1981 	 * 'c_p->mbuf' list and deallocate it as any
1982 	 * other heap fragment...
1983 	 */
1984 	hfrag = ((ErlHeapFragment *)
1985 		 (((char *) c_p->heap)
1986 		  - offsetof(ErlHeapFragment, mem)));
1987 
1988 	ASSERT(!hfrag->off_heap.first);
1989 	ASSERT(!hfrag->off_heap.overhead);
1990 	ASSERT(!hfrag->next);
1991 	ASSERT(c_p->htop - c_p->heap <= hfrag->alloc_size);
1992 
1993 	hfrag->next = c_p->mbuf;
1994 	c_p->mbuf = hfrag;
1995     }
1996 
1997     if (c_p->mbuf) {
1998 	free_message_buffer(c_p->mbuf);
1999 	c_p->mbuf = NULL;
2000     }
2001     if (c_p->msg_frag) {
2002 	erts_cleanup_messages(c_p->msg_frag);
2003 	c_p->msg_frag = NULL;
2004     }
2005     c_p->mbuf_sz = 0;
2006 
2007     ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
2008 		   orig_heap,
2009 		   c_p->heap_sz * sizeof(Eterm));
2010 }
2011 
2012 #ifdef HARDDEBUG
2013 
2014 /*
2015  * Routines to verify that we don't have pointer into heap fragments from
2016  * that are not allowed to have them.
2017  *
2018  * For performance reasons, we use _unchecked_list_val(), _unchecked_boxed_val(),
2019  * and so on to avoid a function call.
2020  */
2021 
2022 static void
disallow_heap_frag_ref_in_heap(Process * p,Eterm * heap,Eterm * htop)2023 disallow_heap_frag_ref_in_heap(Process *p, Eterm *heap, Eterm *htop)
2024 {
2025     Eterm* hp;
2026     Uint heap_size;
2027 
2028     if (p->mbuf == 0) {
2029 	return;
2030     }
2031 
2032     heap_size = (htop - heap)*sizeof(Eterm);
2033 
2034     hp = heap;
2035     while (hp < htop) {
2036 	ErlHeapFragment* qb;
2037 	Eterm* ptr;
2038 	Eterm val;
2039 
2040 	val = *hp++;
2041 	switch (primary_tag(val)) {
2042 	case TAG_PRIMARY_BOXED:
2043 	    ptr = _unchecked_boxed_val(val);
2044 	    if (!ErtsInArea(ptr, heap, heap_size)) {
2045 		for (qb = MBUF(p); qb != NULL; qb = qb->next) {
2046 		    if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
2047 			abort();
2048 		    }
2049 		}
2050 	    }
2051 	    break;
2052 	case TAG_PRIMARY_LIST:
2053 	    ptr = _unchecked_list_val(val);
2054 	    if (!ErtsInArea(ptr, heap, heap_size)) {
2055 		for (qb = MBUF(p); qb != NULL; qb = qb->next) {
2056 		    if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
2057 			abort();
2058 		    }
2059 		}
2060 	    }
2061 	    break;
2062 	case TAG_PRIMARY_HEADER:
2063 	    if (header_is_thing(val)) {
2064 		hp += _unchecked_thing_arityval(val);
2065 	    }
2066 	    break;
2067 	}
2068     }
2069 }
2070 
2071 static void
disallow_heap_frag_ref_in_old_heap(Process * p)2072 disallow_heap_frag_ref_in_old_heap(Process* p)
2073 {
2074     Eterm* hp;
2075     Eterm* htop;
2076     Eterm* old_heap;
2077     Uint old_heap_size;
2078     Eterm* new_heap;
2079     Uint new_heap_size;
2080 
2081     htop = p->old_htop;
2082     old_heap = p->old_heap;
2083     old_heap_size = (htop - old_heap)*sizeof(Eterm);
2084     new_heap = p->heap;
2085     new_heap_size = (p->htop - new_heap)*sizeof(Eterm);
2086 
2087     ASSERT(!p->last_old_htop
2088 	   || (old_heap <= p->last_old_htop && p->last_old_htop <= htop));
2089     hp = p->last_old_htop ? p->last_old_htop : old_heap;
2090     while (hp < htop) {
2091 	ErlHeapFragment* qb;
2092 	Eterm* ptr;
2093 	Eterm val;
2094 
2095 	val = *hp++;
2096 	switch (primary_tag(val)) {
2097 	case TAG_PRIMARY_BOXED:
2098 	    ptr = (Eterm *) val;
2099 	    if (!ErtsInArea(ptr, old_heap, old_heap_size)) {
2100 		if (ErtsInArea(ptr, new_heap, new_heap_size)) {
2101 		    abort();
2102 		}
2103 		for (qb = MBUF(p); qb != NULL; qb = qb->next) {
2104 		    if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
2105 			abort();
2106 		    }
2107 		}
2108 	    }
2109 	    break;
2110 	case TAG_PRIMARY_LIST:
2111 	    ptr = (Eterm *) val;
2112 	    if (!ErtsInArea(ptr, old_heap, old_heap_size)) {
2113 		if (ErtsInArea(ptr, new_heap, new_heap_size)) {
2114 		    abort();
2115 		}
2116 		for (qb = MBUF(p); qb != NULL; qb = qb->next) {
2117 		    if (ErtsInArea(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
2118 			abort();
2119 		    }
2120 		}
2121 	    }
2122 	    break;
2123 	case TAG_PRIMARY_HEADER:
2124 	    if (header_is_thing(val)) {
2125 		hp += _unchecked_thing_arityval(val);
2126 		if (!ErtsInArea(hp, old_heap, old_heap_size+1)) {
2127 		    abort();
2128 		}
2129 	    }
2130 	    break;
2131 	}
2132     }
2133 }
2134 #endif
2135 
2136 typedef enum {
2137     ErtsSweepNewHeap,
2138     ErtsSweepHeaps,
2139     ErtsSweepLiteralArea
2140 } ErtsSweepType;
2141 
2142 static ERTS_FORCE_INLINE Eterm *
sweep(Eterm * n_hp,Eterm * n_htop,ErtsSweepType type,char * oh,Uint ohsz,char * src,Uint src_size)2143 sweep(Eterm *n_hp, Eterm *n_htop,
2144       ErtsSweepType type,
2145       char *oh, Uint ohsz,
2146       char *src, Uint src_size)
2147 {
2148     Eterm* ptr;
2149     Eterm val;
2150     Eterm gval;
2151 
2152 #undef ERTS_IS_IN_SWEEP_AREA
2153 
2154 #define ERTS_IS_IN_SWEEP_AREA(TPtr, Ptr)				\
2155     (type == ErtsSweepHeaps						\
2156      ? !erts_is_literal((TPtr), (Ptr))					\
2157      : (type == ErtsSweepNewHeap					\
2158 	? ErtsInYoungGen((TPtr), (Ptr), oh, ohsz)			\
2159 	: ErtsInArea((Ptr), src, src_size)))
2160 
2161     while (n_hp != n_htop) {
2162 	ASSERT(n_hp < n_htop);
2163 	gval = *n_hp;
2164 	switch (primary_tag(gval)) {
2165 	case TAG_PRIMARY_BOXED: {
2166 	    ptr = boxed_val(gval);
2167 	    val = *ptr;
2168 	    if (IS_MOVED_BOXED(val)) {
2169 		ASSERT(is_boxed(val));
2170 		*n_hp++ = val;
2171 	    } else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
2172 		move_boxed(ptr,val,&n_htop,n_hp++);
2173 	    } else {
2174 		n_hp++;
2175 	    }
2176 	    break;
2177 	}
2178 	case TAG_PRIMARY_LIST: {
2179 	    ptr = list_val(gval);
2180 	    val = *ptr;
2181 	    if (IS_MOVED_CONS(val)) {
2182 		*n_hp++ = ptr[1];
2183 	    } else if (ERTS_IS_IN_SWEEP_AREA(gval, ptr)) {
2184 		move_cons(ptr,val,&n_htop,n_hp++);
2185 	    } else {
2186 		n_hp++;
2187 	    }
2188 	    break;
2189 	}
2190 	case TAG_PRIMARY_HEADER: {
2191 	    if (!header_is_thing(gval)) {
2192 		n_hp++;
2193 	    } else {
2194 		if (header_is_bin_matchstate(gval)) {
2195 		    ErlBinMatchState *ms = (ErlBinMatchState*) n_hp;
2196 		    ErlBinMatchBuffer *mb = &(ms->mb);
2197 		    Eterm* origptr;
2198 		    origptr = &(mb->orig);
2199 		    ptr = boxed_val(*origptr);
2200 		    val = *ptr;
2201 		    if (IS_MOVED_BOXED(val)) {
2202 			*origptr = val;
2203 			mb->base = binary_bytes(*origptr);
2204 		    } else if (ERTS_IS_IN_SWEEP_AREA(*origptr, ptr)) {
2205 			move_boxed(ptr,val,&n_htop,origptr);
2206 			mb->base = binary_bytes(*origptr);
2207 		    }
2208 		}
2209 		n_hp += (thing_arityval(gval)+1);
2210 	    }
2211 	    break;
2212 	}
2213 	default:
2214 	    n_hp++;
2215 	    break;
2216 	}
2217     }
2218     return n_htop;
2219 #undef ERTS_IS_IN_SWEEP_AREA
2220 }
2221 
2222 static Eterm *
sweep_new_heap(Eterm * n_hp,Eterm * n_htop,char * old_heap,Uint old_heap_size)2223 sweep_new_heap(Eterm *n_hp, Eterm *n_htop, char* old_heap, Uint old_heap_size)
2224 {
2225     return sweep(n_hp, n_htop,
2226 		 ErtsSweepNewHeap,
2227 		 old_heap, old_heap_size,
2228 		 NULL, 0);
2229 }
2230 
2231 static Eterm *
sweep_heaps(Eterm * n_hp,Eterm * n_htop,char * old_heap,Uint old_heap_size)2232 sweep_heaps(Eterm *n_hp, Eterm *n_htop, char* old_heap, Uint old_heap_size)
2233 {
2234     return sweep(n_hp, n_htop,
2235 		 ErtsSweepHeaps,
2236 		 old_heap, old_heap_size,
2237 		 NULL, 0);
2238 }
2239 
2240 static Eterm *
sweep_literal_area(Eterm * n_hp,Eterm * n_htop,char * old_heap,Uint old_heap_size,char * src,Uint src_size)2241 sweep_literal_area(Eterm *n_hp, Eterm *n_htop,
2242 		   char* old_heap, Uint old_heap_size,
2243 		   char* src, Uint src_size)
2244 {
2245     return sweep(n_hp, n_htop,
2246 		 ErtsSweepLiteralArea,
2247 		 old_heap, old_heap_size,
2248 		 src, src_size);
2249 }
2250 
2251 static Eterm*
sweep_literals_to_old_heap(Eterm * heap_ptr,Eterm * heap_end,Eterm * htop,char * src,Uint src_size)2252 sweep_literals_to_old_heap(Eterm* heap_ptr, Eterm* heap_end, Eterm* htop,
2253 			   char* src, Uint src_size)
2254 {
2255     while (heap_ptr < heap_end) {
2256 	Eterm* ptr;
2257 	Eterm val;
2258 	Eterm gval = *heap_ptr;
2259 
2260 	switch (primary_tag(gval)) {
2261 	case TAG_PRIMARY_BOXED: {
2262 	    ptr = boxed_val(gval);
2263 	    val = *ptr;
2264 	    if (IS_MOVED_BOXED(val)) {
2265 		ASSERT(is_boxed(val));
2266 		*heap_ptr++ = val;
2267 	    } else if (ErtsInArea(ptr, src, src_size)) {
2268 		move_boxed(ptr,val,&htop,heap_ptr++);
2269 	    } else {
2270 		heap_ptr++;
2271 	    }
2272 	    break;
2273 	}
2274 	case TAG_PRIMARY_LIST: {
2275 	    ptr = list_val(gval);
2276 	    val = *ptr;
2277 	    if (IS_MOVED_CONS(val)) {
2278 		*heap_ptr++ = ptr[1];
2279 	    } else if (ErtsInArea(ptr, src, src_size)) {
2280 		move_cons(ptr,val,&htop,heap_ptr++);
2281 	    } else {
2282 		heap_ptr++;
2283 	    }
2284 	    break;
2285 	}
2286 	case TAG_PRIMARY_HEADER: {
2287 	    if (!header_is_thing(gval)) {
2288 		heap_ptr++;
2289 	    } else {
2290 		if (header_is_bin_matchstate(gval)) {
2291 		    ErlBinMatchState *ms = (ErlBinMatchState*) heap_ptr;
2292 		    ErlBinMatchBuffer *mb = &(ms->mb);
2293 		    Eterm* origptr;
2294 		    origptr = &(mb->orig);
2295 		    ptr = boxed_val(*origptr);
2296 		    val = *ptr;
2297 		    if (IS_MOVED_BOXED(val)) {
2298 			*origptr = val;
2299 			mb->base = binary_bytes(*origptr);
2300 		    } else if (ErtsInArea(ptr, src, src_size)) {
2301 			move_boxed(ptr,val,&htop,origptr);
2302 			mb->base = binary_bytes(*origptr);
2303 		    }
2304 		}
2305 		heap_ptr += (thing_arityval(gval)+1);
2306 	    }
2307 	    break;
2308 	}
2309 	default:
2310 	    heap_ptr++;
2311 	    break;
2312 	}
2313     }
2314     return htop;
2315 }
2316 
2317 /*
2318  * Move an area (heap fragment) by sweeping over it and set move markers.
2319  */
2320 static Eterm*
move_one_area(Eterm * n_htop,char * src,Uint src_size)2321 move_one_area(Eterm* n_htop, char* src, Uint src_size)
2322 {
2323     Eterm* ptr = (Eterm*) src;
2324     Eterm* end = ptr + src_size/sizeof(Eterm);
2325     Eterm dummy_ref;
2326 
2327     while (ptr != end) {
2328 	Eterm val;
2329 	ASSERT(ptr < end);
2330 	val = *ptr;
2331 	ASSERT(val != ERTS_HOLE_MARKER);
2332 	if (is_header(val)) {
2333 	    ASSERT(ptr + header_arity(val) < end);
2334 	    ptr = move_boxed(ptr, val, &n_htop, &dummy_ref);
2335 	}
2336 	else { /* must be a cons cell */
2337 	    ASSERT(ptr+1 < end);
2338 	    move_cons(ptr, val, &n_htop, &dummy_ref);
2339 	    ptr += 2;
2340 	}
2341     }
2342 
2343     return n_htop;
2344 }
2345 
2346 /*
2347  * Collect heap fragments and check that they point in the correct direction.
2348  */
2349 
2350 static Eterm*
collect_live_heap_frags(Process * p,ErlHeapFragment * live_hf_end,Eterm * n_htop)2351 collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, Eterm* n_htop)
2352 {
2353     ErlHeapFragment* qb;
2354     char* frag_begin;
2355     Uint frag_size;
2356 
2357     /*
2358      * Move the heap fragments to the new heap. Note that no GC is done on
2359      * the heap fragments. Any garbage will thus be moved as well and survive
2360      * until next GC.
2361      */
2362     qb = MBUF(p);
2363     while (qb != live_hf_end) {
2364         ASSERT(!qb->off_heap.first);  /* process fragments use the MSO(p) list */
2365 	frag_size = qb->used_size * sizeof(Eterm);
2366 	if (frag_size != 0) {
2367 	    frag_begin = (char *) qb->mem;
2368 	    n_htop = move_one_area(n_htop, frag_begin, frag_size);
2369 	}
2370 	qb = qb->next;
2371     }
2372     return n_htop;
2373 }
2374 
2375 void
erts_copy_one_frag(Eterm ** hpp,ErlOffHeap * off_heap,ErlHeapFragment * bp,Eterm * refs,int nrefs)2376 erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
2377                    ErlHeapFragment *bp, Eterm *refs, int nrefs)
2378 {
2379     Uint sz;
2380     int i;
2381     Sint offs;
2382     struct erl_off_heap_header* oh;
2383     Eterm *fhp, *hp;
2384 
2385     OH_OVERHEAD(off_heap, bp->off_heap.overhead);
2386     sz = bp->used_size;
2387 
2388     fhp = bp->mem;
2389     hp = *hpp;
2390     offs = hp - fhp;
2391 
2392     oh = NULL;
2393     while (sz--) {
2394 	Uint cpy_sz;
2395 	Eterm val = *fhp++;
2396 
2397 	switch (primary_tag(val)) {
2398 	case TAG_PRIMARY_IMMED1:
2399 	    *hp++ = val;
2400 	    break;
2401 	case TAG_PRIMARY_LIST:
2402             if (erts_is_literal(val,list_val(val))) {
2403                 *hp++ = val;
2404             } else {
2405                 *hp++ = offset_ptr(val, offs);
2406             }
2407             break;
2408 	case TAG_PRIMARY_BOXED:
2409             if (erts_is_literal(val,boxed_val(val))) {
2410                 *hp++ = val;
2411             } else {
2412                 *hp++ = offset_ptr(val, offs);
2413             }
2414 	    break;
2415 	case TAG_PRIMARY_HEADER:
2416 	    *hp++ = val;
2417 	    switch (val & _HEADER_SUBTAG_MASK) {
2418 	    case ARITYVAL_SUBTAG:
2419 		break;
2420 	    case REF_SUBTAG:
2421 		if (!is_magic_ref_thing(fhp - 1))
2422 		    goto the_default;
2423 	    case REFC_BINARY_SUBTAG:
2424 	    case FUN_SUBTAG:
2425 	    case EXTERNAL_PID_SUBTAG:
2426 	    case EXTERNAL_PORT_SUBTAG:
2427 	    case EXTERNAL_REF_SUBTAG:
2428 		oh = (struct erl_off_heap_header*) (hp-1);
2429 		cpy_sz = thing_arityval(val);
2430 		goto cpy_words;
2431 	    default:
2432 	    the_default:
2433 		cpy_sz = header_arity(val);
2434 
2435 	    cpy_words:
2436 		ASSERT(sz >= cpy_sz);
2437 		sz -= cpy_sz;
2438                 sys_memcpy(hp, fhp, cpy_sz * sizeof(Eterm));
2439                 hp += cpy_sz;
2440                 fhp += cpy_sz;
2441 		if (oh) {
2442 		    /* Add to offheap list */
2443 		    oh->next = off_heap->first;
2444 		    off_heap->first = oh;
2445 		    ASSERT(*hpp <= (Eterm*)oh);
2446 		    ASSERT(hp > (Eterm*)oh);
2447 		    oh = NULL;
2448 		}
2449 		break;
2450 	    }
2451 	    break;
2452 	}
2453     }
2454 
2455     ASSERT(bp->used_size == hp - *hpp);
2456     *hpp = hp;
2457 
2458     for (i = 0; i < nrefs; i++) {
2459 	if (is_not_immed(refs[i]) && !erts_is_literal(refs[i],ptr_val(refs[i])))
2460 	    refs[i] = offset_ptr(refs[i], offs);
2461     }
2462     bp->off_heap.first = NULL;
2463 }
2464 
2465 static Uint
setup_rootset(Process * p,Eterm * objv,int nobj,Rootset * rootset)2466 setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
2467 {
2468     /*
2469      * NOTE!
2470      *   Remember to update offset_rootset() when changing
2471      *   this function.
2472      */
2473     Roots* roots;
2474     Uint n;
2475 
2476     n = 0;
2477     roots = rootset->roots = rootset->def;
2478     rootset->size = ALENGTH(rootset->def);
2479 
2480     roots[n].v  = p->stop;
2481     roots[n].sz = STACK_START(p) - p->stop;
2482     ++n;
2483 
2484     if (p->dictionary != NULL) {
2485         roots[n].v = ERTS_PD_START(p->dictionary);
2486         roots[n].sz = ERTS_PD_SIZE(p->dictionary);
2487         ++n;
2488     }
2489     if (nobj > 0) {
2490         roots[n].v  = objv;
2491         roots[n].sz = nobj;
2492         ++n;
2493     }
2494 
2495     ASSERT((is_nil(p->seq_trace_token) ||
2496 	    is_tuple(follow_moved(p->seq_trace_token, (Eterm) 0)) ||
2497 	    is_atom(p->seq_trace_token)));
2498     if (is_not_immed(p->seq_trace_token)) {
2499 	roots[n].v = &p->seq_trace_token;
2500 	roots[n].sz = 1;
2501 	n++;
2502     }
2503 #ifdef USE_VM_PROBES
2504     if (is_not_immed(p->dt_utag)) {
2505 	roots[n].v = &p->dt_utag;
2506 	roots[n].sz = 1;
2507 	n++;
2508     }
2509 #endif
2510     ASSERT(IS_TRACER_VALID(ERTS_TRACER(p)));
2511 
2512     ASSERT(is_pid(follow_moved(p->group_leader, (Eterm) 0)));
2513     if (is_not_immed(p->group_leader)) {
2514 	roots[n].v  = &p->group_leader;
2515 	roots[n].sz = 1;
2516 	n++;
2517     }
2518 
2519     /*
2520      * The process may be garbage-collected while it is terminating.
2521      * fvalue contains the EXIT reason.
2522      */
2523     if (is_not_immed(p->fvalue)) {
2524 	roots[n].v  = &p->fvalue;
2525 	roots[n].sz = 1;
2526 	n++;
2527     }
2528 
2529     /*
2530      * The raise/3 BIF will store the stacktrace in ftrace.
2531      */
2532     if (is_not_immed(p->ftrace)) {
2533 	roots[n].v  = &p->ftrace;
2534 	roots[n].sz = 1;
2535 	n++;
2536     }
2537 
2538     if (p->sig_qs.recv_mrk_blk) {
2539 	roots[n].v  = &p->sig_qs.recv_mrk_blk->ref[0];
2540 	roots[n].sz = ERTS_RECV_MARKER_BLOCK_SIZE;
2541 	n++;
2542     }
2543 
2544     /*
2545      * If a NIF or BIF has saved arguments, they need to be added
2546      */
2547     if (erts_setup_nfunc_rootset(p, &roots[n].v, &roots[n].sz))
2548 	n++;
2549 
2550     ASSERT(n <= rootset->size);
2551 
2552     if ((p->sig_qs.flags & (FS_OFF_HEAP_MSGQ
2553 			    | FS_OFF_HEAP_MSGQ_CHNG)) != FS_OFF_HEAP_MSGQ) {
2554 	Uint size;
2555 	ErtsMessage *mp;
2556 	/*
2557 	 * We do not have off heap message queue enabled, i.e. we
2558 	 * need to add signal queues to rootset...
2559 	 */
2560 
2561 #ifdef DEBUG
2562         if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
2563             erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
2564             /*
2565              * Verify that we do not have any messages in the outer
2566              * queue that might refer to the heap...
2567              */
2568             for (mp = p->sig_inq.first; mp; mp = mp->next) {
2569                 if ((ERTS_SIG_IS_INTERNAL_MSG(mp) && !mp->data.attached)
2570                     || ERTS_SIG_IS_HEAP_ALIAS_MSG(mp)) {
2571                     int i = ERTS_SIG_IS_INTERNAL_MSG(mp) ? 0 : 1;
2572                     for (; i < ERL_MESSAGE_REF_ARRAY_SZ; i++) {
2573                         ASSERT(is_immed(mp->m[i])
2574                                || erts_is_literal(mp->m[i],
2575                                                   ptr_val(mp->m[i])));
2576                     }
2577                 }
2578             }
2579             erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
2580         }
2581 #endif
2582 
2583 	size = n + erts_proc_sig_privqs_len(p);
2584 	if (p->sig_qs.cont) {
2585 	    /*
2586 	     * We do not know the exact size needed since
2587 	     * alias-message signals are not included in
2588 	     * length of private queues. We might have to
2589 	     * resize while inspecting the signal queue...
2590 	     */
2591 	    size += 128;
2592 	}
2593 	if (size > rootset->size) {
2594 	    ERTS_GC_ASSERT(roots == rootset->def);
2595 	    roots = erts_alloc(ERTS_ALC_T_ROOTSET,
2596 			       size*sizeof(Roots));
2597 	    sys_memcpy(roots, rootset->def, n*sizeof(Roots));
2598 	    rootset->size = size;
2599 	}
2600 
2601 	mp = p->sig_qs.first;
2602 	while (mp) {
2603 	    if (ERTS_SIG_IS_INTERNAL_MSG(mp) && !mp->data.attached) {
2604 		roots[n].v = mp->m;
2605 		roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ;
2606 		n++;
2607 	    }
2608 	    mp = mp->next;
2609 	}
2610 	mp = p->sig_qs.cont;
2611 	while (mp) {
2612 	    if (size <= n) {
2613 		Roots *old_roots = roots;
2614 		size += 128;
2615 		roots = erts_alloc(ERTS_ALC_T_ROOTSET,
2616 				   size*sizeof(Roots));
2617 		sys_memcpy(roots, old_roots, n*sizeof(Roots));
2618 		if (old_roots != rootset->def)
2619 		    erts_free(ERTS_ALC_T_ROOTSET, old_roots);
2620 		rootset->size = size;
2621 	    }
2622 	    if (ERTS_SIG_IS_INTERNAL_MSG(mp) && !mp->data.attached) {
2623 		roots[n].v = mp->m;
2624 		roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ;
2625 		n++;
2626 	    }
2627 	    else if (ERTS_SIG_IS_HEAP_ALIAS_MSG(mp)) {
2628 		/* Exclude message slot... */
2629 		roots[n].v = &mp->m[1];
2630 		roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ - 1;
2631 		n++;
2632 	    }
2633 	    mp = mp->next;
2634 	}
2635     }
2636 
2637     ASSERT(rootset->size >= n);
2638 
2639     rootset->roots = roots;
2640     rootset->num_roots = n;
2641     return n;
2642 }
2643 
2644 static
cleanup_rootset(Rootset * rootset)2645 void cleanup_rootset(Rootset* rootset)
2646 {
2647     if (rootset->roots != rootset->def) {
2648         erts_free(ERTS_ALC_T_ROOTSET, rootset->roots);
2649     }
2650 }
2651 
2652 static void
grow_new_heap(Process * p,Uint new_sz,Eterm * objv,int nobj)2653 grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj)
2654 {
2655     Eterm* new_heap;
2656     Uint heap_size = HEAP_TOP(p) - HEAP_START(p);
2657     Uint stack_size = p->hend - p->stop;
2658     Sint offs;
2659 
2660     ASSERT(HEAP_SIZE(p) < new_sz);
2661     new_heap = (Eterm *) ERTS_HEAP_REALLOC(ERTS_ALC_T_HEAP,
2662 					   (void *) HEAP_START(p),
2663 					   sizeof(Eterm)*(HEAP_SIZE(p)),
2664 					   sizeof(Eterm)*new_sz);
2665 
2666     if ((offs = new_heap - HEAP_START(p)) == 0) { /* No move. */
2667         HEAP_END(p) = new_heap + new_sz;
2668         sys_memmove(p->hend - stack_size, p->stop, stack_size * sizeof(Eterm));
2669         p->stop = p->hend - stack_size;
2670     } else {
2671 	char* area = (char *) HEAP_START(p);
2672 	Uint area_size = (char *) HEAP_TOP(p) - area;
2673         Eterm* prev_stop = p->stop;
2674 
2675         offset_heap(new_heap, heap_size, offs, area, area_size);
2676 
2677         HIGH_WATER(p) = new_heap + (HIGH_WATER(p) - HEAP_START(p));
2678 
2679         HEAP_END(p) = new_heap + new_sz;
2680         prev_stop = new_heap + (p->stop - p->heap);
2681         p->stop = p->hend - stack_size;
2682         sys_memmove(p->stop, prev_stop, stack_size * sizeof(Eterm));
2683 
2684         offset_rootset(p, offs, area, area_size, objv, nobj);
2685         HEAP_TOP(p) = new_heap + heap_size;
2686         HEAP_START(p) = new_heap;
2687     }
2688 
2689 #ifdef USE_VM_PROBES
2690     if (DTRACE_ENABLED(process_heap_grow)) {
2691 	DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
2692 
2693         dtrace_proc_str(p, pidbuf);
2694 	DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz);
2695     }
2696 #endif
2697 
2698     HEAP_SIZE(p) = new_sz;
2699 }
2700 
2701 static void
shrink_new_heap(Process * p,Uint new_sz,Eterm * objv,int nobj)2702 shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj)
2703 {
2704     Eterm* new_heap;
2705     Uint heap_size = HEAP_TOP(p) - HEAP_START(p);
2706     Sint offs;
2707     Uint stack_size = p->hend - p->stop;
2708 
2709     ASSERT(new_sz < p->heap_sz);
2710     sys_memmove(p->heap + new_sz - stack_size, p->stop, stack_size *
2711                                                         sizeof(Eterm));
2712     new_heap = (Eterm *) ERTS_HEAP_REALLOC(ERTS_ALC_T_HEAP,
2713 					   (void*)p->heap,
2714 					   sizeof(Eterm)*(HEAP_SIZE(p)),
2715 					   sizeof(Eterm)*new_sz);
2716     p->hend = new_heap + new_sz;
2717     p->stop = p->hend - stack_size;
2718 
2719     if ((offs = new_heap - HEAP_START(p)) != 0) {
2720 	char* area = (char *) HEAP_START(p);
2721 	Uint area_size = (char *) HEAP_TOP(p) - area;
2722 
2723         /*
2724          * Normally, we don't expect a shrunk heap to move, but you never
2725          * know on some strange embedded systems...
2726          */
2727 
2728         offset_heap(new_heap, heap_size, offs, area, area_size);
2729 
2730         HIGH_WATER(p) = new_heap + (HIGH_WATER(p) - HEAP_START(p));
2731         offset_rootset(p, offs, area, area_size, objv, nobj);
2732         HEAP_TOP(p) = new_heap + heap_size;
2733         HEAP_START(p) = new_heap;
2734     }
2735 
2736 #ifdef USE_VM_PROBES
2737     if (DTRACE_ENABLED(process_heap_shrink)) {
2738 	DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
2739 
2740         dtrace_proc_str(p, pidbuf);
2741 	DTRACE3(process_heap_shrink, pidbuf, HEAP_SIZE(p), new_sz);
2742     }
2743 #endif
2744 
2745     HEAP_SIZE(p) = new_sz;
2746 }
2747 
2748 static Uint64
do_next_vheap_size(Uint64 vheap,Uint64 vheap_sz)2749 do_next_vheap_size(Uint64 vheap, Uint64 vheap_sz) {
2750 
2751     /*                grow
2752      *
2753      * vheap_sz ======================
2754      *
2755      * vheap 75% +    grow
2756      *          ----------------------
2757      *
2758      * vheap 25 - 75% same
2759      *          ----------------------
2760      *
2761      * vheap ~ - 25% shrink
2762      *
2763      *          ----------------------
2764      */
2765 
2766     if ((Uint64) vheap/3 > (Uint64) (vheap_sz/4)) {
2767 	Uint64 new_vheap_sz = vheap_sz;
2768 
2769 	while((Uint64) vheap/3 > (Uint64) (vheap_sz/4)) {
2770 	    /* the golden ratio = 1.618 */
2771 	    new_vheap_sz = (Uint64) vheap_sz * 1.618;
2772 	    if (new_vheap_sz < vheap_sz ) {
2773 	        return vheap_sz;
2774 	    }
2775 	    vheap_sz = new_vheap_sz;
2776 	}
2777 
2778 	return vheap_sz;
2779     }
2780 
2781     if (vheap < (Uint64) (vheap_sz/4)) {
2782 	return (vheap_sz >> 1);
2783     }
2784 
2785     return vheap_sz;
2786 
2787 }
2788 
2789 static Uint64
next_vheap_size(Process * p,Uint64 vheap,Uint64 vheap_sz)2790 next_vheap_size(Process* p, Uint64 vheap, Uint64 vheap_sz) {
2791     Uint64 new_vheap_sz = do_next_vheap_size(vheap, vheap_sz);
2792     return new_vheap_sz < p->min_vheap_size ? p->min_vheap_size : new_vheap_sz;
2793 }
2794 
2795 struct shrink_cand_data {
2796     struct erl_off_heap_header* new_candidates;
2797     struct erl_off_heap_header* new_candidates_end;
2798     struct erl_off_heap_header* old_candidates;
2799     Uint no_of_candidates;
2800     Uint no_of_active;
2801 };
2802 
2803 static ERTS_INLINE void
link_live_proc_bin(struct shrink_cand_data * shrink,struct erl_off_heap_header *** prevppp,struct erl_off_heap_header ** currpp,int new_heap)2804 link_live_proc_bin(struct shrink_cand_data *shrink,
2805 		   struct erl_off_heap_header*** prevppp,
2806 		   struct erl_off_heap_header** currpp,
2807 		   int new_heap)
2808 {
2809     ProcBin *pbp = (ProcBin*) *currpp;
2810     ASSERT(**prevppp == *currpp);
2811 
2812     *currpp = pbp->next;
2813     if (pbp->flags & (PB_ACTIVE_WRITER|PB_IS_WRITABLE)) {
2814 	ASSERT(pbp->flags & PB_IS_WRITABLE);
2815 
2816 	if (pbp->flags & PB_ACTIVE_WRITER) {
2817             pbp->flags &= ~PB_ACTIVE_WRITER;
2818 	    shrink->no_of_active++;
2819 	}
2820 	else { /* inactive */
2821 	    Uint unused = pbp->val->orig_size - pbp->size;
2822 	    /* Our allocators are 8 byte aligned, i.e., shrinking with
2823 	       less than 8 bytes will have no real effect */
2824 	    if (unused >= 8) { /* A shrink candidate; save in candidate list */
2825 		**prevppp = pbp->next;
2826 		if (new_heap) {
2827 		    if (!shrink->new_candidates)
2828 			shrink->new_candidates_end = (struct erl_off_heap_header*)pbp;
2829 		    pbp->next = shrink->new_candidates;
2830 		    shrink->new_candidates = (struct erl_off_heap_header*)pbp;
2831 		}
2832 		else {
2833 		    pbp->next = shrink->old_candidates;
2834 		    shrink->old_candidates = (struct erl_off_heap_header*)pbp;
2835 		}
2836 		shrink->no_of_candidates++;
2837 		return;
2838 	    }
2839 	}
2840     }
2841 
2842     /* Not a shrink candidate; keep in original mso list */
2843     *prevppp = &pbp->next;
2844 }
2845 
2846 #ifdef ERTS_MAGIC_REF_THING_HEADER
2847 /*
2848  * ERTS_MAGIC_REF_THING_HEADER only defined when there
2849  * is a size difference between magic and ordinary references...
2850  */
2851 # define ERTS_USED_MAGIC_REF_THING_HEADER__ ERTS_MAGIC_REF_THING_HEADER
2852 #else
2853 # define ERTS_USED_MAGIC_REF_THING_HEADER__ ERTS_REF_THING_HEADER
2854 #endif
2855 
2856 
2857 static void
sweep_off_heap(Process * p,int fullsweep)2858 sweep_off_heap(Process *p, int fullsweep)
2859 {
2860     struct shrink_cand_data shrink = {0};
2861     struct erl_off_heap_header* ptr;
2862     struct erl_off_heap_header** prev;
2863     char* oheap = NULL;
2864     Uint oheap_sz = 0;
2865     Uint64 bin_vheap = 0;
2866 #ifdef DEBUG
2867     int seen_mature = 0;
2868 #endif
2869 
2870     if (fullsweep == 0) {
2871 	oheap = (char *) OLD_HEAP(p);
2872 	oheap_sz = (char *) OLD_HEND(p) - oheap;
2873     }
2874 
2875     BIN_OLD_VHEAP(p) = 0;
2876 
2877     prev = &MSO(p).first;
2878     ptr = MSO(p).first;
2879 
2880     /* First part of the list will reside on the (old) new-heap.
2881      * Keep if moved, otherwise deref.
2882      */
2883     while (ptr) {
2884 	if (IS_MOVED_BOXED(ptr->thing_word)) {
2885 	    ASSERT(!ErtsInArea(ptr, oheap, oheap_sz));
2886             if (is_external_header(((struct erl_off_heap_header*) boxed_val(ptr->thing_word))->thing_word)) {
2887                 erts_node_bookkeep(((ExternalThing*)ptr)->node,
2888                                    make_boxed(&ptr->thing_word),
2889                                    ERL_NODE_DEC, __FILE__, __LINE__);
2890             }
2891             *prev = ptr = (struct erl_off_heap_header*) boxed_val(ptr->thing_word);
2892 	    ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
2893 	    switch (ptr->thing_word) {
2894 	    case HEADER_PROC_BIN: {
2895 		int to_new_heap = !ErtsInArea(ptr, oheap, oheap_sz);
2896 		ASSERT(to_new_heap == !seen_mature || (!to_new_heap && (seen_mature=1)));
2897 		if (to_new_heap) {
2898 		    bin_vheap += ptr->size / sizeof(Eterm);
2899 		} else {
2900 		    BIN_OLD_VHEAP(p) += ptr->size / sizeof(Eterm); /* for binary gc (words)*/
2901 		}
2902 		link_live_proc_bin(&shrink, &prev, &ptr, to_new_heap);
2903                 break;
2904             }
2905             case ERTS_USED_MAGIC_REF_THING_HEADER__: {
2906                 Uint size;
2907 		int to_new_heap = !ErtsInArea(ptr, oheap, oheap_sz);
2908                 ASSERT(is_magic_ref_thing(ptr));
2909 		ASSERT(to_new_heap == !seen_mature || (!to_new_heap && (seen_mature=1)));
2910                 size = (Uint) ((ErtsMRefThing *) ptr)->mb->orig_size;
2911 		if (to_new_heap)
2912 		    bin_vheap += size / sizeof(Eterm);
2913                 else
2914 		    BIN_OLD_VHEAP(p) += size / sizeof(Eterm); /* for binary gc (words)*/
2915                 /* fall through... */
2916             }
2917             default:
2918                 if (is_external_header(ptr->thing_word)) {
2919                     erts_node_bookkeep(((ExternalThing*)ptr)->node,
2920                                        make_boxed(&ptr->thing_word),
2921                                        ERL_NODE_INC, __FILE__, __LINE__);
2922                 }
2923 		prev = &ptr->next;
2924 		ptr = ptr->next;
2925 	    }
2926 	}
2927 	else if (ErtsInArea(ptr, oheap, oheap_sz))
2928             break; /* and let old-heap loop continue */
2929         else {
2930 	    /* garbage */
2931 	    switch (thing_subtag(ptr->thing_word)) {
2932 	    case REFC_BINARY_SUBTAG:
2933 		{
2934 		    Binary* bptr = ((ProcBin*)ptr)->val;
2935                     erts_bin_release(bptr);
2936 		    break;
2937 		}
2938 	    case FUN_SUBTAG:
2939 		{
2940 		    ErlFunEntry* fe = ((ErlFunThing*)ptr)->fe;
2941 		    if (erts_refc_dectest(&fe->refc, 0) == 0) {
2942 			erts_erase_fun_entry(fe);
2943 		    }
2944 		    break;
2945 		}
2946 	    case REF_SUBTAG:
2947 		{
2948 		    ErtsMagicBinary *bptr;
2949 		    ASSERT(is_magic_ref_thing(ptr));
2950 		    bptr = ((ErtsMRefThing *) ptr)->mb;
2951                     erts_bin_release((Binary *) bptr);
2952 		    break;
2953 		}
2954 	    default:
2955 		ASSERT(is_external_header(ptr->thing_word));
2956 		erts_deref_node_entry(((ExternalThing*)ptr)->node,
2957                                       make_boxed(&ptr->thing_word));
2958 	    }
2959 	    *prev = ptr = ptr->next;
2960 	}
2961     }
2962 
2963     /* The rest of the list resides on old-heap, and we just did a
2964      * generational collection - keep objects in list.
2965      */
2966     while (ptr) {
2967 	ASSERT(ErtsInArea(ptr, oheap, oheap_sz));
2968 	ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
2969         switch (ptr->thing_word) {
2970         case HEADER_PROC_BIN:
2971 	    BIN_OLD_VHEAP(p) += ptr->size / sizeof(Eterm); /* for binary gc (words)*/
2972 	    link_live_proc_bin(&shrink, &prev, &ptr, 0);
2973             break;
2974         case ERTS_USED_MAGIC_REF_THING_HEADER__:
2975             ASSERT(is_magic_ref_thing(ptr));
2976             BIN_OLD_VHEAP(p) +=
2977                 (((Uint) ((ErtsMRefThing *) ptr)->mb->orig_size)
2978                  / sizeof(Eterm)); /* for binary gc (words)*/
2979             /* fall through... */
2980         default:
2981             ASSERT(is_fun_header(ptr->thing_word) ||
2982                    is_external_header(ptr->thing_word)
2983                    || is_magic_ref_thing(ptr));
2984             prev = &ptr->next;
2985             ptr = ptr->next;
2986             break;
2987 	}
2988     }
2989 
2990     if (fullsweep) {
2991 	BIN_OLD_VHEAP_SZ(p) = next_vheap_size(p, BIN_OLD_VHEAP(p) + MSO(p).overhead, BIN_OLD_VHEAP_SZ(p));
2992     }
2993     BIN_VHEAP_SZ(p)     = next_vheap_size(p, bin_vheap, BIN_VHEAP_SZ(p));
2994     MSO(p).overhead     = bin_vheap;
2995 
2996     /*
2997      * If we got any shrink candidates, check them out.
2998      */
2999 
3000     if (shrink.no_of_candidates) {
3001 	ProcBin *candlist[] = { (ProcBin*)shrink.new_candidates,
3002 	                        (ProcBin*)shrink.old_candidates };
3003 	Uint leave_unused = 0;
3004 	int i;
3005 
3006 	if (shrink.no_of_active == 0) {
3007 	    if (shrink.no_of_candidates <= ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT)
3008 		leave_unused = ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE;
3009 	    else if (shrink.no_of_candidates <= ERTS_INACT_WR_PB_LEAVE_LIMIT)
3010 		leave_unused = ERTS_INACT_WR_PB_LEAVE_PERCENTAGE;
3011 	}
3012 
3013 	for (i = 0; i < sizeof(candlist)/sizeof(candlist[0]); i++) {
3014 	    ProcBin* pb;
3015 	    for (pb = candlist[i]; pb; pb = (ProcBin*)pb->next) {
3016 		Uint new_size = pb->size;
3017 
3018 		if (leave_unused) {
3019 		    new_size += (new_size * 100) / leave_unused;
3020 		    /* Our allocators are 8 byte aligned, i.e., shrinking with
3021 		       less than 8 bytes will have no real effect */
3022 		    if (new_size + 8 >= pb->val->orig_size)
3023 			continue;
3024 		}
3025 
3026 		pb->val = erts_bin_realloc(pb->val, new_size);
3027 		pb->bytes = (byte *) pb->val->orig_bytes;
3028 	    }
3029 	}
3030 
3031 
3032 	/*
3033 	 * We now potentially have the mso list divided into three lists:
3034 	 * - shrink candidates on new heap (inactive writable with unused data)
3035 	 * - shrink candidates on old heap (inactive writable with unused data)
3036 	 * - other binaries (read only + active writable ...) + funs and externals
3037 	 *
3038 	 * Put them back together: new candidates -> other -> old candidates
3039 	 * This order will ensure that the list only refers from new
3040 	 * generation to old and never from old to new *which is important*.
3041 	 */
3042 	if (shrink.new_candidates) {
3043 	    if (prev == &MSO(p).first) /* empty other binaries list */
3044 		prev = &shrink.new_candidates_end->next;
3045 	    else
3046 		shrink.new_candidates_end->next = MSO(p).first;
3047 	    MSO(p).first = shrink.new_candidates;
3048 	}
3049     }
3050     *prev = shrink.old_candidates;
3051 }
3052 
3053 /*
3054  * Offset pointers into the heap (not stack).
3055  */
3056 
3057 static void
offset_heap(Eterm * hp,Uint sz,Sint offs,char * area,Uint area_size)3058 offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
3059 {
3060     while (sz--) {
3061 	Eterm val = *hp;
3062 	switch (primary_tag(val)) {
3063 	  case TAG_PRIMARY_LIST:
3064 	  case TAG_PRIMARY_BOXED:
3065 	      if (ErtsInArea(ptr_val(val), area, area_size)) {
3066 		  *hp = offset_ptr(val, offs);
3067 	      }
3068 	      hp++;
3069 	      break;
3070 	  case TAG_PRIMARY_HEADER: {
3071 	      Uint tari;
3072 
3073 	      if (header_is_transparent(val)) {
3074 		  hp++;
3075 		  continue;
3076 	      }
3077 	      tari = thing_arityval(val);
3078 	      switch (thing_subtag(val)) {
3079 	      case REF_SUBTAG:
3080 		  if (!is_magic_ref_thing(hp))
3081 		      break;
3082 	      case REFC_BINARY_SUBTAG:
3083 	      case FUN_SUBTAG:
3084 	      case EXTERNAL_PID_SUBTAG:
3085 	      case EXTERNAL_PORT_SUBTAG:
3086 	      case EXTERNAL_REF_SUBTAG:
3087 		  {
3088 		      struct erl_off_heap_header* oh = (struct erl_off_heap_header*) hp;
3089 
3090                       if (is_external_header(oh->thing_word)) {
3091                           erts_node_bookkeep(((ExternalThing*)oh)->node,
3092                                              make_boxed(((Eterm*)oh)-offs),
3093                                              ERL_NODE_DEC, __FILE__, __LINE__);
3094                           erts_node_bookkeep(((ExternalThing*)oh)->node,
3095                                              make_boxed((Eterm*)oh), ERL_NODE_INC,
3096                                              __FILE__, __LINE__);
3097                       }
3098 
3099 		      if (ErtsInArea(oh->next, area, area_size)) {
3100 			  Eterm** uptr = (Eterm **) (void *) &oh->next;
3101 			  *uptr += offs; /* Patch the mso chain */
3102 		      }
3103 		  }
3104 		  break;
3105 	      case BIN_MATCHSTATE_SUBTAG:
3106 		{
3107 		  ErlBinMatchState *ms = (ErlBinMatchState*) hp;
3108 		  ErlBinMatchBuffer *mb = &(ms->mb);
3109 		  if (ErtsInArea(ptr_val(mb->orig), area, area_size)) {
3110 		      mb->orig = offset_ptr(mb->orig, offs);
3111 		      mb->base = binary_bytes(mb->orig);
3112 		  }
3113 		}
3114 		break;
3115 	      }
3116 	      sz -= tari;
3117 	      hp += tari + 1;
3118 	      break;
3119 	  }
3120 	  default:
3121 	      hp++;
3122 	      continue;
3123 	}
3124     }
3125 }
3126 
3127 /*
3128  * Offset pointers to heap from stack.
3129  */
3130 
3131 static void
offset_heap_ptr(Eterm * hp,Uint sz,Sint offs,char * area,Uint area_size)3132 offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
3133 {
3134     while (sz--) {
3135 	Eterm val = *hp;
3136 	switch (primary_tag(val)) {
3137 	case TAG_PRIMARY_LIST:
3138 	case TAG_PRIMARY_BOXED:
3139 	    if (ErtsInArea(ptr_val(val), area, area_size)) {
3140 		*hp = offset_ptr(val, offs);
3141 	    }
3142 	    hp++;
3143 	    break;
3144 	default:
3145 	    hp++;
3146 	    break;
3147 	}
3148     }
3149 }
3150 
3151 static void
offset_off_heap(Process * p,Sint offs,char * area,Uint area_size)3152 offset_off_heap(Process* p, Sint offs, char* area, Uint area_size)
3153 {
3154     if (MSO(p).first && ErtsInArea((Eterm *)MSO(p).first, area, area_size)) {
3155         Eterm** uptr = (Eterm**) (void *) &MSO(p).first;
3156         *uptr += offs;
3157     }
3158 }
3159 
3160 #ifndef USE_VM_PROBES
3161 #define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O)
3162 #else
3163 #define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O)                                      \
3164     do {                                                                        \
3165         Eterm utag__ = ERL_MESSAGE_DT_UTAG((MP));                               \
3166         if (is_boxed(utag__) && ErtsInArea(ptr_val(utag__), (A), (ASZ))) {      \
3167             ERL_MESSAGE_DT_UTAG((MP)) = offset_ptr(utag__, (O));                \
3168         }                                                                       \
3169     } while (0)
3170 #endif
3171 
3172 static ERTS_INLINE void
offset_message(ErtsMessage * mp,Sint offs,char * area,Uint area_size)3173 offset_message(ErtsMessage *mp, Sint offs, char* area, Uint area_size)
3174 {
3175     Eterm mesg = ERL_MESSAGE_TERM(mp);
3176     if (ERTS_SIG_IS_MSG_TAG(mesg) || ERTS_SIG_IS_HEAP_ALIAS_MSG_TAG(mesg)) {
3177         if (ERTS_SIG_IS_INTERNAL_MSG_TAG(mesg)) {
3178             switch (primary_tag(mesg)) {
3179             case TAG_PRIMARY_LIST:
3180             case TAG_PRIMARY_BOXED:
3181                 if (ErtsInArea(ptr_val(mesg), area, area_size)) {
3182                     ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs);
3183                 }
3184                 break;
3185             }
3186         }
3187         mesg = ERL_MESSAGE_TOKEN(mp);
3188         if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
3189             ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
3190         }
3191         mesg = ERL_MESSAGE_FROM(mp);
3192         if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) {
3193             ERL_MESSAGE_FROM(mp) = offset_ptr(mesg, offs);
3194         }
3195 
3196         ERTS_OFFSET_DT_UTAG(mp, area, area_size, offs);
3197 
3198         ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
3199                 is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
3200                 is_atom(ERL_MESSAGE_TOKEN(mp))));
3201     }
3202 }
3203 
3204 /*
3205  * Offset pointers in message queue.
3206  */
3207 static void
offset_mqueue(Process * p,Sint offs,char * area,Uint area_size)3208 offset_mqueue(Process *p, Sint offs, char* area, Uint area_size)
3209 {
3210     if ((p->sig_qs.flags & (FS_OFF_HEAP_MSGQ|FS_OFF_HEAP_MSGQ_CHNG)) != FS_OFF_HEAP_MSGQ)
3211         ERTS_FOREACH_SIG_PRIVQS(p, mp, offset_message(mp, offs, area, area_size));
3212 }
3213 
3214 static void ERTS_INLINE
offset_one_rootset(Process * p,Sint offs,char * area,Uint area_size,Eterm * objv,int nobj)3215 offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
3216                    Eterm* objv, int nobj)
3217 {
3218     Eterm *v;
3219     Uint sz;
3220     if (p->dictionary)  {
3221 	offset_heap(ERTS_PD_START(p->dictionary),
3222 		    ERTS_PD_SIZE(p->dictionary),
3223 		    offs, area, area_size);
3224     }
3225 
3226     offset_heap_ptr(&p->fvalue, 1, offs, area, area_size);
3227     offset_heap_ptr(&p->ftrace, 1, offs, area, area_size);
3228     offset_heap_ptr(&p->seq_trace_token, 1, offs, area, area_size);
3229 #ifdef USE_VM_PROBES
3230     offset_heap_ptr(&p->dt_utag, 1, offs, area, area_size);
3231 #endif
3232     offset_heap_ptr(&p->group_leader, 1, offs, area, area_size);
3233     if (p->sig_qs.recv_mrk_blk)
3234 	offset_heap_ptr(&p->sig_qs.recv_mrk_blk->ref[0],
3235 			ERTS_RECV_MARKER_BLOCK_SIZE, offs, area, area_size);
3236     offset_mqueue(p, offs, area, area_size);
3237     offset_heap_ptr(p->stop, (STACK_START(p) - p->stop), offs, area, area_size);
3238     offset_nstack(p, offs, area, area_size);
3239     if (nobj > 0) {
3240 	offset_heap_ptr(objv, nobj, offs, area, area_size);
3241     }
3242     offset_off_heap(p, offs, area, area_size);
3243     if (erts_setup_nfunc_rootset(p, &v, &sz))
3244 	offset_heap_ptr(v, sz, offs, area, area_size);
3245 }
3246 
3247 static void
offset_rootset(Process * p,Sint offs,char * area,Uint area_size,Eterm * objv,int nobj)3248 offset_rootset(Process *p, Sint offs, char* area, Uint area_size,
3249 	       Eterm* objv, int nobj)
3250 {
3251     offset_one_rootset(p, offs, area, area_size, objv, nobj);
3252 }
3253 
3254 static void
init_gc_info(ErtsGCInfo * gcip)3255 init_gc_info(ErtsGCInfo *gcip)
3256 {
3257   gcip->reclaimed = 0;
3258   gcip->garbage_cols = 0;
3259 }
3260 
3261 static void
reply_gc_info(void * vgcirp)3262 reply_gc_info(void *vgcirp)
3263 {
3264     Uint64 reclaimed = 0, garbage_cols = 0;
3265     ErtsSchedulerData *esdp = erts_get_scheduler_data();
3266     ErtsGCInfoReq *gcirp = (ErtsGCInfoReq *) vgcirp;
3267     ErtsProcLocks rp_locks = (gcirp->req_sched == esdp->no
3268 			      ? ERTS_PROC_LOCK_MAIN
3269 			      : 0);
3270     Process *rp = gcirp->proc;
3271     Eterm ref_copy = NIL, msg;
3272     Eterm *hp = NULL;
3273     Eterm **hpp;
3274     Uint sz, *szp;
3275     ErlOffHeap *ohp = NULL;
3276     ErtsMessage *mp = NULL;
3277 
3278     ASSERT(esdp);
3279 
3280     reclaimed = esdp->gc_info.reclaimed;
3281     garbage_cols = esdp->gc_info.garbage_cols;
3282     /*
3283      * Add dirty schedulers info on requesting
3284      * schedulers info
3285      */
3286     if (gcirp->req_sched == esdp->no) {
3287 	erts_mtx_lock(&dirty_gc.mtx);
3288 	reclaimed += dirty_gc.info.reclaimed;
3289 	garbage_cols += dirty_gc.info.garbage_cols;
3290 	erts_mtx_unlock(&dirty_gc.mtx);
3291     }
3292 
3293     sz = 0;
3294     hpp = NULL;
3295     szp = &sz;
3296 
3297     while (1) {
3298 	if (hpp)
3299 	    ref_copy = STORE_NC(hpp, ohp, gcirp->ref);
3300 	else
3301 	    *szp += ERTS_REF_THING_SIZE;
3302 
3303 	msg = erts_bld_tuple(hpp, szp, 3,
3304 			     make_small(esdp->no),
3305 			     erts_bld_uint64(hpp, szp, garbage_cols),
3306 			     erts_bld_uint64(hpp, szp, reclaimed));
3307 
3308 	msg = erts_bld_tuple(hpp, szp, 2, ref_copy, msg);
3309 	if (hpp)
3310 	  break;
3311 
3312 	mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
3313 
3314 	szp = NULL;
3315 	hpp = &hp;
3316     }
3317 
3318     erts_queue_message(rp, rp_locks, mp, msg, am_system);
3319 
3320     if (gcirp->req_sched == esdp->no)
3321 	rp_locks &= ~ERTS_PROC_LOCK_MAIN;
3322 
3323     if (rp_locks)
3324 	erts_proc_unlock(rp, rp_locks);
3325 
3326     erts_proc_dec_refc(rp);
3327 
3328     if (erts_atomic32_dec_read_nob(&gcirp->refc) == 0)
3329 	gcireq_free(vgcirp);
3330 }
3331 
erts_sub_binary_to_heap_binary(Eterm * ptr,Eterm ** hpp,Eterm * orig)3332 Eterm* erts_sub_binary_to_heap_binary(Eterm *ptr, Eterm **hpp, Eterm *orig) {
3333     Eterm *htop = *hpp;
3334     Eterm gval;
3335     ErlSubBin *sb = (ErlSubBin *)ptr;
3336     ErlHeapBin *hb = (ErlHeapBin *)htop;
3337     Eterm *real_bin;
3338     byte *bs;
3339 
3340     real_bin = binary_val(follow_moved(sb->orig, (Eterm)0));
3341 
3342     if (*real_bin == HEADER_PROC_BIN) {
3343         bs = ((ProcBin *) real_bin)->bytes + sb->offs;
3344     } else {
3345         bs = (byte *)(&(((ErlHeapBin *) real_bin)->data)) + sb->offs;
3346     }
3347 
3348     hb->thing_word = header_heap_bin(sb->size);
3349     hb->size = sb->size;
3350     sys_memcpy((byte *)hb->data, bs, sb->size);
3351 
3352     gval  = make_boxed(htop);
3353     *orig = gval;
3354     *ptr  = gval;
3355 
3356     ptr  += ERL_SUB_BIN_SIZE;
3357     htop += heap_bin_size(sb->size);
3358 
3359     *hpp = htop;
3360     return ptr;
3361 }
3362 
3363 
3364 Eterm
erts_gc_info_request(Process * c_p)3365 erts_gc_info_request(Process *c_p)
3366 {
3367     ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
3368     Eterm ref;
3369     ErtsGCInfoReq *gcirp;
3370     Eterm *hp;
3371 
3372     gcirp = gcireq_alloc();
3373     ref = erts_make_ref(c_p);
3374     hp = &gcirp->ref_heap[0];
3375 
3376     gcirp->proc = c_p;
3377     gcirp->ref = STORE_NC(&hp, NULL, ref);
3378     gcirp->req_sched = esdp->no;
3379     erts_atomic32_init_nob(&gcirp->refc,
3380 			       (erts_aint32_t) erts_no_schedulers);
3381 
3382     erts_proc_add_refc(c_p, (Sint) erts_no_schedulers);
3383 
3384     if (erts_no_schedulers > 1)
3385 	erts_schedule_multi_misc_aux_work(1,
3386 					  erts_no_schedulers,
3387 					  reply_gc_info,
3388 					  (void *) gcirp);
3389 
3390     reply_gc_info((void *) gcirp);
3391 
3392     return ref;
3393 }
3394 
3395 Eterm
erts_process_gc_info(Process * p,Uint * sizep,Eterm ** hpp,Uint extra_heap_block,Uint extra_old_heap_block_size)3396 erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp,
3397                      Uint extra_heap_block,
3398                      Uint extra_old_heap_block_size)
3399 {
3400     ERTS_DECL_AM(bin_vheap_size);
3401     ERTS_DECL_AM(bin_vheap_block_size);
3402     ERTS_DECL_AM(bin_old_vheap_size);
3403     ERTS_DECL_AM(bin_old_vheap_block_size);
3404     Eterm tags[] = {
3405         /* If you increase the number of elements here, make sure to update
3406            any call sites as they may have stack allocations that depend
3407            on the number of elements here. */
3408         am_old_heap_block_size,
3409         am_heap_block_size,
3410         am_mbuf_size,
3411         am_recent_size,
3412         am_stack_size,
3413         am_old_heap_size,
3414         am_heap_size,
3415         AM_bin_vheap_size,
3416         AM_bin_vheap_block_size,
3417         AM_bin_old_vheap_size,
3418         AM_bin_old_vheap_block_size
3419     };
3420     UWord values[] = {
3421         OLD_HEAP(p) ? OLD_HEND(p) - OLD_HEAP(p) + extra_old_heap_block_size
3422                     : extra_old_heap_block_size,
3423         HEAP_SIZE(p) + extra_heap_block,
3424         MBUF_SIZE(p),
3425         HIGH_WATER(p) - HEAP_START(p),
3426         STACK_START(p) - p->stop,
3427         OLD_HEAP(p) ? OLD_HTOP(p) - OLD_HEAP(p) : 0,
3428         HEAP_TOP(p) - HEAP_START(p),
3429         MSO(p).overhead,
3430         BIN_VHEAP_SZ(p),
3431         BIN_OLD_VHEAP(p),
3432         BIN_OLD_VHEAP_SZ(p)
3433     };
3434 
3435     Eterm res = THE_NON_VALUE;
3436 
3437     ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags));
3438     ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS);
3439 
3440     if (p->abandoned_heap) {
3441         Eterm *htop, *heap;
3442         ERTS_GET_ORIG_HEAP(p, heap, htop);
3443         values[3] = HIGH_WATER(p) - heap;
3444         values[6] = htop - heap;
3445     }
3446 
3447     if (p->sig_qs.flags & FS_ON_HEAP_MSGQ) {
3448         /* If on heap messages in the internal queue are counted
3449            as being part of the heap, so we have to add them to the
3450            am_mbuf_size value. process_info(total_heap_size) should
3451            be the same as adding old_heap_block_size + heap_block_size
3452            + mbuf_size.
3453         */
3454         ERTS_FOREACH_SIG_PRIVQS(
3455             p, mp,
3456             {
3457                 if (ERTS_SIG_IS_MSG(mp)
3458                     && mp->data.attached
3459                     && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
3460                     values[2] += erts_msg_attached_data_size(mp);
3461                 }
3462             });
3463     }
3464 
3465     res = erts_bld_atom_uword_2tup_list(hpp,
3466                                         sizep,
3467                                         sizeof(values)/sizeof(*values),
3468                                         tags,
3469                                         values);
3470 
3471     return res;
3472 }
3473 
3474 static int
reached_max_heap_size(Process * p,Uint total_heap_size,Uint extra_heap_size,Uint extra_old_heap_size)3475 reached_max_heap_size(Process *p, Uint total_heap_size,
3476                       Uint extra_heap_size, Uint extra_old_heap_size)
3477 {
3478     Uint max_heap_flags = MAX_HEAP_SIZE_FLAGS_GET(p);
3479     if (IS_TRACED_FL(p, F_TRACE_GC) ||
3480         max_heap_flags & MAX_HEAP_SIZE_LOG) {
3481         Eterm msg;
3482         Uint size = 0;
3483         Eterm *o_hp , *hp;
3484         erts_process_gc_info(p, &size, NULL, extra_heap_size,
3485                              extra_old_heap_size);
3486         o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, size * sizeof(Eterm));
3487         msg = erts_process_gc_info(p, NULL, &hp, extra_heap_size,
3488                                    extra_old_heap_size);
3489 
3490         if (max_heap_flags & MAX_HEAP_SIZE_LOG) {
3491             int alive = erts_is_alive;
3492             erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
3493             Eterm *o_hp, *hp, args = NIL;
3494 
3495             /* Build the format message */
3496             erts_dsprintf(dsbufp, "     Process:            ~p ");
3497             if (alive)
3498                 erts_dsprintf(dsbufp, "on node ~p");
3499             erts_dsprintf(dsbufp, "~n     Context:            maximum heap size reached~n");
3500             erts_dsprintf(dsbufp, "     Max Heap Size:      ~p~n");
3501             erts_dsprintf(dsbufp, "     Total Heap Size:    ~p~n");
3502             erts_dsprintf(dsbufp, "     Kill:               ~p~n");
3503             erts_dsprintf(dsbufp, "     Error Logger:       ~p~n");
3504             erts_dsprintf(dsbufp, "     Message Queue Len:  ~p~n");
3505             erts_dsprintf(dsbufp, "     GC Info:            ~p~n");
3506 
3507             /* Build the args in reverse order */
3508             o_hp = hp = erts_alloc(ERTS_ALC_T_TMP, 2*(alive ? 8 : 7) * sizeof(Eterm));
3509             args = CONS(hp, msg, args); hp += 2;
3510             args = CONS(hp, make_small((p)->sig_inq.len), args); hp += 2;
3511             args = CONS(hp, am_true, args); hp += 2;
3512             args = CONS(hp, (max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false), args); hp += 2;
3513             args = CONS(hp, make_small(total_heap_size), args); hp += 2;
3514             args = CONS(hp, make_small(MAX_HEAP_SIZE_GET(p)), args); hp += 2;
3515             if (alive) {
3516                 args = CONS(hp, erts_this_node->sysname, args); hp += 2;
3517             }
3518             args = CONS(hp, p->common.id, args); hp += 2;
3519 
3520             erts_send_error_term_to_logger(p->group_leader, dsbufp, args);
3521             erts_free(ERTS_ALC_T_TMP, o_hp);
3522         }
3523 
3524         if (IS_TRACED_FL(p, F_TRACE_GC))
3525             trace_gc(p, am_gc_max_heap_size, 0, msg);
3526 
3527         erts_free(ERTS_ALC_T_TMP, o_hp);
3528     }
3529     /* returns true if we should kill the process */
3530     return max_heap_flags & MAX_HEAP_SIZE_KILL;
3531 }
3532 
3533 Eterm
erts_max_heap_size_map(Sint max_heap_size,Uint max_heap_flags,Eterm ** hpp,Uint * sz)3534 erts_max_heap_size_map(Sint max_heap_size, Uint max_heap_flags,
3535                        Eterm **hpp, Uint *sz)
3536 {
3537     if (!hpp) {
3538         *sz += ERTS_MAX_HEAP_SIZE_MAP_SZ;
3539         return THE_NON_VALUE;
3540     } else {
3541         Eterm *hp = *hpp;
3542         Eterm keys = TUPLE3(hp, am_error_logger, am_kill, am_size);
3543         flatmap_t *mp;
3544         hp += 4;
3545         mp = (flatmap_t*) hp;
3546         mp->thing_word = MAP_HEADER_FLATMAP;
3547         mp->size = 3;
3548         mp->keys = keys;
3549         hp += MAP_HEADER_FLATMAP_SZ;
3550         *hp++ = max_heap_flags & MAX_HEAP_SIZE_LOG ? am_true : am_false;
3551         *hp++ = max_heap_flags & MAX_HEAP_SIZE_KILL ? am_true : am_false;
3552         *hp++ = make_small(max_heap_size);
3553         *hpp = hp;
3554         return make_flatmap(mp);
3555     }
3556 }
3557 
3558 int
erts_max_heap_size(Eterm arg,Uint * max_heap_size,Uint * max_heap_flags)3559 erts_max_heap_size(Eterm arg, Uint *max_heap_size, Uint *max_heap_flags)
3560 {
3561     Sint sz;
3562     *max_heap_flags = H_MAX_FLAGS;
3563     if (is_small(arg)) {
3564         sz = signed_val(arg);
3565         *max_heap_flags = H_MAX_FLAGS;
3566     } else if (is_map(arg)) {
3567         const Eterm *size = erts_maps_get(am_size, arg);
3568         const Eterm *kill = erts_maps_get(am_kill, arg);
3569         const Eterm *log = erts_maps_get(am_error_logger, arg);
3570         if (size && is_small(*size)) {
3571             sz = signed_val(*size);
3572         } else {
3573             /* size is mandatory */
3574             return 0;
3575         }
3576         if (kill) {
3577             if (*kill == am_true)
3578                 *max_heap_flags |= MAX_HEAP_SIZE_KILL;
3579             else if (*kill == am_false)
3580                 *max_heap_flags &= ~MAX_HEAP_SIZE_KILL;
3581             else
3582                 return 0;
3583         }
3584         if (log) {
3585             if (*log == am_true)
3586                 *max_heap_flags |= MAX_HEAP_SIZE_LOG;
3587             else if (*log == am_false)
3588                 *max_heap_flags &= ~MAX_HEAP_SIZE_LOG;
3589             else
3590                 return 0;
3591         }
3592     } else
3593         return 0;
3594     if (sz < 0)
3595         return 0;
3596     *max_heap_size = sz;
3597     return 1;
3598 }
3599 
3600 #if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG)
3601 
3602 int
erts_dbg_within_proc(Eterm * ptr,Process * p,Eterm * real_htop)3603 erts_dbg_within_proc(Eterm *ptr, Process *p, Eterm *real_htop)
3604 {
3605     ErlHeapFragment* bp;
3606     ErtsMessage* mp;
3607     Eterm *htop, *heap;
3608 
3609     if (p->abandoned_heap) {
3610 	ERTS_GET_ORIG_HEAP(p, heap, htop);
3611 	if (heap <= ptr && ptr < htop)
3612 	    return 1;
3613     }
3614 
3615     heap = p->heap;
3616     htop = real_htop ? real_htop : HEAP_TOP(p);
3617 
3618     if (OLD_HEAP(p) && (OLD_HEAP(p) <= ptr && ptr < OLD_HEND(p))) {
3619         return 1;
3620     }
3621     if (heap <= ptr && ptr < htop) {
3622         return 1;
3623     }
3624 
3625     mp = p->msg_frag;
3626     bp = p->mbuf;
3627 
3628     if (bp)
3629 	goto search_heap_frags;
3630 
3631     while (mp) {
3632 
3633         bp = erts_message_to_heap_frag(mp);
3634 	mp = mp->next;
3635 
3636     search_heap_frags:
3637 
3638 	while (bp) {
3639 	    if (bp->mem <= ptr && ptr < bp->mem + bp->used_size) {
3640 		return 1;
3641 	    }
3642 	    bp = bp->next;
3643 	}
3644     }
3645 
3646     return 0;
3647 }
3648 
3649 #endif
3650 
3651 #ifdef ERTS_OFFHEAP_DEBUG
3652 
3653 #define ERTS_CHK_OFFHEAP_ASSERT(EXP)			\
3654 do {							\
3655     if (!(EXP))						\
3656 	erts_exit(ERTS_ABORT_EXIT,			\
3657 		 "%s:%d: Assertion failed: %s\n",	\
3658 		 __FILE__, __LINE__, #EXP);		\
3659 } while (0)
3660 
3661 
3662 #ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST
3663 #  define ERTS_OFFHEAP_VISITED_BIT ((Eterm) 1 << 31)
3664 #endif
3665 
3666 void
erts_check_off_heap2(Process * p,Eterm * htop)3667 erts_check_off_heap2(Process *p, Eterm *htop)
3668 {
3669     Eterm *oheap = (Eterm *) OLD_HEAP(p);
3670     Eterm *ohtop = (Eterm *) OLD_HTOP(p);
3671     int old;
3672     union erl_off_heap_ptr u;
3673 
3674     old = 0;
3675     for (u.hdr = MSO(p).first; u.hdr; u.hdr = u.hdr->next) {
3676 	erts_aint_t refc;
3677 	switch (thing_subtag(u.hdr->thing_word)) {
3678 	case REFC_BINARY_SUBTAG:
3679 	    refc = erts_refc_read(&u.pb->val->intern.refc, 1);
3680 	    break;
3681 	case FUN_SUBTAG:
3682 	    refc = erts_refc_read(&u.fun->fe->refc, 1);
3683 	    break;
3684 	case EXTERNAL_PID_SUBTAG:
3685 	case EXTERNAL_PORT_SUBTAG:
3686 	case EXTERNAL_REF_SUBTAG:
3687 	    refc = erts_refc_read(&u.ext->node->refc, 1);
3688 	    break;
3689 	case REF_SUBTAG:
3690 	    ASSERT(is_magic_ref_thing(u.hdr));
3691 	    refc = erts_refc_read(&u.mref->mb->intern.refc, 1);
3692 	    break;
3693 	default:
3694 	    ASSERT(!"erts_check_off_heap2: Invalid thing_word");
3695 	}
3696 	ERTS_CHK_OFFHEAP_ASSERT(refc >= 1);
3697 #ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST
3698 	ERTS_CHK_OFFHEAP_ASSERT(!(u.hdr->thing_word & ERTS_OFFHEAP_VISITED_BIT));
3699 	u.hdr->thing_word |= ERTS_OFFHEAP_VISITED_BIT;
3700 #endif
3701 	if (old) {
3702 	    ERTS_CHK_OFFHEAP_ASSERT(oheap <= u.ep && u.ep < ohtop);
3703 	}
3704 	else if (oheap <= u.ep && u.ep < ohtop)
3705 	    old = 1;
3706 	else {
3707 	    ERTS_CHK_OFFHEAP_ASSERT(erts_dbg_within_proc(u.ep, p, htop));
3708 	}
3709     }
3710 
3711 #ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST
3712     for (u.hdr = MSO(p).first; u.hdr; u.hdr = u.hdr->next)
3713 	u.hdr->thing_word &= ~ERTS_OFFHEAP_VISITED_BIT;
3714 #endif
3715 }
3716 
3717 void
erts_check_off_heap(Process * p)3718 erts_check_off_heap(Process *p)
3719 {
3720     erts_check_off_heap2(p, NULL);
3721 }
3722 
3723 #endif
3724