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