2
log.c - This file is part of systemd-bootchart
4
Copyright (C) 2009-2013 Intel Coproration
7
Auke Kok <auke-jan.h.kok@intel.com>
9
systemd is free software; you can redistribute it and/or modify it
10
under the terms of the GNU Lesser General Public License as published by
11
the Free Software Foundation; either version 2.1 of the License, or
12
(at your option) any later version.
14
systemd is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Lesser General Public License for more details.
19
You should have received a copy of the GNU Lesser General Public License
20
along with systemd; If not, see <http://www.gnu.org/licenses/>.
27
#include <sys/types.h>
36
#include "bootchart.h"
40
* Alloc a static 4k buffer for stdio - primarily used to increase
41
* PSS buffering from the default 1k stdin buffer to reduce
44
static char smaps_buf[4096];
48
double gettime_ns(void)
52
clock_gettime(CLOCK_MONOTONIC, &n);
54
return (n.tv_sec + (n.tv_nsec / 1000000000.0));
60
FILE _cleanup_fclose_ *f = NULL;
64
f = fopen("/proc/uptime", "r");
68
if (!fscanf(f, "%s %*s", str))
71
uptime = strtod(str, NULL);
73
log_start = gettime_ns();
75
/* start graph at kernel boot time */
77
graph_start = log_start;
79
graph_start = log_start - uptime;
83
static char *bufgetline(char *buf)
90
c = strchr(buf, '\n');
96
static int pid_cmdline_strncpy(char *buffer, int pid, size_t buf_len) {
97
char filename[PATH_MAX];
98
int _cleanup_close_ fd=-1;
101
sprintf(filename, "%d/cmdline", pid);
102
fd = openat(procfd, filename, O_RDONLY);
106
n = read(fd, buffer, buf_len-1);
109
for (i = 0; i < n; i++)
110
if (buffer[i] == '\0')
117
void log_sample(int sample)
120
static int schedstat;
136
/* all the per-process stuff goes here */
138
/* find all processes */
139
proc = opendir("/proc");
142
procfd = dirfd(proc);
149
vmstat = openat(procfd, "vmstat", O_RDONLY);
151
perror("open /proc/vmstat");
156
n = pread(vmstat, buf, sizeof(buf) - 1, 0);
165
if (sscanf(m, "%s %s", key, val) < 2)
167
if (streq(key, "pgpgin"))
168
blockstat[sample].bi = atoi(val);
169
if (streq(key, "pgpgout")) {
170
blockstat[sample].bo = atoi(val);
180
/* overall CPU utilization */
181
schedstat = openat(procfd, "schedstat", O_RDONLY);
182
if (schedstat == -1) {
183
perror("open /proc/schedstat");
188
n = pread(schedstat, buf, sizeof(buf) - 1, 0);
197
if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
200
if (strstr(key, "cpu")) {
201
c = atoi((const char*)(key+3));
203
/* Oops, we only have room for MAXCPUS data */
205
cpustat[c].sample[sample].runtime = atoll(rt);
206
cpustat[c].sample[sample].waittime = atoll(wt);
219
e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
223
n = pread(e_fd, buf, sizeof(buf) - 1, 0);
226
entropy_avail[sample] = atoi(buf);
231
while ((ent = readdir(proc)) != NULL) {
232
char filename[PATH_MAX];
234
struct ps_struct *ps;
236
if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
239
pid = atoi(ent->d_name);
245
while (ps->next_ps) {
251
/* end of our LL? then append a new record */
252
if (ps->pid != pid) {
253
FILE _cleanup_fclose_ *st = NULL;
255
struct ps_struct *parent;
257
ps->next_ps = calloc(1, sizeof(struct ps_struct));
259
perror("calloc(ps_struct)");
265
ps->sample = calloc(samples_len + 1, sizeof(struct ps_sched_struct));
267
perror("calloc(ps_struct)");
273
/* mark our first sample */
276
/* get name, start time */
278
sprintf(filename, "%d/sched", pid);
279
ps->sched = openat(procfd, filename, O_RDONLY);
284
s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
291
if (!sscanf(buf, "%s %*s %*s", key))
294
strncpy(ps->name, key, 256);
298
pid_cmdline_strncpy(ps->name, pid, 256);
309
if (!sscanf(m, "%*s %*s %s", t))
312
ps->starttime = strtod(t, NULL) / 1000.0;
315
sprintf(filename, "%d/stat", pid);
316
fd = openat(procfd, filename, O_RDONLY);
317
st = fdopen(fd, "r");
320
if (!fscanf(st, "%*s %*s %*s %i", &p)) {
326
* setup child pointers
328
* these are used to paint the tree coherently later
329
* each parent has a LL of children, and a LL of siblings
332
continue; /* nothing to do for init atm */
334
/* kthreadd has ppid=0, which breaks our tree ordering */
339
while ((parent->next_ps && parent->pid != ps->ppid))
340
parent = parent->next_ps;
342
if ((!parent) || (parent->pid != ps->ppid)) {
345
parent = ps_first->next_ps;
350
if (!parent->children) {
351
/* it's the first child */
352
parent->children = ps;
354
/* walk all children and append */
355
struct ps_struct *children;
356
children = parent->children;
357
while (children->next)
358
children = children->next;
363
/* else -> found pid, append data in ps */
365
/* below here is all continuous logging parts - we get here on every
369
if (!ps->schedstat) {
370
sprintf(filename, "%d/schedstat", pid);
371
ps->schedstat = openat(procfd, filename, O_RDONLY);
372
if (ps->schedstat == -1)
375
s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
377
/* clean up our file descriptors - assume that the process exited */
378
close(ps->schedstat);
382
// fclose(ps->smaps);
387
if (!sscanf(buf, "%s %s %*s", rt, wt))
391
ps->sample[sample].runtime = atoll(rt);
392
ps->sample[sample].waittime = atoll(wt);
394
ps->total = (ps->sample[ps->last].runtime
395
- ps->sample[ps->first].runtime)
402
sprintf(filename, "%d/smaps", pid);
403
fd = openat(procfd, filename, O_RDONLY);
404
ps->smaps = fdopen(fd, "r");
407
setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
415
/* skip one line, this contains the object mapped */
416
if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
418
/* then there's a 28 char 14 line block */
419
if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
422
pss_kb = atoi(&buf[61]);
423
ps->sample[sample].pss += pss_kb;
426
if (ps->sample[sample].pss > ps->pss_max)
427
ps->pss_max = ps->sample[sample].pss;
430
/* catch process rename, try to randomize time */
431
mod = (hz < 4.0) ? 4.0 : (hz / 4.0);
432
if (((samples - ps->first) + pid) % (int)(mod) == 0) {
435
/* get name, start time */
437
sprintf(filename, "%d/sched", pid);
438
ps->sched = openat(procfd, filename, O_RDONLY);
442
s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
444
/* clean up file descriptors */
447
close(ps->schedstat);
449
// fclose(ps->smaps);
454
if (!sscanf(buf, "%s %*s %*s", key))
457
strncpy(ps->name, key, 256);
461
pid_cmdline_strncpy(ps->name, pid, 256);