]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/bootchart/store.c
logind: add support for TPS65217 Power Button
[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) {
195 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
196 goto schedstat_next;
197
198 if (strstr(key, "cpu")) {
199 c = atoi((const char*)(key+3));
200 if (c > MAXCPUS)
201 /* Oops, we only have room for MAXCPUS data */
202 break;
8dfb6e71
NC
203 sampledata->runtime[c] = atoll(rt);
204 sampledata->waittime[c] = atoll(wt);
28989b63
TA
205
206 if (c == cpus)
207 cpus = c + 1;
208 }
83fdc450 209schedstat_next:
28989b63
TA
210 m = bufgetline(m);
211 if (!m)
212 break;
213 }
214
6d031c0b 215 if (arg_entropy) {
28989b63 216 if (!e_fd) {
f2f85884 217 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
28989b63
TA
218 }
219
220 if (e_fd) {
221 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
ef2648c1
LN
222 if (n > 0) {
223 buf[n] = '\0';
8dfb6e71 224 sampledata->entropy_avail = atoi(buf);
ef2648c1 225 }
28989b63
TA
226 }
227 }
228
28989b63
TA
229 while ((ent = readdir(proc)) != NULL) {
230 char filename[PATH_MAX];
231 int pid;
232 struct ps_struct *ps;
233
234 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
235 continue;
236
237 pid = atoi(ent->d_name);
238
239 if (pid >= MAXPIDS)
240 continue;
241
242 ps = ps_first;
243 while (ps->next_ps) {
244 ps = ps->next_ps;
245 if (ps->pid == pid)
246 break;
247 }
248
249 /* end of our LL? then append a new record */
250 if (ps->pid != pid) {
7fd1b19b 251 _cleanup_fclose_ FILE *st = NULL;
28989b63
TA
252 char t[32];
253 struct ps_struct *parent;
e10f3c43 254 int r;
28989b63 255
955d98c9 256 ps->next_ps = new0(struct ps_struct, 1);
28989b63 257 if (!ps->next_ps) {
955d98c9 258 log_oom();
28989b63
TA
259 exit (EXIT_FAILURE);
260 }
28989b63
TA
261 ps = ps->next_ps;
262 ps->pid = pid;
263
955d98c9 264 ps->sample = new0(struct ps_sched_struct, 1);
28989b63 265 if (!ps->sample) {
955d98c9 266 log_oom();
28989b63
TA
267 exit (EXIT_FAILURE);
268 }
8dfb6e71 269 ps->sample->sampledata = sampledata;
28989b63
TA
270
271 pscount++;
272
273 /* mark our first sample */
306e6650 274 ps->first = ps->last = ps->sample;
8dfb6e71
NC
275 ps->sample->runtime = atoll(rt);
276 ps->sample->waittime = atoll(wt);
28989b63
TA
277
278 /* get name, start time */
279 if (!ps->sched) {
f2f85884
HH
280 sprintf(filename, "%d/sched", pid);
281 ps->sched = openat(procfd, filename, O_RDONLY);
28989b63
TA
282 if (ps->sched == -1)
283 continue;
284 }
285
286 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
287 if (s <= 0) {
288 close(ps->sched);
289 continue;
290 }
ef2648c1 291 buf[s] = '\0';
28989b63
TA
292
293 if (!sscanf(buf, "%s %*s %*s", key))
294 continue;
295
c309a713 296 strscpy(ps->name, sizeof(ps->name), key);
e90f9fa4
HH
297
298 /* cmdline */
6d031c0b 299 if (arg_show_cmdline)
c309a713 300 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
e90f9fa4 301
28989b63
TA
302 /* discard line 2 */
303 m = bufgetline(buf);
304 if (!m)
305 continue;
306
307 m = bufgetline(m);
308 if (!m)
309 continue;
310
311 if (!sscanf(m, "%*s %*s %s", t))
312 continue;
313
e10f3c43
TG
314 r = safe_atod(t, &ps->starttime);
315 if (r < 0)
316 continue;
317
318 ps->starttime /= 1000.0;
28989b63 319
49e5b2a9
WC
320 if (arg_show_cgroup)
321 /* if this fails, that's OK */
322 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER,
323 ps->pid, &ps->cgroup);
324
28989b63 325 /* ppid */
f2f85884
HH
326 sprintf(filename, "%d/stat", pid);
327 fd = openat(procfd, filename, O_RDONLY);
328 st = fdopen(fd, "r");
28989b63
TA
329 if (!st)
330 continue;
331 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
28989b63
TA
332 continue;
333 }
28989b63
TA
334 ps->ppid = p;
335
336 /*
337 * setup child pointers
338 *
339 * these are used to paint the tree coherently later
340 * each parent has a LL of children, and a LL of siblings
341 */
342 if (pid == 1)
343 continue; /* nothing to do for init atm */
344
345 /* kthreadd has ppid=0, which breaks our tree ordering */
346 if (ps->ppid == 0)
347 ps->ppid = 1;
348
349 parent = ps_first;
350 while ((parent->next_ps && parent->pid != ps->ppid))
351 parent = parent->next_ps;
352
226b735a 353 if (parent->pid != ps->ppid) {
28989b63
TA
354 /* orphan */
355 ps->ppid = 1;
356 parent = ps_first->next_ps;
357 }
358
359 ps->parent = parent;
360
361 if (!parent->children) {
362 /* it's the first child */
363 parent->children = ps;
364 } else {
365 /* walk all children and append */
366 struct ps_struct *children;
367 children = parent->children;
368 while (children->next)
369 children = children->next;
370 children->next = ps;
371 }
372 }
373
374 /* else -> found pid, append data in ps */
375
376 /* below here is all continuous logging parts - we get here on every
377 * iteration */
378
379 /* rt, wt */
380 if (!ps->schedstat) {
f2f85884
HH
381 sprintf(filename, "%d/schedstat", pid);
382 ps->schedstat = openat(procfd, filename, O_RDONLY);
28989b63
TA
383 if (ps->schedstat == -1)
384 continue;
385 }
ef2648c1
LN
386 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
387 if (s <= 0) {
28989b63
TA
388 /* clean up our file descriptors - assume that the process exited */
389 close(ps->schedstat);
390 if (ps->sched)
391 close(ps->sched);
392 //if (ps->smaps)
393 // fclose(ps->smaps);
394 continue;
395 }
ef2648c1
LN
396 buf[s] = '\0';
397
28989b63
TA
398 if (!sscanf(buf, "%s %s %*s", rt, wt))
399 continue;
400
955d98c9 401 ps->sample->next = new0(struct ps_sched_struct, 1);
d498a616 402 if (!ps->sample->next) {
955d98c9
LP
403 log_oom();
404 exit(EXIT_FAILURE);
8dfb6e71
NC
405 }
406 ps->sample->next->prev = ps->sample;
407 ps->sample = ps->sample->next;
408 ps->last = ps->sample;
409 ps->sample->runtime = atoll(rt);
410 ps->sample->waittime = atoll(wt);
411 ps->sample->sampledata = sampledata;
412 ps->sample->ps_new = ps;
413 if (ps_prev) {
414 ps_prev->cross = ps->sample;
415 }
416 ps_prev = ps->sample;
417 ps->total = (ps->last->runtime - ps->first->runtime)
418 / 1000000000.0;
28989b63 419
6d031c0b 420 if (!arg_pss)
28989b63 421 goto catch_rename;
8dfb6e71 422
28989b63
TA
423 /* Pss */
424 if (!ps->smaps) {
f2f85884
HH
425 sprintf(filename, "%d/smaps", pid);
426 fd = openat(procfd, filename, O_RDONLY);
427 ps->smaps = fdopen(fd, "r");
28989b63
TA
428 if (!ps->smaps)
429 continue;
430 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
8dfb6e71
NC
431 }
432 else {
433 rewind(ps->smaps);
434 }
435 /* test to see if we need to skip another field */
436 if (skip == 0) {
437 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
438 continue;
439 }
440 if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
441 continue;
442 }
443 if (buf[392] == 'V') {
444 skip = 2;
445 }
446 else {
447 skip = 1;
448 }
28989b63
TA
449 rewind(ps->smaps);
450 }
28989b63
TA
451 while (1) {
452 int pss_kb;
453
8dfb6e71
NC
454 /* skip one line, this contains the object mapped. */
455 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
28989b63 456 break;
8dfb6e71 457 }
28989b63 458 /* then there's a 28 char 14 line block */
8dfb6e71 459 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
28989b63 460 break;
8dfb6e71 461 }
28989b63 462 pss_kb = atoi(&buf[61]);
8dfb6e71 463 ps->sample->pss += pss_kb;
28989b63 464
8dfb6e71
NC
465 /* skip one more line if this is a newer kernel */
466 if (skip == 2) {
467 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
468 break;
469 }
470 }
471 if (ps->sample->pss > ps->pss_max)
472 ps->pss_max = ps->sample->pss;
83fdc450
AK
473
474catch_rename:
28989b63 475 /* catch process rename, try to randomize time */
6d031c0b 476 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
8dfb6e71 477 if (((samples - ps->pid) + pid) % (int)(mod) == 0) {
28989b63
TA
478
479 /* re-fetch name */
480 /* get name, start time */
481 if (!ps->sched) {
f2f85884
HH
482 sprintf(filename, "%d/sched", pid);
483 ps->sched = openat(procfd, filename, O_RDONLY);
28989b63
TA
484 if (ps->sched == -1)
485 continue;
486 }
ef2648c1
LN
487 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
488 if (s <= 0) {
28989b63
TA
489 /* clean up file descriptors */
490 close(ps->sched);
491 if (ps->schedstat)
492 close(ps->schedstat);
493 //if (ps->smaps)
494 // fclose(ps->smaps);
495 continue;
496 }
ef2648c1 497 buf[s] = '\0';
28989b63
TA
498
499 if (!sscanf(buf, "%s %*s %*s", key))
500 continue;
501
c309a713 502 strscpy(ps->name, sizeof(ps->name), key);
e90f9fa4
HH
503
504 /* cmdline */
6d031c0b 505 if (arg_show_cmdline)
c309a713 506 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
28989b63
TA
507 }
508 }
83fdc450 509}