memusagestat.c 17 KB

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