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');
99 void log_sample(int sample)
102 static int schedstat;
119 /* all the per-process stuff goes here */
121 /* find all processes */
122 proc = opendir("/proc");
125 procfd = dirfd(proc);
132 vmstat = openat(procfd, "vmstat", O_RDONLY);
134 perror("open /proc/vmstat");
139 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
148 if (sscanf(m, "%s %s", key, val) < 2)
150 if (streq(key, "pgpgin"))
151 blockstat[sample].bi = atoi(val);
152 if (streq(key, "pgpgout")) {
153 blockstat[sample].bo = atoi(val);
163 /* overall CPU utilization */
164 schedstat = openat(procfd, "schedstat", O_RDONLY);
165 if (schedstat == -1) {
166 perror("open /proc/schedstat");
171 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
180 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
183 if (strstr(key, "cpu")) {
184 c = atoi((const char*)(key+3));
186 /* Oops, we only have room for MAXCPUS data */
188 cpustat[c].sample[sample].runtime = atoll(rt);
189 cpustat[c].sample[sample].waittime = atoll(wt);
202 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
206 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
209 entropy_avail[sample] = atoi(buf);
214 while ((ent = readdir(proc)) != NULL) {
215 char filename[PATH_MAX];
217 struct ps_struct *ps;
219 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
222 pid = atoi(ent->d_name);
228 while (ps->next_ps) {
234 /* end of our LL? then append a new record */
235 if (ps->pid != pid) {
237 struct ps_struct *parent;
239 ps->next_ps = calloc(1, sizeof(struct ps_struct));
241 perror("calloc(ps_struct)");
247 ps->sample = calloc(len + 1, sizeof(struct ps_sched_struct));
249 perror("calloc(ps_struct)");
255 /* mark our first sample */
258 /* get name, start time */
260 sprintf(filename, "%d/sched", pid);
261 ps->sched = openat(procfd, filename, O_RDONLY);
266 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
273 if (!sscanf(buf, "%s %*s %*s", key))
276 strncpy(ps->name, key, 16);
286 if (!sscanf(m, "%*s %*s %s", t))
289 ps->starttime = strtod(t, NULL) / 1000.0;
292 sprintf(filename, "%d/stat", pid);
293 fd = openat(procfd, filename, O_RDONLY);
294 st = fdopen(fd, "r");
297 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
305 * setup child pointers
307 * these are used to paint the tree coherently later
308 * each parent has a LL of children, and a LL of siblings
311 continue; /* nothing to do for init atm */
313 /* kthreadd has ppid=0, which breaks our tree ordering */
318 while ((parent->next_ps && parent->pid != ps->ppid))
319 parent = parent->next_ps;
321 if ((!parent) || (parent->pid != ps->ppid)) {
324 parent = ps_first->next_ps;
329 if (!parent->children) {
330 /* it's the first child */
331 parent->children = ps;
333 /* walk all children and append */
334 struct ps_struct *children;
335 children = parent->children;
336 while (children->next)
337 children = children->next;
342 /* else -> found pid, append data in ps */
344 /* below here is all continuous logging parts - we get here on every
348 if (!ps->schedstat) {
349 sprintf(filename, "%d/schedstat", pid);
350 ps->schedstat = openat(procfd, filename, O_RDONLY);
351 if (ps->schedstat == -1)
354 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
356 /* clean up our file descriptors - assume that the process exited */
357 close(ps->schedstat);
361 // fclose(ps->smaps);
366 if (!sscanf(buf, "%s %s %*s", rt, wt))
370 ps->sample[sample].runtime = atoll(rt);
371 ps->sample[sample].waittime = atoll(wt);
373 ps->total = (ps->sample[ps->last].runtime
374 - ps->sample[ps->first].runtime)
381 sprintf(filename, "%d/smaps", pid);
382 fd = openat(procfd, filename, O_RDONLY);
383 ps->smaps = fdopen(fd, "r");
386 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
394 /* skip one line, this contains the object mapped */
395 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
397 /* then there's a 28 char 14 line block */
398 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
401 pss_kb = atoi(&buf[61]);
402 ps->sample[sample].pss += pss_kb;
405 if (ps->sample[sample].pss > ps->pss_max)
406 ps->pss_max = ps->sample[sample].pss;
409 /* catch process rename, try to randomize time */
410 mod = (hz < 4.0) ? 4.0 : (hz / 4.0);
411 if (((samples - ps->first) + pid) % (int)(mod) == 0) {
414 /* get name, start time */
416 sprintf(filename, "%d/sched", pid);
417 ps->sched = openat(procfd, filename, O_RDONLY);
421 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
423 /* clean up file descriptors */
426 close(ps->schedstat);
428 // fclose(ps->smaps);
433 if (!sscanf(buf, "%s %*s %*s", key))
436 strncpy(ps->name, key, 16);