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