1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Corporation
9 Auke Kok <auke-jan.h.kok@intel.com>
11 systemd is free software; you can redistribute it and/or modify it
12 under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 2.1 of the License, or
14 (at your option) any later version.
16 systemd is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public License
22 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/types.h>
37 #include "time-util.h"
40 #include "bootchart.h"
41 #include "cgroup-util.h"
44 * Alloc a static 4k buffer for stdio - primarily used to increase
45 * PSS buffering from the default 1k stdin buffer to reduce
48 static char smaps_buf[4096];
53 double gettime_ns(void) {
56 clock_gettime(CLOCK_MONOTONIC, &n);
58 return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
61 static double gettime_up(void) {
64 clock_gettime(CLOCK_BOOTTIME, &n);
65 return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
68 void log_uptime(void) {
70 graph_start = log_start = gettime_ns();
72 double uptime = gettime_up();
74 log_start = gettime_ns();
75 graph_start = log_start - uptime;
79 static char *bufgetline(char *buf) {
85 c = strchr(buf, '\n');
91 static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
92 char filename[PATH_MAX];
93 _cleanup_close_ int fd=-1;
96 sprintf(filename, "%d/cmdline", pid);
97 fd = openat(procfd, filename, O_RDONLY);
101 n = read(fd, buffer, buf_len-1);
104 for (i = 0; i < n; i++)
105 if (buffer[i] == '\0')
112 void log_sample(int sample, struct list_sample_data **ptr) {
114 static int schedstat;
129 struct list_sample_data *sampledata;
130 struct ps_sched_struct *ps_prev = NULL;
134 /* all the per-process stuff goes here */
136 /* find all processes */
137 proc = opendir("/proc");
140 procfd = dirfd(proc);
147 vmstat = openat(procfd, "vmstat", O_RDONLY);
149 log_error("Failed to open /proc/vmstat: %m");
154 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
163 if (sscanf(m, "%s %s", key, val) < 2)
165 if (streq(key, "pgpgin"))
166 sampledata->blockstat.bi = atoi(val);
167 if (streq(key, "pgpgout")) {
168 sampledata->blockstat.bo = atoi(val);
178 /* overall CPU utilization */
179 schedstat = openat(procfd, "schedstat", O_RDONLY);
180 if (schedstat == -1) {
181 log_error("Failed to open /proc/schedstat: %m");
186 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
195 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
198 if (strstr(key, "cpu")) {
199 c = atoi((const char*)(key+3));
201 /* Oops, we only have room for MAXCPUS data */
203 sampledata->runtime[c] = atoll(rt);
204 sampledata->waittime[c] = atoll(wt);
217 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
221 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
224 sampledata->entropy_avail = atoi(buf);
229 while ((ent = readdir(proc)) != NULL) {
230 char filename[PATH_MAX];
232 struct ps_struct *ps;
234 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
237 pid = atoi(ent->d_name);
243 while (ps->next_ps) {
249 /* end of our LL? then append a new record */
250 if (ps->pid != pid) {
251 _cleanup_fclose_ FILE *st = NULL;
253 struct ps_struct *parent;
255 ps->next_ps = new0(struct ps_struct, 1);
263 ps->sample = new0(struct ps_sched_struct, 1);
268 ps->sample->sampledata = sampledata;
272 /* mark our first sample */
273 ps->first = ps->last = ps->sample;
274 ps->sample->runtime = atoll(rt);
275 ps->sample->waittime = atoll(wt);
277 /* get name, start time */
279 sprintf(filename, "%d/sched", pid);
280 ps->sched = openat(procfd, filename, O_RDONLY);
285 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
292 if (!sscanf(buf, "%s %*s %*s", key))
295 strscpy(ps->name, sizeof(ps->name), key);
298 if (arg_show_cmdline)
299 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
310 if (!sscanf(m, "%*s %*s %s", t))
313 ps->starttime = strtod(t, NULL) / 1000.0;
316 /* if this fails, that's OK */
317 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER,
318 ps->pid, &ps->cgroup);
321 sprintf(filename, "%d/stat", pid);
322 fd = openat(procfd, filename, O_RDONLY);
323 st = fdopen(fd, "r");
326 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
332 * setup child pointers
334 * these are used to paint the tree coherently later
335 * each parent has a LL of children, and a LL of siblings
338 continue; /* nothing to do for init atm */
340 /* kthreadd has ppid=0, which breaks our tree ordering */
345 while ((parent->next_ps && parent->pid != ps->ppid))
346 parent = parent->next_ps;
348 if (parent->pid != ps->ppid) {
351 parent = ps_first->next_ps;
356 if (!parent->children) {
357 /* it's the first child */
358 parent->children = ps;
360 /* walk all children and append */
361 struct ps_struct *children;
362 children = parent->children;
363 while (children->next)
364 children = children->next;
369 /* else -> found pid, append data in ps */
371 /* below here is all continuous logging parts - we get here on every
375 if (!ps->schedstat) {
376 sprintf(filename, "%d/schedstat", pid);
377 ps->schedstat = openat(procfd, filename, O_RDONLY);
378 if (ps->schedstat == -1)
381 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
383 /* clean up our file descriptors - assume that the process exited */
384 close(ps->schedstat);
388 // fclose(ps->smaps);
393 if (!sscanf(buf, "%s %s %*s", rt, wt))
396 ps->sample->next = new0(struct ps_sched_struct, 1);
401 ps->sample->next->prev = ps->sample;
402 ps->sample = ps->sample->next;
403 ps->last = ps->sample;
404 ps->sample->runtime = atoll(rt);
405 ps->sample->waittime = atoll(wt);
406 ps->sample->sampledata = sampledata;
407 ps->sample->ps_new = ps;
409 ps_prev->cross = ps->sample;
411 ps_prev = ps->sample;
412 ps->total = (ps->last->runtime - ps->first->runtime)
420 sprintf(filename, "%d/smaps", pid);
421 fd = openat(procfd, filename, O_RDONLY);
422 ps->smaps = fdopen(fd, "r");
425 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
430 /* test to see if we need to skip another field */
432 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
435 if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
438 if (buf[392] == 'V') {
449 /* skip one line, this contains the object mapped. */
450 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
453 /* then there's a 28 char 14 line block */
454 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
457 pss_kb = atoi(&buf[61]);
458 ps->sample->pss += pss_kb;
460 /* skip one more line if this is a newer kernel */
462 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
466 if (ps->sample->pss > ps->pss_max)
467 ps->pss_max = ps->sample->pss;
470 /* catch process rename, try to randomize time */
471 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
472 if (((samples - ps->pid) + pid) % (int)(mod) == 0) {
475 /* get name, start time */
477 sprintf(filename, "%d/sched", pid);
478 ps->sched = openat(procfd, filename, O_RDONLY);
482 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
484 /* clean up file descriptors */
487 close(ps->schedstat);
489 // fclose(ps->smaps);
494 if (!sscanf(buf, "%s %*s %*s", key))
497 strscpy(ps->name, sizeof(ps->name), key);
500 if (arg_show_cmdline)
501 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);