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