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);