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