~ubuntu-branches/ubuntu/trusty/systemd/trusty

« back to all changes in this revision

Viewing changes to src/bootchart/log.c

Tags: upstream-202
ImportĀ upstreamĀ versionĀ 202

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
int procfd=-1;
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 _cleanup_fclose_ *f = NULL;
61
 
        char str[32];
62
 
        double uptime;
63
 
 
64
 
        f = fopen("/proc/uptime", "r");
65
 
 
66
 
        if (!f)
67
 
                return;
68
 
        if (!fscanf(f, "%s %*s", str))
69
 
                return;
70
 
 
71
 
        uptime = strtod(str, NULL);
72
 
 
73
 
        log_start = gettime_ns();
74
 
 
75
 
        /* start graph at kernel boot time */
76
 
        if (relative)
77
 
                graph_start = log_start;
78
 
        else
79
 
                graph_start = log_start - uptime;
80
 
}
81
 
 
82
 
 
83
 
static char *bufgetline(char *buf)
84
 
{
85
 
        char *c;
86
 
 
87
 
        if (!buf)
88
 
                return NULL;
89
 
 
90
 
        c = strchr(buf, '\n');
91
 
        if (c)
92
 
                c++;
93
 
        return c;
94
 
}
95
 
 
96
 
static int pid_cmdline_strncpy(char *buffer, int pid, size_t buf_len) {
97
 
        char filename[PATH_MAX];
98
 
        int _cleanup_close_ fd=-1;
99
 
        ssize_t n;
100
 
 
101
 
        sprintf(filename, "%d/cmdline", pid);
102
 
        fd = openat(procfd, filename, O_RDONLY);
103
 
        if (fd < 0)
104
 
                return -errno;
105
 
 
106
 
        n = read(fd, buffer, buf_len-1);
107
 
        if (n > 0) {
108
 
                int i;
109
 
                for (i = 0; i < n; i++)
110
 
                        if (buffer[i] == '\0')
111
 
                                buffer[i] = ' ';
112
 
                buffer[n] = '\0';
113
 
        }
114
 
        return 0;
115
 
}
116
 
 
117
 
void log_sample(int sample)
118
 
