]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/bootchart/store.c
sd-device: don't use alloca() within loops
[thirdparty/systemd.git] / src / bootchart / store.c
CommitLineData
6d031c0b
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
cd3bccaa 3/***
6d031c0b 4 This file is part of systemd.
cd3bccaa 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
83fdc450
AK
25#include <unistd.h>
26#include <stdlib.h>
27#include <limits.h>
83fdc450
AK
28#include <stdio.h>
29#include <string.h>
30#include <dirent.h>
31#include <fcntl.h>
32#include <time.h>
33
53f5329f 34#include "util.h"
ece74070 35#include "time-util.h"
c309a713 36#include "strxcpyx.h"
6d031c0b
LP
37#include "store.h"
38#include "bootchart.h"
49e5b2a9 39#include "cgroup-util.h"
83fdc450
AK
40
41/*
42 * Alloc a static 4k buffer for stdio - primarily used to increase
43 * PSS buffering from the default 1k stdin buffer to reduce
44 * read() overhead.
45 */
46static char smaps_buf[4096];
8dfb6e71 47static int skip = 0;
83fdc450 48
6d031c0b 49double gettime_ns(void) {
2c408fbf 50 struct timespec n;
83fdc450 51
2c408fbf 52 clock_gettime(CLOCK_MONOTONIC, &n);
83fdc450 53
ece74070 54 return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
83fdc450
AK
55}
56
6d031c0b 57static char *bufgetline(char *buf) {
28989b63 58 char *c;
83fdc450 59
28989b63
TA
60 if (!buf)
61 return NULL;
83fdc450 62
28989b63
TA
63 c = strchr(buf, '\n');
64 if (c)
65 c++;
af672f03 66
28989b63 67 return c;
83fdc450
AK
68}
69
f9178132 70static int pid_cmdline_strscpy(int procfd, char *buffer, size_t buf_len, int pid) {
6d031c0b 71 char filename[PATH_MAX];
af672f03 72 _cleanup_close_ int fd = -1;
6d031c0b 73 ssize_t n;
e90f9fa4 74
6d031c0b 75 sprintf(filename, "%d/cmdline", pid);
af672f03 76 fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
6d031c0b
LP
77 if (fd < 0)
78 return -errno;
e90f9fa4 79
6d031c0b 80 n = read(fd, buffer, buf_len-1);
e90f9fa4
HH
81 if (n > 0) {
82 int i;
83 for (i = 0; i < n; i++)
84 if (buffer[i] == '\0')
85 buffer[i] = ' ';
86 buffer[n] = '\0';
87 }
af672f03 88
6d031c0b 89 return 0;
e90f9fa4 90}
83fdc450 91
1f2ecb03
DM
92int log_sample(DIR *proc,
93 int sample,
94 struct ps_struct *ps_first,
95 struct list_sample_data **ptr,
96 int *pscount,
97 int *cpus) {
98
9964a9eb
AS
99 static int vmstat = -1;
100 static int schedstat = -1;
522cd7f1 101 char buf[4096];
28989b63
TA
102 char key[256];
103 char val[256];
104 char rt[256];
105 char wt[256];
106 char *m;
107 int c;
108 int p;
109 int mod;
c87664fe 110 static int e_fd = -1;
28989b63
TA
111 ssize_t s;
112 ssize_t n;
113 struct dirent *ent;
f2f85884 114 int fd;
8dfb6e71
NC
115 struct list_sample_data *sampledata;
116 struct ps_sched_struct *ps_prev = NULL;
f9178132 117 int procfd;
8dfb6e71 118
8dfb6e71 119 sampledata = *ptr;
f2f85884 120
f9178132
DM
121 procfd = dirfd(proc);
122 if (procfd < 0)
123 return -errno;
28989b63 124
9964a9eb 125 if (vmstat < 0) {
28989b63 126 /* block stuff */
af672f03
DM
127 vmstat = openat(procfd, "vmstat", O_RDONLY|O_CLOEXEC);
128 if (vmstat < 0)
03995863 129 return log_error_errno(errno, "Failed to open /proc/vmstat: %m");
28989b63
TA
130 }
131
132 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
133 if (n <= 0) {
34a4071e
DM
134 vmstat = safe_close(vmstat);
135 if (n < 0)
136 return -errno;
137 return -ENODATA;
28989b63 138 }
af672f03 139
28989b63
TA
140 buf[n] = '\0';
141
142 m = buf;
143 while (m) {
144 if (sscanf(m, "%s %s", key, val) < 2)
145 goto vmstat_next;
53f5329f 146 if (streq(key, "pgpgin"))
8dfb6e71 147 sampledata->blockstat.bi = atoi(val);
53f5329f 148 if (streq(key, "pgpgout")) {
8dfb6e71 149 sampledata->blockstat.bo = atoi(val);
28989b63
TA
150 break;
151 }
83fdc450 152vmstat_next:
28989b63
TA
153 m = bufgetline(m);
154 if (!m)
155 break;
156 }
157
9964a9eb 158 if (schedstat < 0) {
28989b63 159 /* overall CPU utilization */
af672f03
DM
160 schedstat = openat(procfd, "schedstat", O_RDONLY|O_CLOEXEC);
161 if (schedstat < 0)
34a4071e 162 return log_error_errno(errno, "Failed to open /proc/schedstat (requires CONFIG_SCHEDSTATS=y in kernel config): %m");
28989b63
TA
163 }
164
165 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
166 if (n <= 0) {
34a4071e
DM
167 schedstat = safe_close(schedstat);
168 if (n < 0)
169 return -errno;
170 return -ENODATA;
28989b63 171 }
af672f03 172
28989b63
TA
173 buf[n] = '\0';
174
175 m = buf;
176 while (m) {
9bcf7507
TA
177 int r;
178
28989b63
TA
179 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
180 goto schedstat_next;
181
182 if (strstr(key, "cpu")) {
9bcf7507 183 r = safe_atoi((const char*)(key+3), &c);
c119700c 184 if (r < 0 || c > MAXCPUS -1)
28989b63
TA
185 /* Oops, we only have room for MAXCPUS data */
186 break;
8dfb6e71
NC
187 sampledata->runtime[c] = atoll(rt);
188 sampledata->waittime[c] = atoll(wt);
28989b63 189
1f2ecb03
DM
190 if (c == *cpus)
191 *cpus = c + 1;
28989b63 192 }
83fdc450 193schedstat_next:
28989b63
TA
194 m = bufgetline(m);
195 if (!m)
196 break;
197 }
198
6d031c0b 199 if (arg_entropy) {
c87664fe 200 if (e_fd < 0) {
af672f03
DM
201 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY|O_CLOEXEC);
202 if (e_fd < 0)
34a4071e 203 return log_error_errno(errno, "Failed to open /proc/sys/kernel/random/entropy_avail: %m");
28989b63
TA
204 }
205
c87664fe
AS
206 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
207 if (n <= 0) {
af672f03 208 e_fd = safe_close(e_fd);
c87664fe
AS
209 } else {
210 buf[n] = '\0';
211 sampledata->entropy_avail = atoi(buf);
28989b63
TA
212 }
213 }
214
28989b63
TA
215 while ((ent = readdir(proc)) != NULL) {
216 char filename[PATH_MAX];
217 int pid;
218 struct ps_struct *ps;
219
220 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
221 continue;
222
223 pid = atoi(ent->d_name);
224
225 if (pid >= MAXPIDS)
226 continue;
227
228 ps = ps_first;
229 while (ps->next_ps) {
230 ps = ps->next_ps;
231 if (ps->pid == pid)
232 break;
233 }
234
235 /* end of our LL? then append a new record */
236 if (ps->pid != pid) {
7fd1b19b 237 _cleanup_fclose_ FILE *st = NULL;
28989b63
TA
238 char t[32];
239 struct ps_struct *parent;
e10f3c43 240 int r;
28989b63 241
955d98c9 242 ps->next_ps = new0(struct ps_struct, 1);
34a4071e
DM
243 if (!ps->next_ps)
244 return log_oom();
245
28989b63
TA
246 ps = ps->next_ps;
247 ps->pid = pid;
9964a9eb
AS
248 ps->sched = -1;
249 ps->schedstat = -1;
28989b63 250
955d98c9 251 ps->sample = new0(struct ps_sched_struct, 1);
34a4071e
DM
252 if (!ps->sample)
253 return log_oom();
254
8dfb6e71 255 ps->sample->sampledata = sampledata;
28989b63 256
1f2ecb03 257 (*pscount)++;
28989b63
TA
258
259 /* mark our first sample */
306e6650 260 ps->first = ps->last = ps->sample;
8dfb6e71
NC
261 ps->sample->runtime = atoll(rt);
262 ps->sample->waittime = atoll(wt);
28989b63
TA
263
264 /* get name, start time */
9964a9eb 265 if (ps->sched < 0) {
f2f85884 266 sprintf(filename, "%d/sched", pid);
af672f03
DM
267 ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
268 if (ps->sched < 0)
28989b63
TA
269 continue;
270 }
271
272 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
273 if (s <= 0) {
af672f03 274 ps->sched = safe_close(ps->sched);
28989b63
TA
275 continue;
276 }
ef2648c1 277 buf[s] = '\0';
28989b63
TA
278
279 if (!sscanf(buf, "%s %*s %*s", key))
280 continue;
281
c309a713 282 strscpy(ps->name, sizeof(ps->name), key);
e90f9fa4
HH
283
284 /* cmdline */
6d031c0b 285 if (arg_show_cmdline)
f9178132 286 pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);
e90f9fa4 287
28989b63
TA
288 /* discard line 2 */
289 m = bufgetline(buf);
290 if (!m)
291 continue;
292
293 m = bufgetline(m);
294 if (!m)
295 continue;
296
297 if (!sscanf(m, "%*s %*s %s", t))
298 continue;
299
e10f3c43
TG
300 r = safe_atod(t, &ps->starttime);
301 if (r < 0)
302 continue;
303
304 ps->starttime /= 1000.0;
28989b63 305
49e5b2a9
WC
306 if (arg_show_cgroup)
307 /* if this fails, that's OK */
308 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER,
309 ps->pid, &ps->cgroup);
310
28989b63 311 /* ppid */
f2f85884 312 sprintf(filename, "%d/stat", pid);
af672f03
DM
313 fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
314 if (fd < 0)
58ec01b3 315 continue;
af672f03
DM
316
317 st = fdopen(fd, "re");
58ec01b3
AS
318 if (!st) {
319 close(fd);
28989b63 320 continue;
58ec01b3 321 }
af672f03
DM
322
323 if (!fscanf(st, "%*s %*s %*s %i", &p))
28989b63 324 continue;
af672f03 325
28989b63
TA
326 ps->ppid = p;
327
328 /*
329 * setup child pointers
330 *
331 * these are used to paint the tree coherently later
332 * each parent has a LL of children, and a LL of siblings
333 */
334 if (pid == 1)
335 continue; /* nothing to do for init atm */
336
337 /* kthreadd has ppid=0, which breaks our tree ordering */
338 if (ps->ppid == 0)
339 ps->ppid = 1;
340
341 parent = ps_first;
342 while ((parent->next_ps && parent->pid != ps->ppid))
343 parent = parent->next_ps;
344
226b735a 345 if (parent->pid != ps->ppid) {
28989b63
TA
346 /* orphan */
347 ps->ppid = 1;
348 parent = ps_first->next_ps;
349 }
350
351 ps->parent = parent;
352
353 if (!parent->children) {
354 /* it's the first child */
355 parent->children = ps;
356 } else {
357 /* walk all children and append */
358 struct ps_struct *children;
359 children = parent->children;
360 while (children->next)
361 children = children->next;
af672f03 362
28989b63
TA
363 children->next = ps;
364 }
365 }
366
367 /* else -> found pid, append data in ps */
368
369 /* below here is all continuous logging parts - we get here on every
370 * iteration */
371
372 /* rt, wt */
9964a9eb 373 if (ps->schedstat < 0) {
f2f85884 374 sprintf(filename, "%d/schedstat", pid);
af672f03
DM
375 ps->schedstat = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
376 if (ps->schedstat < 0)
28989b63
TA
377 continue;
378 }
af672f03 379
ef2648c1
LN
380 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
381 if (s <= 0) {
28989b63
TA
382 /* clean up our file descriptors - assume that the process exited */
383 close(ps->schedstat);
9964a9eb 384 ps->schedstat = -1;
af672f03 385 ps->sched = safe_close(ps->sched);
28989b63
TA
386 continue;
387 }
af672f03 388
ef2648c1
LN
389 buf[s] = '\0';
390
28989b63
TA
391 if (!sscanf(buf, "%s %s %*s", rt, wt))
392 continue;
393
955d98c9 394 ps->sample->next = new0(struct ps_sched_struct, 1);
34a4071e
DM
395 if (!ps->sample->next)
396 return log_oom();
397
8dfb6e71
NC
398 ps->sample->next->prev = ps->sample;
399 ps->sample = ps->sample->next;
400 ps->last = ps->sample;
401 ps->sample->runtime = atoll(rt);
402 ps->sample->waittime = atoll(wt);
403 ps->sample->sampledata = sampledata;
404 ps->sample->ps_new = ps;
af672f03 405 if (ps_prev)
8dfb6e71 406 ps_prev->cross = ps->sample;
af672f03 407
8dfb6e71
NC
408 ps_prev = ps->sample;
409 ps->total = (ps->last->runtime - ps->first->runtime)
410 / 1000000000.0;
28989b63 411
6d031c0b 412 if (!arg_pss)
28989b63 413 goto catch_rename;
8dfb6e71 414
28989b63
TA
415 /* Pss */
416 if (!ps->smaps) {
f2f85884 417 sprintf(filename, "%d/smaps", pid);
af672f03
DM
418 fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
419 if (fd < 0)
58ec01b3 420 continue;
af672f03 421 ps->smaps = fdopen(fd, "re");
58ec01b3
AS
422 if (!ps->smaps) {
423 close(fd);
28989b63 424 continue;
58ec01b3 425 }
28989b63 426 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
af672f03 427 } else {
8dfb6e71
NC
428 rewind(ps->smaps);
429 }
af672f03 430
8dfb6e71
NC
431 /* test to see if we need to skip another field */
432 if (skip == 0) {
433 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
434 continue;
435 }
436 if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
437 continue;
438 }
439 if (buf[392] == 'V') {
440 skip = 2;
441 }
442 else {
443 skip = 1;
444 }
28989b63
TA
445 rewind(ps->smaps);
446 }
af672f03 447
28989b63
TA
448 while (1) {
449 int pss_kb;
450
8dfb6e71
NC
451 /* skip one line, this contains the object mapped. */
452 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
28989b63 453 break;
8dfb6e71 454 }
28989b63 455 /* then there's a 28 char 14 line block */
8dfb6e71 456 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
28989b63 457 break;
8dfb6e71 458 }
28989b63 459 pss_kb = atoi(&buf[61]);
8dfb6e71 460 ps->sample->pss += pss_kb;
28989b63 461
8dfb6e71
NC
462 /* skip one more line if this is a newer kernel */
463 if (skip == 2) {
464 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
465 break;
466 }
467 }
af672f03 468
8dfb6e71
NC
469 if (ps->sample->pss > ps->pss_max)
470 ps->pss_max = ps->sample->pss;
83fdc450
AK
471
472catch_rename:
28989b63 473 /* catch process rename, try to randomize time */
6d031c0b 474 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
1f2ecb03 475 if (((sample - ps->pid) + pid) % (int)(mod) == 0) {
28989b63
TA
476
477 /* re-fetch name */
478 /* get name, start time */
479 if (!ps->sched) {
f2f85884 480 sprintf(filename, "%d/sched", pid);
af672f03
DM
481 ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC);
482 if (ps->sched < 0)
28989b63
TA
483 continue;
484 }
af672f03 485
ef2648c1
LN
486 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
487 if (s <= 0) {
28989b63 488 /* clean up file descriptors */
af672f03
DM
489 ps->sched = safe_close(ps->sched);
490 ps->schedstat = safe_close(ps->schedstat);
28989b63
TA
491 continue;
492 }
af672f03 493
ef2648c1 494 buf[s] = '\0';
28989b63
TA
495
496 if (!sscanf(buf, "%s %*s %*s", key))
497 continue;
498
c309a713 499 strscpy(ps->name, sizeof(ps->name), key);
e90f9fa4
HH
500
501 /* cmdline */
6d031c0b 502 if (arg_show_cmdline)
f9178132 503 pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);
28989b63
TA
504 }
505 }
34a4071e
DM
506
507 return 0;
83fdc450 508}