diff --git a/src/bootchart.c b/src/bootchart.c index 15aaa2e..b85a43e 100644 --- a/src/bootchart.c +++ b/src/bootchart.c @@ -80,6 +80,7 @@ static int exiting = 0; bool arg_entropy = false; bool arg_initcall = true; bool arg_relative = false; +bool arg_raw_dumps = false; bool arg_filter = true; bool arg_show_cmdline = false; bool arg_show_cgroup = false; @@ -105,6 +106,7 @@ static void parse_conf(void) { { "Bootchart", "Samples", config_parse_int, 0, &arg_samples_len }, { "Bootchart", "Frequency", config_parse_double, 0, &arg_hz }, { "Bootchart", "Relative", config_parse_bool, 0, &arg_relative }, + { "Bootchart", "RawDumps", config_parse_bool, 0, &arg_raw_dumps }, { "Bootchart", "Filter", config_parse_bool, 0, &arg_filter }, { "Bootchart", "Output", config_parse_path, 0, &output }, { "Bootchart", "Init", config_parse_path, 0, &init }, @@ -132,6 +134,7 @@ static void help(void) { printf("Usage: %s [OPTIONS]\n\n" "Options:\n" " -r --rel Record time relative to recording\n" + " -R --keep-raw Save raw data files alongside the SVG\n" " -f --freq=FREQ Sample frequency [%g]\n" " -n --samples=N Stop sampling at [%d] samples\n" " -x --scale-x=N Scale the graph horizontally [%g] \n" @@ -163,6 +166,7 @@ static int parse_argv(int argc, char *argv[]) { static const struct option options[] = { {"rel", no_argument, NULL, 'r' }, + {"keep-raw", no_argument, NULL, 'R' }, {"freq", required_argument, NULL, 'f' }, {"samples", required_argument, NULL, 'n' }, {"pss", no_argument, NULL, 'p' }, @@ -183,12 +187,15 @@ static int parse_argv(int argc, char *argv[]) { if (getpid() == 1) opterr = 0; - while ((c = getopt_long(argc, argv, "erpf:n:o:i:FCchx:y:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "erRpf:n:o:i:FCchx:y:", options, NULL)) >= 0) switch (c) { case 'r': arg_relative = true; break; + case 'R': + arg_raw_dumps = true; + break; case 'f': r = safe_atod(optarg, &arg_hz); if (r < 0) @@ -259,6 +266,91 @@ static int parse_argv(int argc, char *argv[]) { return 1; } +static int write_raw_data(const char *output_file, + struct list_sample_data *head, + struct ps_struct *ps_first, + int n_cpus, + double graph_start, + double log_start) { + FILE *f; + char raw_file[PATH_MAX]; + struct list_sample_data *sampledata; + struct ps_struct *ps; + int i; + + snprintf(raw_file, PATH_MAX, "%.*s.dat", + (int)(strlen(output_file) - (strrchr(output_file, '.') ? + strlen(strrchr(output_file, '.')) : 0)), + output_file); + + f = fopen(raw_file, "we"); + if (!f) + return log_error_errno(errno, "Failed to open raw data file '%s': %m", raw_file); + + fprintf(f, "# bootchart raw data\n"); + fprintf(f, "# graph_start=%.9f log_start=%.9f\n", graph_start, log_start); + fprintf(f, "# Samples=%d Frequency=%f Relative=%d\n", + arg_samples_len, arg_hz, arg_relative); + + fprintf(f, "\n[samples]\n"); + fprintf(f, "# counter sampletime bi bo entropy_avail"); + for (i = 0; i < n_cpus; i++) + fprintf(f, " runtime[%d] waittime[%d]", i, i); + fprintf(f, "\n"); + + LIST_FIND_TAIL(link, sampledata, head); + while (sampledata) { + fprintf(f, "%d %.9f %d %d %d", + sampledata->counter, + sampledata->sampletime, + sampledata->blockstat.bi, + sampledata->blockstat.bo, + sampledata->entropy_avail); + for (i = 0; i < n_cpus; i++) + fprintf(f, " %.0f %.0f", + sampledata->runtime[i], + sampledata->waittime[i]); + fprintf(f, "\n"); + sampledata = sampledata->link_prev; + } + + fprintf(f, "\n[processes]\n"); + fprintf(f, "# pid ppid name starttime total_cpu_s pss_max_kb\n"); + + ps = ps_first; + while (ps->next_ps) { + struct ps_sched_struct *sample; + ps = ps->next_ps; + + fprintf(f, "%d %d %s %.9f %.9f %d\n", + ps->pid, ps->ppid, ps->name, + ps->starttime, ps->total, ps->pss_max); + + fprintf(f, " # sample runtime_ns waittime_ns pss_kb\n"); + sample = ps->first; + while (sample) { + fprintf(f, " %.9f %.0f %.0f %d\n", + sample->sampledata ? sample->sampledata->sampletime : 0.0, + sample->runtime, + sample->waittime, + sample->pss); + sample = sample->next; + } + } + + fclose(f); + + { + char cmd[PATH_MAX + 16]; + snprintf(cmd, sizeof(cmd), "gzip -f %s", raw_file); + if (system(cmd) != 0) + log_warning("Failed to compress raw data file '%s'", raw_file); + } + + log_info("systemd-bootchart wrote raw data to %s.gz\n", raw_file); + return 0; +} + static int do_journal_append(char *file) { #ifdef HAVE_LIBSYSTEMD _cleanup_free_ char *bootchart_message = NULL; @@ -501,6 +593,12 @@ int main(int argc, char *argv[]) { log_info("systemd-bootchart wrote %s\n", output_file); + if (arg_raw_dumps) { + r = write_raw_data(output_file, head, ps_first, n_cpus, graph_start, log_start); + if (r < 0) + return EXIT_FAILURE; + } + r = do_journal_append(output_file); if (r < 0) return EXIT_FAILURE; diff --git a/src/bootchart.h b/src/bootchart.h index 1b0895e..a594253 100644 --- a/src/bootchart.h +++ b/src/bootchart.h @@ -103,6 +103,7 @@ struct ps_struct { }; extern bool arg_relative; +extern bool arg_raw_dumps; extern bool arg_filter; extern bool arg_show_cmdline; extern bool arg_show_cgroup;