]>
Commit | Line | Data |
---|---|---|
c8f3e6db | 1 | /* Generate graphic from memory profiling data. |
dff8da6b | 2 | Copyright (C) 1998-2024 Free Software Foundation, Inc. |
c8f3e6db | 3 | This file is part of the GNU C Library. |
c8f3e6db | 4 | |
43bc8ac6 | 5 | This program is free software; you can redistribute it and/or modify |
2e2efe65 RM |
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. | |
c8f3e6db | 9 | |
43bc8ac6 | 10 | This program is distributed in the hope that it will be useful, |
c8f3e6db | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
43bc8ac6 UD |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | GNU General Public License for more details. | |
c8f3e6db | 14 | |
43bc8ac6 | 15 | You should have received a copy of the GNU General Public License |
5a82c748 | 16 | along with this program; if not, see <https://www.gnu.org/licenses/>. */ |
c8f3e6db | 17 | |
19c589d9 UD |
18 | #define _FILE_OFFSET_BITS 64 |
19 | ||
c8f3e6db UD |
20 | #include <argp.h> |
21 | #include <assert.h> | |
22 | #include <errno.h> | |
23 | #include <error.h> | |
24 | #include <fcntl.h> | |
25 | #include <getopt.h> | |
26 | #include <inttypes.h> | |
27 | #include <libintl.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <unistd.h> | |
200ae471 | 32 | #include <unistd_ext.h> |
e054f494 | 33 | #include <stdint.h> |
c8f3e6db UD |
34 | #include <sys/param.h> |
35 | #include <sys/stat.h> | |
36 | ||
37 | #include <gd.h> | |
38 | #include <gdfontl.h> | |
39 | #include <gdfonts.h> | |
40 | ||
cbbcaf23 UD |
41 | #include "../version.h" |
42 | #define PACKAGE _libc_intl_domainname | |
c8f3e6db UD |
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 | { | |
6c8dbf00 OB |
56 | { "output", 'o', N_ ("FILE"), 0, N_ ("Name output file") }, |
57 | { "string", 's', N_ ("STRING"), 0, N_ ("Title string used in output graphic") }, | |
79c1e109 | 58 | { "time", 't', NULL, 0, N_ ("\ |
0e2b9cdd RM |
59 | Generate output linear to time (default is linear to number of function calls)\ |
60 | ") }, | |
c8f3e6db | 61 | { "total", 'T', NULL, 0, |
6c8dbf00 OB |
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") }, | |
c8f3e6db UD |
66 | { NULL, 0, NULL, 0, NULL } |
67 | }; | |
68 | ||
69 | /* Short description of program. */ | |
6c8dbf00 | 70 | static const char doc[] = N_ ("Generate graphic from memory profiling data"); |
c8f3e6db UD |
71 | |
72 | /* Strings for arguments in help texts. */ | |
6c8dbf00 | 73 | static const char args_doc[] = N_ ("DATAFILE [OUTFILE]"); |
c8f3e6db UD |
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 | ||
cbbcaf23 UD |
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 | ||
c8f3e6db UD |
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 | { | |
11bf311e UD |
94 | uint64_t heap; |
95 | uint64_t stack; | |
c8f3e6db UD |
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; | |
19c589d9 UD |
141 | const char *heap_format, *stack_format; |
142 | int heap_scale, stack_scale, line; | |
c8f3e6db UD |
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, | |
6c8dbf00 | 155 | program_invocation_short_name); |
c8f3e6db UD |
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); | |
685cb083 | 186 | error (EXIT_FAILURE, 0, "input file has incorrect size"); |
c8f3e6db UD |
187 | } |
188 | /* Compute number of data entries. */ | |
189 | total = st.st_size / sizeof (struct entry) - 2; | |
190 | ||
191 | /* Read the administrative information. */ | |
b97c5efd | 192 | read_all (fd, headent, sizeof (headent)); |
c8f3e6db UD |
193 | maxsize_heap = headent[1].heap; |
194 | maxsize_stack = headent[1].stack; | |
195 | maxsize_total = headent[0].stack; | |
c8f3e6db UD |
196 | |
197 | if (maxsize_heap == 0 && maxsize_stack == 0) | |
198 | { | |
ba80a015 | 199 | /* The program aborted before memusage was able to write the |
6c8dbf00 OB |
200 | information about the maximum heap and stack use. Repair |
201 | the file now. */ | |
c8f3e6db UD |
202 | struct entry next; |
203 | ||
204 | while (1) | |
6c8dbf00 OB |
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 | } | |
c8f3e6db | 215 | |
67854c13 UD |
216 | headent[0].stack = maxsize_total; |
217 | headent[1].heap = maxsize_heap; | |
218 | headent[1].stack = maxsize_stack; | |
c8f3e6db UD |
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. */ | |
67854c13 | 223 | lseek (fd, 0, SEEK_SET); |
b97c5efd FB |
224 | write_all (fd, headent, sizeof (headent)); |
225 | ||
67854c13 UD |
226 | } |
227 | ||
228 | if (also_total) | |
229 | { | |
230 | /* We use one scale and since we also draw the total amount of | |
6c8dbf00 | 231 | memory used we have to adapt the maximum. */ |
67854c13 UD |
232 | maxsize_heap = maxsize_total; |
233 | maxsize_stack = maxsize_total; | |
c8f3e6db UD |
234 | } |
235 | ||
236 | start_time = ((uint64_t) headent[0].time_high) << 32 | headent[0].time_low; | |
237 | end_time = ((uint64_t) headent[1].time_high) << 32 | headent[1].time_low; | |
238 | total_time = end_time - start_time; | |
239 | ||
240 | if (xsize < 100) | |
241 | xsize = 100; | |
242 | if (ysize < 80) | |
243 | ysize = 80; | |
244 | ||
245 | /* Create output image with the specified size. */ | |
246 | im_out = gdImageCreate (xsize, ysize); | |
247 | ||
248 | /* First color allocated is background. */ | |
249 | grey = gdImageColorAllocate (im_out, 224, 224, 224); | |
250 | ||
251 | /* Set transparent color. */ | |
252 | gdImageColorTransparent (im_out, grey); | |
253 | ||
254 | /* These are all the other colors we need (in the moment). */ | |
255 | red = gdImageColorAllocate (im_out, 255, 0, 0); | |
256 | green = gdImageColorAllocate (im_out, 0, 130, 0); | |
257 | blue = gdImageColorAllocate (im_out, 0, 0, 255); | |
258 | yellow = gdImageColorAllocate (im_out, 154, 205, 50); | |
259 | black = gdImageColorAllocate (im_out, 0, 0, 0); | |
260 | ||
261 | gdImageRectangle (im_out, 40, 20, xsize - 40, ysize - 20, blue); | |
262 | ||
19c589d9 UD |
263 | if (maxsize_heap < 1024) |
264 | { | |
265 | heap_format = "%Zu"; | |
266 | heap_scale = 1; | |
267 | } | |
268 | else if (maxsize_heap < 1024 * 1024 * 100) | |
269 | { | |
270 | heap_format = "%Zuk"; | |
271 | heap_scale = 1024; | |
272 | } | |
273 | else | |
274 | { | |
275 | heap_format = "%ZuM"; | |
276 | heap_scale = 1024 * 1024; | |
277 | } | |
278 | ||
279 | if (maxsize_stack < 1024) | |
280 | { | |
281 | stack_format = "%Zu"; | |
282 | stack_scale = 1; | |
283 | } | |
284 | else if (maxsize_stack < 1024 * 1024 * 100) | |
285 | { | |
286 | stack_format = "%Zuk"; | |
287 | stack_scale = 1024; | |
288 | } | |
289 | else | |
290 | { | |
291 | stack_format = "%ZuM"; | |
292 | stack_scale = 1024 * 1024; | |
293 | } | |
294 | ||
c8f3e6db | 295 | gdImageString (im_out, gdFontSmall, 38, ysize - 14, (unsigned char *) "0", |
6c8dbf00 | 296 | blue); |
0c1c6c6c | 297 | snprintf (buf, sizeof (buf), heap_format, 0); |
c8f3e6db | 298 | gdImageString (im_out, gdFontSmall, maxsize_heap < 1024 ? 32 : 26, |
6c8dbf00 | 299 | ysize - 26, (unsigned char *) buf, red); |
0c1c6c6c | 300 | snprintf (buf, sizeof (buf), stack_format, 0); |
c8f3e6db | 301 | gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26, |
6c8dbf00 | 302 | (unsigned char *) buf, green); |
c8f3e6db UD |
303 | |
304 | if (string != NULL) | |
305 | gdImageString (im_out, gdFontLarge, (xsize - strlen (string) * 8) / 2, | |
6c8dbf00 | 306 | 2, (unsigned char *) string, green); |
c8f3e6db UD |
307 | |
308 | gdImageStringUp (im_out, gdFontSmall, 1, ysize / 2 - 10, | |
6c8dbf00 | 309 | (unsigned char *) "allocated", red); |
c8f3e6db | 310 | gdImageStringUp (im_out, gdFontSmall, 11, ysize / 2 - 10, |
6c8dbf00 | 311 | (unsigned char *) "memory", red); |
c8f3e6db UD |
312 | |
313 | gdImageStringUp (im_out, gdFontSmall, xsize - 39, ysize / 2 - 10, | |
6c8dbf00 | 314 | (unsigned char *) "used", green); |
c8f3e6db | 315 | gdImageStringUp (im_out, gdFontSmall, xsize - 27, ysize / 2 - 10, |
6c8dbf00 | 316 | (unsigned char *) "stack", green); |
c8f3e6db | 317 | |
19c589d9 | 318 | snprintf (buf, sizeof (buf), heap_format, maxsize_heap / heap_scale); |
0c1c6c6c | 319 | gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14, |
6c8dbf00 | 320 | (unsigned char *) buf, red); |
19c589d9 | 321 | snprintf (buf, sizeof (buf), stack_format, maxsize_stack / stack_scale); |
0c1c6c6c | 322 | gdImageString (im_out, gdFontSmall, xsize - 37, 14, |
6c8dbf00 | 323 | (unsigned char *) buf, green); |
c8f3e6db | 324 | |
19c589d9 | 325 | for (line = 1; line <= 3; ++line) |
c8f3e6db | 326 | { |
7da6d9ed | 327 | if (maxsize_heap > 0) |
6c8dbf00 OB |
328 | { |
329 | cnt = (((ysize - 40) * (maxsize_heap / 4 * line / heap_scale)) | |
330 | / (maxsize_heap / heap_scale)); | |
331 | gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, | |
332 | ysize - 20 - cnt, red); | |
333 | snprintf (buf, sizeof (buf), heap_format, | |
334 | maxsize_heap / 4 * line / heap_scale); | |
335 | gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, | |
336 | ysize - 26 - cnt, (unsigned char *) buf, red); | |
337 | } | |
7da6d9ed | 338 | else |
6c8dbf00 | 339 | cnt = 0; |
7da6d9ed SP |
340 | |
341 | if (maxsize_stack > 0) | |
6c8dbf00 OB |
342 | cnt2 = (((ysize - 40) * (maxsize_stack / 4 * line / stack_scale)) |
343 | / (maxsize_stack / stack_scale)); | |
7da6d9ed | 344 | else |
6c8dbf00 | 345 | cnt2 = 0; |
7da6d9ed | 346 | |
c8f3e6db | 347 | if (cnt != cnt2) |
6c8dbf00 OB |
348 | gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, |
349 | ysize - 20 - cnt2, green); | |
a04549c1 JM |
350 | snprintf (buf, sizeof (buf), stack_format, |
351 | maxsize_stack / 4 * line / stack_scale); | |
c8f3e6db | 352 | gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, |
6c8dbf00 | 353 | (unsigned char *) buf, green); |
c8f3e6db UD |
354 | } |
355 | ||
d15851ec | 356 | snprintf (buf, sizeof (buf), "%llu", (unsigned long long) total); |
0c1c6c6c | 357 | gdImageString (im_out, gdFontSmall, xsize - 50, ysize - 14, |
6c8dbf00 | 358 | (unsigned char *) buf, blue); |
c8f3e6db UD |
359 | |
360 | if (!time_based) | |
361 | { | |
362 | uint64_t previously = start_time; | |
363 | ||
364 | gdImageString (im_out, gdFontSmall, 40 + (xsize - 32 * 6 - 80) / 2, | |
6c8dbf00 OB |
365 | ysize - 12, |
366 | (unsigned char *) "# memory handling function calls", | |
367 | blue); | |
c8f3e6db UD |
368 | |
369 | ||
370 | last_stack = last_heap = last_total = ysize - 20; | |
371 | for (cnt = 1; cnt <= total; ++cnt) | |
6c8dbf00 OB |
372 | { |
373 | struct entry entry; | |
374 | size_t new[2]; | |
375 | uint64_t now; | |
376 | ||
b97c5efd | 377 | read_all (fd, &entry, sizeof (entry)); |
6c8dbf00 OB |
378 | |
379 | now = ((uint64_t) entry.time_high) << 32 | entry.time_low; | |
380 | ||
381 | if ((((previously - start_time) * 100) / total_time) % 10 < 5) | |
382 | gdImageFilledRectangle (im_out, | |
383 | 40 + ((cnt - 1) * (xsize - 80)) / total, | |
384 | ysize - 19, | |
385 | 39 + (cnt * (xsize - 80)) / total, | |
386 | ysize - 14, yellow); | |
387 | previously = now; | |
388 | ||
389 | if (also_total && maxsize_heap > 0) | |
390 | { | |
391 | size_t new3; | |
392 | ||
393 | new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) | |
394 | * (entry.heap + entry.stack)) | |
395 | / maxsize_heap); | |
396 | gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, | |
397 | last_total, | |
398 | 40 + ((xsize - 80) * cnt) / total, new3, | |
399 | black); | |
400 | last_total = new3; | |
401 | } | |
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, 40 + ((xsize - 80) * (cnt - 1)) / total, | |
409 | last_heap, 40 + ((xsize - 80) * cnt) / total, | |
410 | new[0], red); | |
411 | last_heap = new[0]; | |
412 | } | |
413 | ||
414 | if (maxsize_stack > 0) | |
415 | { | |
416 | new[1] = ((ysize - 20) | |
417 | - ((((unsigned long long int) (ysize - 40)) | |
418 | * entry.stack) / maxsize_stack)); | |
419 | gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, | |
420 | last_stack, 40 + ((xsize - 80) * cnt) / total, | |
421 | new[1], green); | |
422 | last_stack = new[1]; | |
423 | } | |
424 | } | |
c8f3e6db UD |
425 | |
426 | cnt = 0; | |
427 | while (cnt < total) | |
6c8dbf00 OB |
428 | { |
429 | gdImageLine (im_out, 40 + ((xsize - 80) * cnt) / total, ysize - 20, | |
430 | 40 + ((xsize - 80) * cnt) / total, ysize - 15, blue); | |
431 | cnt += MAX (1, total / 20); | |
432 | } | |
c8f3e6db | 433 | gdImageLine (im_out, xsize - 40, ysize - 20, xsize - 40, ysize - 15, |
6c8dbf00 | 434 | blue); |
c8f3e6db UD |
435 | } |
436 | else | |
437 | { | |
438 | uint64_t next_tick = MAX (1, total / 20); | |
439 | size_t last_xpos = 40; | |
440 | ||
441 | gdImageString (im_out, gdFontSmall, 40 + (xsize - 39 * 6 - 80) / 2, | |
6c8dbf00 OB |
442 | ysize - 12, |
443 | (unsigned char *) " \ | |
c8f3e6db UD |
444 | # memory handling function calls / time", blue); |
445 | ||
446 | for (cnt = 0; cnt < 20; cnt += 2) | |
6c8dbf00 OB |
447 | gdImageFilledRectangle (im_out, |
448 | 40 + (cnt * (xsize - 80)) / 20, ysize - 19, | |
449 | 39 + ((cnt + 1) * (xsize - 80)) / 20, | |
450 | ysize - 14, yellow); | |
c8f3e6db UD |
451 | |
452 | last_stack = last_heap = last_total = ysize - 20; | |
453 | for (cnt = 1; cnt <= total; ++cnt) | |
6c8dbf00 OB |
454 | { |
455 | struct entry entry; | |
456 | size_t new[2]; | |
457 | size_t xpos; | |
458 | uint64_t now; | |
459 | ||
b97c5efd | 460 | read_all (fd, &entry, sizeof (entry)); |
6c8dbf00 OB |
461 | |
462 | now = ((uint64_t) entry.time_high) << 32 | entry.time_low; | |
463 | xpos = 40 + ((xsize - 80) * (now - start_time)) / total_time; | |
464 | ||
465 | if (cnt == next_tick) | |
466 | { | |
467 | gdImageLine (im_out, xpos, ysize - 20, xpos, ysize - 15, blue); | |
468 | next_tick += MAX (1, total / 20); | |
469 | } | |
470 | ||
471 | if (also_total && maxsize_heap > 0) | |
472 | { | |
473 | size_t new3; | |
474 | ||
475 | new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) | |
476 | * (entry.heap + entry.stack)) | |
477 | / maxsize_heap); | |
478 | gdImageLine (im_out, last_xpos, last_total, xpos, new3, black); | |
479 | last_total = new3; | |
480 | } | |
481 | ||
482 | if (maxsize_heap > 0) | |
483 | { | |
484 | new[0] = ((ysize - 20) | |
485 | - ((((unsigned long long int) (ysize - 40)) | |
486 | * entry.heap) / maxsize_heap)); | |
487 | gdImageLine (im_out, last_xpos, last_heap, xpos, new[0], red); | |
488 | last_heap = new[0]; | |
489 | } | |
490 | ||
491 | if (maxsize_stack > 0) | |
492 | { | |
493 | new[1] = ((ysize - 20) | |
494 | - ((((unsigned long long int) (ysize - 40)) | |
495 | * entry.stack) / maxsize_stack)); | |
496 | gdImageLine (im_out, last_xpos, last_stack, xpos, new[1], | |
497 | green); | |
498 | last_stack = new[1]; | |
499 | } | |
500 | ||
501 | last_xpos = xpos; | |
502 | } | |
c8f3e6db UD |
503 | } |
504 | ||
505 | /* Write out the result. */ | |
506 | outfile = fopen (outname, "w"); | |
507 | if (outfile == NULL) | |
508 | error (EXIT_FAILURE, errno, "cannot open output file"); | |
509 | ||
510 | gdImagePng (im_out, outfile); | |
511 | ||
512 | fclose (outfile); | |
513 | ||
514 | gdImageDestroy (im_out); | |
515 | ||
bf4de8f3 | 516 | return 0; |
c8f3e6db UD |
517 | } |
518 | ||
519 | ||
520 | /* Handle program arguments. */ | |
521 | static error_t | |
522 | parse_opt (int key, char *arg, struct argp_state *state) | |
523 | { | |
524 | switch (key) | |
525 | { | |
526 | case 'o': | |
527 | outname = arg; | |
528 | break; | |
529 | case 's': | |
530 | string = arg; | |
531 | break; | |
532 | case 't': | |
533 | time_based = 1; | |
534 | break; | |
535 | case 'T': | |
536 | also_total = 1; | |
537 | break; | |
538 | case 'x': | |
539 | xsize = atoi (arg); | |
540 | if (xsize == 0) | |
6c8dbf00 | 541 | xsize = XSIZE; |
c8f3e6db UD |
542 | break; |
543 | case 'y': | |
544 | ysize = atoi (arg); | |
545 | if (ysize == 0) | |
6c8dbf00 | 546 | ysize = XSIZE; |
c8f3e6db UD |
547 | break; |
548 | default: | |
549 | return ARGP_ERR_UNKNOWN; | |
550 | } | |
551 | return 0; | |
552 | } | |
553 | ||
554 | ||
555 | static char * | |
556 | more_help (int key, const char *text, void *input) | |
557 | { | |
8b748aed | 558 | char *tp; |
c8f3e6db UD |
559 | |
560 | switch (key) | |
561 | { | |
562 | case ARGP_KEY_HELP_EXTRA: | |
563 | /* We print some extra information. */ | |
8b748aed | 564 | if (asprintf (&tp, gettext ("\ |
d40eb37a | 565 | For bug reporting instructions, please see:\n\ |
8b748aed | 566 | %s.\n"), REPORT_BUGS_TO) < 0) |
6c8dbf00 OB |
567 | return NULL; |
568 | ||
8b748aed | 569 | return tp; |
6c8dbf00 | 570 | |
c8f3e6db UD |
571 | default: |
572 | break; | |
573 | } | |
574 | return (char *) text; | |
575 | } | |
cbbcaf23 UD |
576 | |
577 | /* Print the version information. */ | |
578 | static void | |
579 | print_version (FILE *stream, struct argp_state *state) | |
580 | { | |
8b748aed | 581 | fprintf (stream, "memusagestat %s%s\n", PKGVERSION, VERSION); |
cbbcaf23 UD |
582 | fprintf (stream, gettext ("\ |
583 | Copyright (C) %s Free Software Foundation, Inc.\n\ | |
584 | This is free software; see the source for copying conditions. There is NO\n\ | |
585 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ | |
1059defe | 586 | "), "2024"); |
cbbcaf23 UD |
587 | fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); |
588 | } |