(linenum→info "unix/slp.c:2238")

glibc/2.7/malloc/memusagestat.c

    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.
    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, write to the Free Software Foundation,
   18:    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
   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: /* Default size of the generated image.  */
   43: #define XSIZE 800
   44: #define YSIZE 600
   45: 
   46: #ifndef N_
   47: # define N_(Arg) Arg
   48: #endif
   49: 
   50: 
   51: /* Definitions of arguments for argp functions.  */
   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: /* Short description of program.  */
   65: static const char doc[] = N_("Generate graphic from memory profiling data");
   66: 
   67: /* Strings for arguments in help texts.  */
   68: static const char args_doc[] = N_("DATAFILE [OUTFILE]");
   69: 
   70: /* Prototype for option handler.  */
   71: static error_t parse_opt (int key, char *arg, struct argp_state *state);
   72: 
   73: /* Function to print some extra text in the help message.  */
   74: static char *more_help (int key, const char *text, void *input);
   75: 
   76: /* Data structure to communicate with argp functions.  */
   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: /* Size of the image.  */
   93: static size_t xsize;
   94: static size_t ysize;
   95: 
   96: /* Name of the output file.  */
   97: static char *outname;
   98: 
   99: /* Title string for the graphic.  */
  100: static const char *string;
  101: 
  102: /* Nonzero if graph should be generated linear in time.  */
  103: static int time_based;
  104: 
  105: /* Nonzero if graph to display total use of memory should be drawn as well.  */
  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:   /* Parse and process arguments.  */
  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:   /* 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);
  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:   /* 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))
  175:     {
  176:       close (fd);
  177:       error (EXIT_FAILURE, 0, "input file as incorrect size");
  178:     }
  179:   /* Compute number of data entries.  */
  180:   total = st.st_size / sizeof (struct entry) - 2;
  181: 
  182:   /* Read the administrative information.  */
  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:       /* We use one scale and since we also draw the total amount of
  190:          memory used we have to adapt the maximum.  */
  191:       maxsize_heap = maxsize_total;
  192:       maxsize_stack = maxsize_total;
  193:     }
  194: 
  195:   if (maxsize_heap == 0 && maxsize_stack == 0)
  196:     {
  197:       /* The program aborted before memusage was able to write the
  198:          information about the maximum heap and stack use.  Repair
  199:          the file now.  */
  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:       /* Write the computed values in the file.  */
  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:   /* Create output image with the specified size.  */
  230:   im_out = gdImageCreate (xsize, ysize);
  231: 
  232:   /* First color allocated is background.  */
  233:   grey = gdImageColorAllocate (im_out, 224, 224, 224);
  234: 
  235:   /* Set transparent color. */
  236:   gdImageColorTransparent (im_out, grey);
  237: 
  238:   /* These are all the other colors we need (in the moment).  */
  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:           // assert (entry.heap <= maxsize_heap);
  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:           // assert (entry.stack <= maxsize_stack);
  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:           // assert (entry.stack <= maxsize_stack);
  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:   /* Write out the result.  */
  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: /* Handle program arguments.  */
  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:       /* We print some extra information.  */
  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;<