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