Esempio n. 1
0
int main(int argc, char *argv[]) {
        _cleanup_free_ char *build = NULL;
        struct sigaction sig = {
                .sa_handler = signal_handler,
        };
        struct ps_struct *ps;
        char output_file[PATH_MAX];
        char datestr[200];
        time_t t = 0;
        int r;
        struct rlimit rlim;

        parse_conf();

        r = parse_args(argc, argv);
        if (r < 0)
                return EXIT_FAILURE;

        /*
         * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
         * fork:
         * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
         * - child logs data
         */
        if (getpid() == 1) {
                if (fork()) {
                        /* parent */
                        execl(arg_init_path, arg_init_path, NULL);
                }
        }
        argv[0][0] = '@';

        rlim.rlim_cur = 4096;
        rlim.rlim_max = 4096;
        (void) setrlimit(RLIMIT_NOFILE, &rlim);

        /* start with empty ps LL */
        ps_first = calloc(1, sizeof(struct ps_struct));
        if (!ps_first) {
                perror("calloc(ps_struct)");
                exit(EXIT_FAILURE);
        }

        /* handle TERM/INT nicely */
        sigaction(SIGHUP, &sig, NULL);

        interval = (1.0 / arg_hz) * 1000000000.0;

        log_uptime();

        LIST_HEAD_INIT(struct list_sample_data, head);

        /* main program loop */
        for (samples = 0; !exiting && samples < arg_samples_len; samples++) {
                int res;
                double sample_stop;
                struct timespec req;
                time_t newint_s;
                long newint_ns;
                double elapsed;
                double timeleft;

                sampledata = new0(struct list_sample_data, 1);
                if (sampledata == NULL) {
                        log_error("Failed to allocate memory for a node: %m");
                        return -1;
                }

                sampledata->sampletime = gettime_ns();
                sampledata->counter = samples;

                if (!of && (access(arg_output_path, R_OK|W_OK|X_OK) == 0)) {
                        t = time(NULL);
                        strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
                        snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
                        of = fopen(output_file, "w");
                }

                if (sysfd < 0)
                        sysfd = open("/sys", O_RDONLY);

                if (!build)
                        parse_env_file("/etc/os-release", NEWLINE,
                                       "PRETTY_NAME", &build,
                                       NULL);

                /* wait for /proc to become available, discarding samples */
                if (graph_start <= 0.0)
                        log_uptime();
                else
                        log_sample(samples, &sampledata);

                sample_stop = gettime_ns();

                elapsed = (sample_stop - sampledata->sampletime) * 1000000000.0;
                timeleft = interval - elapsed;

                newint_s = (time_t)(timeleft / 1000000000.0);
                newint_ns = (long)(timeleft - (newint_s * 1000000000.0));

                /*
                 * check if we have not consumed our entire timeslice. If we
                 * do, don't sleep and take a new sample right away.
                 * we'll lose all the missed samples and overrun our total
                 * time
                 */
                if (newint_ns > 0 || newint_s > 0) {
                        req.tv_sec = newint_s;
                        req.tv_nsec = newint_ns;

                        res = nanosleep(&req, NULL);
                        if (res) {
                                if (errno == EINTR) {
                                        /* caught signal, probably HUP! */
                                        break;
                                }
                                perror("nanosleep()");
                                exit (EXIT_FAILURE);
                        }
                } else {
                        overrun++;
                        /* calculate how many samples we lost and scrap them */
                        arg_samples_len -= (int)(newint_ns / interval);
                }
                LIST_PREPEND(struct list_sample_data, link, head, sampledata);
        }

        /* do some cleanup, close fd's */
        ps = ps_first;
        while (ps->next_ps) {
                ps = ps->next_ps;
                if (ps->schedstat)
                        close(ps->schedstat);
                if (ps->sched)
                        close(ps->sched);
                if (ps->smaps)
                        fclose(ps->smaps);
        }

        if (!of) {
                t = time(NULL);
                strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
                snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
                of = fopen(output_file, "w");
        }

        if (!of) {
                fprintf(stderr, "opening output file '%s': %m\n", output_file);
                exit (EXIT_FAILURE);
        }

        svg_do(build);

        fprintf(stderr, "systemd-bootchart wrote %s\n", output_file);

        do_journal_append(output_file);

        if (of)
                fclose(of);

        closedir(proc);
        if (sysfd >= 0)
                close(sysfd);

        /* nitpic cleanups */
        ps = ps_first->next_ps;
        while (ps->next_ps) {
                struct ps_struct *old;

                old = ps;
                old->sample = ps->first;
                ps = ps->next_ps;
                while (old->sample->next) {
                        struct ps_sched_struct *oldsample = old->sample;

                        old->sample = old->sample->next;
                        free(oldsample);
                }
                free(old->sample);
                free(old);
        }
        free(ps->sample);
        free(ps);

        sampledata = head;
        while (sampledata->link_prev) {
                struct list_sample_data *old_sampledata = sampledata;
                sampledata = sampledata->link_prev;
                free(old_sampledata);
        }
        free(sampledata);
        /* don't complain when overrun once, happens most commonly on 1st sample */
        if (overrun > 1)
                fprintf(stderr, "systemd-boochart: Warning: sample time overrun %i times\n", overrun);

        return 0;
}
Esempio n. 2
0
int main(int argc, char *argv[]) {
        static struct list_sample_data *sampledata;
        _cleanup_closedir_ DIR *proc = NULL;
        _cleanup_free_ char *build = NULL;
        _cleanup_fclose_ FILE *of = NULL;
        _cleanup_close_ int sysfd = -1;
        int schfd;
        struct ps_struct *ps_first;
        double graph_start;
        double log_start;
        double interval;
        char output_file[PATH_MAX];
        char datestr[200];
        int pscount = 0;
        int n_cpus = 0;
        int overrun = 0;
        time_t t = 0;
        int r, samples;
        struct ps_struct *ps;
        struct rlimit rlim;
        struct list_sample_data *head;
        struct sigaction sig = {
                .sa_handler = signal_handler,
        };

        parse_conf();

        r = parse_argv(argc, argv);
        if (r < 0)
                return EXIT_FAILURE;

        if (r == 0)
                return EXIT_SUCCESS;

        /*
         * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then
         * fork:
         * - parent execs executable specified via init_path[] (/usr/lib/systemd/systemd by default) as pid=1
         * - child logs data
         */
        if (getpid() == 1) {
                if (fork())
                        /* parent */
                        execl(arg_init_path, arg_init_path, NULL);
        }
        argv[0][0] = '@';

        rlim.rlim_cur = 4096;
        rlim.rlim_max = 4096;
        (void) setrlimit(RLIMIT_NOFILE, &rlim);

        schfd = open("/proc/sys/kernel/sched_schedstats", O_WRONLY);
        if (schfd >= 0) {
                write(schfd, "1\n", 2);
                close(schfd);
        }

        /* start with empty ps LL */
        ps_first = new0(struct ps_struct, 1);
        if (!ps_first) {
                log_oom();
                return EXIT_FAILURE;
        }

        /* handle TERM/INT nicely */
        sigaction(SIGHUP, &sig, NULL);

        interval = (1.0 / arg_hz) * 1000000000.0;

        if (arg_relative)
                graph_start = log_start = gettime_ns();
        else {
                struct timespec n;
                double uptime;

                clock_gettime(clock_boottime_or_monotonic(), &n);
                uptime = (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));

                log_start = gettime_ns();
                graph_start = log_start - uptime;
        }

        if (graph_start < 0.0) {
                log_error("Failed to setup graph start time.\n\n"
                          "The system uptime probably includes time that the system was suspended. "
                          "Use --rel to bypass this issue.");
                return EXIT_FAILURE;
        }

        LIST_HEAD_INIT(head);

        /* main program loop */
        for (samples = 0; !exiting && samples < arg_samples_len; samples++) {
                int res;
                double sample_stop;
                double elapsed;
                double timeleft;

                sampledata = new0(struct list_sample_data, 1);
                if (sampledata == NULL) {
                        log_oom();
                        return EXIT_FAILURE;
                }

                sampledata->sampletime = gettime_ns();
                sampledata->counter = samples;

                if (sysfd < 0)
                        sysfd = open("/sys", O_RDONLY|O_CLOEXEC);

                if (!build) {
                        if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &build, NULL) == -ENOENT)
                                parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &build, NULL);
                }

                if (proc)
                        rewinddir(proc);
                else
                        proc = opendir("/proc");

                /* wait for /proc to become available, discarding samples */
                if (proc) {
                        r = log_sample(proc, samples, ps_first, &sampledata, &pscount, &n_cpus);
                        if (r < 0)
                                return EXIT_FAILURE;
                }

                sample_stop = gettime_ns();

                elapsed = (sample_stop - sampledata->sampletime) * 1000000000.0;
                timeleft = interval - elapsed;

                /*
                 * check if we have not consumed our entire timeslice. If we
                 * do, don't sleep and take a new sample right away.
                 * we'll lose all the missed samples and overrun our total
                 * time
                 */
                if (timeleft > 0) {
                        struct timespec req;

                        req.tv_sec = (time_t)(timeleft / 1000000000.0);
                        req.tv_nsec = (long)(timeleft - (req.tv_sec * 1000000000.0));

                        res = nanosleep(&req, NULL);
                        if (res) {
                                if (errno == EINTR)
                                        /* caught signal, probably HUP! */
                                        break;
                                log_error_errno(errno, "nanosleep() failed: %m");
                                return EXIT_FAILURE;
                        }
                } else {
                        overrun++;
                        /* calculate how many samples we lost and scrap them */
                        arg_samples_len -= (int)(-timeleft / interval);
                }
                LIST_PREPEND(link, head, sampledata);
        }

        /* do some cleanup, close fd's */
        ps = ps_first;
        while (ps->next_ps) {
                ps = ps->next_ps;
                ps->schedstat = safe_close(ps->schedstat);
                ps->sched = safe_close(ps->sched);
                ps->smaps = safe_fclose(ps->smaps);
        }

        if (!of) {
                t = time(NULL);
                r = strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
                assert_se(r > 0);

                snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr);
                of = fopen(output_file, "we");
        }

        if (!of) {
                log_error("Error opening output file '%s': %m\n", output_file);
                return EXIT_FAILURE;
        }

        r = svg_do(of, strna(build), head, ps_first,
                   samples, pscount, n_cpus, graph_start,
                   log_start, interval, overrun);

        if (r < 0) {
                log_error_errno(r, "Error generating svg file: %m");
                return EXIT_FAILURE;
        }

        log_info("systemd-bootchart wrote %s\n", output_file);

        r = do_journal_append(output_file);
        if (r < 0)
                return EXIT_FAILURE;

        /* nitpic cleanups */
        ps = ps_first->next_ps;
        while (ps->next_ps) {
                struct ps_struct *old;

                old = ps;
                old->sample = ps->first;
                ps = ps->next_ps;
                while (old->sample->next) {
                        struct ps_sched_struct *oldsample = old->sample;

                        old->sample = old->sample->next;
                        free(oldsample);
                }
                free(old->cgroup);
                free(old->sample);
                free(old);
        }

        free(ps->cgroup);
        free(ps->sample);
        free(ps);

        sampledata = head;
        while (sampledata->link_prev) {
                struct list_sample_data *old_sampledata = sampledata;
                sampledata = sampledata->link_prev;
                free(old_sampledata);
        }
        free(sampledata);

        /* don't complain when overrun once, happens most commonly on 1st sample */
        if (overrun > 1)
                log_warning("systemd-bootchart: sample time overrun %i times\n", overrun);

        return 0;
}