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));
64 f = fopen("/proc/uptime", "r");
68 if (!fscanf(f, "%s %*s", str)) {
73 uptime = strtod(str, NULL);
75 log_start = gettime_ns();
77 /* start graph at kernel boot time */
79 graph_start = log_start;
81 graph_start = log_start - uptime;
85 static char *bufgetline(char *buf)
92 c = strchr(buf, '\n');
98 static int pid_cmdline_strncpy(char *buffer, int pid, size_t buf_len) {
99 char filename[PATH_MAX];
100 int _cleanup_close_ fd=-1;
103 sprintf(filename, "%d/cmdline", pid);
104 fd = openat(procfd, filename, O_RDONLY);
108 n = read(fd, buffer, buf_len-1);
111 for (i = 0; i < n; i++)
112 if (buffer[i] == '\0')
119 void log_sample(int sample)
122 static int schedstat;
139 /* all the per-process stuff goes here */
141 /* find all processes */
142 proc = opendir("/proc");
145 procfd = dirfd(proc);
152 vmstat = openat(procfd, "vmstat", O_RDONLY);
154 perror("open /proc/vmstat");
159 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
168 if (sscanf(m, "%s %s", key, val) < 2)
170 if (streq(key, "pgpgin"))
171 blockstat[sample].bi = atoi(val);
172 if (streq(key, "pgpgout")) {
173 blockstat[sample].bo = atoi(val);
183 /* overall CPU utilization */
184 schedstat = openat(procfd, "schedstat", O_RDONLY);
185 if (schedstat == -1) {
186 perror("open /proc/schedstat");
191 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
200 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
203 if (strstr(key, "cpu")) {
204 c = atoi((const char*)(key+3));
206 /* Oops, we only have room for MAXCPUS data */
208 cpustat[c].sample[sample].runtime = atoll(rt);
209 cpustat[c].sample[sample].waittime = atoll(wt);
222 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
226 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
229 entropy_avail[sample] = atoi(buf);
234 while ((ent = readdir(proc)) != NULL) {
235 char filename[PATH_MAX];
237 struct ps_struct *ps;
239 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
242 pid = atoi(ent->d_name);
248 while (ps->next_ps) {
254 /* end of our LL? then append a new record */
255 if (ps->pid != pid) {
257 struct ps_struct *parent;
259 ps->next_ps = calloc(1, sizeof(struct ps_struct));
261 perror("calloc(ps_struct)");
267 ps->sample = calloc(len + 1, sizeof(struct ps_sched_struct));
269 perror("calloc(ps_struct)");
275 /* mark our first sample */
278 /* get name, start time */
280 sprintf(filename, "%d/sched", pid);
281 ps->sched = openat(procfd, filename, O_RDONLY);
286 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
293 if (!sscanf(buf, "%s %*s %*s", key))
296 strncpy(ps->name, key, 256);
300 pid_cmdline_strncpy(ps->name, pid, 256);
311 if (!sscanf(m, "%*s %*s %s", t))
314 ps->starttime = strtod(t, NULL) / 1000.0;
317 sprintf(filename, "%d/stat", pid);
318 fd = openat(procfd, filename, O_RDONLY);
319 st = fdopen(fd, "r");
322 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
330 * setup child pointers
332 * these are used to paint the tree coherently later
333 * each parent has a LL of children, and a LL of siblings
336 continue; /* nothing to do for init atm */
338 /* kthreadd has ppid=0, which breaks our tree ordering */
343 while ((parent->next_ps && parent->pid != ps->ppid))
344 parent = parent->next_ps;
346 if ((!parent) || (parent->pid != ps->ppid)) {
349 parent = ps_first->next_ps;
354 if (!parent->children) {
355 /* it's the first child */
356 parent->children = ps;
358 /* walk all children and append */
359 struct ps_struct *children;
360 children = parent->children;
361 while (children->next)
362 children = children->next;
367 /* else -> found pid, append data in ps */
369 /* below here is all continuous logging parts - we get here on every
373 if (!ps->schedstat) {
374 sprintf(filename, "%d/schedstat", pid);
375 ps->schedstat = openat(procfd, filename, O_RDONLY);
376 if (ps->schedstat == -1)
379 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
381 /* clean up our file descriptors - assume that the process exited */
382 close(ps->schedstat);
386 // fclose(ps->smaps);
391 if (!sscanf(buf, "%s %s %*s", rt, wt))
395 ps->sample[sample].runtime = atoll(rt);
396 ps->sample[sample].waittime = atoll(wt);
398 ps->total = (ps->sample[ps->last].runtime
399 - ps->sample[ps->first].runtime)
406 sprintf(filename, "%d/smaps", pid);
407 fd = openat(procfd, filename, O_RDONLY);
408 ps->smaps = fdopen(fd, "r");
411 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
419 /* skip one line, this contains the object mapped */
420 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
422 /* then there's a 28 char 14 line block */
423 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
426 pss_kb = atoi(&buf[61]);
427 ps->sample[sample].pss += pss_kb;
430 if (ps->sample[sample].pss > ps->pss_max)
431 ps->pss_max = ps->sample[sample].pss;
434 /* catch process rename, try to randomize time */
435 mod = (hz < 4.0) ? 4.0 : (hz / 4.0);
436 if (((samples - ps->first) + pid) % (int)(mod) == 0) {
439 /* get name, start time */
441 sprintf(filename, "%d/sched", pid);
442 ps->sched = openat(procfd, filename, O_RDONLY);
446 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
448 /* clean up file descriptors */
451 close(ps->schedstat);
453 // fclose(ps->smaps);
458 if (!sscanf(buf, "%s %*s %*s", key))
461 strncpy(ps->name, key, 256);
465 pid_cmdline_strncpy(ps->name, pid, 256);