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