]>
Commit | Line | Data |
---|---|---|
c8f3e6db | 1 | /* Generate graphic from memory profiling data. |
d614a753 | 2 | Copyright (C) 1998-2020 Free Software Foundation, Inc. |
c8f3e6db UD |
3 | This file is part of the GNU C Library. |
4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. | |
5 | ||
43bc8ac6 | 6 | This program is free software; you can redistribute it and/or modify |
2e2efe65 RM |
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. | |
c8f3e6db | 10 | |
43bc8ac6 | 11 | This program is distributed in the hope that it will be useful, |
c8f3e6db | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
43bc8ac6 UD |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. | |
c8f3e6db | 15 | |
43bc8ac6 | 16 | You should have received a copy of the GNU General Public License |
5a82c748 | 17 | along with this program; if not, see <https://www.gnu.org/licenses/>. */ |
c8f3e6db | 18 | |
19c589d9 UD |
19 | #define _FILE_OFFSET_BITS 64 |
20 | ||
c8f3e6db UD |
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> | |
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. */ | |
192 | read (fd, headent, sizeof (headent)); | |
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 UD |
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 | |
6c8dbf00 | 230 | memory used we have to adapt the maximum. */ |
67854c13 UD |
231 | maxsize_heap = maxsize_total; |
232 | maxsize_stack = maxsize_total; | |
c8f3e6db UD |
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 | ||
19c589d9 UD |
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 | ||
c8f3e6db | 294 | gdImageString (im_out, gdFontSmall, 38, ysize - 14, (unsigned char *) "0", |
6c8dbf00 | 295 | blue); |
0c1c6c6c | 296 | snprintf (buf, sizeof (buf), heap_format, 0); |
c8f3e6db | 297 | gdImageString (im_out, gdFontSmall, maxsize_heap < 1024 ? 32 : 26, |
6c8dbf00 | 298 | ysize - 26, (unsigned char *) buf, red); |
0c1c6c6c | 299 | snprintf (buf, sizeof (buf), stack_format, 0); |
c8f3e6db | 300 | gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26, |
6c8dbf00 | 301 | (unsigned char *) buf, green); |
c8f3e6db UD |
302 | |
303 | if (string != NULL) | |
304 | gdImageString (im_out, gdFontLarge, (xsize - strlen (string) * 8) / 2, | |
6c8dbf00 | 305 | 2, (unsigned char *) string, green); |
c8f3e6db UD |
306 | |
307 | gdImageStringUp (im_out, gdFontSmall, 1, ysize / 2 - 10, | |
6c8dbf00 | 308 | (unsigned char *) "allocated", red); |
c8f3e6db | 309 | gdImageStringUp (im_out, gdFontSmall, 11, ysize / 2 - 10, |
6c8dbf00 | 310 | (unsigned char *) "memory", red); |
c8f3e6db UD |
311 | |
312 | gdImageStringUp (im_out, gdFontSmall, xsize - 39, ysize / 2 - 10, | |
6c8dbf00 | 313 | (unsigned char *) "used", green); |
c8f3e6db | 314 | gdImageStringUp (im_out, gdFontSmall, xsize - 27, ysize / 2 - 10, |
6c8dbf00 | 315 | (unsigned char *) "stack", green); |
c8f3e6db | 316 | |
19c589d9 | 317 | snprintf (buf, sizeof (buf), heap_format, maxsize_heap / heap_scale); |
0c1c6c6c | 318 | gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14, |
6c8dbf00 | 319 | (unsigned char *) buf, red); |
19c589d9 | 320 | snprintf (buf, sizeof (buf), stack_format, maxsize_stack / stack_scale); |
0c1c6c6c | 321 | gdImageString (im_out, gdFontSmall, xsize - 37, 14, |
6c8dbf00 | 322 | (unsigned char *) buf, green); |
c8f3e6db | 323 | |
19c589d9 | 324 | for (line = 1; line <= 3; ++line) |
c8f3e6db | 325 | { |
7da6d9ed | 326 | if (maxsize_heap > 0) |
6c8dbf00 OB |
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 | } | |
7da6d9ed | 337 | else |
6c8dbf00 | 338 | cnt = 0; |
7da6d9ed SP |
339 | |
340 | if (maxsize_stack > 0) | |
6c8dbf00 OB |
341 | cnt2 = (((ysize - 40) * (maxsize_stack / 4 * line / stack_scale)) |
342 | / (maxsize_stack / stack_scale)); | |
7da6d9ed | 343 | else |
6c8dbf00 | 344 | cnt2 = 0; |
7da6d9ed | 345 | |
c8f3e6db | 346 | if (cnt != cnt2) |
6c8dbf00 OB |
347 | gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, |
348 | ysize - 20 - cnt2, green); | |
a04549c1 JM |
349 | snprintf (buf, sizeof (buf), stack_format, |
350 | maxsize_stack / 4 * line / stack_scale); | |
c8f3e6db | 351 | gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, |
6c8dbf00 | 352 | (unsigned char *) buf, green); |
c8f3e6db UD |
353 | } |
354 | ||
d15851ec | 355 | snprintf (buf, sizeof (buf), "%llu", (unsigned long long) total); |
0c1c6c6c | 356 | gdImageString (im_out, gdFontSmall, xsize - 50, ysize - 14, |
6c8dbf00 | 357 | (unsigned char *) buf, blue); |
c8f3e6db UD |
358 | |
359 | if (!time_based) | |
360 | { | |
361 | uint64_t previously = start_time; | |
362 | ||
363 | gdImageString (im_out, gdFontSmall, 40 + (xsize - 32 * 6 - 80) / 2, | |
6c8dbf00 OB |
364 | ysize - 12, |
365 | (unsigned char *) "# memory handling function calls", | |
366 | blue); | |
c8f3e6db UD |
367 | |
368 | ||
369 | last_stack = last_heap = last_total = ysize - 20; | |
370 | for (cnt = 1; cnt <= total; ++cnt) | |
6c8dbf00 OB |
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 | } | |
c8f3e6db UD |
424 | |
425 | cnt = 0; | |
426 | while (cnt < total) | |
6c8dbf00 OB |
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 | } | |
c8f3e6db | 432 | gdImageLine (im_out, xsize - 40, ysize - 20, xsize - 40, ysize - 15, |
6c8dbf00 | 433 | blue); |
c8f3e6db UD |
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, | |
6c8dbf00 OB |
441 | ysize - 12, |
442 | (unsigned char *) " \ | |
c8f3e6db UD |
443 | # memory handling function calls / time", blue); |
444 | ||
445 | for (cnt = 0; cnt < 20; cnt += 2) | |
6c8dbf00 OB |
446 | gdImageFilledRectangle (im_out, |
447 | 40 + (cnt * (xsize - 80)) / 20, ysize - 19, | |
448 | 39 + ((cnt + 1) * (xsize - 80)) / 20, | |
449 | ysize - 14, yellow); | |
c8f3e6db UD |
450 | |
451 | last_stack = last_heap = last_total = ysize - 20; | |
452 | for (cnt = 1; cnt <= total; ++cnt) | |
6c8dbf00 OB |
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 | } | |
c8f3e6db UD |
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 | ||
bf4de8f3 | 515 | return 0; |
c8f3e6db UD |
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) | |
6c8dbf00 | 540 | xsize = XSIZE; |
c8f3e6db UD |
541 | break; |
542 | case 'y': | |
543 | ysize = atoi (arg); | |
544 | if (ysize == 0) | |
6c8dbf00 | 545 | ysize = XSIZE; |
c8f3e6db UD |
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 | { | |
8b748aed | 557 | char *tp; |
c8f3e6db UD |
558 | |
559 | switch (key) | |
560 | { | |
561 | case ARGP_KEY_HELP_EXTRA: | |
562 | /* We print some extra information. */ | |
8b748aed | 563 | if (asprintf (&tp, gettext ("\ |
d40eb37a | 564 | For bug reporting instructions, please see:\n\ |
8b748aed | 565 | %s.\n"), REPORT_BUGS_TO) < 0) |
6c8dbf00 OB |
566 | return NULL; |
567 | ||
8b748aed | 568 | return tp; |
6c8dbf00 | 569 | |
c8f3e6db UD |
570 | default: |
571 | break; | |
572 | } | |
573 | return (char *) text; | |
574 | } | |
cbbcaf23 UD |
575 | |
576 | /* Print the version information. */ | |
577 | static void | |
578 | print_version (FILE *stream, struct argp_state *state) | |
579 | { | |
8b748aed | 580 | fprintf (stream, "memusagestat %s%s\n", PKGVERSION, VERSION); |
cbbcaf23 UD |
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\ | |
5f72f980 | 585 | "), "2020"); |
cbbcaf23 UD |
586 | fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); |
587 | } |