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