1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #define _FILE_OFFSET_BITS 64
21:
22: #include <argp.h>
23: #include <assert.h>
24: #include <errno.h>
25: #include <error.h>
26: #include <fcntl.h>
27: #include <getopt.h>
28: #include <inttypes.h>
29: #include <libintl.h>
30: #include <stdio.h>
31: #include <stdlib.h>
32: #include <string.h>
33: #include <unistd.h>
34: #include <sys/param.h>
35: #include <sys/stat.h>
36:
37: #include <gd.h>
38: #include <gdfontl.h>
39: #include <gdfonts.h>
40:
41:
42:
43: #define XSIZE 800
44: #define YSIZE 600
45:
46: #ifndef N_
47: # define N_(Arg) Arg
48: #endif
49:
50:
51:
52: static const struct argp_option options[] =
53: {
54: { "output", 'o', "FILE", 0, N_("Name output file") },
55: { "string", 's', "STRING", 0, N_("Title string used in output graphic") },
56: { "time", 't', NULL, 0, N_("Generate output linear to time (default is linear to number of function calls)") },
57: { "total", 'T', NULL, 0,
58: N_("Also draw graph for total memory consumption") },
59: { "x-size", 'x', "VALUE", 0, N_("Make output graphic VALUE pixels wide") },
60: { "y-size", 'y', "VALUE", 0, N_("Make output graphic VALUE pixels high") },
61: { NULL, 0, NULL, 0, NULL }
62: };
63:
64:
65: static const char doc[] = N_("Generate graphic from memory profiling data");
66:
67:
68: static const char args_doc[] = N_("DATAFILE [OUTFILE]");
69:
70:
71: static error_t parse_opt (int key, char *arg, struct argp_state *state);
72:
73:
74: static char *more_help (int key, const char *text, void *input);
75:
76:
77: static struct argp argp =
78: {
79: options, parse_opt, args_doc, doc, NULL, more_help
80: };
81:
82:
83: struct entry
84: {
85: uint64_t heap;
86: uint64_t stack;
87: uint32_t time_low;
88: uint32_t time_high;
89: };
90:
91:
92:
93: static size_t xsize;
94: static size_t ysize;
95:
96:
97: static char *outname;
98:
99:
100: static const char *string;
101:
102:
103: static int time_based;
104:
105:
106: static int also_total = 0;
107:
108:
109: int
110: main (int argc, char *argv[])
111: {
112: int remaining;
113: const char *inname;
114: gdImagePtr im_out;
115: int grey, blue, red, green, yellow, black;
116: int fd;
117: struct stat st;
118: size_t maxsize_heap;
119: size_t maxsize_stack;
120: size_t maxsize_total;
121: uint64_t total;
122: uint64_t cnt, cnt2;
123: FILE *outfile;
124: char buf[30];
125: size_t last_heap;
126: size_t last_stack;
127: size_t last_total;
128: struct entry headent[2];
129: uint64_t start_time;
130: uint64_t end_time;
131: uint64_t total_time;
132: const char *heap_format, *stack_format;
133: int heap_scale, stack_scale, line;
134:
135: outname = NULL;
136: xsize = XSIZE;
137: ysize = YSIZE;
138: string = NULL;
139:
140:
141: argp_parse (&argp, argc, argv, 0, &remaining, NULL);
142:
143: if (remaining >= argc || remaining + 2 < argc)
144: {
145: argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
146: program_invocation_short_name);
147: exit (1);
148: }
149:
150: inname = argv[remaining++];
151:
152: if (remaining < argc)
153: outname = argv[remaining];
154: else if (outname == NULL)
155: {
156: size_t len = strlen (inname);
157: outname = alloca (len + 5);
158: stpcpy (stpcpy (outname, inname), ".png");
159: }
160:
161:
162:
163: fd = open (inname, O_RDWR);
164: if (fd == -1)
165: error (EXIT_FAILURE, errno, "cannot open input file");
166: if (fstat (fd, &st) != 0)
167: {
168: close (fd);
169: error (EXIT_FAILURE, errno, "cannot get size of input file");
170: }
171:
172: if ((st.st_size % sizeof (struct entry)) != 0
173:
174: || st.st_size < 2 * sizeof (struct entry))
175: {
176: close (fd);
177: error (EXIT_FAILURE, 0, "input file as incorrect size");
178: }
179:
180: total = st.st_size / sizeof (struct entry) - 2;
181:
182:
183: read (fd, headent, sizeof (headent));
184: maxsize_heap = headent[1].heap;
185: maxsize_stack = headent[1].stack;
186: maxsize_total = headent[0].stack;
187: if (also_total)
188: {
189:
190:
191: maxsize_heap = maxsize_total;
192: maxsize_stack = maxsize_total;
193: }
194:
195: if (maxsize_heap == 0 && maxsize_stack == 0)
196: {
197:
198:
199:
200: struct entry next;
201:
202: while (1)
203: {
204: if (read (fd, &next, sizeof (next)) == 0)
205: break;
206: if (next.heap > headent[1].heap)
207: headent[1].heap = next.heap;
208: if (next.stack > headent[1].stack)
209: headent[1].stack = next.stack;
210: }
211:
212: headent[1].time_low = next.time_low;
213: headent[1].time_high = next.time_high;
214:
215:
216: lseek (fd, sizeof (struct entry), SEEK_SET);
217: write (fd, &headent[1], sizeof (struct entry));
218: }
219:
220: start_time = ((uint64_t) headent[0].time_high) << 32 | headent[0].time_low;
221: end_time = ((uint64_t) headent[1].time_high) << 32 | headent[1].time_low;
222: total_time = end_time - start_time;
223:
224: if (xsize < 100)
225: xsize = 100;
226: if (ysize < 80)
227: ysize = 80;
228:
229:
230: im_out = gdImageCreate (xsize, ysize);
231:
232:
233: grey = gdImageColorAllocate (im_out, 224, 224, 224);
234:
235:
236: gdImageColorTransparent (im_out, grey);
237:
238:
239: red = gdImageColorAllocate (im_out, 255, 0, 0);
240: green = gdImageColorAllocate (im_out, 0, 130, 0);
241: blue = gdImageColorAllocate (im_out, 0, 0, 255);
242: yellow = gdImageColorAllocate (im_out, 154, 205, 50);
243: black = gdImageColorAllocate (im_out, 0, 0, 0);
244:
245: gdImageRectangle (im_out, 40, 20, xsize - 40, ysize - 20, blue);
246:
247: if (maxsize_heap < 1024)
248: {
249: heap_format = "%Zu";
250: heap_scale = 1;
251: }
252: else if (maxsize_heap < 1024 * 1024 * 100)
253: {
254: heap_format = "%Zuk";
255: heap_scale = 1024;
256: }
257: else
258: {
259: heap_format = "%ZuM";
260: heap_scale = 1024 * 1024;
261: }
262:
263: if (maxsize_stack < 1024)
264: {
265: stack_format = "%Zu";
266: stack_scale = 1;
267: }
268: else if (maxsize_stack < 1024 * 1024 * 100)
269: {
270: stack_format = "%Zuk";
271: stack_scale = 1024;
272: }
273: else
274: {
275: stack_format = "%ZuM";
276: stack_scale = 1024 * 1024;
277: }
278:
279: gdImageString (im_out, gdFontSmall, 38, ysize - 14, (unsigned char *) "0",
280: blue);
281: snprintf (buf, sizeof (buf), heap_format, 0);
282: gdImageString (im_out, gdFontSmall, maxsize_heap < 1024 ? 32 : 26,
283: ysize - 26, (unsigned char *) buf, red);
284: snprintf (buf, sizeof (buf), stack_format, 0);
285: gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26,
286: (unsigned char *) buf, green);
287:
288: if (string != NULL)
289: gdImageString (im_out, gdFontLarge, (xsize - strlen (string) * 8) / 2,
290: 2, (unsigned char *) string, green);
291:
292: gdImageStringUp (im_out, gdFontSmall, 1, ysize / 2 - 10,
293: (unsigned char *) "allocated", red);
294: gdImageStringUp (im_out, gdFontSmall, 11, ysize / 2 - 10,
295: (unsigned char *) "memory", red);
296:
297: gdImageStringUp (im_out, gdFontSmall, xsize - 39, ysize / 2 - 10,
298: (unsigned char *) "used", green);
299: gdImageStringUp (im_out, gdFontSmall, xsize - 27, ysize / 2 - 10,
300: (unsigned char *) "stack", green);
301:
302: snprintf (buf, sizeof (buf), heap_format, maxsize_heap / heap_scale);
303: gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14,
304: (unsigned char *) buf, red);
305: snprintf (buf, sizeof (buf), stack_format, maxsize_stack / stack_scale);
306: gdImageString (im_out, gdFontSmall, xsize - 37, 14,
307: (unsigned char *) buf, green);
308:
309: for (line = 1; line <= 3; ++line)
310: {
311: cnt = ((ysize - 40) * (maxsize_heap / 4 * line / heap_scale)) /
312: (maxsize_heap / heap_scale);
313: gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40,
314: ysize - 20 - cnt, red);
315: snprintf (buf, sizeof (buf), heap_format, maxsize_heap / 4 * line /
316: heap_scale);
317: gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6,
318: ysize - 26 - cnt, (unsigned char *) buf, red);
319:
320: cnt2 = ((ysize - 40) * (maxsize_stack / 4 * line / stack_scale)) /
321: (maxsize_stack / stack_scale);
322: if (cnt != cnt2)
323: gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40,
324: ysize - 20 - cnt2, green);
325: snprintf (buf, sizeof (buf), stack_format, maxsize_stack / 4 * line /
326: stack_scale);
327: gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2,
328: (unsigned char *) buf, green);
329: }
330:
331: snprintf (buf, sizeof (buf), "%llu", (unsigned long long) total);
332: gdImageString (im_out, gdFontSmall, xsize - 50, ysize - 14,
333: (unsigned char *) buf, blue);
334:
335: if (!time_based)
336: {
337: uint64_t previously = start_time;
338:
339: gdImageString (im_out, gdFontSmall, 40 + (xsize - 32 * 6 - 80) / 2,
340: ysize - 12,
341: (unsigned char *) "# memory handling function calls",
342: blue);
343:
344:
345: last_stack = last_heap = last_total = ysize - 20;
346: for (cnt = 1; cnt <= total; ++cnt)
347: {
348: struct entry entry;
349: size_t new[2];
350: uint64_t now;
351:
352: read (fd, &entry, sizeof (entry));
353:
354: now = ((uint64_t) entry.time_high) << 32 | entry.time_low;
355:
356: if ((((previously - start_time) * 100) / total_time) % 10 < 5)
357: gdImageFilledRectangle (im_out,
358: 40 + ((cnt - 1) * (xsize - 80)) / total,
359: ysize - 19,
360: 39 + (cnt * (xsize - 80)) / total,
361: ysize - 14, yellow);
362: previously = now;
363:
364: if (also_total)
365: {
366: size_t new3;
367:
368: new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
369: * (entry.heap + entry.stack))
370: / maxsize_heap);
371: gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total,
372: last_total,
373: 40 + ((xsize - 80) * cnt) / total, new3,
374: black);
375: last_total = new3;
376: }
377:
378:
379: new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
380: * entry.heap) / maxsize_heap);
381: gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total,
382: last_heap, 40 + ((xsize - 80) * cnt) / total, new[0],
383: red);
384: last_heap = new[0];
385:
386:
387: new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
388: * entry.stack) / maxsize_stack);
389: gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total,
390: last_stack, 40 + ((xsize - 80) * cnt) / total, new[1],
391: green);
392: last_stack = new[1];
393: }
394:
395: cnt = 0;
396: while (cnt < total)
397: {
398: gdImageLine (im_out, 40 + ((xsize - 80) * cnt) / total, ysize - 20,
399: 40 + ((xsize - 80) * cnt) / total, ysize - 15, blue);
400: cnt += MAX (1, total / 20);
401: }
402: gdImageLine (im_out, xsize - 40, ysize - 20, xsize - 40, ysize - 15,
403: blue);
404: }
405: else
406: {
407: uint64_t next_tick = MAX (1, total / 20);
408: size_t last_xpos = 40;
409:
410: gdImageString (im_out, gdFontSmall, 40 + (xsize - 39 * 6 - 80) / 2,
411: ysize - 12,
412: (unsigned char *) "\
413: # memory handling function calls / time", blue);
414:
415: for (cnt = 0; cnt < 20; cnt += 2)
416: gdImageFilledRectangle (im_out,
417: 40 + (cnt * (xsize - 80)) / 20, ysize - 19,
418: 39 + ((cnt + 1) * (xsize - 80)) / 20,
419: ysize - 14, yellow);
420:
421: last_stack = last_heap = last_total = ysize - 20;
422: for (cnt = 1; cnt <= total; ++cnt)
423: {
424: struct entry entry;
425: size_t new[2];
426: size_t xpos;
427: uint64_t now;
428:
429: read (fd, &entry, sizeof (entry));
430:
431: now = ((uint64_t) entry.time_high) << 32 | entry.time_low;
432: xpos = 40 + ((xsize - 80) * (now - start_time)) / total_time;
433:
434: if (cnt == next_tick)
435: {
436: gdImageLine (im_out, xpos, ysize - 20, xpos, ysize - 15, blue);
437: next_tick += MAX (1, total / 20);
438: }
439:
440: if (also_total)
441: {
442: size_t new3;
443:
444: new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
445: * (entry.heap + entry.stack))
446: / maxsize_heap);
447: gdImageLine (im_out, last_xpos, last_total, xpos, new3, black);
448: last_total = new3;
449: }
450:
451: new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
452: * entry.heap) / maxsize_heap);
453: gdImageLine (im_out, last_xpos, last_heap, xpos, new[0], red);
454: last_heap = new[0];
455:
456:
457: new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
458: * entry.stack) / maxsize_stack);
459: gdImageLine (im_out, last_xpos, last_stack, xpos, new[1], green);
460: last_stack = new[1];
461:
462: last_xpos = xpos;
463: }
464: }
465:
466:
467: outfile = fopen (outname, "w");
468: if (outfile == NULL)
469: error (EXIT_FAILURE, errno, "cannot open output file");
470:
471: gdImagePng (im_out, outfile);
472:
473: fclose (outfile);
474:
475: gdImageDestroy (im_out);
476:
477: return 0;
478: }
479:
480:
481:
482: static error_t
483: parse_opt (int key, char *arg, struct argp_state *state)
484: {
485: switch (key)
486: {
487: case 'o':
488: outname = arg;
489: break;
490: case 's':
491: string = arg;
492: break;
493: case 't':
494: time_based = 1;
495: break;
496: case 'T':
497: also_total = 1;
498: break;
499: case 'x':
500: xsize = atoi (arg);
501: if (xsize == 0)
502: xsize = XSIZE;
503: break;
504: case 'y':
505: ysize = atoi (arg);
506: if (ysize == 0)
507: ysize = XSIZE;
508: break;
509: default:
510: return ARGP_ERR_UNKNOWN;
511: }
512: return 0;
513: }
514:
515:
516: static char *
517: more_help (int key, const char *text, void *input)
518: {
519: char *orig;
520: char *cp;
521:
522: switch (key)
523: {
524: case ARGP_KEY_HELP_EXTRA:
525:
526: orig = gettext ("\
527: For bug reporting instructions, please see:\n\
528: <http://www.gnu.org/software/libc/bugs.html>.\n");
529: cp = strdup (orig);
530: if (cp == NULL)
531: cp = orig;<