1 /* Generate graphic from memory profiling data.
2 Copyright (C) 1998, 1999, 2000, 2005, 2006 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
11 This program 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
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 #define _FILE_OFFSET_BITS 64
34 #include <sys/param.h>
42 /* Default size of the generated image. */
51 /* Definitions of arguments for argp functions. */
52 static const struct argp_option options[] =
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 }
64 /* Short description of program. */
65 static const char doc[] = N_("Generate graphic from memory profiling data");
67 /* Strings for arguments in help texts. */
68 static const char args_doc[] = N_("DATAFILE [OUTFILE]");
70 /* Prototype for option handler. */
71 static error_t parse_opt (int key, char *arg, struct argp_state *state);
73 /* Function to print some extra text in the help message. */
74 static char *more_help (int key, const char *text, void *input);
76 /* Data structure to communicate with argp functions. */
77 static struct argp argp =
79 options, parse_opt, args_doc, doc, NULL, more_help
92 /* Size of the image. */
96 /* Name of the output file. */
99 /* Title string for the graphic. */
100 static const char *string;
102 /* Nonzero if graph should be generated linear in time. */
103 static int time_based;
105 /* Nonzero if graph to display total use of memory should be drawn as well. */
106 static int also_total = 0;
110 main (int argc, char *argv[])
115 int grey, blue, red, green, yellow, black;
118 uint64_t maxsize_heap;
119 uint64_t maxsize_stack;
120 uint64_t maxsize_total;
128 struct entry headent[2];
132 const char *heap_format, *stack_format;
133 int heap_scale, stack_scale, line;
140 /* Parse and process arguments. */
141 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
143 if (remaining >= argc || remaining + 2 < argc)
145 argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
150 inname = argv[remaining++];
152 if (remaining < argc)
153 outname = argv[remaining];
154 else if (outname == NULL)
156 size_t len = strlen (inname);
157 outname = alloca (len + 5);
158 stpcpy (stpcpy (outname, inname), ".png");
161 /* Open for read/write since we try to repair the file in case the
162 application hasn't terminated cleanly. */
163 fd = open (inname, O_RDWR);
165 error (EXIT_FAILURE, errno, "cannot open input file");
166 if (fstat (fd, &st) != 0)
169 error (EXIT_FAILURE, errno, "cannot get size of input file");
171 /* Test whether the file contains only full records. */
172 if ((st.st_size % sizeof (struct entry)) != 0
173 /* The file must at least contain the two administrative records. */
174 || st.st_size < 2 * sizeof (struct entry))
177 error (EXIT_FAILURE, 0, "input file has incorrect size");
179 /* Compute number of data entries. */
180 total = st.st_size / sizeof (struct entry) - 2;
182 /* Read the administrative information. */
183 read (fd, headent, sizeof (headent));
186 maxsize_heap = headent[1].heap;
187 maxsize_stack = headent[1].stack;
188 maxsize_total = headent[0].stack;
191 /* We use one scale and since we also draw the total amount of
192 memory used we have to adapt the maximum. */
193 maxsize_heap = maxsize_total;
194 maxsize_stack = maxsize_total;
197 if (maxsize_heap == 0 && maxsize_stack == 0)
199 /* The program aborted before memusage was able to write the
200 information about the maximum heap and stack use. Repair
206 if (read (fd, &next, sizeof (next)) == 0)
208 if (next.heap > headent[1].heap)
209 headent[1].heap = next.heap;
210 if (next.stack > headent[1].stack)
211 headent[1].stack = next.stack;
214 headent[1].time_low = next.time_low;
215 headent[1].time_high = next.time_high;
217 /* Write the computed values in the file. */
218 lseek (fd, sizeof (struct entry), SEEK_SET);
219 write (fd, &headent[1], sizeof (struct entry));
223 printf("MAX: total: %llu stack: %llu heap: %llu\n", maxsize_total, maxsize_stack, maxsize_heap);
224 start_time = ((uint64_t) headent[0].time_high) << 32 | headent[0].time_low;
225 end_time = ((uint64_t) headent[1].time_high) << 32 | headent[1].time_low;
226 total_time = end_time - start_time;
233 /* Create output image with the specified size. */
234 im_out = gdImageCreate (xsize, ysize);
236 /* First color allocated is background. */
237 grey = gdImageColorAllocate (im_out, 224, 224, 224);
239 /* Set transparent color. */
240 gdImageRectangle(im_out, 0, 0, xsize, ysize, grey);
242 /* These are all the other colors we need (in the moment). */
243 red = gdImageColorAllocate (im_out, 255, 0, 0);
244 green = gdImageColorAllocate (im_out, 0, 130, 0);
245 blue = gdImageColorAllocate (im_out, 0, 0, 255);
246 yellow = gdImageColorAllocate (im_out, 154, 205, 50);
247 black = gdImageColorAllocate (im_out, 0, 0, 0);
249 gdImageRectangle (im_out, 40, 20, xsize - 40, ysize - 20, blue);
251 if (maxsize_heap < 1024)
256 else if (maxsize_heap < 1024 * 1024 * 100)
258 heap_format = "%Zuk";
263 heap_format = "%ZuM";
264 heap_scale = 1024 * 1024;
267 if (maxsize_stack < 1024)
269 stack_format = "%Zu";
272 else if (maxsize_stack < 1024 * 1024 * 100)
274 stack_format = "%Zuk";
279 stack_format = "%ZuM";
280 stack_scale = 1024 * 1024;
283 gdImageString (im_out, gdFontSmall, 38, ysize - 14, (unsigned char *) "0",
285 snprintf (buf, sizeof (buf), heap_format, 0);
286 gdImageString (im_out, gdFontSmall, maxsize_heap < 1024 ? 32 : 26,
287 ysize - 26, (unsigned char *) buf, red);
288 snprintf (buf, sizeof (buf), stack_format, 0);
289 gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26,
290 (unsigned char *) buf, green);
293 gdImageString (im_out, gdFontLarge, (xsize - strlen (string) * 8) / 2,
294 2, (unsigned char *) string, green);
296 gdImageStringUp (im_out, gdFontSmall, 1, ysize / 2 - 10,
297 (unsigned char *) "allocated", red);
298 gdImageStringUp (im_out, gdFontSmall, 11, ysize / 2 - 10,
299 (unsigned char *) "memory", red);
301 gdImageStringUp (im_out, gdFontSmall, xsize - 39, ysize / 2 - 10,
302 (unsigned char *) "used", green);
303 gdImageStringUp (im_out, gdFontSmall, xsize - 27, ysize / 2 - 10,
304 (unsigned char *) "stack", green);
306 snprintf (buf, sizeof (buf), heap_format, maxsize_heap / heap_scale);
307 gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14,
308 (unsigned char *) buf, red);
309 snprintf (buf, sizeof (buf), stack_format, maxsize_stack / stack_scale);
310 gdImageString (im_out, gdFontSmall, xsize - 37, 14,
311 (unsigned char *) buf, green);
313 for (line = 1; line <= 3; ++line)
315 cnt = ((ysize - 40) * (maxsize_heap / 4 * line / heap_scale)) /
316 (maxsize_heap / heap_scale);
317 gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40,
318 ysize - 20 - cnt, red);
319 snprintf (buf, sizeof (buf), heap_format, maxsize_heap / 4 * line /
321 gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6,
322 ysize - 26 - cnt, (unsigned char *) buf, red);
324 cnt2 = ((ysize - 40) * (maxsize_stack / 4 * line / stack_scale)) /
325 (maxsize_stack / stack_scale);
327 gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40,
328 ysize - 20 - cnt2, green);
329 snprintf (buf, sizeof (buf), stack_format, maxsize_stack / 4 * line /
331 gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2,
332 (unsigned char *) buf, green);
335 snprintf (buf, sizeof (buf), "%llu", (unsigned long long) total);
336 gdImageString (im_out, gdFontSmall, xsize - 50, ysize - 14,
337 (unsigned char *) buf, blue);
341 uint64_t previously = start_time;
343 gdImageString (im_out, gdFontSmall, 40 + (xsize - 32 * 6 - 80) / 2,
345 (unsigned char *) "# memory handling function calls",
349 last_stack = last_heap = last_total = ysize - 20;
350 for (cnt = 1; cnt <= total; ++cnt)
356 read (fd, &entry, sizeof (entry));
358 now = ((uint64_t) entry.time_high) << 32 | entry.time_low;
360 if ((((previously - start_time) * 100) / total_time) % 10 < 5)
361 gdImageFilledRectangle (im_out,
362 40 + ((cnt - 1) * (xsize - 80)) / total,
364 39 + (cnt * (xsize - 80)) / total,
372 new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
373 * (entry.heap + entry.stack))
375 gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total,
377 40 + ((xsize - 80) * cnt) / total, new3,
382 // assert (entry.heap <= maxsize_heap);
383 new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
384 * entry.heap) / maxsize_heap);
385 gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total,
386 last_heap, 40 + ((xsize - 80) * cnt) / total, new[0],
390 // assert (entry.stack <= maxsize_stack);
391 new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
392 * entry.stack) / maxsize_stack);
393 gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total,
394 last_stack, 40 + ((xsize - 80) * cnt) / total, new[1],
402 gdImageLine (im_out, 40 + ((xsize - 80) * cnt) / total, ysize - 20,
403 40 + ((xsize - 80) * cnt) / total, ysize - 15, blue);
404 cnt += MAX (1, total / 20);
406 gdImageLine (im_out, xsize - 40, ysize - 20, xsize - 40, ysize - 15,
411 uint64_t next_tick = MAX (1, total / 20);
412 size_t last_xpos = 40;
414 gdImageString (im_out, gdFontSmall, 40 + (xsize - 39 * 6 - 80) / 2,
417 # memory handling function calls / time", blue);
419 for (cnt = 0; cnt < 20; cnt += 2)
420 gdImageFilledRectangle (im_out,
421 40 + (cnt * (xsize - 80)) / 20, ysize - 19,
422 39 + ((cnt + 1) * (xsize - 80)) / 20,
425 last_stack = last_heap = last_total = ysize - 20;
426 for (cnt = 1; cnt <= total; ++cnt)
433 read (fd, &entry, sizeof (entry));
435 now = ((uint64_t) entry.time_high) << 32 | entry.time_low;
436 xpos = 40 + ((xsize - 80) * (now - start_time)) / total_time;
438 if (cnt == next_tick)
440 gdImageLine (im_out, xpos, ysize - 20, xpos, ysize - 15, blue);
441 next_tick += MAX (1, total / 20);
448 new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
449 * (entry.heap + entry.stack))
451 gdImageLine (im_out, last_xpos, last_total, xpos, new3, black);
455 new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
456 * entry.heap) / maxsize_heap);
457 gdImageLine (im_out, last_xpos, last_heap, xpos, new[0], red);
460 // assert (entry.stack <= maxsize_stack);
461 new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40))
462 * entry.stack) / maxsize_stack);
463 gdImageLine (im_out, last_xpos, last_stack, xpos, new[1], green);
470 /* Write out the result. */
471 outfile = fopen (outname, "w");
473 error (EXIT_FAILURE, errno, "cannot open output file");
475 gdImagePng (im_out, outfile);
479 gdImageDestroy (im_out);
485 /* Handle program arguments. */
487 parse_opt (int key, char *arg, struct argp_state *state)
514 return ARGP_ERR_UNKNOWN;
521 more_help (int key, const char *text, void *input)
528 case ARGP_KEY_HELP_EXTRA:
529 /* We print some extra information. */
531 For bug reporting instructions, please see:\n\
532 <http://www.gnu.org/software/libc/bugs.html>.\n");
540 return (char *) text;