X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fbootchart%2Fsvg.c;h=231d3daa8002a1526873f4f9d968dad4b8dca7e6;hp=6ad7348ecbe76ff7302821c614fa7809bed31af1;hb=6d031c0b6037ac7308d31562c09fb8ac714e82b4;hpb=895aeb27795c00f365ce3b30b1dca549b4f5468e diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c index 6ad7348ec..231d3daa8 100644 --- a/src/bootchart/svg.c +++ b/src/bootchart/svg.c @@ -1,5 +1,7 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** - bootchart.c - This file is part of systemd-bootchart + This file is part of systemd. Copyright (C) 2009-2013 Intel Coproration @@ -28,15 +30,18 @@ #include #include #include +#include +#include -#include "bootchart.h" #include "util.h" #include "macro.h" +#include "store.h" +#include "svg.h" +#include "bootchart.h" - -#define time_to_graph(t) ((t) * scale_x) -#define ps_to_graph(n) ((n) * scale_y) -#define kb_to_graph(m) ((m) * scale_y * 0.0001) +#define time_to_graph(t) ((t) * arg_scale_x) +#define ps_to_graph(n) ((n) * arg_scale_y) +#define kb_to_graph(m) ((m) * arg_scale_y * 0.0001) #define to_color(n) (192.0 - ((n) * 192.0)) #define max(x, y) (((x) > (y)) ? (x) : (y)) @@ -46,7 +51,7 @@ static char str[8092]; #define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0) -static const char *colorwheel[12] = { +static const char * const colorwheel[12] = { "rgb(255,32,32)", // red "rgb(32,192,192)", // cyan "rgb(255,128,32)", // orange @@ -69,9 +74,7 @@ static float psize = 0; static float ksize = 0; static float esize = 0; - -static void svg_header(void) -{ +static void svg_header(void) { float w; float h; @@ -80,8 +83,8 @@ static void svg_header(void) w = ((w < 1600.0) ? 1600.0 : w); /* height is variable based on pss, psize, ksize */ - h = 400.0 + (scale_y * 30.0) /* base graphs and title */ - + (pss ? (100.0 * scale_y) + (scale_y * 7.0) : 0.0) /* pss estimate */ + h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */ + + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */ + psize + ksize + esize; svg("\n"); @@ -101,11 +104,11 @@ static void svg_header(void) svg("\n\n"); svg("\n", VERSION); - svg("\n", hz, len); - svg("\n", scale_x, scale_y); - svg("\n", relative, filter); - svg("\n", pss, entropy); - svg("\n\n", output_path, init_path); + svg("\n", arg_hz, arg_samples_len); + svg("\n", arg_scale_x, arg_scale_y); + svg("\n", arg_relative, arg_filter); + svg("\n", arg_pss, arg_entropy); + svg("\n\n", arg_output_path, arg_init_path); /* style sheet */ svg("\n \n\n\n"); - } - -static void svg_title(void) -{ +static void svg_title(const char *build) { char cmdline[256] = ""; char filename[PATH_MAX]; char buf[256]; @@ -147,14 +147,15 @@ static void svg_title(void) char model[256] = "Unknown"; char date[256] = "Unknown"; char cpu[256] = "Unknown"; - char build[256] = "Unknown"; char *c; FILE *f; time_t t; + int fd; struct utsname uts; /* grab /proc/cmdline */ - f = fopen("/proc/cmdline", "r"); + fd = openat(procfd, "cmdline", O_RDONLY); + f = fdopen(fd, "r"); if (f) { if (!fgets(cmdline, 255, f)) sprintf(cmdline, "Unknown"); @@ -162,17 +163,19 @@ static void svg_title(void) } /* extract root fs so we can find disk model name in sysfs */ + /* FIXME: this works only in the simple case */ c = strstr(cmdline, "root=/dev/"); if (c) { strncpy(rootbdev, &c[10], 3); rootbdev[3] = '\0'; - } - sprintf(filename, "/sys/block/%s/device/model", rootbdev); - f = fopen(filename, "r"); - if (f) { - if (!fgets(model, 255, f)) - fprintf(stderr, "Error reading disk model for %s\n", rootbdev); - fclose(f); + sprintf(filename, "block/%s/device/model", rootbdev); + fd = openat(sysfd, filename, O_RDONLY); + f = fdopen(fd, "r"); + if (f) { + if (!fgets(model, 255, f)) + fprintf(stderr, "Error reading disk model for %s\n", rootbdev); + fclose(f); + } } /* various utsname parameters */ @@ -184,7 +187,8 @@ static void svg_title(void) strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t)); /* CPU type */ - f = fopen("/proc/cpuinfo", "r"); + fd = openat(procfd, "cpuinfo", O_RDONLY); + f = fdopen(fd, "r"); if (f) { while (fgets(buf, 255, f)) { if (strstr(buf, "model name")) { @@ -195,14 +199,6 @@ static void svg_title(void) fclose(f); } - /* Build - 1st line from /etc/system-release */ - f = fopen("/etc/system-release", "r"); - if (f) { - if (fgets(buf, 255, f)) - strncpy(build, buf, 255); - fclose(f); - } - svg("Bootchart for %s - %s\n", uts.nodename, date); svg("System: %s %s %s %s\n", @@ -224,12 +220,10 @@ static void svg_title(void) svg("Not detected"); svg("\n"); svg("Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered\n", - hz, len, overrun, pscount, pfiltered); + arg_hz, arg_samples_len, overrun, pscount, pfiltered); } - -static void svg_graph_box(int height) -{ +static void svg_graph_box(int height) { double d = 0.0; int i = 0; @@ -240,7 +234,7 @@ static void svg_graph_box(int height) ps_to_graph(height)); for (d = graph_start; d <= sampletime[samples-1]; - d += (scale_x < 2.0 ? 60.0 : scale_x < 10.0 ? 1.0 : 0.1)) { + d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) { /* lines for each second */ if (i % 50 == 0) svg(" \n", @@ -269,9 +263,22 @@ static void svg_graph_box(int height) } } +/* xml comments must not contain "--" */ +static char* xml_comment_encode(const char* name) { + char *enc_name, *p; + + enc_name = strdup(name); + if (!enc_name) + return NULL; + + for (p = enc_name; *p; p++) + if (p[0] == '-' && p[1] == '-') + p[1] = '_'; -static void svg_pss_graph(void) -{ + return enc_name; +} + +static void svg_pss_graph(void) { struct ps_struct *ps; int i; @@ -308,7 +315,7 @@ static void svg_pss_graph(void) ps = ps->next_ps; if (!ps) continue; - if (ps->sample[i].pss <= (100 * scale_y)) + if (ps->sample[i].pss <= (100 * arg_scale_y)) top += ps->sample[i].pss; }; svg(" \n", @@ -327,7 +334,7 @@ static void svg_pss_graph(void) if (!ps) continue; /* don't draw anything smaller than 2mb */ - if (ps->sample[i].pss > (100 * scale_y)) { + if (ps->sample[i].pss > (100 * arg_scale_y)) { top = bottom + ps->sample[i].pss; svg(" \n", colorwheel[ps->pid % 12], @@ -354,7 +361,7 @@ static void svg_pss_graph(void) ps = ps->next_ps; if (!ps) continue; - if (ps->sample[i].pss <= (100 * scale_y)) + if (ps->sample[i].pss <= (100 * arg_scale_y)) top += ps->sample[i].pss; }; @@ -367,11 +374,11 @@ static void svg_pss_graph(void) if (!ps) continue; /* don't draw anything smaller than 2mb */ - if (ps->sample[i].pss > (100 * scale_y)) { + if (ps->sample[i].pss > (100 * arg_scale_y)) { top = bottom + ps->sample[i].pss; /* draw a label with the process / PID */ - if ((i == 1) || (ps->sample[i - 1].pss <= (100 * scale_y))) - svg(" %s [%i]\n", + if ((i == 1) || (ps->sample[i - 1].pss <= (100 * arg_scale_y))) + svg(" [%i]\n", time_to_graph(sampletime[i] - graph_start), kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)), ps->name, @@ -385,10 +392,17 @@ static void svg_pss_graph(void) svg("\n\n\n"); ps = ps_first; while (ps->next_ps) { + char _cleanup_free_*enc_name; ps = ps->next_ps; if (!ps) continue; - svg("\n"); @@ -586,15 +596,14 @@ static void svg_cpu_bar(void) if (ptrt > 0.001) { svg("\n", time_to_graph(sampletime[i - 1] - graph_start), - (scale_y * 5) - (ptrt * (scale_y * 5)), + (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)), time_to_graph(sampletime[i] - sampletime[i - 1]), - ptrt * (scale_y * 5)); + ptrt * (arg_scale_y * 5)); } } } -static void svg_wait_bar(void) -{ +static void svg_wait_bar(void) { int i; svg("\n"); @@ -628,16 +637,15 @@ static void svg_wait_bar(void) if (ptwt > 0.001) { svg("\n", time_to_graph(sampletime[i - 1] - graph_start), - ((scale_y * 5) - (ptwt * (scale_y * 5))), + ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))), time_to_graph(sampletime[i] - sampletime[i - 1]), - ptwt * (scale_y * 5)); + ptwt * (arg_scale_y * 5)); } } } -static void svg_entropy_bar(void) -{ +static void svg_entropy_bar(void) { int i; svg("\n"); @@ -651,15 +659,13 @@ static void svg_entropy_bar(void) /* svg("\n", sampletime[i], entropy_avail[i]); */ svg("\n", time_to_graph(sampletime[i - 1] - graph_start), - ((scale_y * 5) - ((entropy_avail[i] / 4096.) * (scale_y * 5))), + ((arg_scale_y * 5) - ((entropy_avail[i] / 4096.) * (arg_scale_y * 5))), time_to_graph(sampletime[i] - sampletime[i - 1]), - (entropy_avail[i] / 4096.) * (scale_y * 5)); + (entropy_avail[i] / 4096.) * (arg_scale_y * 5)); } } - -static struct ps_struct *get_next_ps(struct ps_struct *ps) -{ +static struct ps_struct *get_next_ps(struct ps_struct *ps) { /* * walk the list of processes and return the next one to be * painted @@ -688,10 +694,8 @@ static struct ps_struct *get_next_ps(struct ps_struct *ps) return NULL; } - -static int ps_filter(struct ps_struct *ps) -{ - if (!filter) +static int ps_filter(struct ps_struct *ps) { + if (!arg_filter) return 0; /* can't draw data when there is only 1 sample (need start + stop) */ @@ -709,9 +713,7 @@ static int ps_filter(struct ps_struct *ps) return 0; } - -static void svg_do_initcall(int count_only) -{ +static void svg_do_initcall(int count_only) { FILE _cleanup_pclose_ *f = NULL; double t; char func[256]; @@ -719,7 +721,7 @@ static void svg_do_initcall(int count_only) int usecs; /* can't plot initcall when disabled or in relative mode */ - if (!initcall || relative) { + if (!initcall || arg_relative) { kcount = 0; return; } @@ -799,9 +801,7 @@ static void svg_do_initcall(int count_only) } } - -static void svg_ps_bars(void) -{ +static void svg_ps_bars(void) { struct ps_struct *ps; int i = 0; int j = 0; @@ -818,14 +818,20 @@ static void svg_ps_bars(void) /* pass 2 - ps boxes */ ps = ps_first; while ((ps = get_next_ps(ps))) { + char _cleanup_free_*enc_name; + double starttime; int t; if (!ps) continue; + enc_name = xml_comment_encode(ps->name); + if(!enc_name) + continue; + /* leave some trace of what we actually filtered etc. */ - svg("\n", ps->name, ps->pid, + svg("\n", enc_name, ps->pid, ps->ppid, ps->total); /* it would be nice if we could use exec_start from /proc/pid/sched, @@ -900,7 +906,7 @@ static void svg_ps_bars(void) w = ps->first; /* text label of process name */ - svg(" %s [%i] %.03fs\n", + svg(" [%i]%.03fs\n", time_to_graph(sampletime[w] - graph_start) + 5.0, ps_to_graph(j) + 14.0, ps->name, @@ -941,7 +947,7 @@ static void svg_ps_bars(void) break; } - for (i = ps->first; i < samples - (hz / 2); i++) { + for (i = ps->first; i < samples - (arg_hz / 2); i++) { double crt; double brt; int c; @@ -949,8 +955,8 @@ static void svg_ps_bars(void) /* subtract bootchart cpu utilization from total */ crt = 0.0; for (c = 0; c < cpus; c++) - crt += cpustat[c].sample[i + ((int)hz / 2)].runtime - cpustat[c].sample[i].runtime; - brt = ps->sample[i + ((int)hz / 2)].runtime - ps->sample[i].runtime; + crt += cpustat[c].sample[i + ((int)arg_hz / 2)].runtime - cpustat[c].sample[i].runtime; + brt = ps->sample[i + ((int)arg_hz / 2)].runtime - ps->sample[i].runtime; /* * our definition of "idle": @@ -964,21 +970,19 @@ static void svg_ps_bars(void) idletime); svg("\n", time_to_graph(idletime), - -scale_y, + -arg_scale_y, time_to_graph(idletime), - ps_to_graph(pcount) + scale_y); + ps_to_graph(pcount) + arg_scale_y); svg("%.01fs\n", time_to_graph(idletime) + 5.0, - ps_to_graph(pcount) + scale_y, + ps_to_graph(pcount) + arg_scale_y, idletime); break; } } } - -static void svg_top_ten_cpu(void) -{ +static void svg_top_ten_cpu(void) { struct ps_struct *top[10]; struct ps_struct emptyps; struct ps_struct *ps; @@ -1004,16 +1008,14 @@ static void svg_top_ten_cpu(void) svg("Top CPU consumers:\n"); for (n = 0; n < 10; n++) - svg("%3.03fs - %s[%d]\n", + svg("%3.03fs - [%d]\n", 20 + (n * 13), top[n]->total, top[n]->name, top[n]->pid); } - -static void svg_top_ten_pss(void) -{ +static void svg_top_ten_pss(void) { struct ps_struct *top[10]; struct ps_struct emptyps; struct ps_struct *ps; @@ -1039,16 +1041,14 @@ static void svg_top_ten_pss(void) svg("Top PSS consumers:\n"); for (n = 0; n < 10; n++) - svg("%dK - %s[%d]\n", + svg("%dK - [%d]\n", 20 + (n * 13), top[n]->pss_max, top[n]->name, top[n]->pid); } - -void svg_do(void) -{ +void svg_do(const char *build) { struct ps_struct *ps; memset(&str, 0, sizeof(str)); @@ -1057,7 +1057,7 @@ void svg_do(void) /* count initcall thread count first */ svg_do_initcall(1); - ksize = (kcount ? ps_to_graph(kcount) + (scale_y * 2) : 0); + ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0); /* then count processes */ while ((ps = get_next_ps(ps))) { @@ -1066,9 +1066,9 @@ void svg_do(void) else pfiltered++; } - psize = ps_to_graph(pcount) + (scale_y * 2); + psize = ps_to_graph(pcount) + (arg_scale_y * 2); - esize = (entropy ? scale_y * 7 : 0); + esize = (arg_entropy ? arg_scale_y * 7 : 0); /* after this, we can draw the header with proper sizing */ svg_header(); @@ -1077,44 +1077,44 @@ void svg_do(void) svg_io_bi_bar(); svg("\n\n"); - svg("\n", 400.0 + (scale_y * 7.0)); + svg("\n", 400.0 + (arg_scale_y * 7.0)); svg_io_bo_bar(); svg("\n\n"); - svg("\n", 400.0 + (scale_y * 14.0)); + svg("\n", 400.0 + (arg_scale_y * 14.0)); svg_cpu_bar(); svg("\n\n"); - svg("\n", 400.0 + (scale_y * 21.0)); + svg("\n", 400.0 + (arg_scale_y * 21.0)); svg_wait_bar(); svg("\n\n"); if (kcount) { - svg("\n", 400.0 + (scale_y * 28.0)); + svg("\n", 400.0 + (arg_scale_y * 28.0)); svg_do_initcall(0); svg("\n\n"); } - svg("\n", 400.0 + (scale_y * 28.0) + ksize); + svg("\n", 400.0 + (arg_scale_y * 28.0) + ksize); svg_ps_bars(); svg("\n\n"); svg("\n"); - svg_title(); + svg_title(build); svg("\n\n"); svg("\n"); svg_top_ten_cpu(); svg("\n\n"); - if (entropy) { - svg("\n", 400.0 + (scale_y * 28.0) + ksize + psize); + if (arg_entropy) { + svg("\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize); svg_entropy_bar(); svg("\n\n"); } - if (pss) { - svg("\n", 400.0 + (scale_y * 28.0) + ksize + psize + esize); + if (arg_pss) { + svg("\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize); svg_pss_graph(); svg("\n\n");