1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2003-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 
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 
25 #include "sys.h"
26 #include "erl_vm.h"
27 #include "global.h"
28 #include "erl_process.h"
29 #include "error.h"
30 #include "bif.h"
31 #include "erl_db.h"
32 #include "dist.h"
33 #include "beam_catches.h"
34 #include "erl_binary.h"
35 #include "erl_map.h"
36 #define ERTS_WANT_EXTERNAL_TAGS
37 #include "external.h"
38 #include "erl_proc_sig_queue.h"
39 
40 #define PTR_FMT "%bpX"
41 #define ETERM_FMT "%beX"
42 
43 #define OUR_NIL	_make_header(0,_TAG_HEADER_FLOAT)
44 
45 static void dump_process_info(fmtfn_t to, void *to_arg, Process *p);
46 static void dump_element(fmtfn_t to, void *to_arg, Eterm x);
47 static void dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep);
48 static void dump_element_nl(fmtfn_t to, void *to_arg, Eterm x);
49 static int stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp,
50 			      int yreg);
51 static void stack_trace_dump(fmtfn_t to, void *to_arg, Eterm* sp);
52 static void print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x);
53 static void heap_dump(fmtfn_t to, void *to_arg, Eterm x);
54 static void dump_binaries(fmtfn_t to, void *to_arg, Binary* root);
55 void erts_print_base64(fmtfn_t to, void *to_arg,
56                        byte* src, Uint size);
57 static void dump_externally(fmtfn_t to, void *to_arg, Eterm term);
58 static void mark_literal(Eterm* ptr);
59 static void init_literal_areas(void);
60 static void dump_literals(fmtfn_t to, void *to_arg);
61 static void dump_persistent_terms(fmtfn_t to, void *to_arg);
62 static void dump_module_literals(fmtfn_t to, void *to_arg,
63                                  ErtsLiteralArea* lit_area);
64 
65 static Binary* all_binaries;
66 
67 extern BeamInstr beam_apply[];
68 extern BeamInstr beam_exit[];
69 extern BeamInstr beam_continue_exit[];
70 
71 void
erts_deep_process_dump(fmtfn_t to,void * to_arg)72 erts_deep_process_dump(fmtfn_t to, void *to_arg)
73 {
74     int i, max = erts_ptab_max(&erts_proc);
75 
76     all_binaries = NULL;
77     init_literal_areas();
78     erts_init_persistent_dumping();
79 
80     for (i = 0; i < max; i++) {
81 	Process *p = erts_pix2proc(i);
82 	if (p && p->i != ENULL) {
83 	    erts_aint32_t state = erts_atomic32_read_acqb(&p->state);
84 	    if (state & ERTS_PSFLG_EXITING)
85                 continue;
86             if (state & ERTS_PSFLG_GC) {
87                 ErtsSchedulerData *sdp = erts_get_scheduler_data();
88                 if (!sdp || p != sdp->current_process)
89                     continue;
90 
91                 /* We want to dump the garbing process that caused the dump */
92             }
93 
94             dump_process_info(to, to_arg, p);
95        }
96     }
97 
98     dump_persistent_terms(to, to_arg);
99     dump_literals(to, to_arg);
100     dump_binaries(to, to_arg, all_binaries);
101 }
102 
103 static int
monitor_size(ErtsMonitor * mon,void * vsize,Sint reds)104 monitor_size(ErtsMonitor *mon, void *vsize, Sint reds)
105 {
106     *((Uint *) vsize) += erts_monitor_size(mon);
107     return 1;
108 }
109 
110 static int
link_size(ErtsMonitor * lnk,void * vsize,Sint reds)111 link_size(ErtsMonitor *lnk, void *vsize, Sint reds)
112 {
113     *((Uint *) vsize) += erts_link_size(lnk);
114     return 1;
115 }
116 
erts_process_memory(Process * p,int include_sigs_in_transit)117 Uint erts_process_memory(Process *p, int include_sigs_in_transit)
118 {
119     Uint size = 0;
120     struct saved_calls *scb;
121 
122     size += sizeof(Process);
123 
124     if ((erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING) == 0) {
125         erts_link_tree_foreach(ERTS_P_LINKS(p),
126                                link_size, (void *) &size);
127         erts_monitor_tree_foreach(ERTS_P_MONITORS(p),
128                                   monitor_size, (void *) &size);
129         erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p),
130                                   monitor_size, (void *) &size);
131     }
132     size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm);
133     if (p->abandoned_heap)
134         size += (p->hend - p->heap) * sizeof(Eterm);
135     if (p->old_hend && p->old_heap)
136         size += (p->old_hend - p->old_heap) * sizeof(Eterm);
137 
138     if (!include_sigs_in_transit) {
139         /*
140          * Size of message queue!
141          *
142          * Note that this assumes that any part of message
143          * queue located in middle queue have been moved
144          * into the inner queue prior to this call.
145          * process_info() management ensures this is done-
146          */
147         ErtsMessage *mp;
148         for (mp = p->sig_qs.first; mp; mp = mp->next) {
149             ASSERT(ERTS_SIG_IS_MSG((ErtsSignal *) mp));
150             size += sizeof(ErtsMessage);
151             if (mp->data.attached)
152                 size += erts_msg_attached_data_size(mp) * sizeof(Eterm);
153         }
154     }
155     else {
156         /*
157          * Size of message queue plus size of all signals
158          * in transit to the process!
159          */
160         erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
161         erts_proc_sig_fetch(p);
162         erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ);
163 
164         ERTS_FOREACH_SIG_PRIVQS(
165             p, mp,
166             {
167                 size += sizeof(ErtsMessage);
168                 if (ERTS_SIG_IS_NON_MSG((ErtsSignal *) mp))
169                     size += erts_proc_sig_signal_size((ErtsSignal *) mp);
170                 else if (mp->data.attached)
171                     size += erts_msg_attached_data_size(mp) * sizeof(Eterm);
172             });
173     }
174 
175     if (p->arg_reg != p->def_arg_reg) {
176         size += p->arity * sizeof(p->arg_reg[0]);
177     }
178 
179     if (erts_atomic_read_nob(&p->psd) != (erts_aint_t) NULL)
180         size += sizeof(ErtsPSD);
181 
182     scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
183     if (scb) {
184         size += (sizeof(struct saved_calls)
185                  + (scb->len-1) * sizeof(scb->ct[0]));
186     }
187 
188     size += erts_dicts_mem_size(p);
189     return size;
190 }
191 
192 static ERTS_INLINE void
dump_msg(fmtfn_t to,void * to_arg,ErtsMessage * mp)193 dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp)
194 {
195     if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) {
196         Eterm mesg;
197         if (ERTS_SIG_IS_INTERNAL_MSG(mp))
198             dump_element(to, to_arg, ERL_MESSAGE_TERM(mp));
199         else
200             dump_dist_ext(to, to_arg, erts_get_dist_ext(mp->data.heap_frag));
201         mesg = ERL_MESSAGE_TOKEN(mp);
202         erts_print(to, to_arg, ":");
203         dump_element(to, to_arg, mesg);
204         erts_print(to, to_arg, "\n");
205     }
206 }
207 
208 static ERTS_INLINE void
heap_dump_msg(fmtfn_t to,void * to_arg,ErtsMessage * mp)209 heap_dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp)
210 {
211     if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) {
212         Eterm mesg = ERL_MESSAGE_TERM(mp);
213         if (is_value(mesg))
214             heap_dump(to, to_arg, mesg);
215         mesg = ERL_MESSAGE_TOKEN(mp);
216         heap_dump(to, to_arg, mesg);
217     }
218 }
219 
220 static void
dump_process_info(fmtfn_t to,void * to_arg,Process * p)221 dump_process_info(fmtfn_t to, void *to_arg, Process *p)
222 {
223     Eterm* sp;
224     int yreg = -1;
225 
226     if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE)
227         return;
228 
229     erts_proc_sig_fetch(p);
230 
231     if (p->sig_qs.first || p->sig_qs.cont) {
232 	erts_print(to, to_arg, "=proc_messages:%T\n", p->common.id);
233         ERTS_FOREACH_SIG_PRIVQS(p, mp, dump_msg(to, to_arg, mp));
234     }
235 
236     if (p->dictionary) {
237         erts_print(to, to_arg, "=proc_dictionary:%T\n", p->common.id);
238         erts_deep_dictionary_dump(to, to_arg,
239                                   p->dictionary, dump_element_nl);
240     }
241 
242     erts_print(to, to_arg, "=proc_stack:%T\n", p->common.id);
243     for (sp = p->stop; sp < STACK_START(p); sp++) {
244         yreg = stack_element_dump(to, to_arg, sp, yreg);
245     }
246 
247     erts_print(to, to_arg, "=proc_heap:%T\n", p->common.id);
248     for (sp = p->stop; sp < STACK_START(p); sp++) {
249         Eterm term = *sp;
250 
251         if (!is_catch(term) && !is_CP(term)) {
252             heap_dump(to, to_arg, term);
253         }
254     }
255 
256     if (p->sig_qs.first || p->sig_qs.cont)
257         ERTS_FOREACH_SIG_PRIVQS(p, mp, heap_dump_msg(to, to_arg, mp));
258 
259     if (p->dictionary) {
260         erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump);
261     }
262 }
263 
264 static void
dump_dist_ext(fmtfn_t to,void * to_arg,ErtsDistExternal * edep)265 dump_dist_ext(fmtfn_t to, void *to_arg, ErtsDistExternal *edep)
266 {
267     if (!edep)
268 	erts_print(to, to_arg, "D0:E0:");
269     else {
270 	byte *e;
271 	size_t sz;
272         int i;
273 
274 	if (!(edep->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB))
275 	    erts_print(to, to_arg, "D0:");
276 	else {
277 	    int i;
278 	    erts_print(to, to_arg, "D%X:", edep->attab.size);
279 	    for (i = 0; i < edep->attab.size; i++)
280 		dump_element(to, to_arg, edep->attab.atom[i]);
281 	}
282 	sz = edep->data->ext_endp - edep->data->extp;
283 	e = edep->data->extp;
284 	if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR) {
285 	    ASSERT(*e != VERSION_MAGIC);
286 	    sz++;
287 	}
288 	else {
289 	    ASSERT(*e == VERSION_MAGIC);
290 	}
291 	erts_print(to, to_arg, "E%X:", sz);
292         if (edep->flags & ERTS_DIST_EXT_DFLAG_HDR) {
293             byte sbuf[3];
294 
295             i = 0;
296 
297             sbuf[i++] = VERSION_MAGIC;
298             while (i < sizeof(sbuf) && e < edep->data->ext_endp) {
299                 sbuf[i++] = *e++;
300             }
301             erts_print_base64(to, to_arg, sbuf, i);
302         }
303         erts_print_base64(to, to_arg, e, edep->data->ext_endp - e);
304         for (i = 1; i < edep->data->frag_id; i++)
305             erts_print_base64(to, to_arg, edep->data[i].extp,
306                               edep->data[i].ext_endp - edep->data[i].extp);
307     }
308 }
309 
310 static void
dump_element(fmtfn_t to,void * to_arg,Eterm x)311 dump_element(fmtfn_t to, void *to_arg, Eterm x)
312 {
313     if (is_list(x)) {
314 	erts_print(to, to_arg, "H" PTR_FMT, list_val(x));
315     } else if (is_boxed(x)) {
316 	erts_print(to, to_arg, "H" PTR_FMT, boxed_val(x));
317     } else if (is_immed(x)) {
318 	if (is_atom(x)) {
319 	    unsigned char* s = atom_tab(atom_val(x))->name;
320 	    int len = atom_tab(atom_val(x))->len;
321 	    int i;
322 
323 	    erts_print(to, to_arg, "A%X:", atom_tab(atom_val(x))->len);
324 	    for (i = 0; i < len; i++) {
325 		erts_putc(to, to_arg, *s++);
326 	    }
327 	} else if (is_small(x)) {
328 	    erts_print(to, to_arg, "I%T", x);
329 	} else if (is_pid(x)) {
330 	    erts_print(to, to_arg, "P%T", x);
331 	} else if (is_port(x)) {
332 	    erts_print(to, to_arg, "p<%beu.%beu>",
333 		       port_channel_no(x), port_number(x));
334 	} else if (is_nil(x)) {
335 	    erts_putc(to, to_arg, 'N');
336 	}
337     }
338 }
339 
340 static void
dump_element_nl(fmtfn_t to,void * to_arg,Eterm x)341 dump_element_nl(fmtfn_t to, void *to_arg, Eterm x)
342 {
343     dump_element(to, to_arg, x);
344     erts_putc(to, to_arg, '\n');
345 }
346 
347 static void
stack_trace_dump(fmtfn_t to,void * to_arg,Eterm * sp)348 stack_trace_dump(fmtfn_t to, void *to_arg, Eterm *sp) {
349     Eterm x = *sp;
350     if (is_CP(x)) {
351         erts_print(to, to_arg, "%p:", sp);
352         erts_print(to, to_arg, "SReturn addr 0x%X (", cp_val(x));
353         print_function_from_pc(to, to_arg, cp_val(x));
354         erts_print(to, to_arg, ")\n");
355     }
356 }
357 
358 void
erts_limited_stack_trace(fmtfn_t to,void * to_arg,Process * p)359 erts_limited_stack_trace(fmtfn_t to, void *to_arg, Process *p)
360 {
361     Eterm* sp;
362 
363 
364     if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) {
365 	return;
366     }
367 
368     if (STACK_START(p) < STACK_TOP(p)) {
369         return;
370     }
371 
372     if ((STACK_START(p) - STACK_TOP(p)) < 512) {
373         if (erts_sys_is_area_readable((char*)STACK_TOP(p),
374                                       (char*)STACK_START(p)))
375             for (sp = STACK_TOP(p); sp < STACK_START(p); sp++)
376                 stack_trace_dump(to, to_arg, sp);
377         else
378             erts_print(to, to_arg, "Could not read from stack memory: %p - %p\n",
379                        STACK_TOP(p), STACK_START(p));
380     } else {
381         sp = STACK_TOP(p);
382         if (erts_sys_is_area_readable((char*)STACK_TOP(p),
383                                       (char*)(STACK_TOP(p) + 25)))
384             for (; sp < (STACK_TOP(p) + 256); sp++)
385                 stack_trace_dump(to, to_arg, sp);
386         else
387             erts_print(to, to_arg, "Could not read from stack memory: %p - %p\n",
388                        STACK_TOP(p), STACK_TOP(p) + 256);
389 
390         erts_print(to, to_arg, "%p: skipping %d frames\n",
391                    sp, STACK_START(p) - STACK_TOP(p) - 512);
392 
393         if (erts_sys_is_area_readable((char*)(STACK_START(p) - 256),
394                                       (char*)STACK_START(p)))
395             for (sp = STACK_START(p) - 256; sp < STACK_START(p); sp++)
396                 stack_trace_dump(to, to_arg, sp);
397         else
398             erts_print(to, to_arg, "Could not read from stack memory: %p - %p\n",
399                        STACK_START(p) - 256, STACK_START(p));
400     }
401 
402 }
403 
404 static int
stack_element_dump(fmtfn_t to,void * to_arg,Eterm * sp,int yreg)405 stack_element_dump(fmtfn_t to, void *to_arg, Eterm* sp, int yreg)
406 {
407     Eterm x = *sp;
408 
409     if (yreg < 0 || is_CP(x)) {
410         erts_print(to, to_arg, "%p:", sp);
411     } else {
412         erts_print(to, to_arg, "y%d:", yreg);
413         yreg++;
414     }
415 
416     if (is_CP(x)) {
417         erts_print(to, to_arg, "SReturn addr 0x%X (", cp_val(x));
418         print_function_from_pc(to, to_arg, cp_val(x));
419         erts_print(to, to_arg, ")\n");
420         yreg = 0;
421     } else if is_catch(x) {
422         erts_print(to, to_arg, "SCatch 0x%X (", catch_pc(x));
423         print_function_from_pc(to, to_arg, catch_pc(x));
424         erts_print(to, to_arg, ")\n");
425     } else {
426 	dump_element(to, to_arg, x);
427 	erts_putc(to, to_arg, '\n');
428     }
429     return yreg;
430 }
431 
432 static void
print_function_from_pc(fmtfn_t to,void * to_arg,BeamInstr * x)433 print_function_from_pc(fmtfn_t to, void *to_arg, BeamInstr* x)
434 {
435     ErtsCodeMFA* cmfa = find_function_from_pc(x);
436     if (cmfa == NULL) {
437         if (x == beam_exit) {
438             erts_print(to, to_arg, "<terminate process>");
439         } else if (x == beam_continue_exit) {
440             erts_print(to, to_arg, "<continue terminate process>");
441         } else if (x == beam_apply+1) {
442             erts_print(to, to_arg, "<terminate process normally>");
443         } else {
444             erts_print(to, to_arg, "unknown function");
445         }
446     } else {
447 	erts_print(to, to_arg, "%T:%T/%bpu + %bpu",
448 		   cmfa->module, cmfa->function, cmfa->arity,
449                    (x-(BeamInstr*)cmfa) * sizeof(Eterm));
450     }
451 }
452 
453 static void
heap_dump(fmtfn_t to,void * to_arg,Eterm x)454 heap_dump(fmtfn_t to, void *to_arg, Eterm x)
455 {
456     DeclareTmpHeapNoproc(last,1);
457     Eterm* next = last;
458     Eterm* ptr;
459 
460     if (is_immed(x) || is_CP(x)) {
461 	return;
462     }
463     UseTmpHeapNoproc(1);
464     *last = OUR_NIL;
465 
466     while (x != OUR_NIL) {
467 	if (is_CP(x)) {
468 	    next = (Eterm *) x;
469 	} else if (is_list(x)) {
470 	    ptr = list_val(x);
471             if (erts_is_literal(x, ptr)) {
472                 mark_literal(ptr);
473             } else if (ptr[0] != OUR_NIL) {
474 		erts_print(to, to_arg, PTR_FMT ":l", ptr);
475 		dump_element(to, to_arg, ptr[0]);
476 		erts_putc(to, to_arg, '|');
477 		dump_element(to, to_arg, ptr[1]);
478 		erts_putc(to, to_arg, '\n');
479 		if (is_immed(ptr[1])) {
480 		    ptr[1] = make_small(0);
481 		}
482 		x = ptr[0];
483 		ptr[0] = (Eterm) next;
484 		next = ptr + 1;
485 		continue;
486 	    }
487 	} else if (is_boxed(x)) {
488 	    Eterm hdr;
489 
490 	    ptr = boxed_val(x);
491 	    hdr = *ptr;
492             if (erts_is_literal(x, ptr)) {
493                 mark_literal(ptr);
494             } else if (hdr != OUR_NIL) {
495 		erts_print(to, to_arg, PTR_FMT ":", ptr);
496 	        if (is_arity_value(hdr)) {
497 		    Uint i;
498 		    Uint arity = arityval(hdr);
499 
500 		    erts_print(to, to_arg, "t" ETERM_FMT ":", arity);
501 		    for (i = 1; i <= arity; i++) {
502 			dump_element(to, to_arg, ptr[i]);
503 			if (is_immed(ptr[i])) {
504 			    ptr[i] = make_small(0);
505 			}
506 			if (i < arity) {
507 			    erts_putc(to, to_arg, ',');
508 			}
509 		    }
510 		    erts_putc(to, to_arg, '\n');
511 		    if (arity == 0) {
512 			ptr[0] = OUR_NIL;
513 		    } else {
514 			x = ptr[arity];
515 			ptr[0] = (Eterm) next;
516 			next = ptr + arity - 1;
517 			continue;
518 		    }
519 		} else if (hdr == HEADER_FLONUM) {
520 		    FloatDef f;
521 		    char sbuf[31];
522 		    int i;
523 
524 		    GET_DOUBLE_DATA((ptr+1), f);
525 		    i = sys_double_to_chars(f.fd, (char*) sbuf, sizeof(sbuf));
526 		    sys_memset(sbuf+i, 0, 31-i);
527 		    erts_print(to, to_arg, "F%X:%s\n", i, sbuf);
528 		    *ptr = OUR_NIL;
529 		} else if (_is_bignum_header(hdr)) {
530 		    erts_print(to, to_arg, "B%T\n", x);
531 		    *ptr = OUR_NIL;
532 		} else if (is_binary_header(hdr)) {
533 		    Uint tag = thing_subtag(hdr);
534 		    Uint size = binary_size(x);
535 
536 		    if (tag == HEAP_BINARY_SUBTAG) {
537 			byte* p;
538 
539 			erts_print(to, to_arg, "Yh%X:", size);
540 			p = binary_bytes(x);
541                         erts_print_base64(to, to_arg, p, size);
542 		    } else if (tag == REFC_BINARY_SUBTAG) {
543 			ProcBin* pb = (ProcBin *) binary_val(x);
544 			Binary* val = pb->val;
545 
546 			if (erts_atomic_xchg_nob(&val->intern.refc, 0) != 0) {
547 			    val->intern.flags = (UWord) all_binaries;
548 			    all_binaries = val;
549 			}
550 			erts_print(to, to_arg,
551 				   "Yc" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
552 				   val,
553 				   pb->bytes - (byte *)val->orig_bytes,
554 				   size);
555 		    } else if (tag == SUB_BINARY_SUBTAG) {
556 			ErlSubBin* Sb = (ErlSubBin *) binary_val(x);
557 			Eterm* real_bin;
558 			void* val;
559 
560 			/*
561 			 * Must use boxed_val() here, because the original
562 			 * binary may have been visited and have had its
563 			 * header word changed to OUR_NIL (in which case
564 			 * binary_val() will cause an assertion failure in
565 			 * the DEBUG emulator).
566 			 */
567 
568 			real_bin = boxed_val(Sb->orig);
569 
570 			if (thing_subtag(*real_bin) == REFC_BINARY_SUBTAG) {
571 			    /*
572 			     * Unvisited REFC_BINARY: Point directly to
573 			     * the binary.
574 			     */
575 			    ProcBin* pb = (ProcBin *) real_bin;
576 			    val = pb->val;
577 			} else {
578 			    /*
579 			     * Heap binary or visited REFC binary: Point
580 			     * to heap binary or ProcBin on the heap.
581 			     */
582 			    val = real_bin;
583 			}
584 			erts_print(to, to_arg,
585 				   "Ys" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
586 				   val, Sb->offs, size);
587 		    }
588 		    erts_putc(to, to_arg, '\n');
589 		    *ptr = OUR_NIL;
590 		} else if (is_external_pid_header(hdr)) {
591 		    erts_print(to, to_arg, "P%T\n", x);
592 		    *ptr = OUR_NIL;
593 		} else if (is_external_port_header(hdr)) {
594 		    erts_print(to, to_arg, "p<%beu.%beu>\n",
595 			       port_channel_no(x), port_number(x));
596 		    *ptr = OUR_NIL;
597 		} else if (is_map_header(hdr)) {
598                     if (is_flatmap_header(hdr)) {
599                         flatmap_t* fmp = (flatmap_t *) flatmap_val(x);
600                         Eterm* values = ptr + sizeof(flatmap_t) / sizeof(Eterm);
601                         Uint map_size = fmp->size;
602                         int i;
603 
604                         erts_print(to, to_arg, "Mf" ETERM_FMT ":", map_size);
605                         dump_element(to, to_arg, fmp->keys);
606                         erts_putc(to, to_arg, ':');
607                         for (i = 0; i < map_size; i++) {
608                             dump_element(to, to_arg, values[i]);
609                             if (is_immed(values[i])) {
610                                 values[i] = make_small(0);
611                             }
612                             if (i < map_size-1) {
613                                 erts_putc(to, to_arg, ',');
614                             }
615                         }
616                         erts_putc(to, to_arg, '\n');
617                         *ptr = OUR_NIL;
618                         x = fmp->keys;
619                         if (map_size) {
620                             fmp->keys = (Eterm) next;
621                             next = &values[map_size-1];
622                         }
623                         continue;
624                     } else {
625                         Uint i;
626                         Uint sz = 0;
627                         Eterm* nodes = ptr + 1;
628 
629                         switch (MAP_HEADER_TYPE(hdr)) {
630                         case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
631                             nodes++;
632                             sz = 16;
633                             erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
634                                        hashmap_size(x), sz);
635                             break;
636                         case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
637                             nodes++;
638                             sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
639                             erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
640                                        hashmap_size(x), sz);
641                             break;
642                         case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
643                             sz = hashmap_bitcount(MAP_HEADER_VAL(hdr));
644                             erts_print(to, to_arg, "Mn" ETERM_FMT ":", sz);
645                             break;
646                         }
647                         *ptr = OUR_NIL;
648                         for (i = 0; i < sz; i++) {
649                             dump_element(to, to_arg, nodes[i]);
650                             if (is_immed(nodes[i])) {
651                                 nodes[i] = make_small(0);
652                             }
653                             if (i < sz-1) {
654                                 erts_putc(to, to_arg, ',');
655                             }
656                         }
657                         erts_putc(to, to_arg, '\n');
658                         x = nodes[0];
659                         nodes[0] = (Eterm) next;
660                         next = &nodes[sz-1];
661                         continue;
662                     }
663 		} else {
664 		    /*
665 		     * All other we dump in the external term format.
666 		     */
667                     dump_externally(to, to_arg, x);
668 		    erts_putc(to, to_arg, '\n');
669 		    *ptr = OUR_NIL;
670 		}
671 	    }
672 	}
673 	x = *next;
674 	*next = OUR_NIL;
675 	next--;
676     }
677     UnUseTmpHeapNoproc(1);
678 }
679 
680 static void
dump_binaries(fmtfn_t to,void * to_arg,Binary * current)681 dump_binaries(fmtfn_t to, void *to_arg, Binary* current)
682 {
683     while (current) {
684 	SWord size = current->orig_size;
685 	byte* bytes = (byte*) current->orig_bytes;
686 
687 	erts_print(to, to_arg, "=binary:" PTR_FMT "\n", current);
688 	erts_print(to, to_arg, "%X:", size);
689         erts_print_base64(to, to_arg, bytes, size);
690         erts_putc(to, to_arg, '\n');
691 	current = (Binary *) current->intern.flags;
692     }
693 }
694 
695 static void
dump_externally(fmtfn_t to,void * to_arg,Eterm term)696 dump_externally(fmtfn_t to, void *to_arg, Eterm term)
697 {
698     byte sbuf[1024]; /* encode and hope for the best ... */
699     byte* s;
700     byte* p;
701 
702     if (is_fun(term)) {
703 	/*
704 	 * The fun's environment used to cause trouble. There were
705 	 * two kind of problems:
706 	 *
707 	 * 1. A term used in the environment could already have been
708 	 *    dumped and thus destroyed (since dumping is destructive).
709 	 *
710 	 * 2. A term in the environment could be too big, so that
711 	 *    the buffer for external format overflowed (allocating
712 	 *    memory is not really a solution, as it could be exhausted).
713 	 *
714 	 * Simple solution: Set all variables in the environment to NIL.
715 	 * The crashdump_viewer does not allow inspection of them anyway.
716 	 */
717 	ErlFunThing* funp = (ErlFunThing *) fun_val(term);
718 	Uint num_free = funp->num_free;
719 	Uint i;
720 
721 	for (i = 0; i < num_free; i++) {
722 	    funp->env[i] = NIL;
723 	}
724     }
725 
726     s = p = sbuf;
727     erts_encode_ext(term, &p);
728     erts_print(to, to_arg, "E%X:", p-s);
729     erts_print_base64(to, to_arg, sbuf, p-s);
730 }
731 
732 /*
733  * Handle dumping of literal areas.
734  */
735 
736 static ErtsLiteralArea** lit_areas;
737 static Uint num_lit_areas;
738 
compare_areas(const void * a,const void * b)739 static int compare_areas(const void * a, const void * b)
740 {
741     ErtsLiteralArea** a_p = (ErtsLiteralArea **) a;
742     ErtsLiteralArea** b_p = (ErtsLiteralArea **) b;
743 
744     if (*a_p < *b_p) {
745         return -1;
746     } else if (*b_p < *a_p) {
747         return 1;
748     } else {
749         return 0;
750     }
751 }
752 
753 
754 static void
init_literal_areas(void)755 init_literal_areas(void)
756 {
757     int i;
758     Module* modp;
759     ErtsCodeIndex code_ix;
760     ErtsLiteralArea** area_p;
761 
762     code_ix = erts_active_code_ix();
763     erts_rlock_old_code(code_ix);
764 
765     lit_areas = area_p = erts_dump_lit_areas;
766     num_lit_areas = 0;
767     for (i = 0; i < module_code_size(code_ix); i++) {
768 	modp = module_code(i, code_ix);
769         if (modp == NULL) {
770             continue;
771         }
772         if (modp->curr.code_length > 0 &&
773             modp->curr.code_hdr->literal_area) {
774             *area_p++ = modp->curr.code_hdr->literal_area;
775         }
776         if (modp->old.code_length > 0 && modp->old.code_hdr->literal_area) {
777             *area_p++ = modp->old.code_hdr->literal_area;
778         }
779     }
780 
781     num_lit_areas = area_p - lit_areas;
782     ASSERT(num_lit_areas <= erts_dump_num_lit_areas);
783     for (i = 0; i < num_lit_areas; i++) {
784         lit_areas[i]->off_heap = 0;
785     }
786 
787     qsort(lit_areas, num_lit_areas, sizeof(ErtsLiteralArea *),
788           compare_areas);
789 
790     qsort(erts_persistent_areas, erts_num_persistent_areas,
791           sizeof(ErtsLiteralArea *), compare_areas);
792 
793     erts_runlock_old_code(code_ix);
794 }
795 
search_areas(const void * a,const void * b)796 static int search_areas(const void * a, const void * b) {
797     Eterm* key = (Eterm *) a;
798     ErtsLiteralArea** b_p = (ErtsLiteralArea **) b;
799     if (key < b_p[0]->start) {
800         return -1;
801     } else if (b_p[0]->end <= key) {
802         return 1;
803     } else {
804         return 0;
805     }
806 }
807 
mark_literal(Eterm * ptr)808 static void mark_literal(Eterm* ptr)
809 {
810     ErtsLiteralArea** ap;
811 
812     ap = bsearch(ptr, lit_areas, num_lit_areas, sizeof(ErtsLiteralArea*),
813                  search_areas);
814     if (ap == 0) {
815         ap = bsearch(ptr, erts_persistent_areas,
816                      erts_num_persistent_areas,
817                      sizeof(ErtsLiteralArea*),
818                      search_areas);
819     }
820 
821 
822     /*
823      * If the literal was created by native code, this search will not
824      * find it and ap will be NULL.
825      */
826 
827     if (ap) {
828         ap[0]->off_heap = (struct erl_off_heap_header *) 1;
829     }
830 }
831 
832 static void
dump_literals(fmtfn_t to,void * to_arg)833 dump_literals(fmtfn_t to, void *to_arg)
834 {
835     ErtsCodeIndex code_ix;
836     int i;
837     Uint idx;
838 
839     code_ix = erts_active_code_ix();
840     erts_rlock_old_code(code_ix);
841 
842     erts_print(to, to_arg, "=literals\n");
843     for (i = 0; i < num_lit_areas; i++) {
844         if (lit_areas[i]->off_heap) {
845             dump_module_literals(to, to_arg, lit_areas[i]);
846         }
847     }
848 
849     erts_runlock_old_code(code_ix);
850 
851     for (idx = 0; idx < erts_num_persistent_areas; idx++) {
852         dump_module_literals(to, to_arg, erts_persistent_areas[idx]);
853     }
854 }
855 
856 static void
dump_persistent_terms(fmtfn_t to,void * to_arg)857 dump_persistent_terms(fmtfn_t to, void *to_arg)
858 {
859     Uint idx;
860 
861     erts_print(to, to_arg, "=persistent_terms\n");
862 
863     for (idx = 0; idx < erts_num_persistent_areas; idx++) {
864         ErtsLiteralArea* ap = erts_persistent_areas[idx];
865         Eterm tuple = make_tuple(ap->start);
866         Eterm* tup_val = tuple_val(tuple);
867 
868 	dump_element(to, to_arg, tup_val[1]);
869         erts_putc(to, to_arg, '|');
870 	dump_element_nl(to, to_arg, tup_val[2]);
871     }
872 }
873 
874 static void
dump_module_literals(fmtfn_t to,void * to_arg,ErtsLiteralArea * lit_area)875 dump_module_literals(fmtfn_t to, void *to_arg, ErtsLiteralArea* lit_area)
876 {
877     Eterm* htop;
878     Eterm* hend;
879 
880     htop = lit_area->start;
881     hend = lit_area->end;
882     while (htop < hend) {
883         Eterm w = *htop;
884         Eterm term;
885         Uint size;
886 
887         switch (primary_tag(w)) {
888         case TAG_PRIMARY_HEADER:
889             term = make_boxed(htop);
890             erts_print(to, to_arg, PTR_FMT ":", htop);
891             if (is_arity_value(w)) {
892                 Uint i;
893                 Uint arity = arityval(w);
894 
895                 erts_print(to, to_arg, "t" ETERM_FMT ":", arity);
896                 for (i = 1; i <= arity; i++) {
897                     dump_element(to, to_arg, htop[i]);
898                     if (i < arity) {
899                         erts_putc(to, to_arg, ',');
900                     }
901                 }
902                 erts_putc(to, to_arg, '\n');
903             } else if (w == HEADER_FLONUM) {
904                 FloatDef f;
905                 char sbuf[31];
906                 int i;
907 
908                 GET_DOUBLE_DATA((htop+1), f);
909                 i = sys_double_to_chars(f.fd, sbuf, sizeof(sbuf));
910                 sys_memset(sbuf+i, 0, 31-i);
911                 erts_print(to, to_arg, "F%X:%s\n", i, sbuf);
912             } else if (_is_bignum_header(w)) {
913                 erts_print(to, to_arg, "B%T\n", term);
914             } else if (is_binary_header(w)) {
915                 Uint tag = thing_subtag(w);
916                 Uint size = binary_size(term);
917 
918                 if (tag == HEAP_BINARY_SUBTAG) {
919                     byte* p;
920 
921                     erts_print(to, to_arg, "Yh%X:", size);
922                     p = binary_bytes(term);
923                     erts_print_base64(to, to_arg, p, size);
924                 } else if (tag == REFC_BINARY_SUBTAG) {
925                     ProcBin* pb = (ProcBin *) binary_val(term);
926                     Binary* val = pb->val;
927 
928                     if (erts_atomic_xchg_nob(&val->intern.refc, 0) != 0) {
929                         val->intern.flags = (UWord) all_binaries;
930                         all_binaries = val;
931                     }
932                     erts_print(to, to_arg,
933                                "Yc" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
934                                val,
935                                pb->bytes - (byte *)val->orig_bytes,
936                                size);
937                 } else if (tag == SUB_BINARY_SUBTAG) {
938                     ErlSubBin* Sb = (ErlSubBin *) binary_val(term);
939                     Eterm* real_bin;
940                     void* val;
941 
942                     real_bin = boxed_val(Sb->orig);
943                     if (thing_subtag(*real_bin) == REFC_BINARY_SUBTAG) {
944                         /*
945                          * Unvisited REFC_BINARY: Point directly to
946                          * the binary.
947                          */
948                         ProcBin* pb = (ProcBin *) real_bin;
949                         val = pb->val;
950                     } else {
951                         /*
952                          * Heap binary or visited REFC binary: Point
953                          * to heap binary or ProcBin on the heap.
954                          */
955                         val = real_bin;
956                     }
957                     erts_print(to, to_arg,
958                                "Ys" PTR_FMT ":" PTR_FMT ":" PTR_FMT,
959                                val, Sb->offs, size);
960                 }
961                 erts_putc(to, to_arg, '\n');
962             } else if (is_map_header(w)) {
963                 if (is_flatmap_header(w)) {
964                     flatmap_t* fmp = (flatmap_t *) flatmap_val(term);
965                     Eterm* values = htop + sizeof(flatmap_t) / sizeof(Eterm);
966                     Uint map_size = fmp->size;
967                     int i;
968 
969                     erts_print(to, to_arg, "Mf" ETERM_FMT ":", map_size);
970                     dump_element(to, to_arg, fmp->keys);
971                     erts_putc(to, to_arg, ':');
972                     for (i = 0; i < map_size; i++) {
973                         dump_element(to, to_arg, values[i]);
974                         if (i < map_size-1) {
975                             erts_putc(to, to_arg, ',');
976                         }
977                     }
978                     erts_putc(to, to_arg, '\n');
979                 } else {
980                     Uint i;
981                     Uint sz = 0;
982                     Eterm* nodes = htop + 1;
983 
984                     switch (MAP_HEADER_TYPE(w)) {
985                     case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
986                         nodes++;
987                         sz = 16;
988                         erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
989                                    hashmap_size(term), sz);
990                         break;
991                     case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
992                         nodes++;
993                         sz = hashmap_bitcount(MAP_HEADER_VAL(w));
994                         erts_print(to, to_arg, "Mh" ETERM_FMT ":" ETERM_FMT ":",
995                                    hashmap_size(term), sz);
996                         break;
997                     case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
998                         sz = hashmap_bitcount(MAP_HEADER_VAL(w));
999                         erts_print(to, to_arg, "Mn" ETERM_FMT ":", sz);
1000                         break;
1001                     }
1002                     for (i = 0; i < sz; i++) {
1003                         dump_element(to, to_arg, nodes[i]);
1004                         if (i < sz-1) {
1005                             erts_putc(to, to_arg, ',');
1006                         }
1007                     }
1008                     erts_putc(to, to_arg, '\n');
1009                 }
1010             } else {
1011                 /* Dump everything else in the external format */
1012                 dump_externally(to, to_arg, term);
1013                 erts_putc(to, to_arg, '\n');
1014             }
1015             size = 1 + header_arity(w);
1016             switch (w & _HEADER_SUBTAG_MASK) {
1017             case FUN_SUBTAG:
1018                 ASSERT(((ErlFunThing*)(htop))->num_free == 0);
1019                 size += 1;
1020                 break;
1021             case MAP_SUBTAG:
1022                 if (is_flatmap_header(w)) {
1023                     size += 1 + flatmap_get_size(htop);
1024                 } else {
1025                     size += hashmap_bitcount(MAP_HEADER_VAL(w));
1026                 }
1027                 break;
1028             case SUB_BINARY_SUBTAG:
1029                 size += 1;
1030                 break;
1031             }
1032             break;
1033         default:
1034             ASSERT(!is_header(htop[1]));
1035             erts_print(to, to_arg, PTR_FMT ":l", htop);
1036             dump_element(to, to_arg, htop[0]);
1037             erts_putc(to, to_arg, '|');
1038             dump_element(to, to_arg, htop[1]);
1039             erts_putc(to, to_arg, '\n');
1040             size = 2;
1041             break;
1042         }
1043         htop += size;
1044     }
1045 }
1046 
erts_dump_process_state(fmtfn_t to,void * to_arg,erts_aint32_t psflg)1047 void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg)
1048 {
1049     char *s;
1050     switch (erts_process_state2status(psflg)) {
1051     case am_free: s = "Non Existing"; break; /* Should never happen */
1052     case am_exiting: s = "Exiting"; break;
1053     case am_garbage_collecting: s = "Garbing"; break;
1054     case am_suspended: s = "Suspended"; break;
1055     case am_running: s = "Running"; break;
1056     case am_runnable: s = "Scheduled"; break;
1057     case am_waiting: s = "Waiting"; break;
1058     default: s = "Undefined"; break; /* Should never happen */
1059     }
1060 
1061     erts_print(to, to_arg, "%s\n", s);
1062 }
1063 
1064 void
erts_dump_extended_process_state(fmtfn_t to,void * to_arg,erts_aint32_t psflg)1065 erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg) {
1066 
1067     int i;
1068 
1069     switch (ERTS_PSFLGS_GET_ACT_PRIO(psflg)) {
1070     case PRIORITY_MAX: erts_print(to, to_arg, "ACT_PRIO_MAX | "); break;
1071     case PRIORITY_HIGH: erts_print(to, to_arg, "ACT_PRIO_HIGH | "); break;
1072     case PRIORITY_NORMAL: erts_print(to, to_arg, "ACT_PRIO_NORMAL | "); break;
1073     case PRIORITY_LOW: erts_print(to, to_arg, "ACT_PRIO_LOW | "); break;
1074     }
1075     switch (ERTS_PSFLGS_GET_USR_PRIO(psflg)) {
1076     case PRIORITY_MAX: erts_print(to, to_arg, "USR_PRIO_MAX | "); break;
1077     case PRIORITY_HIGH: erts_print(to, to_arg, "USR_PRIO_HIGH | "); break;
1078     case PRIORITY_NORMAL: erts_print(to, to_arg, "USR_PRIO_NORMAL | "); break;
1079     case PRIORITY_LOW: erts_print(to, to_arg, "USR_PRIO_LOW | "); break;
1080     }
1081     switch (ERTS_PSFLGS_GET_PRQ_PRIO(psflg)) {
1082     case PRIORITY_MAX: erts_print(to, to_arg, "PRQ_PRIO_MAX"); break;
1083     case PRIORITY_HIGH: erts_print(to, to_arg, "PRQ_PRIO_HIGH"); break;
1084     case PRIORITY_NORMAL: erts_print(to, to_arg, "PRQ_PRIO_NORMAL"); break;
1085     case PRIORITY_LOW: erts_print(to, to_arg, "PRQ_PRIO_LOW"); break;
1086     }
1087 
1088     psflg &= ~(ERTS_PSFLGS_ACT_PRIO_MASK |
1089                ERTS_PSFLGS_USR_PRIO_MASK |
1090                ERTS_PSFLGS_PRQ_PRIO_MASK);
1091 
1092     if (psflg)
1093         erts_print(to, to_arg, " | ");
1094 
1095     for (i = 0; i <= ERTS_PSFLG_MAX && psflg; i++) {
1096         erts_aint32_t chk = (1 << i);
1097         if (psflg & chk) {
1098             switch (chk) {
1099             case ERTS_PSFLG_IN_PRQ_MAX:
1100                 erts_print(to, to_arg, "IN_PRQ_MAX"); break;
1101             case ERTS_PSFLG_IN_PRQ_HIGH:
1102                 erts_print(to, to_arg, "IN_PRQ_HIGH"); break;
1103             case ERTS_PSFLG_IN_PRQ_NORMAL:
1104                 erts_print(to, to_arg, "IN_PRQ_NORMAL"); break;
1105             case ERTS_PSFLG_IN_PRQ_LOW:
1106                 erts_print(to, to_arg, "IN_PRQ_LOW"); break;
1107             case ERTS_PSFLG_FREE:
1108                 erts_print(to, to_arg, "FREE"); break;
1109             case ERTS_PSFLG_EXITING:
1110                 erts_print(to, to_arg, "EXITING"); break;
1111             case ERTS_PSFLG_UNUSED:
1112                 erts_print(to, to_arg, "UNUSED"); break;
1113             case ERTS_PSFLG_ACTIVE:
1114                 erts_print(to, to_arg, "ACTIVE"); break;
1115             case ERTS_PSFLG_IN_RUNQ:
1116                 erts_print(to, to_arg, "IN_RUNQ"); break;
1117             case ERTS_PSFLG_RUNNING:
1118                 erts_print(to, to_arg, "RUNNING"); break;
1119             case ERTS_PSFLG_SUSPENDED:
1120                 erts_print(to, to_arg, "SUSPENDED"); break;
1121             case ERTS_PSFLG_GC:
1122                 erts_print(to, to_arg, "GC"); break;
1123             case ERTS_PSFLG_SYS_TASKS:
1124                 erts_print(to, to_arg, "SYS_TASKS"); break;
1125             case ERTS_PSFLG_SIG_IN_Q:
1126                 erts_print(to, to_arg, "SIG_IN_Q"); break;
1127             case ERTS_PSFLG_ACTIVE_SYS:
1128                 erts_print(to, to_arg, "ACTIVE_SYS"); break;
1129             case ERTS_PSFLG_RUNNING_SYS:
1130                 erts_print(to, to_arg, "RUNNING_SYS"); break;
1131             case ERTS_PSFLG_PROXY:
1132                 erts_print(to, to_arg, "PROXY"); break;
1133             case ERTS_PSFLG_DELAYED_SYS:
1134                 erts_print(to, to_arg, "DELAYED_SYS"); break;
1135             case ERTS_PSFLG_OFF_HEAP_MSGQ:
1136                 erts_print(to, to_arg, "OFF_HEAP_MSGQ"); break;
1137             case ERTS_PSFLG_SIG_Q:
1138                 erts_print(to, to_arg, "SIG_Q"); break;
1139             case ERTS_PSFLG_DIRTY_CPU_PROC:
1140                 erts_print(to, to_arg, "DIRTY_CPU_PROC"); break;
1141             case ERTS_PSFLG_DIRTY_IO_PROC:
1142                 erts_print(to, to_arg, "DIRTY_IO_PROC"); break;
1143             case ERTS_PSFLG_DIRTY_ACTIVE_SYS:
1144                 erts_print(to, to_arg, "DIRTY_ACTIVE_SYS"); break;
1145             case ERTS_PSFLG_DIRTY_RUNNING:
1146                 erts_print(to, to_arg, "DIRTY_RUNNING"); break;
1147             case ERTS_PSFLG_DIRTY_RUNNING_SYS:
1148                 erts_print(to, to_arg, "DIRTY_RUNNING_SYS"); break;
1149             default:
1150                 erts_print(to, to_arg, "UNKNOWN(%d)", chk); break;
1151             }
1152             if (psflg > chk)
1153                 erts_print(to, to_arg, " | ");
1154             psflg -= chk;
1155         }
1156     }
1157     erts_print(to, to_arg, "\n");
1158 }
1159