{
119
 
        static int vmstat;
120
 
        static int schedstat;
121
 
        char buf[4095];
122
 
        char key[256];
123
 
        char val[256];
124
 
        char rt[256];
125
 
        char wt[256];
126
 
        char *m;
127
 
        int c;
128
 
        int p;
129
 
        int mod;
130
 
        static int e_fd;
131
 
        ssize_t s;
132
 
        ssize_t n;
133
 
        struct dirent *ent;
134
 
        int fd;
135
 
 
136
 
        /* all the per-process stuff goes here */
137
 
        if (!proc) {
138
 
                /* find all processes */
139
 
                proc = opendir("/proc");
140
 
                if (!proc)
141
 
                        return;
142
 
                procfd = dirfd(proc);
143
 
        } else {
144
 
                rewinddir(proc);
145
 
        }
146
 
 
147
 
        if (!vmstat) {
148
 
                /* block stuff */
149
 
                vmstat = openat(procfd, "vmstat", O_RDONLY);
150
 
                if (vmstat == -1) {
151
 
                        perror("open /proc/vmstat");
152
 
                        exit (EXIT_FAILURE);
153
 
                }
154
 
        }
155
 
 
156
 
        n = pread(vmstat, buf, sizeof(buf) - 1, 0);
157
 
        if (n <= 0) {
158
 
                close(vmstat);
159
 
                return;
160
 
        }
161
 
        buf[n] = '\0';
162
 
 
163
 
        m = buf;
164
 
        while (m) {
165
 
                if (sscanf(m, "%s %s", key, val) < 2)
166
 
                        goto vmstat_next;
167
 
                if (streq(key, "pgpgin"))
168
 
                        blockstat[sample].bi = atoi(val);
169
 
                if (streq(key, "pgpgout")) {
170
 
                        blockstat[sample].bo = atoi(val);
171
 
                        break;
172
 
                }
173
 
vmstat_next:
174
 
                m = bufgetline(m);
175
 
                if (!m)
176
 
                        break;
177
 
        }
178
 
 
179
 
        if (!schedstat) {
180
 
                /* overall CPU utilization */
181
 
                schedstat = openat(procfd, "schedstat", O_RDONLY);
182
 
                if (schedstat == -1) {
183
 
                        perror("open /proc/schedstat");
184
 
                        exit (EXIT_FAILURE);
185
 
                }
186
 
        }
187
 
 
188
 
        n = pread(schedstat, buf, sizeof(buf) - 1, 0);
189
 
        if (n <= 0) {
190
 
                close(schedstat);
191
 
                return;
192
 
        }
193
 
        buf[n] = '\0';
194
 
 
195
 
        m = buf;
196
 
        while (m) {
197
 
                if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
198
 
                        goto schedstat_next;
199
 
 
200
 
                if (strstr(key, "cpu")) {
201
 
                        c = atoi((const char*)(key+3));
202
 
                        if (c > MAXCPUS)
203
 
                                /* Oops, we only have room for MAXCPUS data */
204
 
                                break;
205
 
                        cpustat[c].sample[sample].runtime = atoll(rt);
206
 
                        cpustat[c].sample[sample].waittime = atoll(wt);
207
 
 
208
 
                        if (c == cpus)
209
 
                                cpus = c + 1;
210
 
                }
211
 
schedstat_next:
212
 
                m = bufgetline(m);
213
 
                if (!m)
214
 
                        break;
215
 
        }
216
 
 
217
 
        if (entropy) {
218
 
                if (!e_fd) {
219
 
                        e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
220
 
                }
221
 
 
222
 
                if (e_fd) {
223
 
                        n = pread(e_fd, buf, sizeof(buf) - 1, 0);
224
 
                        if (n > 0) {
225
 
                                buf[n] = '\0';
226
 
                                entropy_avail[sample] = atoi(buf);
227
 
                        }
228
 
                }
229
 
        }
230
 
 
231
 
        while ((ent = readdir(proc)) != NULL) {
232
 
                char filename[PATH_MAX];
233
 
                int pid;
234
 
                struct ps_struct *ps;
235
 
 
236
 
                if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
237
 
                        continue;
238
 
 
239
 
                pid = atoi(ent->d_name);
240
 
 
241
 
                if (pid >= MAXPIDS)
242
 
                        continue;
243
 
 
244
 
                ps = ps_first;
245
 
                while (ps->next_ps) {
246
 
                        ps = ps->next_ps;
247
 
                        if (ps->pid == pid)
248
 
                                break;
249
 
                }
250
 
 
251
 
                /* end of our LL? then append a new record */
252
 
                if (ps->pid != pid) {
253
 
                        FILE _cleanup_fclose_ *st = NULL;
254
 
                        char t[32];
255
 
                        struct ps_struct *parent;
256
 
 
257
 
                        ps->next_ps = calloc(1, sizeof(struct ps_struct));
258
 
                        if (!ps->next_ps) {
259
 
                                perror("calloc(ps_struct)");
260
 
                                exit (EXIT_FAILURE);
261
 
                        }
262
 
                        ps = ps->next_ps;
263
 
                        ps->pid = pid;
264
 
 
265
 
                        ps->sample = calloc(samples_len + 1, sizeof(struct ps_sched_struct));
266
 
                        if (!ps->sample) {
267
 
                                perror("calloc(ps_struct)");
268
 
                                exit (EXIT_FAILURE);
269
 
                        }
270
 
 
271
 
                        pscount++;
272
 
 
273
 
                        /* mark our first sample */
274
 
                        ps->first = sample;
275
 
 
276
 
                        /* get name, start time */
277
 
                        if (!ps->sched) {
278
 
                                sprintf(filename, "%d/sched", pid);
279
 
                                ps->sched = openat(procfd, filename, O_RDONLY);
280
 
                                if (ps->sched == -1)
281
 
                                        continue;
282
 
                        }
283
 
 
284
 
                        s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
285
 
                        if (s <= 0) {
286
 
                                close(ps->sched);
287
 
                                continue;
288
 
                        }
289
 
                        buf[s] = '\0';
290
 
 
291
 
                        if (!sscanf(buf, "%s %*s %*s", key))
292
 
                                continue;
293
 
 
294
 
                        strncpy(ps->name, key, 256);
295
 
 
296
 
                        /* cmdline */
297
 
                        if (show_cmdline)
298
 
                                pid_cmdline_strncpy(ps->name, pid, 256);
299
 
 
300
 
                        /* discard line 2 */
301
 
                        m = bufgetline(buf);
302
 
                        if (!m)
303
 
                                continue;
304
 
 
305
 
                        m = bufgetline(m);
306
 
                        if (!m)
307
 
                                continue;
308
 
 
309
 
                        if (!sscanf(m, "%*s %*s %s", t))
310
 
                                continue;
311
 
 
312
 
                        ps->starttime = strtod(t, NULL) / 1000.0;
313
 
 
314
 
                        /* ppid */
315
 
                        sprintf(filename, "%d/stat", pid);
316
 
                        fd = openat(procfd, filename, O_RDONLY);
317
 
                        st = fdopen(fd, "r");
318
 
                        if (!st)
319
 
                                continue;
320
 
                        if (!fscanf(st, "%*s %*s %*s %i", &p)) {
321
 
                                continue;
322
 
                        }
323
 
                        ps->ppid = p;
324
 
 
325
 
                        /*
326
 
                         * setup child pointers
327
 
                         *
328
 
                         * these are used to paint the tree coherently later
329
 
                         * each parent has a LL of children, and a LL of siblings
330
 
                         */
331
 
                        if (pid == 1)
332
 
                                continue; /* nothing to do for init atm */
333
 
 
334
 
                        /* kthreadd has ppid=0, which breaks our tree ordering */
335
 
                        if (ps->ppid == 0)
336
 
                                ps->ppid = 1;
337
 
 
338
 
                        parent = ps_first;
339
 
                        while ((parent->next_ps && parent->pid != ps->ppid))
340
 
                                parent = parent->next_ps;
341
 
 
342
 
                        if ((!parent) || (parent->pid != ps->ppid)) {
343
 
                                /* orphan */
344
 
                                ps->ppid = 1;
345
 
                                parent = ps_first->next_ps;
346
 
                        }
347
 
 
348
 
                        ps->parent = parent;
349
 
 
350
 
                        if (!parent->children) {
351
 
                                /* it's the first child */
352
 
                                parent->children = ps;
353
 
                        } else {
354
 
                                /* walk all children and append */
355
 
                                struct ps_struct *children;
356
 
                                children = parent->children;
357
 
                                while (children->next)
358
 
                                        children = children->next;
359
 
                                children->next = ps;
360
 
                        }
361
 
                }
362
 
 
363
 
                /* else -> found pid, append data in ps */
364
 
 
365
 
                /* below here is all continuous logging parts - we get here on every
366
 
                 * iteration */
367
 
 
368
 
                /* rt, wt */
369
 
                if (!ps->schedstat) {
370
 
                        sprintf(filename, "%d/schedstat", pid);
371
 
                        ps->schedstat = openat(procfd, filename, O_RDONLY);
372
 
                        if (ps->schedstat == -1)
373
 
                                continue;
374
 
                }
375
 
                s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
376
 
                if (s <= 0) {
377
 
                        /* clean up our file descriptors - assume that the process exited */
378
 
                        close(ps->schedstat);
379
 
                        if (ps->sched)
380
 
                                close(ps->sched);
381
 
                        //if (ps->smaps)
382
 
                        //        fclose(ps->smaps);
383
 
                        continue;
384
 
                }
385
 
                buf[s] = '\0';
386
 
 
387
 
                if (!sscanf(buf, "%s %s %*s", rt, wt))
388
 
                        continue;
389
 
 
390
 
                ps->last = sample;
391
 
                ps->sample[sample].runtime = atoll(rt);
392
 
                ps->sample[sample].waittime = atoll(wt);
393
 
 
394
 
                ps->total = (ps->sample[ps->last].runtime
395
 
                                 - ps->sample[ps->first].runtime)
396
 
                                 / 1000000000.0;
397
 
 
398
 
                if (!pss)
399
 
                        goto catch_rename;
400
 
                /* Pss */
401
 
                if (!ps->smaps) {
402
 
                        sprintf(filename, "%d/smaps", pid);
403
 
                        fd = openat(procfd, filename, O_RDONLY);
404
 
                        ps->smaps = fdopen(fd, "r");
405
 
                        if (!ps->smaps)
406
 
                                continue;
407
 
                        setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
408
 
                } else {
409
 
                        rewind(ps->smaps);
410
 
                }
411
 
 
412
 
                while (1) {
413
 
                        int pss_kb;
414
 
 
415
 
                        /* skip one line, this contains the object mapped */
416
 
                        if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
417
 
                                break;
418
 
                        /* then there's a 28 char 14 line block */
419
 
                        if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
420
 
                                break;
421
 
 
422
 
                        pss_kb = atoi(&buf[61]);
423
 
                        ps->sample[sample].pss += pss_kb;
424
 
                }
425
 
 
426
 
                if (ps->sample[sample].pss > ps->pss_max)
427
 
                        ps->pss_max = ps->sample[sample].pss;
428
 
 
429
 
catch_rename:
430
 
                /* catch process rename, try to randomize time */
431
 
                mod = (hz < 4.0) ? 4.0 : (hz / 4.0);
432
 
                if (((samples - ps->first) + pid) % (int)(mod) == 0) {
433
 
 
434
 
                        /* re-fetch name */
435
 
                        /* get name, start time */
436
 
                        if (!ps->sched) {
437
 
                                sprintf(filename, "%d/sched", pid);
438
 
                                ps->sched = openat(procfd, filename, O_RDONLY);
439
 
                                if (ps->sched == -1)
440
 
                                        continue;
441
 
                        }
442
 
                        s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
443
 
                        if (s <= 0) {
444
 
                                /* clean up file descriptors */
445
 
                                close(ps->sched);
446
 
                                if (ps->schedstat)
447
 
                                        close(ps->schedstat);
448
 
                                //if (ps->smaps)
449
 
                                //        fclose(ps->smaps);
450
 
                                continue;
451
 
                        }
452
 
                        buf[s] = '\0';
453
 
 
454
 
                        if (!sscanf(buf, "%s %*s %*s", key))
455
 
                                continue;
456
 
 
457
 
                        strncpy(ps->name, key, 256);
458
 
 
459
 
                        /* cmdline */
460
 
                        if (show_cmdline)
461
 
                                pid_cmdline_strncpy(ps->name, pid, 256);
462
 
                }
463
 
        }
464
 
}