chiark / gitweb /
e34c08a5eb54f16bb71e570f991ff4fb6c3464c0
[elogind.git] / src / bootchart / bootchart.c
1 /***
2   bootchart.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 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/resource.h>
26 #include <sys/stat.h>
27 #include <stdio.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <getopt.h>
34 #include <limits.h>
35 #include <errno.h>
36 #include <fcntl.h>
37
38
39 #include "bootchart.h"
40 #include "util.h"
41 #include "fileio.h"
42
43 double graph_start;
44 double log_start;
45 double sampletime[MAXSAMPLES];
46 struct ps_struct *ps_first;
47 struct block_stat_struct blockstat[MAXSAMPLES];
48 int entropy_avail[MAXSAMPLES];
49 struct cpu_stat_struct cpustat[MAXCPUS];
50 int pscount;
51 int cpus;
52 double interval;
53 FILE *of = NULL;
54 int overrun = 0;
55 static int exiting = 0;
56 int sysfd=-1;
57
58 /* graph defaults */
59 int entropy = 0;
60 int initcall = 1;
61 int relative;
62 int filter = 1;
63 int pss = 0;
64 int samples;
65 int len = 500; /* we record len+1 (1 start sample) */
66 double hz = 25.0;   /* 20 seconds log time */
67 double scale_x = 100.0; /* 100px = 1sec */
68 double scale_y = 20.0;  /* 16px = 1 process bar */
69
70 char init_path[PATH_MAX] = "/sbin/init";
71 char output_path[PATH_MAX] = "/run/log";
72
73 static struct rlimit rlim;
74
75 static void signal_handler(int sig)
76 {
77         if (sig++)
78                 sig--;
79         exiting = 1;
80 }
81
82
83 int main(int argc, char *argv[])
84 {
85         struct sigaction sig;
86         struct ps_struct *ps;
87         char output_file[PATH_MAX];
88         char datestr[200];
89         time_t t = 0;
90         FILE *f;
91         int gind;
92         int i;
93
94         rlim.rlim_cur = 4096;
95         rlim.rlim_max = 4096;
96         (void) setrlimit(RLIMIT_NOFILE, &rlim);
97
98         f = fopen("/etc/systemd/bootchart.conf", "r");
99         if (f) {
100                 char buf[256];
101                 char *key;
102                 char *val;
103
104                 while (fgets(buf, 80, f) != NULL) {
105                         char *c;
106
107                         c = strchr(buf, '\n');
108                         if (c) *c = 0; /* remove trailing \n */
109
110                         if (buf[0] == '#')
111                                 continue; /* comment line */
112
113                         key = strtok(buf, "=");
114                         if (!key)
115                                 continue;
116                         val = strtok(NULL, "=");
117                         if (!val)
118                                 continue;
119
120                         // todo: filter leading/trailing whitespace
121
122                         if (streq(key, "samples"))
123                                 len = atoi(val);
124                         if (streq(key, "freq"))
125                                 hz = atof(val);
126                         if (streq(key, "rel"))
127                                 relative = atoi(val);
128                         if (streq(key, "filter"))
129                                 filter = atoi(val);
130                         if (streq(key, "pss"))
131                                 pss = atoi(val);
132                         if (streq(key, "output"))
133                                 strncpy(output_path, val, PATH_MAX - 1);
134                         if (streq(key, "init"))
135                                 strncpy(init_path, val, PATH_MAX - 1);
136                         if (streq(key, "scale_x"))
137                                 scale_x = atof(val);
138                         if (streq(key, "scale_y"))
139                                 scale_y = atof(val);
140                         if (streq(key, "entropy"))
141                                 entropy = atoi(val);
142                 }
143                 fclose(f);
144         }
145
146         while (1) {
147                 static struct option opts[] = {
148                         {"rel", 0, NULL, 'r'},
149                         {"freq", 1, NULL, 'f'},
150                         {"samples", 1, NULL, 'n'},
151                         {"pss", 0, NULL, 'p'},
152                         {"output", 1, NULL, 'o'},
153                         {"init", 1, NULL, 'i'},
154                         {"filter", 0, NULL, 'F'},
155                         {"help", 0, NULL, 'h'},
156                         {"scale-x", 1, NULL, 'x'},
157                         {"scale-y", 1, NULL, 'y'},
158                         {"entropy", 0, NULL, 'e'},
159                         {NULL, 0, NULL, 0}
160                 };
161
162                 gind = 0;
163
164                 i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
165                 if (i == -1)
166                         break;
167                 switch (i) {
168                 case 'r':
169                         relative = 1;
170                         break;
171                 case 'f':
172                         hz = atof(optarg);
173                         break;
174                 case 'F':
175                         filter = 0;
176                         break;
177                 case 'n':
178                         len = atoi(optarg);
179                         break;
180                 case 'o':
181                         strncpy(output_path, optarg, PATH_MAX - 1);
182                         break;
183                 case 'i':
184                         strncpy(init_path, optarg, PATH_MAX - 1);
185                         break;
186                 case 'p':
187                         pss = 1;
188                         break;
189                 case 'x':
190                         scale_x = atof(optarg);
191                         break;
192                 case 'y':
193                         scale_y = atof(optarg);
194                         break;
195                 case 'e':
196                         entropy = 1;
197                         break;
198                 case 'h':
199                         fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
200                         fprintf(stderr, " --rel,     -r            Record time relative to recording\n");
201                         fprintf(stderr, " --freq,    -f N          Sample frequency [%f]\n", hz);
202                         fprintf(stderr, " --samples, -n N          Stop sampling at [%d] samples\n", len);
203                         fprintf(stderr, " --scale-x, -x N          Scale the graph horizontally [%f] \n", scale_x);
204                         fprintf(stderr, " --scale-y, -y N          Scale the graph vertically [%f] \n", scale_y);
205                         fprintf(stderr, " --pss,     -p            Enable PSS graph (CPU intensive)\n");
206                         fprintf(stderr, " --entropy, -e            Enable the entropy_avail graph\n");
207                         fprintf(stderr, " --output,  -o [PATH]     Path to output files [%s]\n", output_path);
208                         fprintf(stderr, " --init,    -i [PATH]     Path to init executable [%s]\n", init_path);
209                         fprintf(stderr, " --filter,  -F            Disable filtering of processes from the graph\n");
210                         fprintf(stderr, "                          that are of less importance or short-lived\n");
211                         fprintf(stderr, " --help,    -h            Display this message\n");
212                         fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
213                         exit (EXIT_SUCCESS);
214                         break;
215                 default:
216                         break;
217                 }
218         }
219
220         if (len > MAXSAMPLES) {
221                 fprintf(stderr, "Error: samples exceeds maximum\n");
222                 exit(EXIT_FAILURE);
223         }
224
225         if (hz <= 0.0) {
226                 fprintf(stderr, "Error: Frequency needs to be > 0\n");
227                 exit(EXIT_FAILURE);
228         }
229
230         /*
231          * If the kernel executed us through init=/sbin/bootchartd, then
232          * fork:
233          * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
234          * - child logs data
235          */
236         if (getpid() == 1) {
237                 if (fork()) {
238                         /* parent */
239                         execl(init_path, init_path, NULL);
240                 }
241         }
242         argv[0][0] = '@';
243
244         /* start with empty ps LL */
245         ps_first = calloc(1, sizeof(struct ps_struct));
246         if (!ps_first) {
247                 perror("calloc(ps_struct)");
248                 exit(EXIT_FAILURE);
249         }
250
251         /* handle TERM/INT nicely */
252         memset(&sig, 0, sizeof(struct sigaction));
253         sig.sa_handler = signal_handler;
254         sigaction(SIGHUP, &sig, NULL);
255
256         interval = (1.0 / hz) * 1000000000.0;
257
258         log_uptime();
259
260         /* main program loop */
261         while (!exiting) {
262                 int res;
263                 double sample_stop;
264                 struct timespec req;
265                 time_t newint_s;
266                 long newint_ns;
267                 double elapsed;
268                 double timeleft;
269
270                 sampletime[samples] = gettime_ns();
271
272                 if (!of && (access(output_path, R_OK|W_OK|X_OK) == 0)) {
273                         t = time(NULL);
274                         strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
275                         snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
276                         of = fopen(output_file, "w");
277                 }
278
279                 if (sysfd < 0) {
280                         sysfd = open("/sys", O_RDONLY);
281                 }
282
283                 /* wait for /proc to become available, discarding samples */
284                 if (!(graph_start > 0.0))
285                         log_uptime();
286                 else
287                         log_sample(samples);
288
289                 sample_stop = gettime_ns();
290
291                 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
292                 timeleft = interval - elapsed;
293
294                 newint_s = (time_t)(timeleft / 1000000000.0);
295                 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
296
297                 /*
298                  * check if we have not consumed our entire timeslice. If we
299                  * do, don't sleep and take a new sample right away.
300                  * we'll lose all the missed samples and overrun our total
301                  * time
302                  */
303                 if ((newint_ns > 0) || (newint_s > 0)) {
304                         req.tv_sec = newint_s;
305                         req.tv_nsec = newint_ns;
306
307                         res = nanosleep(&req, NULL);
308                         if (res) {
309                                 if (errno == EINTR) {
310                                         /* caught signal, probably HUP! */
311                                         break;
312                                 }
313                                 perror("nanosleep()");
314                                 exit (EXIT_FAILURE);
315                         }
316                 } else {
317                         overrun++;
318                         /* calculate how many samples we lost and scrap them */
319                         len = len + ((int)(newint_ns / interval));
320                 }
321
322                 samples++;
323
324                 if (samples > len)
325                         break;
326
327         }
328
329         /* do some cleanup, close fd's */
330         ps = ps_first;
331         while (ps->next_ps) {
332                 ps = ps->next_ps;
333                 if (ps->schedstat)
334                         close(ps->schedstat);
335                 if (ps->sched)
336                         close(ps->sched);
337                 if (ps->smaps)
338                         fclose(ps->smaps);
339         }
340
341         if (!of) {
342                 t = time(NULL);
343                 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
344                 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
345                 of = fopen(output_file, "w");
346         }
347
348         if (!of) {
349                 perror("open output_file");
350                 exit (EXIT_FAILURE);
351         }
352
353         svg_do();
354
355         fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
356         fclose(of);
357
358         closedir(proc);
359         close(sysfd);
360
361         /* nitpic cleanups */
362         ps = ps_first;
363         while (ps->next_ps) {
364                 struct ps_struct *old = ps;
365                 ps = ps->next_ps;
366                 free(old->sample);
367                 free(old);
368         }
369         free(ps->sample);
370         free(ps);
371
372         /* don't complain when overrun once, happens most commonly on 1st sample */
373         if (overrun > 1)
374                 fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);
375
376         return 0;
377 }