]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/bootchart/log.c
bootchart: Convert malloc/memset to calloc
[thirdparty/systemd.git] / src / bootchart / log.c
CommitLineData
83fdc450
AK
1/*
2 * log.c
3 *
4 * Copyright (C) 2009-2012 Intel Coproration
5 *
6 * Authors:
7 * Auke Kok <auke-jan.h.kok@intel.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; version 2
12 * of the License.
13 */
14
15#define _GNU_SOURCE 1
16#include <unistd.h>
17#include <stdlib.h>
18#include <limits.h>
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <stdio.h>
22#include <string.h>
23#include <dirent.h>
24#include <fcntl.h>
25#include <time.h>
26
27
28#include "bootchart.h"
53f5329f 29#include "util.h"
83fdc450
AK
30
31/*
32 * Alloc a static 4k buffer for stdio - primarily used to increase
33 * PSS buffering from the default 1k stdin buffer to reduce
34 * read() overhead.
35 */
36static char smaps_buf[4096];
37DIR *proc;
38
39
40double gettime_ns(void)
41{
28989b63 42 struct timespec now;
83fdc450 43
28989b63 44 clock_gettime(CLOCK_MONOTONIC, &now);
83fdc450 45
28989b63 46 return (now.tv_sec + (now.tv_nsec / 1000000000.0));
83fdc450
AK
47}
48
49
50void log_uptime(void)
51{
28989b63
TA
52 FILE *f;
53 char str[32];
54 double uptime;
55
56 f = fopen("/proc/uptime", "r");
57 if (!f)
58 return;
59 if (!fscanf(f, "%s %*s", str)) {
60 fclose(f);
61 return;
62 }
63 fclose(f);
64 uptime = strtod(str, NULL);
65
66 log_start = gettime_ns();
67
68 /* start graph at kernel boot time */
69 if (relative)
70 graph_start = log_start;
71 else
72 graph_start = log_start - uptime;
83fdc450
AK
73}
74
75
76static char *bufgetline(char *buf)
77{
28989b63 78 char *c;
83fdc450 79
28989b63
TA
80 if (!buf)
81 return NULL;
83fdc450 82
28989b63
TA
83 c = strchr(buf, '\n');
84 if (c)
85 c++;
86 return c;
83fdc450
AK
87}
88
89
90void log_sample(int sample)
91{
28989b63
TA
92 static int vmstat;
93 static int schedstat;
94 FILE *st;
95 char buf[4095];
96 char key[256];
97 char val[256];
98 char rt[256];
99 char wt[256];
100 char *m;
101 int c;
102 int p;
103 int mod;
104 static int e_fd;
105 ssize_t s;
106 ssize_t n;
107 struct dirent *ent;
108
109 if (!vmstat) {
110 /* block stuff */
111 vmstat = open("/proc/vmstat", O_RDONLY);
112 if (vmstat == -1) {
113 perror("open /proc/vmstat");
114 exit (EXIT_FAILURE);
115 }
116 }
117
118 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
119 if (n <= 0) {
120 close(vmstat);
121 return;
122 }
123 buf[n] = '\0';
124
125 m = buf;
126 while (m) {
127 if (sscanf(m, "%s %s", key, val) < 2)
128 goto vmstat_next;
53f5329f 129 if (streq(key, "pgpgin"))
28989b63 130 blockstat[sample].bi = atoi(val);
53f5329f 131 if (streq(key, "pgpgout")) {
28989b63
TA
132 blockstat[sample].bo = atoi(val);
133 break;
134 }
83fdc450 135vmstat_next:
28989b63
TA
136 m = bufgetline(m);
137 if (!m)
138 break;
139 }
140
141 if (!schedstat) {
142 /* overall CPU utilization */
143 schedstat = open("/proc/schedstat", O_RDONLY);
144 if (schedstat == -1) {
145 perror("open /proc/schedstat");
146 exit (EXIT_FAILURE);
147 }
148 }
149
150 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
151 if (n <= 0) {
152 close(schedstat);
153 return;
154 }
155 buf[n] = '\0';
156
157 m = buf;
158 while (m) {
159 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
160 goto schedstat_next;
161
162 if (strstr(key, "cpu")) {
163 c = atoi((const char*)(key+3));
164 if (c > MAXCPUS)
165 /* Oops, we only have room for MAXCPUS data */
166 break;
167 cpustat[c].sample[sample].runtime = atoll(rt);
168 cpustat[c].sample[sample].waittime = atoll(wt);
169
170 if (c == cpus)
171 cpus = c + 1;
172 }
83fdc450 173schedstat_next:
28989b63
TA
174 m = bufgetline(m);
175 if (!m)
176 break;
177 }
178
179 if (entropy) {
180 if (!e_fd) {
181 e_fd = open("/proc/sys/kernel/random/entropy_avail", O_RDONLY);
182 }
183
184 if (e_fd) {
185 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
ef2648c1
LN
186 if (n > 0) {
187 buf[n] = '\0';
28989b63 188 entropy_avail[sample] = atoi(buf);
ef2648c1 189 }
28989b63
TA
190 }
191 }
192
193 /* all the per-process stuff goes here */
194 if (!proc) {
195 /* find all processes */
196 proc = opendir("/proc");
197 if (!proc)
198 return;
199 } else {
200 rewinddir(proc);
201 }
202
203 while ((ent = readdir(proc)) != NULL) {
204 char filename[PATH_MAX];
205 int pid;
206 struct ps_struct *ps;
207
208 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
209 continue;
210
211 pid = atoi(ent->d_name);
212
213 if (pid >= MAXPIDS)
214 continue;
215
216 ps = ps_first;
217 while (ps->next_ps) {
218 ps = ps->next_ps;
219 if (ps->pid == pid)
220 break;
221 }
222
223 /* end of our LL? then append a new record */
224 if (ps->pid != pid) {
225 char t[32];
226 struct ps_struct *parent;
227
a2e9b338 228 ps->next_ps = calloc(1, sizeof(struct ps_struct));
28989b63 229 if (!ps->next_ps) {
a2e9b338 230 perror("calloc(ps_struct)");
28989b63
TA
231 exit (EXIT_FAILURE);
232 }
28989b63
TA
233 ps = ps->next_ps;
234 ps->pid = pid;
235
a2e9b338 236 ps->sample = calloc(len + 1, sizeof(struct ps_sched_struct));
28989b63 237 if (!ps->sample) {
a2e9b338 238 perror("calloc(ps_struct)");
28989b63
TA
239 exit (EXIT_FAILURE);
240 }
28989b63
TA
241
242 pscount++;
243
244 /* mark our first sample */
245 ps->first = sample;
246
247 /* get name, start time */
248 if (!ps->sched) {
249 sprintf(filename, "/proc/%d/sched", pid);
250 ps->sched = open(filename, O_RDONLY);
251 if (ps->sched == -1)
252 continue;
253 }
254
255 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
256 if (s <= 0) {
257 close(ps->sched);
258 continue;
259 }
ef2648c1 260 buf[s] = '\0';
28989b63
TA
261
262 if (!sscanf(buf, "%s %*s %*s", key))
263 continue;
264
265 strncpy(ps->name, key, 16);
266 /* discard line 2 */
267 m = bufgetline(buf);
268 if (!m)
269 continue;
270
271 m = bufgetline(m);
272 if (!m)
273 continue;
274
275 if (!sscanf(m, "%*s %*s %s", t))
276 continue;
277
278 ps->starttime = strtod(t, NULL) / 1000.0;
279
280 /* ppid */
281 sprintf(filename, "/proc/%d/stat", pid);
282 st = fopen(filename, "r");
283 if (!st)
284 continue;
285 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
286 fclose(st);
287 continue;
288 }
289 fclose(st);
290 ps->ppid = p;
291
292 /*
293 * setup child pointers
294 *
295 * these are used to paint the tree coherently later
296 * each parent has a LL of children, and a LL of siblings
297 */
298 if (pid == 1)
299 continue; /* nothing to do for init atm */
300
301 /* kthreadd has ppid=0, which breaks our tree ordering */
302 if (ps->ppid == 0)
303 ps->ppid = 1;
304
305 parent = ps_first;
306 while ((parent->next_ps && parent->pid != ps->ppid))
307 parent = parent->next_ps;
308
309 if ((!parent) || (parent->pid != ps->ppid)) {
310 /* orphan */
311 ps->ppid = 1;
312 parent = ps_first->next_ps;
313 }
314
315 ps->parent = parent;
316
317 if (!parent->children) {
318 /* it's the first child */
319 parent->children = ps;
320 } else {
321 /* walk all children and append */
322 struct ps_struct *children;
323 children = parent->children;
324 while (children->next)
325 children = children->next;
326 children->next = ps;
327 }
328 }
329
330 /* else -> found pid, append data in ps */
331
332 /* below here is all continuous logging parts - we get here on every
333 * iteration */
334
335 /* rt, wt */
336 if (!ps->schedstat) {
337 sprintf(filename, "/proc/%d/schedstat", pid);
338 ps->schedstat = open(filename, O_RDONLY);
339 if (ps->schedstat == -1)
340 continue;
341 }
ef2648c1
LN
342 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
343 if (s <= 0) {
28989b63
TA
344 /* clean up our file descriptors - assume that the process exited */
345 close(ps->schedstat);
346 if (ps->sched)
347 close(ps->sched);
348 //if (ps->smaps)
349 // fclose(ps->smaps);
350 continue;
351 }
ef2648c1
LN
352 buf[s] = '\0';
353
28989b63
TA
354 if (!sscanf(buf, "%s %s %*s", rt, wt))
355 continue;
356
357 ps->last = sample;
358 ps->sample[sample].runtime = atoll(rt);
359 ps->sample[sample].waittime = atoll(wt);
360
361 ps->total = (ps->sample[ps->last].runtime
362 - ps->sample[ps->first].runtime)
363 / 1000000000.0;
364
365 if (!pss)
366 goto catch_rename;
367 /* Pss */
368 if (!ps->smaps) {
369 sprintf(filename, "/proc/%d/smaps", pid);
370 ps->smaps = fopen(filename, "r");
371 if (!ps->smaps)
372 continue;
373 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
374 } else {
375 rewind(ps->smaps);
376 }
377
378 while (1) {
379 int pss_kb;
380
381 /* skip one line, this contains the object mapped */
382 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
383 break;
384 /* then there's a 28 char 14 line block */
385 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
386 break;
387
388 pss_kb = atoi(&buf[61]);
389 ps->sample[sample].pss += pss_kb;
390 }
391
392 if (ps->sample[sample].pss > ps->pss_max)
393 ps->pss_max = ps->sample[sample].pss;
83fdc450
AK
394
395catch_rename:
28989b63
TA
396 /* catch process rename, try to randomize time */
397 mod = (hz < 4.0) ? 4.0 : (hz / 4.0);
398 if (((samples - ps->first) + pid) % (int)(mod) == 0) {
399
400 /* re-fetch name */
401 /* get name, start time */
402 if (!ps->sched) {
403 sprintf(filename, "/proc/%d/sched", pid);
404 ps->sched = open(filename, O_RDONLY);
405 if (ps->sched == -1)
406 continue;
407 }
ef2648c1
LN
408 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
409 if (s <= 0) {
28989b63
TA
410 /* clean up file descriptors */
411 close(ps->sched);
412 if (ps->schedstat)
413 close(ps->schedstat);
414 //if (ps->smaps)
415 // fclose(ps->smaps);
416 continue;
417 }
ef2648c1 418 buf[s] = '\0';
28989b63
TA
419
420 if (!sscanf(buf, "%s %*s %*s", key))
421 continue;
422
423 strncpy(ps->name, key, 16);
424 }
425 }
83fdc450 426}