diff options
Diffstat (limited to 'src/bootchart/log.c')
-rw-r--r-- | src/bootchart/log.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/src/bootchart/log.c b/src/bootchart/log.c new file mode 100644 index 0000000000..89c7b3523c --- /dev/null +++ b/src/bootchart/log.c @@ -0,0 +1,420 @@ +/* + * log.c + * + * Copyright (C) 2009-2012 Intel Coproration + * + * Authors: + * Auke Kok <auke-jan.h.kok@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#define _GNU_SOURCE 1 +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <dirent.h> +#include <fcntl.h> +#include <time.h> + + +#include "bootchart.h" + +/* + * Alloc a static 4k buffer for stdio - primarily used to increase + * PSS buffering from the default 1k stdin buffer to reduce + * read() overhead. + */ +static char smaps_buf[4096]; +DIR *proc; + + +double gettime_ns(void) +{ + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC, &now); + + return (now.tv_sec + (now.tv_nsec / 1000000000.0)); +} + + +void log_uptime(void) +{ + FILE *f; + char str[32]; + double uptime; + + f = fopen("/proc/uptime", "r"); + if (!f) + return; + if (!fscanf(f, "%s %*s", str)) { + fclose(f); + return; + } + fclose(f); + uptime = strtod(str, NULL); + + log_start = gettime_ns(); + + /* start graph at kernel boot time */ + if (relative) + graph_start = log_start; + else + graph_start = log_start - uptime; +} + + +static char *bufgetline(char *buf) +{ + char *c; + + if (!buf) + return NULL; + + c = strchr(buf, '\n'); + if (c) + c++; + return c; +} + + +void log_sample(int sample) +{ + static int vmstat; + static int schedstat; + FILE *st; + char buf[4095]; + char key[256]; + char val[256]; + char rt[256]; + char wt[256]; + char *m; + int c; + int p; + int mod; + static int e_fd; + ssize_t s; + ssize_t n; + struct dirent *ent; + + if (!vmstat) { + /* block stuff */ + vmstat = open("/proc/vmstat", O_RDONLY); + if (vmstat == -1) { + perror("open /proc/vmstat"); + exit (EXIT_FAILURE); + } + } + + n = pread(vmstat, buf, sizeof(buf) - 1, 0); + if (n <= 0) { + close(vmstat); + return; + } + buf[n] = '\0'; + + m = buf; + while (m) { + if (sscanf(m, "%s %s", key, val) < 2) + goto vmstat_next; + if (!strcmp(key, "pgpgin")) + blockstat[sample].bi = atoi(val); + if (!strcmp(key, "pgpgout")) { + blockstat[sample].bo = atoi(val); + break; + } +vmstat_next: + m = bufgetline(m); + if (!m) + break; + } + + if (!schedstat) { + /* overall CPU utilization */ + schedstat = open("/proc/schedstat", O_RDONLY); + if (schedstat == -1) { + perror("open /proc/schedstat"); + exit (EXIT_FAILURE); + } + } + + n = pread(schedstat, buf, sizeof(buf) - 1, 0); + if (n <= 0) { + close(schedstat); + return; + } + buf[n] = '\0'; + + m = buf; + while (m) { + if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3) + goto schedstat_next; + + if (strstr(key, "cpu")) { + c = atoi((const char*)(key+3)); + if (c > MAXCPUS) + /* Oops, we only have room for MAXCPUS data */ + break; + cpustat[c].sample[sample].runtime = atoll(rt); + cpustat[c].sample[sample].waittime = atoll(wt); + + if (c == cpus) + cpus = c + 1; + } +schedstat_next: + m = bufgetline(m); + if (!m) + break; + } + + if (entropy) { + if (!e_fd) { + e_fd = open("/proc/sys/kernel/random/entropy_avail", O_RDONLY); + } + + if (e_fd) { + n = pread(e_fd, buf, sizeof(buf) - 1, 0); + if (n > 0) + entropy_avail[sample] = atoi(buf); + } + } + + /* all the per-process stuff goes here */ + if (!proc) { + /* find all processes */ + proc = opendir("/proc"); + if (!proc) + return; + } else { + rewinddir(proc); + } + + while ((ent = readdir(proc)) != NULL) { + char filename[PATH_MAX]; + int pid; + struct ps_struct *ps; + + if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9')) + continue; + + pid = atoi(ent->d_name); + + if (pid >= MAXPIDS) + continue; + + ps = ps_first; + while (ps->next_ps) { + ps = ps->next_ps; + if (ps->pid == pid) + break; + } + + /* end of our LL? then append a new record */ + if (ps->pid != pid) { + char t[32]; + struct ps_struct *parent; + + ps->next_ps = malloc(sizeof(struct ps_struct)); + if (!ps->next_ps) { + perror("malloc(ps_struct)"); + exit (EXIT_FAILURE); + } + memset(ps->next_ps, 0, sizeof(struct ps_struct)); + ps = ps->next_ps; + ps->pid = pid; + + ps->sample = malloc(sizeof(struct ps_sched_struct) * (len + 1)); + if (!ps->sample) { + perror("malloc(ps_struct)"); + exit (EXIT_FAILURE); + } + memset(ps->sample, 0, sizeof(struct ps_sched_struct) * (len + 1)); + + pscount++; + + /* mark our first sample */ + ps->first = sample; + + /* get name, start time */ + if (!ps->sched) { + sprintf(filename, "/proc/%d/sched", pid); + ps->sched = open(filename, O_RDONLY); + if (ps->sched == -1) + continue; + } + + s = pread(ps->sched, buf, sizeof(buf) - 1, 0); + if (s <= 0) { + close(ps->sched); + continue; + } + + if (!sscanf(buf, "%s %*s %*s", key)) + continue; + + strncpy(ps->name, key, 16); + /* discard line 2 */ + m = bufgetline(buf); + if (!m) + continue; + + m = bufgetline(m); + if (!m) + continue; + + if (!sscanf(m, "%*s %*s %s", t)) + continue; + + ps->starttime = strtod(t, NULL) / 1000.0; + + /* ppid */ + sprintf(filename, "/proc/%d/stat", pid); + st = fopen(filename, "r"); + if (!st) + continue; + if (!fscanf(st, "%*s %*s %*s %i", &p)) { + fclose(st); + continue; + } + fclose(st); + ps->ppid = p; + + /* + * setup child pointers + * + * these are used to paint the tree coherently later + * each parent has a LL of children, and a LL of siblings + */ + if (pid == 1) + continue; /* nothing to do for init atm */ + + /* kthreadd has ppid=0, which breaks our tree ordering */ + if (ps->ppid == 0) + ps->ppid = 1; + + parent = ps_first; + while ((parent->next_ps && parent->pid != ps->ppid)) + parent = parent->next_ps; + + if ((!parent) || (parent->pid != ps->ppid)) { + /* orphan */ + ps->ppid = 1; + parent = ps_first->next_ps; + } + + ps->parent = parent; + + if (!parent->children) { + /* it's the first child */ + parent->children = ps; + } else { + /* walk all children and append */ + struct ps_struct *children; + children = parent->children; + while (children->next) + children = children->next; + children->next = ps; + } + } + + /* else -> found pid, append data in ps */ + + /* below here is all continuous logging parts - we get here on every + * iteration */ + + /* rt, wt */ + if (!ps->schedstat) { + sprintf(filename, "/proc/%d/schedstat", pid); + ps->schedstat = open(filename, O_RDONLY); + if (ps->schedstat == -1) + continue; + } + + if (pread(ps->schedstat, buf, sizeof(buf) - 1, 0) <= 0) { + /* clean up our file descriptors - assume that the process exited */ + close(ps->schedstat); + if (ps->sched) + close(ps->sched); + //if (ps->smaps) + // fclose(ps->smaps); + continue; + } + if (!sscanf(buf, "%s %s %*s", rt, wt)) + continue; + + ps->last = sample; + ps->sample[sample].runtime = atoll(rt); + ps->sample[sample].waittime = atoll(wt); + + ps->total = (ps->sample[ps->last].runtime + - ps->sample[ps->first].runtime) + / 1000000000.0; + + if (!pss) + goto catch_rename; + /* Pss */ + if (!ps->smaps) { + sprintf(filename, "/proc/%d/smaps", pid); + ps->smaps = fopen(filename, "r"); + setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf)); + if (!ps->smaps) + continue; + } else { + rewind(ps->smaps); + } + + while (1) { + int pss_kb; + + /* skip one line, this contains the object mapped */ + if (fgets(buf, sizeof(buf), ps->smaps) == NULL) + break; + /* then there's a 28 char 14 line block */ + if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) + break; + + pss_kb = atoi(&buf[61]); + ps->sample[sample].pss += pss_kb; + } + + if (ps->sample[sample].pss > ps->pss_max) + ps->pss_max = ps->sample[sample].pss; + +catch_rename: + /* catch process rename, try to randomize time */ + mod = (hz < 4.0) ? 4.0 : (hz / 4.0); + if (((samples - ps->first) + pid) % (int)(mod) == 0) { + + /* re-fetch name */ + /* get name, start time */ + if (!ps->sched) { + sprintf(filename, "/proc/%d/sched", pid); + ps->sched = open(filename, O_RDONLY); + if (ps->sched == -1) + continue; + } + if (pread(ps->sched, buf, sizeof(buf) - 1, 0) <= 0) { + /* clean up file descriptors */ + close(ps->sched); + if (ps->schedstat) + close(ps->schedstat); + //if (ps->smaps) + // fclose(ps->smaps); + continue; + } + + if (!sscanf(buf, "%s %*s %*s", key)) + continue; + + strncpy(ps->name, key, 16); + } + } +} |