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