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_errno(errno
, "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_errno(errno
, "Failed to open /proc/schedstat: %m");
186 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 r
= safe_atoi((const char*)(key
+3), &c
);
202 if (r
< 0 || c
> MAXCPUS
-1)
203 /* Oops, we only have room for MAXCPUS data */
205 sampledata
->runtime
[c
] = atoll(rt
);
206 sampledata
->waittime
[c
] = 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 sampledata
->entropy_avail
= 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 _cleanup_fclose_
FILE *st
= NULL
;
255 struct ps_struct
*parent
;
258 ps
->next_ps
= new0(struct ps_struct
, 1);
266 ps
->sample
= new0(struct ps_sched_struct
, 1);
271 ps
->sample
->sampledata
= sampledata
;
275 /* mark our first sample */
276 ps
->first
= ps
->last
= ps
->sample
;
277 ps
->sample
->runtime
= atoll(rt
);
278 ps
->sample
->waittime
= atoll(wt
);
280 /* get name, start time */
282 sprintf(filename
, "%d/sched", pid
);
283 ps
->sched
= openat(procfd
, filename
, O_RDONLY
);
288 s
= pread(ps
->sched
, buf
, sizeof(buf
) - 1, 0);
295 if (!sscanf(buf
, "%s %*s %*s", key
))
298 strscpy(ps
->name
, sizeof(ps
->name
), key
);
301 if (arg_show_cmdline
)
302 pid_cmdline_strscpy(ps
->name
, sizeof(ps
->name
), pid
);
313 if (!sscanf(m
, "%*s %*s %s", t
))
316 r
= safe_atod(t
, &ps
->starttime
);
320 ps
->starttime
/= 1000.0;
323 /* if this fails, that's OK */
324 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER
,
325 ps
->pid
, &ps
->cgroup
);
328 sprintf(filename
, "%d/stat", pid
);
329 fd
= openat(procfd
, filename
, O_RDONLY
);
330 st
= fdopen(fd
, "r");
333 if (!fscanf(st
, "%*s %*s %*s %i", &p
)) {
339 * setup child pointers
341 * these are used to paint the tree coherently later
342 * each parent has a LL of children, and a LL of siblings
345 continue; /* nothing to do for init atm */
347 /* kthreadd has ppid=0, which breaks our tree ordering */
352 while ((parent
->next_ps
&& parent
->pid
!= ps
->ppid
))
353 parent
= parent
->next_ps
;
355 if (parent
->pid
!= ps
->ppid
) {
358 parent
= ps_first
->next_ps
;
363 if (!parent
->children
) {
364 /* it's the first child */
365 parent
->children
= ps
;
367 /* walk all children and append */
368 struct ps_struct
*children
;
369 children
= parent
->children
;
370 while (children
->next
)
371 children
= children
->next
;
376 /* else -> found pid, append data in ps */
378 /* below here is all continuous logging parts - we get here on every
382 if (!ps
->schedstat
) {
383 sprintf(filename
, "%d/schedstat", pid
);
384 ps
->schedstat
= openat(procfd
, filename
, O_RDONLY
);
385 if (ps
->schedstat
== -1)
388 s
= pread(ps
->schedstat
, buf
, sizeof(buf
) - 1, 0);
390 /* clean up our file descriptors - assume that the process exited */
391 close(ps
->schedstat
);
395 // fclose(ps->smaps);
400 if (!sscanf(buf
, "%s %s %*s", rt
, wt
))
403 ps
->sample
->next
= new0(struct ps_sched_struct
, 1);
404 if (!ps
->sample
->next
) {
408 ps
->sample
->next
->prev
= ps
->sample
;
409 ps
->sample
= ps
->sample
->next
;
410 ps
->last
= ps
->sample
;
411 ps
->sample
->runtime
= atoll(rt
);
412 ps
->sample
->waittime
= atoll(wt
);
413 ps
->sample
->sampledata
= sampledata
;
414 ps
->sample
->ps_new
= ps
;
416 ps_prev
->cross
= ps
->sample
;
418 ps_prev
= ps
->sample
;
419 ps
->total
= (ps
->last
->runtime
- ps
->first
->runtime
)
427 sprintf(filename
, "%d/smaps", pid
);
428 fd
= openat(procfd
, filename
, O_RDONLY
);
429 ps
->smaps
= fdopen(fd
, "r");
432 setvbuf(ps
->smaps
, smaps_buf
, _IOFBF
, sizeof(smaps_buf
));
437 /* test to see if we need to skip another field */
439 if (fgets(buf
, sizeof(buf
), ps
->smaps
) == NULL
) {
442 if (fread(buf
, 1, 28 * 15, ps
->smaps
) != (28 * 15)) {
445 if (buf
[392] == 'V') {
456 /* skip one line, this contains the object mapped. */
457 if (fgets(buf
, sizeof(buf
), ps
->smaps
) == NULL
) {
460 /* then there's a 28 char 14 line block */
461 if (fread(buf
, 1, 28 * 14, ps
->smaps
) != 28 * 14) {
464 pss_kb
= atoi(&buf
[61]);
465 ps
->sample
->pss
+= pss_kb
;
467 /* skip one more line if this is a newer kernel */
469 if (fgets(buf
, sizeof(buf
), ps
->smaps
) == NULL
)
473 if (ps
->sample
->pss
> ps
->pss_max
)
474 ps
->pss_max
= ps
->sample
->pss
;
477 /* catch process rename, try to randomize time */
478 mod
= (arg_hz
< 4.0) ? 4.0 : (arg_hz
/ 4.0);
479 if (((samples
- ps
->pid
) + pid
) % (int)(mod
) == 0) {
482 /* get name, start time */
484 sprintf(filename
, "%d/sched", pid
);
485 ps
->sched
= openat(procfd
, filename
, O_RDONLY
);
489 s
= pread(ps
->sched
, buf
, sizeof(buf
) - 1, 0);
491 /* clean up file descriptors */
494 close(ps
->schedstat
);
496 // fclose(ps->smaps);
501 if (!sscanf(buf
, "%s %*s %*s", key
))
504 strscpy(ps
->name
, sizeof(ps
->name
), key
);
507 if (arg_show_cmdline
)
508 pid_cmdline_strscpy(ps
->name
, sizeof(ps
->name
), pid
);