1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1998-2018. 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 "big.h"
30 #include "bif.h"
31 #include "beam_catches.h"
32 #include "erl_debug.h"
33 #include "erl_map.h"
34
35 #define WITHIN(ptr, x, y) ((x) <= (ptr) && (ptr) < (y))
36
37 #define IN_HEAP(p, ptr) \
38 (WITHIN((ptr), p->heap, p->hend) || \
39 (OLD_HEAP(p) && WITHIN((ptr), OLD_HEAP(p), OLD_HEND(p))))
40
41
42 #ifdef __GNUC__
43 /*
44 * Does not work in Microsoft C. Since this is debugging code that will
45 * hardly be used on Windows, get rid of it unless we have Gnu compiler.
46 */
47 #define PTR_SIZE 2*(int)sizeof(long)
48
49 static const char dashes[PTR_SIZE+3] = {
50 [0 ... PTR_SIZE+1] = '-'
51 };
52 #endif
53
54 #if defined(DEBUG) && defined(__GNUC__)
55
56 /*
57 * This file defines functions for use within a debugger like gdb
58 * and the declarations below is just to make gcc quiet.
59 */
60
61 void pps(Process*, Eterm*);
62 void ptd(Process*, Eterm);
63 void paranoid_display(fmtfn_t, void*, Process*, Eterm);
64 static int dcount;
65
66 static int pdisplay1(fmtfn_t to, void *to_arg, Process* p, Eterm obj);
67
ptd(Process * p,Eterm x)68 void ptd(Process* p, Eterm x)
69 {
70 pdisplay1(ERTS_PRINT_STDERR, NULL, p, x);
71 erts_putc(ERTS_PRINT_STDERR, NULL, '\n');
72 }
73
74 /*
75 * Paranoid version of display which doesn't crasch as easily if there
76 * are errors in the data structures.
77 */
78
79 void
paranoid_display(fmtfn_t to,void * to_arg,Process * p,Eterm obj)80 paranoid_display(fmtfn_t to, void *to_arg, Process* p, Eterm obj)
81 {
82 dcount = 100000;
83 pdisplay1(to, to_arg, p, obj);
84 }
85
86 static int
pdisplay1(fmtfn_t to,void * to_arg,Process * p,Eterm obj)87 pdisplay1(fmtfn_t to, void *to_arg, Process* p, Eterm obj)
88 {
89 int i, k;
90 Eterm* nobj;
91
92 if (dcount-- <= 0)
93 return(1);
94
95 if (is_CP(obj)) {
96 erts_print(to, to_arg, "<cp/header:%0*lX",PTR_SIZE,obj);
97 return 0;
98 }
99
100 switch (tag_val_def(obj)) {
101 case NIL_DEF:
102 erts_print(to, to_arg, "[]");
103 break;
104 case ATOM_DEF:
105 erts_print(to, to_arg, "%T", obj);
106 break;
107 case SMALL_DEF:
108 erts_print(to, to_arg, "%ld", signed_val(obj));
109 break;
110
111 case BIG_DEF:
112 nobj = big_val(obj);
113 if (!IN_HEAP(p, nobj)) {
114 erts_print(to, to_arg, "#<bad big %X>#", obj);
115 return 1;
116 }
117
118 i = BIG_SIZE(nobj);
119 if (BIG_SIGN(nobj))
120 erts_print(to, to_arg, "-#integer(%d) = {", i);
121 else
122 erts_print(to, to_arg, "#integer(%d) = {", i);
123 erts_print(to, to_arg, "%d", BIG_DIGIT(nobj, 0));
124 for (k = 1; k < i; k++)
125 erts_print(to, to_arg, ",%d", BIG_DIGIT(nobj, k));
126 erts_putc(to, to_arg, '}');
127 break;
128 case REF_DEF:
129 case EXTERNAL_REF_DEF: {
130 Uint32 *ref_num;
131 erts_print(to, to_arg, "#Ref<%lu", ref_channel_no(obj));
132 ref_num = ref_numbers(obj);
133 for (i = ref_no_numbers(obj)-1; i >= 0; i--)
134 erts_print(to, to_arg, ",%lu", ref_num[i]);
135 erts_print(to, to_arg, ">");
136 break;
137 }
138 case PID_DEF:
139 case EXTERNAL_PID_DEF:
140 erts_print(to, to_arg, "<%lu.%lu.%lu>",
141 pid_channel_no(obj),
142 pid_number(obj),
143 pid_serial(obj));
144 break;
145 case PORT_DEF:
146 case EXTERNAL_PORT_DEF:
147 erts_print(to, to_arg, "#Port<%lu.%lu>",
148 port_channel_no(obj),
149 port_number(obj));
150 break;
151 case LIST_DEF:
152 erts_putc(to, to_arg, '[');
153 nobj = list_val(obj);
154 while (1) {
155 if (!IN_HEAP(p, nobj)) {
156 erts_print(to, to_arg, "#<bad list %X>", obj);
157 return 1;
158 }
159 if (pdisplay1(to, to_arg, p, *nobj++) != 0)
160 return(1);
161 if (is_not_list(*nobj))
162 break;
163 erts_putc(to, to_arg, ',');
164 nobj = list_val(*nobj);
165 }
166 if (is_not_nil(*nobj)) {
167 erts_putc(to, to_arg, '|');
168 if (pdisplay1(to, to_arg, p, *nobj) != 0)
169 return(1);
170 }
171 erts_putc(to, to_arg, ']');
172 break;
173 case TUPLE_DEF:
174 nobj = tuple_val(obj); /* pointer to arity */
175 i = arityval(*nobj); /* arity */
176 erts_putc(to, to_arg, '{');
177 while (i--) {
178 if (pdisplay1(to, to_arg, p, *++nobj) != 0) return(1);
179 if (i >= 1) erts_putc(to, to_arg, ',');
180 }
181 erts_putc(to, to_arg, '}');
182 break;
183 case FLOAT_DEF: {
184 FloatDef ff;
185 GET_DOUBLE(obj, ff);
186 erts_print(to, to_arg, "%.20e", ff.fd);
187 }
188 break;
189 case BINARY_DEF:
190 erts_print(to, to_arg, "#Bin");
191 break;
192 case MATCHSTATE_DEF:
193 erts_print(to, to_arg, "#Matchstate");
194 break;
195 default:
196 erts_print(to, to_arg, "unknown object %x", obj);
197 }
198 return(0);
199 }
200
201 void
pps(Process * p,Eterm * stop)202 pps(Process* p, Eterm* stop)
203 {
204 fmtfn_t to = ERTS_PRINT_STDOUT;
205 void *to_arg = NULL;
206 Eterm* sp = STACK_START(p) - 1;
207
208 if (stop <= STACK_END(p)) {
209 stop = STACK_END(p) + 1;
210 }
211
212 while(sp >= stop) {
213 erts_print(to, to_arg, "%0*lx: ", PTR_SIZE, (UWord) sp);
214 if (is_catch(*sp)) {
215 erts_print(to, to_arg, "catch %ld", (UWord)catch_pc(*sp));
216 } else {
217 paranoid_display(to, to_arg, p, *sp);
218 }
219 erts_putc(to, to_arg, '\n');
220 sp--;
221 }
222 }
223
224 #endif /* DEBUG */
225
226 static int verify_eterm(Process *p,Eterm element);
verify_eterm(Process * p,Eterm element)227 static int verify_eterm(Process *p,Eterm element)
228 {
229 Eterm *ptr;
230 ErlHeapFragment* mbuf;
231
232 switch (primary_tag(element)) {
233 case TAG_PRIMARY_LIST: ptr = list_val(element); break;
234 case TAG_PRIMARY_BOXED: ptr = boxed_val(element); break;
235 default: /* Immediate or header/cp */ return 1;
236 }
237
238 if (p) {
239 if (IN_HEAP(p, ptr))
240 return 1;
241
242 for (mbuf = p->mbuf; mbuf; mbuf = mbuf->next) {
243 if (WITHIN(ptr, &mbuf->mem[0], &mbuf->mem[0] + mbuf->used_size)) {
244 return 1;
245 }
246 }
247 }
248 return 0;
249 }
250
erts_check_stack(Process * p)251 void erts_check_stack(Process *p)
252 {
253 Eterm *elemp;
254 Eterm *stack_start = p->heap + p->heap_sz;
255 Eterm *stack_end = p->htop;
256
257 if (p->stop > stack_start)
258 erts_exit(ERTS_ERROR_EXIT,
259 "<%lu.%lu.%lu>: Stack underflow\n",
260 internal_pid_channel_no(p->common.id),
261 internal_pid_number(p->common.id),
262 internal_pid_serial(p->common.id));
263
264 if (p->stop < stack_end)
265 erts_exit(ERTS_ERROR_EXIT,
266 "<%lu.%lu.%lu>: Stack overflow\n",
267 internal_pid_channel_no(p->common.id),
268 internal_pid_number(p->common.id),
269 internal_pid_serial(p->common.id));
270
271 for (elemp = p->stop; elemp < stack_start; elemp++) {
272 int in_mbuf = 0;
273 Eterm *ptr;
274 ErlHeapFragment* mbuf;
275 switch (primary_tag(*elemp)) {
276 case TAG_PRIMARY_LIST: ptr = list_val(*elemp); break;
277 case TAG_PRIMARY_BOXED: ptr = boxed_val(*elemp); break;
278 default: /* Immediate or cp */ continue;
279 }
280 if (IN_HEAP(p, ptr))
281 continue;
282 for (mbuf = p->mbuf; mbuf; mbuf = mbuf->next)
283 if (WITHIN(ptr, &mbuf->mem[0], &mbuf->mem[0] + mbuf->used_size)) {
284 in_mbuf = 1;
285 break;
286 }
287 if (in_mbuf)
288 continue;
289
290 erts_exit(ERTS_ERROR_EXIT,
291 "<%lu.%lu.%lu>: Wild stack pointer\n",
292 internal_pid_channel_no(p->common.id),
293 internal_pid_number(p->common.id),
294 internal_pid_serial(p->common.id));
295 }
296
297 }
298
299 #if defined(CHECK_FOR_HOLES)
300 static void check_memory(Eterm *start, Eterm *end);
301
erts_check_for_holes(Process * p)302 void erts_check_for_holes(Process* p)
303 {
304 ErlHeapFragment* hf;
305 Eterm* start;
306
307 if (p->flags & F_DISABLE_GC)
308 return;
309
310 start = p->last_htop ? p->last_htop : HEAP_START(p);
311 check_memory(start, HEAP_TOP(p));
312 p->last_htop = HEAP_TOP(p);
313
314 for (hf = MBUF(p); hf != 0; hf = hf->next) {
315 if (hf == p->heap_hfrag)
316 continue;
317 if (hf == p->last_mbuf) {
318 break;
319 }
320 check_memory(hf->mem, hf->mem+hf->used_size);
321 }
322 p->last_mbuf = MBUF(p);
323 }
324
check_memory(Eterm * start,Eterm * end)325 static void check_memory(Eterm *start, Eterm *end)
326 {
327 Eterm *pos = start;
328
329 while (pos < end) {
330 Eterm hval = *pos++;
331
332 if (hval == ERTS_HOLE_MARKER) {
333 erts_fprintf(stderr,"%s, line %d: ERTS_HOLE_MARKER found at 0x%0*lx\n",
334 __FILE__, __LINE__,PTR_SIZE,(unsigned long)(pos-1));
335 print_untagged_memory(start,end); /* DEBUGSTUFF */
336 abort();
337 } else if (is_thing(hval)) {
338 pos += (thing_arityval(hval));
339 }
340 }
341 }
342 #endif
343
344 #ifdef __GNUC__
345
346 /*
347 * erts_check_heap and erts_check_memory will run through the heap
348 * silently if everything is ok. If there are strange (untagged) data
349 * in the heap or wild pointers, the system will be halted with an
350 * error message.
351 */
erts_check_heap(Process * p)352 void erts_check_heap(Process *p)
353 {
354 ErlHeapFragment* bp = MBUF(p);
355
356 erts_check_memory(p,HEAP_START(p),HEAP_TOP(p));
357 if (OLD_HEAP(p) != NULL) {
358 erts_check_memory(p,OLD_HEAP(p),OLD_HTOP(p));
359 }
360
361 while (bp) {
362 erts_check_memory(p,bp->mem,bp->mem + bp->used_size);
363 bp = bp->next;
364 }
365 }
366
erts_check_memory(Process * p,Eterm * start,Eterm * end)367 void erts_check_memory(Process *p, Eterm *start, Eterm *end)
368 {
369 Eterm *pos = start;
370
371 while (pos < end) {
372 Eterm hval = *pos++;
373
374 #ifdef DEBUG
375 if (hval == DEBUG_BAD_WORD) {
376 print_untagged_memory(start, end);
377 erts_exit(ERTS_ERROR_EXIT, "Uninitialized HAlloc'ed memory found @ 0x%0*lx!\n",
378 PTR_SIZE,(unsigned long)(pos - 1));
379 }
380 #endif
381
382 if (is_thing(hval)) {
383 pos += thing_arityval(hval);
384 continue;
385 }
386
387 if (verify_eterm(p,hval))
388 continue;
389
390 erts_exit(ERTS_ERROR_EXIT, "Wild pointer found @ 0x%0*lx!\n",
391 PTR_SIZE,(unsigned long)(pos - 1));
392 }
393 }
394
verify_process(Process * p)395 void verify_process(Process *p)
396 {
397 #define VERIFY_AREA(name,ptr,sz) { \
398 int n = (sz); \
399 while (n--) if(!verify_eterm(p,*(ptr+n))) \
400 erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); }
401
402 #define VERIFY_ETERM(name,eterm) { \
403 if(!verify_eterm(p,eterm)) \
404 erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); }
405
406
407 VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->common.id));
408
409 ERTS_FOREACH_SIG_PRIVQS(
410 p, mp,
411 {
412 if (ERTS_SIG_IS_MSG(mp)) {
413 VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp));
414 VERIFY_ETERM("message token",ERL_MESSAGE_TOKEN(mp));
415 }
416 });
417
418 erts_check_stack(p);
419 erts_check_heap(p);
420
421 if (p->dictionary)
422 VERIFY_AREA("dictionary", ERTS_PD_START(p->dictionary), ERTS_PD_SIZE(p->dictionary));
423 VERIFY_ETERM("seq trace token",p->seq_trace_token);
424 VERIFY_ETERM("group leader",p->group_leader);
425 VERIFY_ETERM("fvalue",p->fvalue);
426 VERIFY_ETERM("ftrace",p->ftrace);
427
428 VERBOSE(DEBUG_MEMORY,("...done\n"));
429
430 #undef VERIFY_AREA
431 #undef VERIFY_ETERM
432 }
433
434 /*
435 * print_untagged_memory will print the contents of given memory area.
436 */
print_untagged_memory(Eterm * pos,Eterm * end)437 void print_untagged_memory(Eterm *pos, Eterm *end)
438 {
439 int i = 0;
440 erts_printf("| %*s | Range: 0x%0*lx - 0x%0*lx%*s|\n",
441 PTR_SIZE, "",
442 PTR_SIZE,(unsigned long)pos,
443 PTR_SIZE,(unsigned long)(end - 1),2 * PTR_SIZE - 2,"");
444 erts_printf("| %-*s | %-*s |\n",PTR_SIZE+2,"Address",
445 4*PTR_SIZE+11,"Contents");
446 erts_printf("|-%s-|-%s-%s-%s-%s-|\n",dashes,dashes,dashes,dashes,dashes);
447 while( pos < end ) {
448 if (i == 0)
449 erts_printf("| 0x%0*lx | ", PTR_SIZE, (unsigned long)pos);
450 erts_printf("0x%0*lx ",PTR_SIZE,(unsigned long)*pos);
451 pos++; i++;
452 if (i == 4) {
453 erts_printf("|\n");
454 i = 0;
455 }
456 }
457 while (i && i < 4) {
458 erts_printf("%*s",PTR_SIZE+3,"");
459 i++;
460 }
461 if (i != 0)
462 erts_printf("|\n");
463 erts_printf("+-%s-+-%s-%s-%s-%s-+\n",dashes,dashes,dashes,dashes,dashes);
464 }
465
466 /*
467 * print_tagged_memory will print contents of given memory area and
468 * display it as if it was tagged Erlang terms (which it hopefully
469 * is). This function knows about forwarding pointers to be able to
470 * print a heap during garbage collection. erts_printf("%T",val)
471 * do not know about forwarding pointers though, so it will still
472 * crash if they are encoutered...
473 */
print_tagged_memory(Eterm * pos,Eterm * end)474 void print_tagged_memory(Eterm *pos, Eterm *end)
475 {
476 erts_printf("+-%s-+-%s-+\n",dashes,dashes);
477 erts_printf("| 0x%0*lx - 0x%0*lx |\n",
478 PTR_SIZE,(unsigned long)pos,
479 PTR_SIZE,(unsigned long)(end - 1));
480 erts_printf("| %-*s | %-*s |\n",PTR_SIZE,"Address",PTR_SIZE,"Contents");
481 erts_printf("|-%s-|-%s-|\n",dashes,dashes);
482 while( pos < end ) {
483 Eterm val = pos[0];
484 erts_printf("| 0x%0*lx | 0x%0*lx | ",
485 PTR_SIZE,(unsigned long)pos, PTR_SIZE,(unsigned long)val);
486 ++pos;
487 if( is_arity_value(val) ) {
488 erts_printf("Arity(%lu)", arityval(val));
489 } else if( is_thing(val) ) {
490 unsigned int ari = thing_arityval(val);
491 erts_printf("Thing Arity(%u) Tag(%lu)", ari, thing_subtag(val));
492 while( ari ) {
493 erts_printf("\n| 0x%0*lx | 0x%0*lx | THING",
494 PTR_SIZE, (unsigned long)pos,
495 PTR_SIZE, (unsigned long)*pos);
496 ++pos;
497 --ari;
498 }
499 } else {
500 switch (primary_tag(val)) {
501 case TAG_PRIMARY_BOXED:
502 if (!is_header(*boxed_val(val))) {
503 erts_printf("Moved -> 0x%0*lx\n",PTR_SIZE,
504 (unsigned long)*boxed_val(val));
505 continue;
506 }
507 break;
508
509 case TAG_PRIMARY_LIST:
510 if (is_non_value(*list_val(val))) {
511 erts_printf("Moved -> 0x%0*lx\n",PTR_SIZE,
512 (unsigned long)*(list_val(val) + 1));
513 continue;
514 }
515 break;
516 }
517 erts_printf("%.30T", val);
518 }
519 erts_printf("\n");
520 }
521 erts_printf("+-%s-+-%s-+\n",dashes,dashes);
522 }
523
524 static void print_process_memory(Process *p);
print_process_memory(Process * p)525 static void print_process_memory(Process *p)
526 {
527 ErlHeapFragment* bp = MBUF(p);
528
529 erts_printf("==============================\n");
530 erts_printf("|| Memory info for %T ||\n",p->common.id);
531 erts_printf("==============================\n");
532
533 erts_printf("-- %-*s ---%s-%s-%s-%s--\n",
534 PTR_SIZE, "PCB", dashes, dashes, dashes, dashes);
535
536
537 erts_printf(" Message Queue:\n");
538 ERTS_FOREACH_SIG_PRIVQS(
539 p, mp,
540 {
541 if (ERTS_SIG_IS_MSG(mp))
542 erts_printf("| 0x%0*lx | 0x%0*lx |\n",PTR_SIZE,
543 ERL_MESSAGE_TERM(mp),PTR_SIZE,ERL_MESSAGE_TOKEN(mp));
544 });
545
546 if (p->dictionary != NULL) {
547 int n = ERTS_PD_SIZE(p->dictionary);
548 Eterm *ptr = ERTS_PD_START(p->dictionary);
549 erts_printf(" Dictionary: ");
550 while (n--) erts_printf("0x%0*lx ",PTR_SIZE,(unsigned long)ptr++);
551 erts_printf("\n");
552 }
553
554 if (p->arity > 0) {
555 int n = p->arity;
556 Eterm *ptr = p->arg_reg;
557 erts_printf(" Argument Registers: ");
558 while (n--) erts_printf("0x%0*lx ",PTR_SIZE,(unsigned long)*ptr++);
559 erts_printf("\n");
560 }
561
562 erts_printf(" Trace Token: 0x%0*lx\n",PTR_SIZE,p->seq_trace_token);
563 erts_printf(" Group Leader: 0x%0*lx\n",PTR_SIZE,p->group_leader);
564 erts_printf(" Fvalue: 0x%0*lx\n",PTR_SIZE,p->fvalue);
565 erts_printf(" Ftrace: 0x%0*lx\n",PTR_SIZE,p->ftrace);
566
567 erts_printf("+- %-*s -+ 0x%0*lx 0x%0*lx %s-%s-+\n",
568 PTR_SIZE, "Stack",
569 PTR_SIZE, (unsigned long)STACK_TOP(p),
570 PTR_SIZE, (unsigned long)STACK_START(p),
571 dashes, dashes);
572 print_untagged_memory(STACK_TOP(p),STACK_START(p));
573
574 erts_printf("+- %-*s -+ 0x%0*lx 0x%0*lx 0x%0*lx 0x%0*lx +\n",
575 PTR_SIZE, "Heap",
576 PTR_SIZE, (unsigned long)HEAP_START(p),
577 PTR_SIZE, (unsigned long)HIGH_WATER(p),
578 PTR_SIZE, (unsigned long)HEAP_TOP(p),
579 PTR_SIZE, (unsigned long)HEAP_END(p));
580 print_untagged_memory(HEAP_START(p),HEAP_TOP(p));
581
582 if (OLD_HEAP(p)) {
583 erts_printf("+- %-*s -+ 0x%0*lx 0x%0*lx 0x%0*lx %s-+\n",
584 PTR_SIZE, "Old Heap",
585 PTR_SIZE, (unsigned long)OLD_HEAP(p),
586 PTR_SIZE, (unsigned long)OLD_HTOP(p),
587 PTR_SIZE, (unsigned long)OLD_HEND(p),
588 dashes);
589 print_untagged_memory(OLD_HEAP(p),OLD_HTOP(p));
590 }
591
592 if (bp)
593 erts_printf("+- %-*s -+-%s-%s-%s-%s-+\n",
594 PTR_SIZE, "heap fragments",
595 dashes, dashes, dashes, dashes);
596 while (bp) {
597 print_untagged_memory(bp->mem,bp->mem + bp->used_size);
598 bp = bp->next;
599 }
600 }
601
602
print_memory(Process * p)603 void print_memory(Process *p)
604 {
605 if (p != NULL) {
606 print_process_memory(p);
607 }
608 }
609
print_memory_info(Process * p)610 void print_memory_info(Process *p)
611 {
612 if (p != NULL) {
613 erts_printf("======================================\n");
614 erts_printf("|| Memory info for %-12T ||\n",p->common.id);
615 erts_printf("======================================\n");
616 erts_printf("+- local heap ----%s-%s-%s-%s-+\n",
617 dashes,dashes,dashes,dashes);
618 erts_printf("| Young | 0x%0*lx - (0x%0*lx) - 0x%0*lx - 0x%0*lx |\n",
619 PTR_SIZE, (unsigned long)HEAP_START(p),
620 PTR_SIZE, (unsigned long)HIGH_WATER(p),
621 PTR_SIZE, (unsigned long)HEAP_TOP(p),
622 PTR_SIZE, (unsigned long)HEAP_END(p));
623 if (OLD_HEAP(p) != NULL)
624 erts_printf("| Old | 0x%0*lx - 0x%0*lx - 0x%0*lx %*s |\n",
625 PTR_SIZE, (unsigned long)OLD_HEAP(p),
626 PTR_SIZE, (unsigned long)OLD_HTOP(p),
627 PTR_SIZE, (unsigned long)OLD_HEND(p),
628 PTR_SIZE, "");
629 } else {
630 erts_printf("=================\n");
631 erts_printf("|| Memory info ||\n");
632 erts_printf("=================\n");
633 }
634 erts_printf("+-----------------%s-%s-%s-%s-+\n",dashes,dashes,dashes,dashes);
635 }
636 #endif
637