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