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");
67 if (!fscanf(f, "%s %*s", str)) {
72 uptime = strtod(str, NULL);
74 log_start = gettime_ns();
76 /* start graph at kernel boot time */
78 graph_start = log_start;
80 graph_start = log_start - uptime;
84 static char *bufgetline(char *buf)
91 c = strchr(buf, '\n');
98 void log_sample(int sample)
101 static int schedstat;
119 vmstat = open("/proc/vmstat", O_RDONLY);
121 perror("open /proc/vmstat");
126 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
135 if (sscanf(m, "%s %s", key, val) < 2)
137 if (streq(key, "pgpgin"))
138 blockstat[sample].bi = atoi(val);
139 if (streq(key, "pgpgout")) {
140 blockstat[sample].bo = atoi(val);
150 /* overall CPU utilization */
151 schedstat = open("/proc/schedstat", O_RDONLY);
152 if (schedstat == -1) {
153 perror("open /proc/schedstat");
158 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
167 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
170 if (strstr(key, "cpu")) {
171 c = atoi((const char*)(key+3));
173 /* Oops, we only have room for MAXCPUS data */
175 cpustat[c].sample[sample].runtime = atoll(rt);
176 cpustat[c].sample[sample].waittime = atoll(wt);
189 e_fd = open("/proc/sys/kernel/random/entropy_avail", O_RDONLY);
193 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
196 entropy_avail[sample] = atoi(buf);
201 /* all the per-process stuff goes here */
203 /* find all processes */
204 proc = opendir("/proc");
211 while ((ent = readdir(proc)) != NULL) {
212 char filename[PATH_MAX];
214 struct ps_struct *ps;
216 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
219 pid = atoi(ent->d_name);
225 while (ps->next_ps) {
231 /* end of our LL? then append a new record */
232 if (ps->pid != pid) {
234 struct ps_struct *parent;
236 ps->next_ps = calloc(1, sizeof(struct ps_struct));
238 perror("calloc(ps_struct)");
244 ps->sample = calloc(len + 1, sizeof(struct ps_sched_struct));
246 perror("calloc(ps_struct)");
252 /* mark our first sample */
255 /* get name, start time */
257 sprintf(filename, "/proc/%d/sched", pid);
258 ps->sched = open(filename, O_RDONLY);
263 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
270 if (!sscanf(buf, "%s %*s %*s", key))
273 strncpy(ps->name, key, 16);
283 if (!sscanf(m, "%*s %*s %s", t))
286 ps->starttime = strtod(t, NULL) / 1000.0;
289 sprintf(filename, "/proc/%d/stat", pid);
290 st = fopen(filename, "r");
293 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
301 * setup child pointers
303 * these are used to paint the tree coherently later
304 * each parent has a LL of children, and a LL of siblings
307 continue; /* nothing to do for init atm */
309 /* kthreadd has ppid=0, which breaks our tree ordering */
314 while ((parent->next_ps && parent->pid != ps->ppid))
315 parent = parent->next_ps;
317 if ((!parent) || (parent->pid != ps->ppid)) {
320 parent = ps_first->next_ps;
325 if (!parent->children) {
326 /* it's the first child */
327 parent->children = ps;
329 /* walk all children and append */
330 struct ps_struct *children;
331 children = parent->children;
332 while (children->next)
333 children = children->next;
338 /* else -> found pid, append data in ps */
340 /* below here is all continuous logging parts - we get here on every
344 if (!ps->schedstat) {
345 sprintf(filename, "/proc/%d/schedstat", pid);
346 ps->schedstat = open(filename, O_RDONLY);
347 if (ps->schedstat == -1)
350 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
352 /* clean up our file descriptors - assume that the process exited */
353 close(ps->schedstat);
357 // fclose(ps->smaps);
362 if (!sscanf(buf, "%s %s %*s", rt, wt))
366 ps->sample[sample].runtime = atoll(rt);
367 ps->sample[sample].waittime = atoll(wt);
369 ps->total = (ps->sample[ps->last].runtime
370 - ps->sample[ps->first].runtime)
377 sprintf(filename, "/proc/%d/smaps", pid);
378 ps->smaps = fopen(filename, "r");
381 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
389 /* skip one line, this contains the object mapped */
390 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
392 /* then there's a 28 char 14 line block */
393 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
396 pss_kb = atoi(&buf[61]);
397 ps->sample[sample].pss += pss_kb;
400 if (ps->sample[sample].pss > ps->pss_max)
401 ps->pss_max = ps->sample[sample].pss;
404 /* catch process rename, try to randomize time */
405 mod = (hz < 4.0) ? 4.0 : (hz / 4.0);
406 if (((samples - ps->first) + pid) % (int)(mod) == 0) {
409 /* get name, start time */
411 sprintf(filename, "/proc/%d/sched", pid);
412 ps->sched = open(filename, O_RDONLY);
416 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
418 /* clean up file descriptors */
421 close(ps->schedstat);
423 // fclose(ps->smaps);
428 if (!sscanf(buf, "%s %*s %*s", key))
431 strncpy(ps->name, key, 16);