1 /*
2 * mpatrol
3 * A library for controlling and tracing dynamic memory allocations.
4 * Copyright (C) 1997-2002 Graeme S. Roy <graeme.roy@analog.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19 * MA 02111-1307, USA.
20 */
21
22
23 /*
24 * A tool designed to read a tracing output file produced by the mpatrol
25 * library and display the tracing information that was obtained. It can
26 * also produce a C source file containing a trace-driven memory allocation
27 * simulation of the program which produced the corresponding tracing output
28 * file.
29 */
30
31
32 #include "tree.h"
33 #include "slots.h"
34 #include "getopt.h"
35 #include "utils.h"
36 #include "version.h"
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #if MP_GUI_SUPPORT
41 #include <Xm/DrawingA.h>
42 #include <Xm/ScrolledW.h>
43 #endif /* MP_GUI_SUPPORT */
44
45
46 #if MP_IDENT_SUPPORT
47 #ident "$Id: mptrace.c,v 1.28 2002/01/08 20:13:59 graeme Exp $"
48 #else /* MP_IDENT_SUPPORT */
49 static MP_CONST MP_VOLATILE char *mptrace_id = "$Id: mptrace.c,v 1.28 2002/01/08 20:13:59 graeme Exp $";
50 #endif /* MP_IDENT_SUPPORT */
51
52
53 #define PROGVERSION "1.4" /* the current version of this program */
54
55
56 /* The flags used to parse the command line options.
57 */
58
59 typedef enum options_flags
60 {
61 OF_HATFFILE = 'H',
62 OF_HELP = 'h',
63 OF_SIMFILE = 'S',
64 OF_SOURCE = 's',
65 OF_VERSION = 'V',
66 OF_VERBOSE = 'v',
67 OF_GUI = 'w'
68 }
69 options_flags;
70
71
72 /* Structure containing the allocation details for a single memory allocation.
73 */
74
75 typedef struct allocation
76 {
77 treenode node; /* tree node */
78 unsigned long event; /* event number */
79 void *entry; /* pointer array entry */
80 void *addr; /* allocation address */
81 size_t size; /* allocation size */
82 unsigned long time; /* allocation lifetime */
83 }
84 allocation;
85
86
87 /* Structure containing the statistics for a tracing output file.
88 */
89
90 typedef struct statistics
91 {
92 size_t acount; /* total number of allocated blocks */
93 size_t atotal; /* total size of allocated blocks */
94 size_t fcount; /* total number of freed blocks */
95 size_t ftotal; /* total size of freed blocks */
96 size_t rcount; /* total number of reserved blocks */
97 size_t rtotal; /* total size of reserved blocks */
98 size_t icount; /* total number of internal blocks */
99 size_t itotal; /* total size of internal blocks */
100 size_t pcount; /* peak number of allocated blocks */
101 size_t ptotal; /* peak size of allocated blocks */
102 size_t lsize; /* smallest size of an allocation */
103 size_t usize; /* largest size of an allocation */
104 }
105 statistics;
106
107
108 /* The version of the mpatrol library which produced the tracing output file.
109 */
110
111 static unsigned long version;
112
113
114 /* The tree containing information about each memory allocation.
115 */
116
117 static treeroot alloctree;
118
119
120 /* The number of the current event in the tracing output file.
121 */
122
123 static unsigned long currentevent;
124
125
126 /* The input buffer used to read from the tracing output file, and also the
127 * current buffer position and length.
128 */
129
130 static char buffer[MP_BUFFER_SIZE];
131 static char *bufferpos;
132 static size_t bufferlen;
133
134
135 /* The slot table allows us to reuse entries in the pointer array when we are
136 * writing a simulation file. The size of the tableslots array sets a limit
137 * on the maximum number of allocations that can be in use at any one time.
138 */
139
140 static slottable table;
141 static void *tableslots[4096];
142 static size_t maxslots;
143
144
145 /* The name caches for the function names and the file names.
146 */
147
148 static char *funcnames[MP_NAMECACHE_SIZE];
149 static char *filenames[MP_NAMECACHE_SIZE];
150
151
152 /* The statistics gathered from the tracing output file.
153 */
154
155 static statistics stats;
156
157
158 /* The tracing output file produced by mpatrol.
159 */
160
161 static FILE *tracefile;
162
163
164 /* The HATF file produced from the tracing output file.
165 */
166
167 static FILE *hatffile;
168
169
170 /* The simulation file produced from the tracing output file.
171 */
172
173 static FILE *simfile;
174
175
176 /* The filename used to invoke this tool.
177 */
178
179 static char *progname;
180
181
182 /* Indicates if the tracing table should be displayed.
183 */
184
185 static int verbose;
186
187
188 /* Indicates if source-level information should be displayed in the tracing
189 * table if it is available.
190 */
191
192 static int displaysource;
193
194
195 #if MP_GUI_SUPPORT
196 /* Indicates if the GUI should be used or not.
197 */
198
199 static int usegui;
200
201
202 /* The X toolkit context for this application.
203 */
204
205 static XtAppContext appcontext;
206
207
208 /* The X display and screen pointers for this application.
209 */
210
211 static Display *appdisplay;
212 static Screen *appscreen;
213
214
215 /* The X widgets for the application shell, main application and drawing area.
216 */
217
218 static Widget appwidget, mainwidget, drawwidget;
219
220
221 /* The X pixmap for the backup drawing area.
222 */
223
224 static Pixmap pixmap;
225
226
227 /* The X graphics contexts for unallocated, internal, free and allocated memory.
228 */
229
230 static GC ungc, ingc, frgc, algc;
231
232
233 /* The colours for unallocated, internal, free and allocated memory.
234 */
235
236 static Pixel uncol, incol, frcol, alcol;
237
238
239 /* The width and height (in pixels) of the window and the drawing area.
240 */
241
242 static Dimension vwidth, vheight, width, height;
243
244
245 /* The delay between drawing each event.
246 */
247
248 static unsigned long delay;
249
250
251 /* The base address and the size (in megabytes) of the address space covered by
252 * the drawing area.
253 */
254
255 static void *addrbase;
256 static unsigned long addrspace;
257
258
259 /* The scaling factor of the drawing area.
260 */
261
262 static unsigned long addrscale;
263
264
265 /* The X resources for this application.
266 */
267
268 static XtResource resources[] =
269 {
270 {"alloc", XmCColor, XmRPixel, sizeof(Pixel),
271 (Cardinal) &alcol, XmRString, (XtPointer) "black"},
272 {"base", "Base", XmRInt, sizeof(void *),
273 (Cardinal) &addrbase, XmRImmediate, (XtPointer) NULL},
274 {"delay", "Delay", XmRInt, sizeof(unsigned long),
275 (Cardinal) &delay, XmRImmediate, (XtPointer) 0},
276 {"free", XmCColor, XmRPixel, sizeof(Pixel),
277 (Cardinal) &frcol, XmRString, (XtPointer) "white"},
278 {"height", XmCHeight, XmRShort, sizeof(Dimension),
279 (Cardinal) &height, XmRImmediate, (XtPointer) 512},
280 {"internal", XmCColor, XmRPixel, sizeof(Pixel),
281 (Cardinal) &incol, XmRString, (XtPointer) "red"},
282 {"space", "Space", XmRInt, sizeof(unsigned long),
283 (Cardinal) &addrspace, XmRImmediate, (XtPointer) 4},
284 {"unalloc", XmCColor, XmRPixel, sizeof(Pixel),
285 (Cardinal) &uncol, XmRString, (XtPointer) "blue"},
286 {"view-height", XmCHeight, XmRShort, sizeof(Dimension),
287 (Cardinal) &vheight, XmRImmediate, (XtPointer) 256},
288 {"view-width", XmCWidth, XmRShort, sizeof(Dimension),
289 (Cardinal) &vwidth, XmRImmediate, (XtPointer) 256},
290 {"width", XmCWidth, XmRShort, sizeof(Dimension),
291 (Cardinal) &width, XmRImmediate, (XtPointer) 512}
292 };
293
294
295 /* The X options for this application.
296 */
297
298 static XrmOptionDescRec options[] =
299 {
300 {"--alloc", "alloc", XrmoptionSepArg, NULL},
301 {"--base", "base", XrmoptionSepArg, NULL},
302 {"--delay", "delay", XrmoptionSepArg, NULL},
303 {"--free", "free", XrmoptionSepArg, NULL},
304 {"--height", "height", XrmoptionSepArg, NULL},
305 {"--internal", "internal", XrmoptionSepArg, NULL},
306 {"--space", "space", XrmoptionSepArg, NULL},
307 {"--unalloc", "unalloc", XrmoptionSepArg, NULL},
308 {"--view-height", "view-height", XrmoptionSepArg, NULL},
309 {"--view-width", "view-width", XrmoptionSepArg, NULL},
310 {"--width", "width", XrmoptionSepArg, NULL}
311 };
312 #endif /* MP_GUI_SUPPORT */
313
314
315 /* The table describing all recognised options.
316 */
317
318 static option options_table[] =
319 {
320 {"gui", OF_GUI, NULL,
321 "\tDisplays the GUI (if supported).\n"},
322 {"hatf-file", OF_HATFFILE, "file",
323 "\tSpecifies that the trace should also be written to a file in Heap\n"
324 "\tAllocation Trace Format (HATF).\n"},
325 {"help", OF_HELP, NULL,
326 "\tDisplays this quick-reference option summary.\n"},
327 {"sim-file", OF_SIMFILE, "file",
328 "\tSpecifies that a trace-driven memory allocation simulation program\n"
329 "\twritten in C should be written to a file.\n"},
330 {"source", OF_SOURCE, NULL,
331 "\tDisplays source-level information for each event in the tracing\n"
332 "\ttable, if available.\n"},
333 {"verbose", OF_VERBOSE, NULL,
334 "\tSpecifies that the tracing table should be displayed.\n"},
335 {"version", OF_VERSION, NULL,
336 "\tDisplays the version number of this program.\n"},
337 NULL
338 };
339
340
341 #define slotentry(n) \
342 (unsigned long) ((sizeof(tableslots) - ((char *) (n)->entry - \
343 (char *) tableslots)) / sizeof(void *))
344
345
346 /* Create a new memory allocation.
347 */
348
349 static
350 allocation *
newalloc(unsigned long i,unsigned long e,void * a,size_t l)351 newalloc(unsigned long i, unsigned long e, void *a, size_t l)
352 {
353 allocation *n;
354
355 if (n = (allocation *) __mp_search(alloctree.root, i))
356 {
357 if (n->time == 0)
358 fprintf(stderr, "%s: Allocation index `%lu' has been allocated "
359 "twice without being freed\n", progname, i);
360 }
361 else
362 {
363 if ((n = (allocation *) malloc(sizeof(allocation))) == NULL)
364 {
365 fprintf(stderr, "%s: Out of memory\n", progname);
366 exit(EXIT_FAILURE);
367 }
368 __mp_treeinsert(&alloctree, &n->node, i);
369 n->event = e;
370 }
371 if (simfile != NULL)
372 {
373 if ((n->entry = __mp_getslot(&table)) == NULL)
374 {
375 fprintf(stderr, "%s: Too many allocations in use\n", progname);
376 exit(EXIT_FAILURE);
377 }
378 }
379 else
380 n->entry = NULL;
381 n->addr = a;
382 n->size = l;
383 n->time = 0;
384 return n;
385 }
386
387
388 /* Free all existing memory allocations.
389 */
390
391 static
392 void
freeallocs(void)393 freeallocs(void)
394 {
395 allocation *n, *p;
396
397 for (n = (allocation *) __mp_minimum(alloctree.root); n != NULL; n = p)
398 {
399 p = (allocation *) __mp_successor(&n->node);
400 __mp_treeremove(&alloctree, &n->node);
401 free(n);
402 }
403 }
404
405
406 /* Byte-swap a block of memory.
407 */
408
409 static
410 void
byteswap(void * b,size_t n)411 byteswap(void *b, size_t n)
412 {
413 char *s, *t;
414 char c;
415
416 s = (char *) b;
417 t = (char *) b + n - 1;
418 while (s < t)
419 {
420 c = *s;
421 *s++ = *t;
422 *t-- = c;
423 }
424 }
425
426
427 /* Refill the input buffer. The input buffer is necessary since we need to
428 * have a minimum number of bytes to read an LEB128 number from the input
429 * file.
430 */
431
432 static
433 size_t
refill(size_t s)434 refill(size_t s)
435 {
436 static int e;
437 size_t l, n;
438
439 /* We only need to refill the input buffer if there are not enough bytes
440 * in the buffer and we have not reached the end of the file.
441 */
442 if ((e == 1) || (bufferlen >= s))
443 return bufferlen;
444 /* Check that the requested number of bytes will fit into the buffer.
445 */
446 if (s > MP_BUFFER_SIZE)
447 {
448 fprintf(stderr, "%s: Buffer overflow\n", progname);
449 exit(EXIT_FAILURE);
450 }
451 /* If there are any remaining bytes in the buffer then move them to the
452 * start of the buffer so that we can fill up the rest of the buffer.
453 */
454 if ((bufferpos != buffer) && (bufferlen > 0))
455 memmove(buffer, bufferpos, bufferlen);
456 bufferpos = buffer;
457 /* Attempt to fill up the buffer with bytes from the input file.
458 */
459 l = MP_BUFFER_SIZE - bufferlen;
460 if ((n = fread(buffer + bufferlen, sizeof(char), l, tracefile)) != l)
461 if (feof(tracefile))
462 e = 1;
463 else
464 {
465 fprintf(stderr, "%s: Error reading file\n", progname);
466 exit(EXIT_FAILURE);
467 }
468 bufferlen += n;
469 /* If the buffer has not been completely filled then we zero the remaining
470 * bytes. This is done simply to prevent running off the end of the buffer
471 * if we are reading an LEB128 number from an unterminated file.
472 */
473 if (l = MP_BUFFER_SIZE - bufferlen)
474 memset(buffer + bufferlen, 0, l);
475 return bufferlen;
476 }
477
478
479 /* Read an entry from the tracing output file.
480 */
481
482 static
483 void
getentry(void * d,size_t l,size_t n,int b)484 getentry(void *d, size_t l, size_t n, int b)
485 {
486 size_t i, s;
487
488 s = l * n;
489 if (refill(s) < s)
490 {
491 fprintf(stderr, "%s: Error reading file\n", progname);
492 exit(EXIT_FAILURE);
493 }
494 memcpy(d, bufferpos, s);
495 bufferpos += s;
496 bufferlen -= s;
497 /* Byte-swap all of the elements if necessary.
498 */
499 if (b != 0)
500 for (i = 0; i < n; i++)
501 {
502 byteswap(d, l);
503 d = (char *) d + l;
504 }
505 }
506
507
508 /* Read a signed LEB128 number from the tracing output file.
509 */
510
511 static
512 long
getsleb128(void)513 getsleb128(void)
514 {
515 size_t s;
516 long n;
517
518 /* Since the tracing output file must end with a magic sequence of 4 bytes,
519 * it is not unreasonable to request at least 5 bytes from the input file
520 * since an LEB128 number must be at least 1 byte.
521 */
522 if (refill(5) < 5)
523 {
524 fprintf(stderr, "%s: Error reading file\n", progname);
525 exit(EXIT_FAILURE);
526 }
527 n = __mp_decodesleb128(bufferpos, &s);
528 bufferpos += s;
529 bufferlen -= s;
530 return n;
531 }
532
533
534 /* Read an unsigned LEB128 number from the tracing output file.
535 */
536
537 static
538 unsigned long
getuleb128(void)539 getuleb128(void)
540 {
541 size_t s;
542 unsigned long n;
543
544 /* Since the tracing output file must end with a magic sequence of 4 bytes,
545 * it is not unreasonable to request at least 5 bytes from the input file
546 * since an LEB128 number must be at least 1 byte.
547 */
548 if (refill(5) < 5)
549 {
550 fprintf(stderr, "%s: Error reading file\n", progname);
551 exit(EXIT_FAILURE);
552 }
553 n = __mp_decodeuleb128(bufferpos, &s);
554 bufferpos += s;
555 bufferlen -= s;
556 return n;
557 }
558
559
560 /* Read a null-terminated string from the tracing output file.
561 */
562
563 static
564 char *
getstring(void)565 getstring(void)
566 {
567 static char b[1024];
568 size_t i;
569
570 for (i = 0; i < sizeof(b); i++)
571 {
572 if (!refill(1))
573 {
574 fprintf(stderr, "%s: Error reading file\n", progname);
575 exit(EXIT_FAILURE);
576 }
577 b[i] = *bufferpos;
578 bufferpos++;
579 bufferlen--;
580 if (b[i] == '\0')
581 return b;
582 }
583 fprintf(stderr, "%s: Buffer overflow\n", progname);
584 exit(EXIT_FAILURE);
585 return NULL;
586 }
587
588
589 /* Read a (possibly cached) function name from the tracing output file.
590 */
591
592 static
593 char *
getfuncname(void)594 getfuncname(void)
595 {
596 char *s;
597 int d;
598 unsigned char i;
599
600 if (!refill(1))
601 {
602 fprintf(stderr, "%s: Error reading file\n", progname);
603 exit(EXIT_FAILURE);
604 }
605 i = (unsigned char) *bufferpos;
606 bufferpos++;
607 bufferlen--;
608 if (i == 0)
609 return NULL;
610 d = ((i & 0x80) != 0);
611 i = (i & 0x7F) - 1;
612 if (d != 0)
613 {
614 s = getstring();
615 if (funcnames[i] != NULL)
616 free(funcnames[i]);
617 if ((funcnames[i] = (char *) malloc(strlen(s) + 1)) == NULL)
618 {
619 fprintf(stderr, "%s: Out of memory\n", progname);
620 exit(EXIT_FAILURE);
621 }
622 strcpy(funcnames[i], s);
623 }
624 return funcnames[i];
625 }
626
627
628 /* Read a (possibly cached) file name from the tracing output file.
629 */
630
631 static
632 char *
getfilename(void)633 getfilename(void)
634 {
635 char *s;
636 int d;
637 unsigned char i;
638
639 if (!refill(1))
640 {
641 fprintf(stderr, "%s: Error reading file\n", progname);
642 exit(EXIT_FAILURE);
643 }
644 i = (unsigned char) *bufferpos;
645 bufferpos++;
646 bufferlen--;
647 if (i == 0)
648 return NULL;
649 d = ((i & 0x80) != 0);
650 i = (i & 0x7F) - 1;
651 if (d != 0)
652 {
653 s = getstring();
654 if (filenames[i] != NULL)
655 free(filenames[i]);
656 if ((filenames[i] = (char *) malloc(strlen(s) + 1)) == NULL)
657 {
658 fprintf(stderr, "%s: Out of memory\n", progname);
659 exit(EXIT_FAILURE);
660 }
661 strcpy(filenames[i], s);
662 }
663 return filenames[i];
664 }
665
666
667 /* Read the current event's thread id, source function name, file name and line
668 * number from the tracing output file.
669 */
670
671 static
672 void
getsource(unsigned long * i,char ** s,char ** t,unsigned long * u)673 getsource(unsigned long *i, char **s, char **t, unsigned long *u)
674 {
675 char *a;
676
677 *s = *t = NULL;
678 *i = *u = 0;
679 /* From mpatrol release 1.4.5, the thread id, source function name, file
680 * name and line number for each allocation, reallocation and deallocation
681 * is also written to the tracing output file.
682 */
683 if (version >= 10405)
684 {
685 *i = getuleb128();
686 *s = getfuncname();
687 *t = getfilename();
688 *u = getuleb128();
689 }
690 }
691
692
693 #if MP_GUI_SUPPORT
694 /* Refresh the memory display window.
695 */
696
697 static
698 void
redrawmemory(Widget w,XtPointer d,XmDrawingAreaCallbackStruct * s)699 redrawmemory(Widget w, XtPointer d, XmDrawingAreaCallbackStruct *s)
700 {
701 XCopyArea(appdisplay, pixmap, s->window, ungc, 0, 0, width - 1, height - 1,
702 0, 0);
703 }
704 #endif /* MP_GUI_SUPPORT */
705
706
707 #if MP_GUI_SUPPORT
708 /* Update the memory display window.
709 */
710
711 static
712 void
drawmemory(void * a,size_t s,GC g)713 drawmemory(void *a, size_t s, GC g)
714 {
715 unsigned long i, j, l, u, x1, x2, y1, y2;
716
717 if (a < addrbase)
718 return;
719 l = (unsigned long) a - (unsigned long) addrbase;
720 u = l + s - 1;
721 l /= addrscale;
722 u /= addrscale;
723 x1 = l % width;
724 y1 = l / width;
725 x2 = u % width;
726 y2 = u / width;
727 if (y2 >= height)
728 return;
729 if (y1 == y2)
730 {
731 XDrawLine(appdisplay, XtWindow(drawwidget), g, x1, y1, x2, y1);
732 XDrawLine(appdisplay, pixmap, g, x1, y1, x2, y1);
733 }
734 else
735 {
736 for (i = x1, j = y1; j < y2; i = 0, j++)
737 {
738 XDrawLine(appdisplay, XtWindow(drawwidget), g, i, j, width - 1, j);
739 XDrawLine(appdisplay, pixmap, g, i, j, width - 1, j);
740 }
741 XDrawLine(appdisplay, XtWindow(drawwidget), g, 0, y2, x2, y2);
742 XDrawLine(appdisplay, pixmap, g, 0, y2, x2, y2);
743 }
744 /* We just do a busy loop here since sleep() does not have enough accuracy
745 * and usleep() is only available on a small number of platforms. We need
746 * to modify the global variable "delay" in case some clever compilers
747 * optimise away the pointless modification of a local variable.
748 */
749 if (delay > 0)
750 {
751 i = delay;
752 for (delay *= 100000; delay > 0; delay--);
753 delay = i;
754 }
755 }
756 #endif /* MP_GUI_SUPPORT */
757
758
759 /* Divide two integers, rounding to the nearest integer.
760 */
761
762 static
763 unsigned long
rounddivide(unsigned long n,unsigned long d)764 rounddivide(unsigned long n, unsigned long d)
765 {
766 unsigned long q;
767 double r;
768
769 if (d == 0)
770 return 0;
771 q = n / d;
772 r = (double) (n - (q * d)) / (double) d;
773 if ((r < 0.5) || ((0.5 <= r) && (r <= 0.5) && !(q & 1)))
774 return q;
775 return q + 1;
776 }
777
778
779 /* Display a size in bytes.
780 */
781
782 static
783 void
printsize(size_t l)784 printsize(size_t l)
785 {
786 fprintf(stdout, "%lu byte", l);
787 if (l != 1)
788 fputc('s', stdout);
789 }
790
791
792 /* Display source-level information for a given event.
793 */
794
795 static
796 void
printsource(unsigned long i,char * s,char * t,unsigned long u)797 printsource(unsigned long i, char *s, char *t, unsigned long u)
798 {
799 if ((i != 0) || (s != NULL) || ((t != NULL) && (u != 0)))
800 {
801 fputs(" ", stdout);
802 if (i != 0)
803 fprintf(stdout, " thread %lu", i);
804 if (s != NULL)
805 fprintf(stdout, " in %s", s);
806 if ((t != NULL) && (u != 0))
807 fprintf(stdout, " at %s line %lu", t, u);
808 fputc('\n', stdout);
809 }
810 }
811
812
813 /* Display the statistics gathered from the tracing output file.
814 */
815
816 static
817 void
showstats(void)818 showstats(void)
819 {
820 fputs("memory allocation tracing statistics\n", stdout);
821 fputs("------------------------------------\n", stdout);
822 fprintf(stdout, "allocated: %lu (", stats.acount);
823 printsize(stats.atotal);
824 fprintf(stdout, ")\nfreed: %lu (", stats.fcount);
825 printsize(stats.ftotal);
826 fprintf(stdout, ")\nunfreed: %lu (", stats.acount - stats.fcount);
827 printsize(stats.atotal - stats.ftotal);
828 fprintf(stdout, ")\npeak: %lu (", stats.pcount);
829 printsize(stats.ptotal);
830 fprintf(stdout, ")\nreserved: %lu (", stats.rcount);
831 printsize(stats.rtotal);
832 fprintf(stdout, ")\ninternal: %lu (", stats.icount);
833 printsize(stats.itotal);
834 fprintf(stdout, ")\ntotal: %lu (", stats.rcount + stats.icount);
835 printsize(stats.rtotal + stats.itotal);
836 fputs(")\n\n", stdout);
837 fputs("smallest size: ", stdout);
838 printsize(stats.lsize);
839 fputs("\nlargest size: ", stdout);
840 printsize(stats.usize);
841 fputs("\naverage size: ", stdout);
842 printsize(rounddivide(stats.atotal, stats.acount));
843 fputc('\n', stdout);
844 }
845
846
847 /* Read an event from the tracing output file.
848 */
849
850 #if MP_GUI_SUPPORT
851 static
852 int
readevent(XtPointer p)853 readevent(XtPointer p)
854 #else /* MP_GUI_SUPPORT */
855 static
856 int
857 readevent(void)
858 #endif /* MP_GUI_SUPPORT */
859 {
860 char s[4];
861 allocation *f;
862 char *g, *h;
863 void *a;
864 size_t i, l, m;
865 unsigned long n, t, u;
866
867 if (refill(1))
868 switch (*bufferpos)
869 {
870 case 'A':
871 bufferpos++;
872 bufferlen--;
873 currentevent++;
874 n = getuleb128();
875 a = (void *) getuleb128();
876 l = getuleb128();
877 getsource(&t, &g, &h, &u);
878 f = newalloc(n, currentevent, a, l);
879 stats.acount++;
880 stats.atotal += l;
881 if (stats.pcount < stats.acount - stats.fcount)
882 stats.pcount = stats.acount - stats.fcount;
883 if (stats.ptotal < stats.atotal - stats.ftotal)
884 stats.ptotal = stats.atotal - stats.ftotal;
885 if ((stats.lsize == 0) || (stats.lsize > l))
886 stats.lsize = l;
887 if (stats.usize < l)
888 stats.usize = l;
889 if (verbose)
890 {
891 fprintf(stdout, "%6lu alloc %6lu " MP_POINTER " %8lu"
892 " %6lu %8lu\n", currentevent, n, a, l,
893 stats.acount - stats.fcount,
894 stats.atotal - stats.ftotal);
895 if (displaysource)
896 printsource(t, g, h, u);
897 }
898 if (hatffile != NULL)
899 fprintf(hatffile, "1 %lu 0x%lx\n", l, a);
900 if (f->entry != NULL)
901 {
902 if ((m = slotentry(f)) > maxslots)
903 maxslots = m;
904 fprintf(simfile, " {%lu, %lu, 0},\n", m, l);
905 }
906 #if MP_GUI_SUPPORT
907 if (usegui)
908 {
909 if (addrbase == NULL)
910 addrbase = (void *) __mp_rounddown((unsigned long) a, 1024);
911 drawmemory(a, l, algc);
912 return 0;
913 }
914 #endif /* MP_GUI_SUPPORT */
915 return 1;
916 case 'R':
917 bufferpos++;
918 bufferlen--;
919 currentevent++;
920 n = getuleb128();
921 a = (void *) getuleb128();
922 l = getuleb128();
923 getsource(&t, &g, &h, &u);
924 if (f = (allocation *) __mp_search(alloctree.root, n))
925 {
926 if (f->time != 0)
927 fprintf(stderr, "%s: Allocation index `%lu' has already "
928 "been freed\n", progname, n);
929 stats.acount++;
930 stats.atotal += l;
931 stats.fcount++;
932 stats.ftotal += f->size;
933 if (stats.pcount < stats.acount - stats.fcount)
934 stats.pcount = stats.acount - stats.fcount;
935 if (stats.ptotal < stats.atotal - stats.ftotal)
936 stats.ptotal = stats.atotal - stats.ftotal;
937 if ((stats.lsize == 0) || (stats.lsize > l))
938 stats.lsize = l;
939 if (stats.usize < l)
940 stats.usize = l;
941 if (verbose)
942 {
943 fprintf(stdout, "%6lu realloc %6lu " MP_POINTER
944 " %8lu %6lu %8lu\n", currentevent, n, a,
945 l, stats.acount - stats.fcount,
946 stats.atotal - stats.ftotal);
947 if (displaysource)
948 printsource(t, g, h, u);
949 }
950 if (hatffile != NULL)
951 fprintf(hatffile, "4 %lu 0x%lx 0x%lx\n", l, f->addr, a);
952 if (f->entry != NULL)
953 {
954 m = slotentry(f);
955 fprintf(simfile, " {%lu, %lu, 1},\n", m, l);
956 }
957 #if MP_GUI_SUPPORT
958 if (usegui)
959 {
960 drawmemory(f->addr, f->size, frgc);
961 drawmemory(a, l, algc);
962 }
963 #endif /* MP_GUI_SUPPORT */
964 f->addr = a;
965 f->size = l;
966 }
967 else
968 fprintf(stderr, "%s: Unknown allocation index `%lu'\n",
969 progname, n);
970 #if MP_GUI_SUPPORT
971 if (usegui)
972 return 0;
973 #endif /* MP_GUI_SUPPORT */
974 return 1;
975 case 'F':
976 bufferpos++;
977 bufferlen--;
978 currentevent++;
979 n = getuleb128();
980 getsource(&t, &g, &h, &u);
981 if (f = (allocation *) __mp_search(alloctree.root, n))
982 {
983 if (f->time != 0)
984 fprintf(stderr, "%s: Allocation index `%lu' has already "
985 "been freed\n", progname, n);
986 f->time = currentevent - f->event;
987 stats.fcount++;
988 stats.ftotal += f->size;
989 if (verbose)
990 {
991 fprintf(stdout, "%6lu free %6lu " MP_POINTER " %8lu "
992 "%6lu %6lu %8lu\n", currentevent, n, f->addr,
993 f->size, f->time, stats.acount - stats.fcount,
994 stats.atotal - stats.ftotal);
995 if (displaysource)
996 printsource(t, g, h, u);
997 }
998 if (hatffile != NULL)
999 fprintf(hatffile, "2 0x%lx\n", f->addr);
1000 if (f->entry != NULL)
1001 {
1002 fprintf(simfile, " {%lu, 0, 0},\n", slotentry(f));
1003 __mp_freeslot(&table, f->entry);
1004 f->entry = NULL;
1005 }
1006 #if MP_GUI_SUPPORT
1007 if (usegui)
1008 drawmemory(f->addr, f->size, frgc);
1009 #endif /* MP_GUI_SUPPORT */
1010 }
1011 else
1012 fprintf(stderr, "%s: Unknown allocation index `%lu'\n",
1013 progname, n);
1014 #if MP_GUI_SUPPORT
1015 if (usegui)
1016 return 0;
1017 #endif /* MP_GUI_SUPPORT */
1018 return 1;
1019 case 'H':
1020 bufferpos++;
1021 bufferlen--;
1022 a = (void *) getuleb128();
1023 l = getuleb128();
1024 if (verbose)
1025 fprintf(stdout, " reserve " MP_POINTER
1026 " %8lu\n", a, l);
1027 stats.rcount++;
1028 stats.rtotal += l;
1029 #if MP_GUI_SUPPORT
1030 if (usegui)
1031 {
1032 if (addrbase == NULL)
1033 addrbase = (void *) __mp_rounddown((unsigned long) a, 1024);
1034 drawmemory(a, l, frgc);
1035 return 0;
1036 }
1037 #endif /* MP_GUI_SUPPORT */
1038 return 1;
1039 case 'I':
1040 bufferpos++;
1041 bufferlen--;
1042 a = (void *) getuleb128();
1043 l = getuleb128();
1044 if (verbose)
1045 fprintf(stdout, " internal " MP_POINTER
1046 " %8lu\n", a, l);
1047 stats.icount++;
1048 stats.itotal += l;
1049 #if MP_GUI_SUPPORT
1050 if (usegui)
1051 {
1052 drawmemory(a, l, ingc);
1053 return 0;
1054 }
1055 #endif /* MP_GUI_SUPPORT */
1056 return 1;
1057 default:
1058 break;
1059 }
1060 if ((hatffile != NULL) && (hatffile != stdout) && (hatffile != stderr))
1061 fclose(hatffile);
1062 if (simfile != NULL)
1063 {
1064 fputs(" {0, 0, 0}\n};\n\n\n", simfile);
1065 fputs("int main(void)\n{\n", simfile);
1066 fprintf(simfile, " void *p[%lu];\n", maxslots);
1067 fputs(" event *e;\n\n", simfile);
1068 fputs(" for (e = events; e->index != 0; e++)\n", simfile);
1069 fputs(" if (e->resize)\n", simfile);
1070 fputs(" {\n", simfile);
1071 fputs(" if ((p[e->index - 1] = realloc(p[e->index - 1], "
1072 "e->size)) == NULL)\n", simfile);
1073 fputs(" {\n", simfile);
1074 fputs(" fputs(\"out of memory\\n\", stderr);\n",
1075 simfile);
1076 fputs(" exit(EXIT_FAILURE);\n", simfile);
1077 fputs(" }\n", simfile);
1078 fputs(" }\n", simfile);
1079 fputs(" else if (e->size == 0)\n", simfile);
1080 fputs(" free(p[e->index - 1]);\n", simfile);
1081 fputs(" else if ((p[e->index - 1] = malloc(e->size)) == NULL)\n",
1082 simfile);
1083 fputs(" {\n", simfile);
1084 fputs(" fputs(\"out of memory\\n\", stderr);\n", simfile);
1085 fputs(" exit(EXIT_FAILURE);\n", simfile);
1086 fputs(" }\n", simfile);
1087 fputs(" return EXIT_SUCCESS;\n}\n", simfile);
1088 if ((simfile != stdout) && (simfile != stderr))
1089 fclose(simfile);
1090 }
1091 getentry(s, sizeof(char), 4, 0);
1092 if (memcmp(s, MP_TRACEMAGIC, 4) != 0)
1093 {
1094 fprintf(stderr, "%s: Invalid file format\n", progname);
1095 exit(EXIT_FAILURE);
1096 }
1097 if (verbose)
1098 fputc('\n', stdout);
1099 showstats();
1100 for (i = 0; i < MP_NAMECACHE_SIZE; i++)
1101 {
1102 if (funcnames[i] != NULL)
1103 free(funcnames[i]);
1104 if (filenames[i] != NULL)
1105 free(filenames[i]);
1106 }
1107 freeallocs();
1108 fclose(tracefile);
1109 #if MP_GUI_SUPPORT
1110 if (usegui)
1111 return 1;
1112 #endif /* MP_GUI_SUPPORT */
1113 return 0;
1114 }
1115
1116
1117 /* Log the allocations and deallocations from the tracing output file.
1118 */
1119
1120 static
1121 void
readfile(void)1122 readfile(void)
1123 {
1124 char s[4];
1125 size_t i;
1126 int b;
1127
1128 /* When reading the tracing output file, we assume that if it begins and
1129 * ends with the magic sequence of characters then it is a valid tracing
1130 * output file from the mpatrol library. There are probably an infinite
1131 * number of checks we could do to ensure that the rest of the data in the
1132 * file is valid, but that would be overcomplicated and probably slow this
1133 * program down. However, if the file is only partially written then the
1134 * getentry() function will catch the error before we do something silly.
1135 */
1136 getentry(s, sizeof(char), 4, 0);
1137 if (memcmp(s, MP_TRACEMAGIC, 4) != 0)
1138 {
1139 fprintf(stderr, "%s: Invalid file format\n", progname);
1140 exit(EXIT_FAILURE);
1141 }
1142 /* The following test allows us to read tracing output files that were
1143 * produced on a different processor architecture. If the next word in the
1144 * file does not contain the value 1 then we have to byte-swap any further
1145 * data that we read from the file. Note that this test only works if the
1146 * word size is the same on both machines.
1147 */
1148 getentry(&i, sizeof(size_t), 1, 0);
1149 b = (i != 1);
1150 /* Get the version number of the mpatrol library which produced the
1151 * tracing output file. We assume that we can't read files produced by
1152 * later versions of mpatrol.
1153 */
1154 getentry(&version, sizeof(unsigned long), 1, b);
1155 if (version / 100 > MP_VERNUM / 100)
1156 {
1157 fprintf(stderr, "%s: Tracing file version too new\n", progname);
1158 exit(EXIT_FAILURE);
1159 }
1160 /* Display the tracing table headings.
1161 */
1162 if (verbose)
1163 {
1164 fputs(" event type index ", stdout);
1165 #if ENVIRON == ENVIRON_64
1166 fputs(" allocation ", stdout);
1167 #else /* ENVIRON */
1168 fputs("allocation", stdout);
1169 #endif /* ENVIRON */
1170 fputs(" size life", stdout);
1171 fputs(" count bytes\n", stdout);
1172 fputs("------ ------ ------ ", stdout);
1173 #if ENVIRON == ENVIRON_64
1174 fputs("------------------", stdout);
1175 #else /* ENVIRON */
1176 fputs("----------", stdout);
1177 #endif /* ENVIRON */
1178 fputs(" -------- ------", stdout);
1179 fputs(" ------ --------\n", stdout);
1180 }
1181 /* Read each allocation or deallocation entry.
1182 */
1183 #if MP_GUI_SUPPORT
1184 if (!usegui)
1185 while (readevent(NULL));
1186 #else /* MP_GUI_SUPPORT */
1187 while (readevent());
1188 #endif /* MP_GUI_SUPPORT */
1189 }
1190
1191
1192 /* Read the tracing output file and display all specified information.
1193 */
1194
1195 int
main(int argc,char ** argv)1196 main(int argc, char **argv)
1197 {
1198 struct { char x; void *y; } z;
1199 char b[256];
1200 char *f, *s, *t;
1201 #if MP_GUI_SUPPORT
1202 XGCValues g;
1203 #endif /* MP_GUI_SUPPORT */
1204 long n;
1205 int c, e, h, v;
1206
1207 #if MP_GUI_SUPPORT
1208 appwidget = XtVaAppInitialize(&appcontext, "MPTrace", options,
1209 XtNumber(options), &argc, argv, NULL, NULL);
1210 XtVaGetApplicationResources(appwidget, NULL, resources, XtNumber(resources),
1211 NULL);
1212 #endif /* MP_GUI_SUPPORT */
1213 s = t = NULL;
1214 e = h = v = 0;
1215 progname = __mp_basename(argv[0]);
1216 while ((c = __mp_getopt(argc, argv, __mp_shortopts(b, options_table),
1217 options_table)) != EOF)
1218 switch (c)
1219 {
1220 case OF_GUI:
1221 #if MP_GUI_SUPPORT
1222 usegui = 1;
1223 #endif /* MP_GUI_SUPPORT */
1224 break;
1225 case OF_HATFFILE:
1226 t = __mp_optarg;
1227 break;
1228 case OF_HELP:
1229 h = 1;
1230 break;
1231 case OF_SIMFILE:
1232 s = __mp_optarg;
1233 break;
1234 case OF_SOURCE:
1235 displaysource = 1;
1236 break;
1237 case OF_VERBOSE:
1238 verbose = 1;
1239 break;
1240 case OF_VERSION:
1241 v = 1;
1242 break;
1243 default:
1244 e = 1;
1245 break;
1246 }
1247 argc -= __mp_optindex;
1248 argv += __mp_optindex;
1249 if (v == 1)
1250 {
1251 fprintf(stdout, "%s %s\n%s %s\n\n", progname, PROGVERSION,
1252 __mp_copyright, __mp_author);
1253 fputs("This is free software, and you are welcome to redistribute it "
1254 "under certain\n", stdout);
1255 fputs("conditions; see the GNU Library General Public License for "
1256 "details.\n\n", stdout);
1257 fputs("For the latest mpatrol release and documentation,\n", stdout);
1258 fprintf(stdout, "visit %s.\n\n", __mp_homepage);
1259 }
1260 if (argc > 1)
1261 e = 1;
1262 if ((e == 1) || (h == 1))
1263 {
1264 fprintf(stdout, "Usage: %s [options] [file]\n\n", progname);
1265 if (h == 0)
1266 fprintf(stdout, "Type `%s --help' for a complete list of "
1267 "options.\n", progname);
1268 else
1269 __mp_showopts(options_table);
1270 if (e == 1)
1271 exit(EXIT_FAILURE);
1272 exit(EXIT_SUCCESS);
1273 }
1274 if (argc == 1)
1275 f = argv[0];
1276 else
1277 f = MP_TRACEFILE;
1278 __mp_newtree(&alloctree);
1279 if (strcmp(f, "-") == 0)
1280 tracefile = stdin;
1281 else if ((tracefile = fopen(f, "rb")) == NULL)
1282 {
1283 fprintf(stderr, "%s: Cannot open file `%s'\n", progname, f);
1284 exit(EXIT_FAILURE);
1285 }
1286 currentevent = 0;
1287 bufferpos = buffer;
1288 bufferlen = 0;
1289 n = (char *) &z.y - &z.x;
1290 __mp_newslots(&table, sizeof(void *), __mp_poweroftwo(n));
1291 __mp_initslots(&table, tableslots, sizeof(tableslots));
1292 maxslots = 1;
1293 if (s != NULL)
1294 {
1295 if (strcmp(s, "stdout") == 0)
1296 simfile = stdout;
1297 else if (strcmp(s, "stderr") == 0)
1298 simfile = stderr;
1299 else if ((simfile = fopen(s, "w")) == NULL)
1300 {
1301 fprintf(stderr, "%s: Cannot open file `%s'\n", progname, s);
1302 exit(EXIT_FAILURE);
1303 }
1304 fprintf(simfile, "/* produced by %s %s from %s */\n\n\n", progname,
1305 PROGVERSION, f);
1306 fputs("#include <stdio.h>\n", simfile);
1307 fputs("#include <stdlib.h>\n\n\n", simfile);
1308 fputs("typedef struct event\n{\n", simfile);
1309 fputs(" unsigned long index;\n", simfile);
1310 fputs(" unsigned long size;\n", simfile);
1311 fputs(" char resize;\n", simfile);
1312 fputs("}\nevent;\n\n\n", simfile);
1313 fputs("static event events[] =\n{\n", simfile);
1314 }
1315 if (t != NULL)
1316 {
1317 if (strcmp(t, "stdout") == 0)
1318 hatffile = stdout;
1319 else if (strcmp(t, "stderr") == 0)
1320 hatffile = stderr;
1321 else if ((hatffile = fopen(t, "w")) == NULL)
1322 {
1323 fprintf(stderr, "%s: Cannot open file `%s'\n", progname, t);
1324 exit(EXIT_FAILURE);
1325 }
1326 fprintf(hatffile, "## Tracename: %s\n", t);
1327 fputs("## Author: Unknown\n", hatffile);
1328 fputs("## Date: Unknown\n", hatffile);
1329 fputs("## DTDURL: hatf.dtd\n", hatffile);
1330 fprintf(hatffile, "## Description: Converted to HATF by %s %s.\n\n",
1331 progname, PROGVERSION);
1332 }
1333 readfile();
1334 #if MP_GUI_SUPPORT
1335 if (usegui)
1336 {
1337 appdisplay = XtDisplay(appwidget);
1338 appscreen = XtScreen(appwidget);
1339 addrscale = (((addrspace * 1048576) - 1) / (width * height)) + 1;
1340 /* Set up the main application window and scrollable drawing area.
1341 * Also set up a pixmap to backup the drawing area.
1342 */
1343 mainwidget = XtVaCreateManagedWidget("main",
1344 xmScrolledWindowWidgetClass,
1345 appwidget, XmNwidth, vwidth,
1346 XmNheight, vheight,
1347 XmNscrollingPolicy, XmAUTOMATIC,
1348 XmNscrollBarDisplayPolicy,
1349 XmAS_NEEDED, NULL);
1350 drawwidget = XtVaCreateManagedWidget("draw", xmDrawingAreaWidgetClass,
1351 mainwidget, XmNwidth, width,
1352 XmNheight, height, NULL);
1353 pixmap = XCreatePixmap(appdisplay, RootWindowOfScreen(appscreen), width,
1354 height, DefaultDepthOfScreen(appscreen));
1355 /* Set up the graphics contexts that are used for drawing in different
1356 * colours.
1357 */
1358 g.foreground = uncol;
1359 ungc = XCreateGC(appdisplay, RootWindowOfScreen(appscreen),
1360 GCForeground, &g);
1361 g.foreground = incol;
1362 ingc = XCreateGC(appdisplay, RootWindowOfScreen(appscreen),
1363 GCForeground, &g);
1364 g.foreground = frcol;
1365 frgc = XCreateGC(appdisplay, RootWindowOfScreen(appscreen),
1366 GCForeground, &g);
1367 g.foreground = alcol;
1368 algc = XCreateGC(appdisplay, RootWindowOfScreen(appscreen),
1369 GCForeground, &g);
1370 /* Add a callback procedure to handle the refreshing of the drawing
1371 * area and also a work procedure to read events from the tracing
1372 * output file. Then initialise the drawing area and enter the main X
1373 * application loop.
1374 */
1375 XtAddCallback(drawwidget, XmNexposeCallback,
1376 (XtCallbackProc) redrawmemory, NULL);
1377 XtAppAddWorkProc(appcontext, (XtWorkProc) readevent, NULL);
1378 XtRealizeWidget(appwidget);
1379 XFillRectangle(appdisplay, XtWindow(drawwidget), ungc, 0, 0, width - 1,
1380 height - 1);
1381 XFillRectangle(appdisplay, pixmap, ungc, 0, 0, width - 1, height - 1);
1382 XtAppMainLoop(appcontext);
1383 }
1384 #endif /* MP_GUI_SUPPORT */
1385 return EXIT_SUCCESS;
1386 }
1387