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