]>
Commit | Line | Data |
---|---|---|
6d031c0b LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
cd3bccaa | 3 | /*** |
6d031c0b | 4 | This file is part of systemd. |
83fdc450 | 5 | |
3c527fd1 | 6 | Copyright (C) 2009-2013 Intel Corporation |
cd3bccaa AK |
7 | |
8 | Authors: | |
9 | Auke Kok <auke-jan.h.kok@intel.com> | |
10 | ||
11 | systemd is free software; you can redistribute it and/or modify it | |
12 | under the terms of the GNU Lesser General Public License as published by | |
13 | the Free Software Foundation; either version 2.1 of the License, or | |
14 | (at your option) any later version. | |
15 | ||
16 | systemd is distributed in the hope that it will be useful, but | |
17 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | Lesser General Public License for more details. | |
20 | ||
21 | You should have received a copy of the GNU Lesser General Public License | |
22 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
23 | ***/ | |
83fdc450 | 24 | |
f1c24fea ZJS |
25 | /*** |
26 | ||
27 | Many thanks to those who contributed ideas and code: | |
28 | - Ziga Mahkovec - Original bootchart author | |
29 | - Anders Norgaard - PyBootchartgui | |
30 | - Michael Meeks - bootchart2 | |
31 | - Scott James Remnant - Ubuntu C-based logger | |
32 | - Arjan van der Ven - for the idea to merge bootgraph.pl functionality | |
33 | ||
34 | ***/ | |
35 | ||
83fdc450 AK |
36 | #include <sys/time.h> |
37 | #include <sys/types.h> | |
38 | #include <sys/resource.h> | |
b823b5e2 | 39 | #include <sys/stat.h> |
83fdc450 AK |
40 | #include <stdio.h> |
41 | #include <signal.h> | |
42 | #include <stdlib.h> | |
43 | #include <string.h> | |
44 | #include <unistd.h> | |
45 | #include <time.h> | |
46 | #include <getopt.h> | |
47 | #include <limits.h> | |
48 | #include <errno.h> | |
b823b5e2 | 49 | #include <fcntl.h> |
f7900e25 | 50 | #include <stdbool.h> |
73f860db | 51 | #include "systemd/sd-journal.h" |
83fdc450 | 52 | |
53f5329f | 53 | #include "util.h" |
a5c32cff | 54 | #include "fileio.h" |
f7900e25 TA |
55 | #include "macro.h" |
56 | #include "conf-parser.h" | |
57 | #include "strxcpyx.h" | |
0e4ffbff | 58 | #include "path-util.h" |
6d031c0b LP |
59 | #include "store.h" |
60 | #include "svg.h" | |
61 | #include "bootchart.h" | |
8dfb6e71 | 62 | #include "list.h" |
83fdc450 AK |
63 | |
64 | double graph_start; | |
65 | double log_start; | |
83fdc450 | 66 | struct ps_struct *ps_first; |
83fdc450 AK |
67 | int pscount; |
68 | int cpus; | |
69 | double interval; | |
1f3523ba | 70 | FILE *of = NULL; |
83fdc450 AK |
71 | int overrun = 0; |
72 | static int exiting = 0; | |
1f3523ba | 73 | int sysfd=-1; |
83fdc450 | 74 | |
c7fc641e ZJS |
75 | #define DEFAULT_SAMPLES_LEN 500 |
76 | #define DEFAULT_HZ 25.0 | |
77 | #define DEFAULT_SCALE_X 100.0 /* 100px = 1sec */ | |
78 | #define DEFAULT_SCALE_Y 20.0 /* 16px = 1 process bar */ | |
79 | #define DEFAULT_INIT "/sbin/init" | |
80 | #define DEFAULT_OUTPUT "/run/log" | |
81 | ||
83fdc450 | 82 | /* graph defaults */ |
6d031c0b | 83 | bool arg_entropy = false; |
f7900e25 | 84 | bool initcall = true; |
6d031c0b LP |
85 | bool arg_relative = false; |
86 | bool arg_filter = true; | |
87 | bool arg_show_cmdline = false; | |
49e5b2a9 | 88 | bool arg_show_cgroup = false; |
6d031c0b | 89 | bool arg_pss = false; |
83fdc450 | 90 | int samples; |
c7fc641e ZJS |
91 | int arg_samples_len = DEFAULT_SAMPLES_LEN; /* we record len+1 (1 start sample) */ |
92 | double arg_hz = DEFAULT_HZ; | |
93 | double arg_scale_x = DEFAULT_SCALE_X; | |
94 | double arg_scale_y = DEFAULT_SCALE_Y; | |
8dfb6e71 NC |
95 | static struct list_sample_data *sampledata; |
96 | struct list_sample_data *head; | |
83fdc450 | 97 | |
c7fc641e ZJS |
98 | char arg_init_path[PATH_MAX] = DEFAULT_INIT; |
99 | char arg_output_path[PATH_MAX] = DEFAULT_OUTPUT; | |
83fdc450 | 100 | |
6d031c0b | 101 | static void signal_handler(int sig) { |
28989b63 TA |
102 | if (sig++) |
103 | sig--; | |
104 | exiting = 1; | |
83fdc450 AK |
105 | } |
106 | ||
4cd5f79d | 107 | #define BOOTCHART_CONF "/etc/systemd/bootchart.conf" |
f7900e25 | 108 | |
c4d58b0b AK |
109 | #define BOOTCHART_MAX (16*1024*1024) |
110 | ||
4cd5f79d ZJS |
111 | static void parse_conf(void) { |
112 | char *init = NULL, *output = NULL; | |
f7900e25 | 113 | const ConfigTableItem items[] = { |
6d031c0b LP |
114 | { "Bootchart", "Samples", config_parse_int, 0, &arg_samples_len }, |
115 | { "Bootchart", "Frequency", config_parse_double, 0, &arg_hz }, | |
116 | { "Bootchart", "Relative", config_parse_bool, 0, &arg_relative }, | |
117 | { "Bootchart", "Filter", config_parse_bool, 0, &arg_filter }, | |
118 | { "Bootchart", "Output", config_parse_path, 0, &output }, | |
119 | { "Bootchart", "Init", config_parse_path, 0, &init }, | |
120 | { "Bootchart", "PlotMemoryUsage", config_parse_bool, 0, &arg_pss }, | |
121 | { "Bootchart", "PlotEntropyGraph", config_parse_bool, 0, &arg_entropy }, | |
122 | { "Bootchart", "ScaleX", config_parse_double, 0, &arg_scale_x }, | |
123 | { "Bootchart", "ScaleY", config_parse_double, 0, &arg_scale_y }, | |
49e5b2a9 | 124 | { "Bootchart", "ControlGroup", config_parse_bool, 0, &arg_show_cgroup }, |
f7900e25 TA |
125 | { NULL, NULL, NULL, 0, NULL } |
126 | }; | |
28989b63 | 127 | |
36f822c4 ZJS |
128 | config_parse(NULL, BOOTCHART_CONF, NULL, |
129 | NULL, | |
130 | config_item_table_lookup, items, | |
131 | true, false, true, NULL); | |
4cd5f79d ZJS |
132 | |
133 | if (init != NULL) | |
134 | strscpy(arg_init_path, sizeof(arg_init_path), init); | |
135 | if (output != NULL) | |
136 | strscpy(arg_output_path, sizeof(arg_output_path), output); | |
137 | } | |
138 | ||
c7fc641e ZJS |
139 | static void help(void) { |
140 | fprintf(stdout, | |
141 | "Usage: %s [OPTIONS]\n\n" | |
142 | "Options:\n" | |
143 | " -r, --rel Record time relative to recording\n" | |
144 | " -f, --freq=FREQ Sample frequency [%g]\n" | |
145 | " -n, --samples=N Stop sampling at [%d] samples\n" | |
146 | " -x, --scale-x=N Scale the graph horizontally [%g] \n" | |
147 | " -y, --scale-y=N Scale the graph vertically [%g] \n" | |
148 | " -p, --pss Enable PSS graph (CPU intensive)\n" | |
149 | " -e, --entropy Enable the entropy_avail graph\n" | |
150 | " -o, --output=PATH Path to output files [%s]\n" | |
151 | " -i, --init=PATH Path to init executable [%s]\n" | |
152 | " -F, --no-filter Disable filtering of unimportant or ephemeral processes\n" | |
153 | " -C, --cmdline Display full command lines with arguments\n" | |
154 | " -c, --control-group Display process control group\n" | |
155 | " -h, --help Display this message\n\n" | |
156 | "See bootchart.conf for more information.\n", | |
157 | program_invocation_short_name, | |
158 | DEFAULT_HZ, | |
159 | DEFAULT_SAMPLES_LEN, | |
160 | DEFAULT_SCALE_X, | |
161 | DEFAULT_SCALE_Y, | |
162 | DEFAULT_OUTPUT, | |
163 | DEFAULT_INIT); | |
164 | } | |
165 | ||
601185b4 | 166 | static int parse_argv(int argc, char *argv[]) { |
5d459d6b ZJS |
167 | static const struct option options[] = { |
168 | {"rel", no_argument, NULL, 'r'}, | |
169 | {"freq", required_argument, NULL, 'f'}, | |
170 | {"samples", required_argument, NULL, 'n'}, | |
171 | {"pss", no_argument, NULL, 'p'}, | |
172 | {"output", required_argument, NULL, 'o'}, | |
173 | {"init", required_argument, NULL, 'i'}, | |
174 | {"no-filter", no_argument, NULL, 'F'}, | |
175 | {"cmdline", no_argument, NULL, 'C'}, | |
176 | {"control-group", no_argument, NULL, 'c'}, | |
177 | {"help", no_argument, NULL, 'h'}, | |
178 | {"scale-x", required_argument, NULL, 'x'}, | |
179 | {"scale-y", required_argument, NULL, 'y'}, | |
180 | {"entropy", no_argument, NULL, 'e'}, | |
181 | {} | |
4cd5f79d | 182 | }; |
601185b4 | 183 | int c, r; |
4cd5f79d | 184 | |
601185b4 ZJS |
185 | if (getpid() == 1) |
186 | opterr = 0; | |
4cd5f79d | 187 | |
601185b4 | 188 | while ((c = getopt_long(argc, argv, "erpf:n:o:i:FCchx:y:", options, NULL)) >= 0) |
4cd5f79d | 189 | switch (c) { |
601185b4 | 190 | |
28989b63 | 191 | case 'r': |
6d031c0b | 192 | arg_relative = true; |
28989b63 TA |
193 | break; |
194 | case 'f': | |
6d031c0b | 195 | r = safe_atod(optarg, &arg_hz); |
547ba5a9 | 196 | if (r < 0) |
c33b3297 MS |
197 | log_warning_errno(r, "failed to parse --freq/-f argument '%s': %m", |
198 | optarg); | |
28989b63 TA |
199 | break; |
200 | case 'F': | |
6d031c0b | 201 | arg_filter = false; |
28989b63 | 202 | break; |
e90f9fa4 | 203 | case 'C': |
6d031c0b | 204 | arg_show_cmdline = true; |
e90f9fa4 | 205 | break; |
49e5b2a9 WC |
206 | case 'c': |
207 | arg_show_cgroup = true; | |
208 | break; | |
28989b63 | 209 | case 'n': |
6d031c0b | 210 | r = safe_atoi(optarg, &arg_samples_len); |
547ba5a9 | 211 | if (r < 0) |
c33b3297 MS |
212 | log_warning_errno(r, "failed to parse --samples/-n argument '%s': %m", |
213 | optarg); | |
28989b63 TA |
214 | break; |
215 | case 'o': | |
0e4ffbff | 216 | path_kill_slashes(optarg); |
6d031c0b | 217 | strscpy(arg_output_path, sizeof(arg_output_path), optarg); |
28989b63 TA |
218 | break; |
219 | case 'i': | |
0e4ffbff | 220 | path_kill_slashes(optarg); |
6d031c0b | 221 | strscpy(arg_init_path, sizeof(arg_init_path), optarg); |
28989b63 TA |
222 | break; |
223 | case 'p': | |
6d031c0b | 224 | arg_pss = true; |
28989b63 TA |
225 | break; |
226 | case 'x': | |
6d031c0b | 227 | r = safe_atod(optarg, &arg_scale_x); |
547ba5a9 | 228 | if (r < 0) |
c33b3297 MS |
229 | log_warning_errno(r, "failed to parse --scale-x/-x argument '%s': %m", |
230 | optarg); | |
28989b63 TA |
231 | break; |
232 | case 'y': | |
6d031c0b | 233 | r = safe_atod(optarg, &arg_scale_y); |
547ba5a9 | 234 | if (r < 0) |
c33b3297 MS |
235 | log_warning_errno(r, "failed to parse --scale-y/-y argument '%s': %m", |
236 | optarg); | |
28989b63 TA |
237 | break; |
238 | case 'e': | |
6d031c0b | 239 | arg_entropy = true; |
28989b63 TA |
240 | break; |
241 | case 'h': | |
c7fc641e | 242 | help(); |
601185b4 ZJS |
243 | return 0; |
244 | case '?': | |
245 | if (getpid() != 1) | |
246 | return -EINVAL; | |
247 | else | |
248 | return 0; | |
28989b63 | 249 | default: |
601185b4 | 250 | assert_not_reached("Unhandled option code."); |
28989b63 | 251 | } |
28989b63 | 252 | |
601185b4 ZJS |
253 | if (arg_hz <= 0) { |
254 | log_error("Frequency needs to be > 0"); | |
4cd5f79d | 255 | return -EINVAL; |
28989b63 TA |
256 | } |
257 | ||
601185b4 | 258 | return 1; |
4cd5f79d ZJS |
259 | } |
260 | ||
f168c273 | 261 | static void do_journal_append(char *file) { |
c4d58b0b AK |
262 | struct iovec iovec[5]; |
263 | int r, f, j = 0; | |
264 | ssize_t n; | |
7fd1b19b | 265 | _cleanup_free_ char *bootchart_file = NULL, *bootchart_message = NULL, |
c4d58b0b AK |
266 | *p = NULL; |
267 | ||
268 | bootchart_file = strappend("BOOTCHART_FILE=", file); | |
269 | if (bootchart_file) | |
270 | IOVEC_SET_STRING(iovec[j++], bootchart_file); | |
271 | ||
272 | IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=9f26aa562cf440c2b16c773d0479b518"); | |
273 | IOVEC_SET_STRING(iovec[j++], "PRIORITY=7"); | |
274 | bootchart_message = strjoin("MESSAGE=Bootchart created: ", file, NULL); | |
275 | if (bootchart_message) | |
276 | IOVEC_SET_STRING(iovec[j++], bootchart_message); | |
277 | ||
278 | p = malloc(9 + BOOTCHART_MAX); | |
279 | if (!p) { | |
872c8faa | 280 | log_oom(); |
c4d58b0b AK |
281 | return; |
282 | } | |
283 | ||
284 | memcpy(p, "BOOTCHART=", 10); | |
285 | ||
c8a202b7 | 286 | f = open(file, O_RDONLY|O_CLOEXEC); |
c4d58b0b | 287 | if (f < 0) { |
56f64d95 | 288 | log_error_errno(errno, "Failed to read bootchart data: %m"); |
c4d58b0b AK |
289 | return; |
290 | } | |
291 | n = loop_read(f, p + 10, BOOTCHART_MAX, false); | |
292 | if (n < 0) { | |
da927ba9 | 293 | log_error_errno(n, "Failed to read bootchart data: %m"); |
c4d58b0b AK |
294 | close(f); |
295 | return; | |
296 | } | |
297 | close(f); | |
298 | ||
299 | iovec[j].iov_base = p; | |
300 | iovec[j].iov_len = 10 + n; | |
301 | j++; | |
302 | ||
303 | r = sd_journal_sendv(iovec, j); | |
304 | if (r < 0) | |
da927ba9 | 305 | log_error_errno(r, "Failed to send bootchart: %m"); |
c4d58b0b AK |
306 | } |
307 | ||
4cd5f79d ZJS |
308 | int main(int argc, char *argv[]) { |
309 | _cleanup_free_ char *build = NULL; | |
1c633045 ZJS |
310 | struct sigaction sig = { |
311 | .sa_handler = signal_handler, | |
312 | }; | |
4cd5f79d ZJS |
313 | struct ps_struct *ps; |
314 | char output_file[PATH_MAX]; | |
315 | char datestr[200]; | |
316 | time_t t = 0; | |
317 | int r; | |
318 | struct rlimit rlim; | |
c358d728 | 319 | bool has_procfs = false; |
4cd5f79d ZJS |
320 | |
321 | parse_conf(); | |
322 | ||
601185b4 ZJS |
323 | r = parse_argv(argc, argv); |
324 | if (r <= 0) | |
325 | return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; | |
4cd5f79d | 326 | |
28989b63 | 327 | /* |
547ba5a9 | 328 | * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then |
28989b63 TA |
329 | * fork: |
330 | * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1 | |
331 | * - child logs data | |
332 | */ | |
333 | if (getpid() == 1) { | |
334 | if (fork()) { | |
335 | /* parent */ | |
6d031c0b | 336 | execl(arg_init_path, arg_init_path, NULL); |
28989b63 TA |
337 | } |
338 | } | |
0e4ffbff | 339 | argv[0][0] = '@'; |
28989b63 | 340 | |
361514ac LP |
341 | rlim.rlim_cur = 4096; |
342 | rlim.rlim_max = 4096; | |
343 | (void) setrlimit(RLIMIT_NOFILE, &rlim); | |
344 | ||
28989b63 | 345 | /* start with empty ps LL */ |
955d98c9 | 346 | ps_first = new0(struct ps_struct, 1); |
28989b63 | 347 | if (!ps_first) { |
955d98c9 LP |
348 | log_oom(); |
349 | return EXIT_FAILURE; | |
28989b63 | 350 | } |
28989b63 TA |
351 | |
352 | /* handle TERM/INT nicely */ | |
28989b63 TA |
353 | sigaction(SIGHUP, &sig, NULL); |
354 | ||
6d031c0b | 355 | interval = (1.0 / arg_hz) * 1000000000.0; |
28989b63 TA |
356 | |
357 | log_uptime(); | |
358 | ||
9a6f36c0 KZ |
359 | if (graph_start < 0.0) { |
360 | fprintf(stderr, | |
361 | "Failed to setup graph start time.\n\nThe system uptime " | |
362 | "probably includes time that the system was suspended. " | |
363 | "Use --rel to bypass this issue.\n"); | |
364 | exit (EXIT_FAILURE); | |
365 | } | |
366 | ||
c358d728 KZ |
367 | has_procfs = access("/proc/vmstat", F_OK) == 0; |
368 | ||
71fda00f | 369 | LIST_HEAD_INIT(head); |
8dfb6e71 | 370 | |
28989b63 | 371 | /* main program loop */ |
522cd7f1 | 372 | for (samples = 0; !exiting && samples < arg_samples_len; samples++) { |
28989b63 TA |
373 | int res; |
374 | double sample_stop; | |
375 | struct timespec req; | |
376 | time_t newint_s; | |
377 | long newint_ns; | |
378 | double elapsed; | |
379 | double timeleft; | |
380 | ||
8dfb6e71 NC |
381 | sampledata = new0(struct list_sample_data, 1); |
382 | if (sampledata == NULL) { | |
4155f7d4 LP |
383 | log_oom(); |
384 | return EXIT_FAILURE; | |
8dfb6e71 NC |
385 | } |
386 | ||
387 | sampledata->sampletime = gettime_ns(); | |
388 | sampledata->counter = samples; | |
28989b63 | 389 | |
6d031c0b | 390 | if (!of && (access(arg_output_path, R_OK|W_OK|X_OK) == 0)) { |
f2f85884 | 391 | t = time(NULL); |
e931d3f4 TA |
392 | r = strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t)); |
393 | assert_se(r > 0); | |
394 | ||
6d031c0b | 395 | snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr); |
c8a202b7 | 396 | of = fopen(output_file, "we"); |
f2f85884 HH |
397 | } |
398 | ||
6d031c0b | 399 | if (sysfd < 0) |
c8a202b7 | 400 | sysfd = open("/sys", O_RDONLY|O_CLOEXEC); |
f2f85884 | 401 | |
5ae4d543 LP |
402 | if (!build) { |
403 | if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &build, NULL) == -ENOENT) | |
404 | parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &build, NULL); | |
405 | } | |
e93450c6 | 406 | |
c358d728 | 407 | if (has_procfs) |
8dfb6e71 | 408 | log_sample(samples, &sampledata); |
c358d728 KZ |
409 | else |
410 | /* wait for /proc to become available, discarding samples */ | |
411 | has_procfs = access("/proc/vmstat", F_OK) == 0; | |
28989b63 TA |
412 | |
413 | sample_stop = gettime_ns(); | |
414 | ||
8dfb6e71 | 415 | elapsed = (sample_stop - sampledata->sampletime) * 1000000000.0; |
28989b63 TA |
416 | timeleft = interval - elapsed; |
417 | ||
418 | newint_s = (time_t)(timeleft / 1000000000.0); | |
419 | newint_ns = (long)(timeleft - (newint_s * 1000000000.0)); | |
420 | ||
421 | /* | |
422 | * check if we have not consumed our entire timeslice. If we | |
423 | * do, don't sleep and take a new sample right away. | |
424 | * we'll lose all the missed samples and overrun our total | |
425 | * time | |
426 | */ | |
522cd7f1 | 427 | if (newint_ns > 0 || newint_s > 0) { |
28989b63 TA |
428 | req.tv_sec = newint_s; |
429 | req.tv_nsec = newint_ns; | |
430 | ||
431 | res = nanosleep(&req, NULL); | |
432 | if (res) { | |
433 | if (errno == EINTR) { | |
434 | /* caught signal, probably HUP! */ | |
435 | break; | |
436 | } | |
56f64d95 | 437 | log_error_errno(errno, "nanosleep() failed: %m"); |
955d98c9 | 438 | exit(EXIT_FAILURE); |
28989b63 TA |
439 | } |
440 | } else { | |
441 | overrun++; | |
442 | /* calculate how many samples we lost and scrap them */ | |
522cd7f1 | 443 | arg_samples_len -= (int)(newint_ns / interval); |
28989b63 | 444 | } |
71fda00f | 445 | LIST_PREPEND(link, head, sampledata); |
28989b63 TA |
446 | } |
447 | ||
448 | /* do some cleanup, close fd's */ | |
449 | ps = ps_first; | |
450 | while (ps->next_ps) { | |
451 | ps = ps->next_ps; | |
452 | if (ps->schedstat) | |
453 | close(ps->schedstat); | |
454 | if (ps->sched) | |
455 | close(ps->sched); | |
456 | if (ps->smaps) | |
457 | fclose(ps->smaps); | |
458 | } | |
28989b63 | 459 | |
f2f85884 HH |
460 | if (!of) { |
461 | t = time(NULL); | |
e931d3f4 TA |
462 | r = strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t)); |
463 | assert_se(r > 0); | |
464 | ||
6d031c0b | 465 | snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr); |
c8a202b7 | 466 | of = fopen(output_file, "we"); |
f2f85884 | 467 | } |
28989b63 | 468 | |
28989b63 | 469 | if (!of) { |
547ba5a9 | 470 | fprintf(stderr, "opening output file '%s': %m\n", output_file); |
28989b63 TA |
471 | exit (EXIT_FAILURE); |
472 | } | |
473 | ||
1c92ff85 | 474 | svg_do(strna(build)); |
28989b63 | 475 | |
547ba5a9 | 476 | fprintf(stderr, "systemd-bootchart wrote %s\n", output_file); |
6d031c0b | 477 | |
c4d58b0b AK |
478 | do_journal_append(output_file); |
479 | ||
6d031c0b LP |
480 | if (of) |
481 | fclose(of); | |
28989b63 | 482 | |
b823b5e2 | 483 | closedir(proc); |
6d031c0b LP |
484 | if (sysfd >= 0) |
485 | close(sysfd); | |
b823b5e2 | 486 | |
28989b63 | 487 | /* nitpic cleanups */ |
8dfb6e71 | 488 | ps = ps_first->next_ps; |
28989b63 | 489 | while (ps->next_ps) { |
8dfb6e71 NC |
490 | struct ps_struct *old; |
491 | ||
492 | old = ps; | |
493 | old->sample = ps->first; | |
28989b63 | 494 | ps = ps->next_ps; |
8dfb6e71 NC |
495 | while (old->sample->next) { |
496 | struct ps_sched_struct *oldsample = old->sample; | |
497 | ||
498 | old->sample = old->sample->next; | |
499 | free(oldsample); | |
500 | } | |
49e5b2a9 | 501 | free(old->cgroup); |
28989b63 TA |
502 | free(old->sample); |
503 | free(old); | |
504 | } | |
49e5b2a9 | 505 | free(ps->cgroup); |
28989b63 TA |
506 | free(ps->sample); |
507 | free(ps); | |
508 | ||
8dfb6e71 NC |
509 | sampledata = head; |
510 | while (sampledata->link_prev) { | |
511 | struct list_sample_data *old_sampledata = sampledata; | |
512 | sampledata = sampledata->link_prev; | |
513 | free(old_sampledata); | |
514 | } | |
515 | free(sampledata); | |
28989b63 TA |
516 | /* don't complain when overrun once, happens most commonly on 1st sample */ |
517 | if (overrun > 1) | |
547ba5a9 | 518 | fprintf(stderr, "systemd-boochart: Warning: sample time overrun %i times\n", overrun); |
28989b63 TA |
519 | |
520 | return 0; | |
83fdc450 | 521 | } |