svg.c revision 40c2cce772ed74e7c6d302a6143ad818e9a2720d
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering/***
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering This file is part of systemd.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Copyright (C) 2009-2013 Intel Coproration
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Authors:
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Auke Kok <auke-jan.h.kok@intel.com>
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering systemd is free software; you can redistribute it and/or modify it
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering under the terms of the GNU Lesser General Public License as published by
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering (at your option) any later version.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering systemd is distributed in the hope that it will be useful, but
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Lesser General Public License for more details.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering You should have received a copy of the GNU Lesser General Public License
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ***/
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include <stdio.h>
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include <stdarg.h>
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include <stdlib.h>
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include <string.h>
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering#include <time.h>
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include <limits.h>
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include <unistd.h>
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering#include <sys/utsname.h>
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include <sys/stat.h>
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering#include <fcntl.h>
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
c34255bdb217c2a1d3ac6348252437ab8be9ca46Lennart Poettering#include "util.h"
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering#include "macro.h"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include "store.h"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include "svg.h"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#include "bootchart.h"
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define time_to_graph(t) ((t) * arg_scale_x)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define ps_to_graph(n) ((n) * arg_scale_y)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define kb_to_graph(m) ((m) * arg_scale_y * 0.0001)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define to_color(n) (192.0 - ((n) * 192.0))
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt#define max(x, y) (((x) > (y)) ? (x) : (y))
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt#define min(x, y) (((x) < (y)) ? (x) : (y))
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic char str[8092];
d3e84ddb885e9d5f0ae9930eb905910e3a81f157Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering#define svg(a...) do { snprintf(str, 8092, ## a); fputs(str, of); fflush(of); } while (0)
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poetteringstatic const char * const colorwheel[12] = {
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering "rgb(255,32,32)", // red
afc6adb5ec7e73bc13156c43f52fb015cd80cc68Lennart Poettering "rgb(32,192,192)", // cyan
a658cafa98ab55ea948c29bc87eb3945d515fb41Lennart Poettering "rgb(255,128,32)", // orange
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(128,32,192)", // blue-violet
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(255,255,32)", // yellow
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(192,32,128)", // red-violet
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(32,255,32)", // green
cde93897cdefdd7c7f66c400a61e42ceee5f6a46Lennart Poettering "rgb(255,64,32)", // red-orange
cde93897cdefdd7c7f66c400a61e42ceee5f6a46Lennart Poettering "rgb(32,32,255)", // blue
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(255,192,32)", // yellow-orange
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(192,32,192)", // violet
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(32,192,32)" // yellow-green
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering};
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic double idletime = -1.0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic int pfiltered = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic int pcount = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic int kcount = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic float psize = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic float ksize = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic float esize = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
d3e84ddb885e9d5f0ae9930eb905910e3a81f157Lennart Poetteringstatic void svg_header(void) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering float w;
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering float h;
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering /* min width is about 1600px due to the label */
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering w = 150.0 + 10.0 + time_to_graph(sampletime[samples-1] - graph_start);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering w = ((w < 1600.0) ? 1600.0 : w);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* height is variable based on pss, psize, ksize */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering + psize + ksize + esize;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<?xml version=\"1.0\" standalone=\"no\"?>\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" ");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering //svg("<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering w, h);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("xmlns=\"http://www.w3.org/2000/svg\">\n\n");
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* write some basic info as a comment, including some help */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- inkscape, etc. To display the files on your system, just point -->\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- generated by bootchart version %s, running with options: -->\n", VERSION);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y);
b87633c4b20e3221748d6c98336cf6c85123cd66Lennart Poettering svg("<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter);
b87633c4b20e3221748d6c98336cf6c85123cd66Lennart Poettering svg("<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy);
b87633c4b20e3221748d6c98336cf6c85123cd66Lennart Poettering svg("<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path);
b87633c4b20e3221748d6c98336cf6c85123cd66Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* style sheet */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect { stroke-width: 1; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" line { stroke: rgb(64,64,64); stroke-width: 1; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("// line.sec1 { }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" line.sec5 { stroke-width: 2; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" line.dot { stroke-dasharray: 2 4; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering svg(" .run { font-size: 8; font-style: italic; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" text { font-family: Verdana, Helvetica; font-size: 10; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" text.sec { font-size: 8; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" text.t1 { font-size: 24; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" text.t2 { font-size: 12; }\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" text.idle { font-size: 18; }\n");
76b543756ef69ce69784d571aefe8de65eaeb331Lennart Poettering
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt svg(" ]]>\n </style>\n</defs>\n\n");
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt}
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
19befb2d5fc087f96e40ddc432b2cc9385666209Lennart Poetteringstatic void svg_title(const char *build) {
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt char cmdline[256] = "";
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt char filename[PATH_MAX];
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering char buf[256];
19befb2d5fc087f96e40ddc432b2cc9385666209Lennart Poettering char rootbdev[16] = "Unknown";
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt char model[256] = "Unknown";
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt char date[256] = "Unknown";
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering char cpu[256] = "Unknown";
19befb2d5fc087f96e40ddc432b2cc9385666209Lennart Poettering char *c;
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt FILE *f;
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt time_t t;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering int fd;
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering struct utsname uts;
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering /* grab /proc/cmdline */
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering fd = openat(procfd, "cmdline", O_RDONLY);
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering f = fdopen(fd, "r");
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering if (f) {
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering if (!fgets(cmdline, 255, f))
ebeccf9eecf5939a2ef772c3160e89efcad96194Lennart Poettering sprintf(cmdline, "Unknown");
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering fclose(f);
19befb2d5fc087f96e40ddc432b2cc9385666209Lennart Poettering }
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering /* extract root fs so we can find disk model name in sysfs */
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering /* FIXME: this works only in the simple case */
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering c = strstr(cmdline, "root=/dev/");
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering if (c) {
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering strncpy(rootbdev, &c[10], 3);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering rootbdev[3] = '\0';
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt sprintf(filename, "block/%s/device/model", rootbdev);
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt fd = openat(sysfd, filename, O_RDONLY);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering f = fdopen(fd, "r");
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering if (f) {
19befb2d5fc087f96e40ddc432b2cc9385666209Lennart Poettering if (!fgets(model, 255, f))
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering fprintf(stderr, "Error reading disk model for %s\n", rootbdev);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering fclose(f);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering }
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering }
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering /* various utsname parameters */
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering if (uname(&uts))
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt fprintf(stderr, "Error getting uname info\n");
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt
943aca8efb39453e3994ccdd1e08534b788c5aeeLennart Poettering /* date */
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering t = time(NULL);
19befb2d5fc087f96e40ddc432b2cc9385666209Lennart Poettering strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t));
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering /* CPU type */
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering fd = openat(procfd, "cpuinfo", O_RDONLY);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering f = fdopen(fd, "r");
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering if (f) {
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering while (fgets(buf, 255, f)) {
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt if (strstr(buf, "model name")) {
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt strncpy(cpu, &buf[13], 255);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering break;
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering }
19befb2d5fc087f96e40ddc432b2cc9385666209Lennart Poettering }
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering fclose(f);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering }
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering svg("<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n",
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering uts.nodename, date);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering svg("<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n",
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering uts.sysname, uts.release, uts.version, uts.machine);
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt svg("<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n",
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt cpu);
6797c324a653f119a3d7133122648aaa4878ddd6Lennart Poettering svg("<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n",
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering model);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering cmdline);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering build);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<text class=\"t2\" x=\"20\" y=\"140\">Idle time: ");
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (idletime >= 0.0)
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering svg("%.03fs", idletime);
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering else
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("Not detected");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("</text>\n");
5bb658a1784a0fd4f0f32adb4b1fb636ff503f7dKay Sievers svg("<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n",
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt arg_hz, arg_samples_len, overrun, pscount, pfiltered);
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt}
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poetteringstatic void svg_graph_box(int height) {
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt double d = 0.0;
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt int i = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* outside box, fill */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(0.0),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(sampletime[samples-1] - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ps_to_graph(height));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering for (d = graph_start; d <= sampletime[samples-1];
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* lines for each second */
71fda00f320379f5cbee8e118848de98caaa229dLennart Poettering if (i % 50 == 0)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(d - graph_start),
a658cafa98ab55ea948c29bc87eb3945d515fb41Lennart Poettering time_to_graph(d - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ps_to_graph(height));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering else if (i % 10 == 0)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(d - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(d - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ps_to_graph(height));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering else
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(d - graph_start),
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering time_to_graph(d - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ps_to_graph(height));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* time label */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (i % 10 == 0)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(d - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering -5.0,
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering d - graph_start);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering i++;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering }
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering}
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering/* xml comments must not contain "--" */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic char* xml_comment_encode(const char* name) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering char *enc_name, *p;
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering enc_name = strdup(name);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (!enc_name)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering return NULL;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering for (p = enc_name; *p; p++)
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering if (p[0] == '-' && p[1] == '-')
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering p[1] = '_';
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering return enc_name;
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering}
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic void svg_pss_graph(void) {
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering struct ps_struct *ps;
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering int i;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering svg("\n\n<!-- Pss memory size graph -->\n");
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering svg("\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n");
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering /* vsize 1000 == 1000mb */
d9e34bfda3d34dcde00a876cb052e7de0655e1cbLennart Poettering svg_graph_box(100);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* draw some hlines for usable memory sizes */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering for (i = 100000; i < 1000000; i += 100000) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(.0),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering kb_to_graph(i),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(sampletime[samples-1] - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering kb_to_graph(i));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(sampletime[samples-1] - graph_start) + 5,
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering kb_to_graph(i), (1000000 - i) / 1000);
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering }
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg("\n");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* now plot the graph itself */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering for (i = 1; i < samples ; i++) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering int bottom;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering int top;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering bottom = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering top = 0;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering /* put all the small pss blocks into the bottom */
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering ps = ps_first;
c335068380fe8c9d843cdb2cf8a00f822cfabed3Lennart Poettering while (ps->next_ps) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ps = ps->next_ps;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (!ps)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering continue;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (ps->sample[i].pss <= (100 * arg_scale_y))
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering top += ps->sample[i].pss;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering };
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "rgb(64,64,64)",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(sampletime[i - 1] - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering kb_to_graph(1000000.0 - top),
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt time_to_graph(sampletime[i] - sampletime[i - 1]),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering kb_to_graph(top - bottom));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering bottom = top;
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* now plot the ones that are of significant size */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ps = ps_first;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering while (ps->next_ps) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering ps = ps->next_ps;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering if (!ps)
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering continue;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* don't draw anything smaller than 2mb */
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek if (ps->sample[i].pss > (100 * arg_scale_y)) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering top = bottom + ps->sample[i].pss;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering svg(" <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering colorwheel[ps->pid % 12],
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(sampletime[i - 1] - graph_start),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering kb_to_graph(1000000.0 - top),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering time_to_graph(sampletime[i] - sampletime[i - 1]),
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering kb_to_graph(top - bottom));
bottom = top;
}
}
}
/* overlay all the text labels */
for (i = 1; i < samples ; i++) {
int bottom;
int top;
bottom = 0;
top = 0;
/* put all the small pss blocks into the bottom */
ps = ps_first;
while (ps->next_ps) {
ps = ps->next_ps;
if (!ps)
continue;
if (ps->sample[i].pss <= (100 * arg_scale_y))
top += ps->sample[i].pss;
};
bottom = top;
/* now plot the ones that are of significant size */
ps = ps_first;
while (ps->next_ps) {
ps = ps->next_ps;
if (!ps)
continue;
/* don't draw anything smaller than 2mb */
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 * arg_scale_y)))
svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n",
time_to_graph(sampletime[i] - graph_start),
kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)),
ps->name,
ps->pid);
bottom = top;
}
}
}
/* debug output - full data dump */
svg("\n\n<!-- PSS map - csv format -->\n");
ps = ps_first;
while (ps->next_ps) {
char _cleanup_free_ *enc_name = NULL;
ps = ps->next_ps;
if (!ps)
continue;
enc_name = xml_comment_encode(ps->name);
if(!enc_name)
continue;
svg("<!-- %s [%d] pss=", enc_name, ps->pid);
for (i = 0; i < samples ; i++) {
svg("%d," , ps->sample[i].pss);
}
svg(" -->\n");
}
}
static void svg_io_bi_bar(void) {
double max = 0.0;
double range;
int max_here = 0;
int i;
svg("<!-- IO utilization graph - In -->\n");
svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n");
/*
* calculate rounding range
*
* We need to round IO data since IO block data is not updated on
* each poll. Applying a smoothing function loses some burst data,
* so keep the smoothing range short.
*/
range = 0.25 / (1.0 / arg_hz);
if (range < 2.0)
range = 2.0; /* no smoothing */
/* surrounding box */
svg_graph_box(5);
/* find the max IO first */
for (i = 1; i < samples; i++) {
int start;
int stop;
double tot;
start = max(i - ((range / 2) - 1), 0);
stop = min(i + (range / 2), samples - 1);
tot = (double)(blockstat[stop].bi - blockstat[start].bi)
/ (stop - start);
if (tot > max) {
max = tot;
max_here = i;
}
tot = (double)(blockstat[stop].bo - blockstat[start].bo)
/ (stop - start);
if (tot > max)
max = tot;
}
/* plot bi */
for (i = 1; i < samples; i++) {
int start;
int stop;
double tot;
double pbi;
start = max(i - ((range / 2) - 1), 0);
stop = min(i + (range / 2), samples);
tot = (double)(blockstat[stop].bi - blockstat[start].bi)
/ (stop - start);
pbi = tot / max;
if (pbi > 0.001)
svg("<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(sampletime[i - 1] - graph_start),
(arg_scale_y * 5) - (pbi * (arg_scale_y * 5)),
time_to_graph(sampletime[i] - sampletime[i - 1]),
pbi * (arg_scale_y * 5));
/* labels around highest value */
if (i == max_here) {
svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
time_to_graph(sampletime[i] - graph_start) + 5,
((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15,
max / 1024.0 / (interval / 1000000000.0));
}
}
}
static void svg_io_bo_bar(void) {
double max = 0.0;
double range;
int max_here = 0;
int i;
svg("<!-- IO utilization graph - out -->\n");
svg("<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n");
/*
* calculate rounding range
*
* We need to round IO data since IO block data is not updated on
* each poll. Applying a smoothing function loses some burst data,
* so keep the smoothing range short.
*/
range = 0.25 / (1.0 / arg_hz);
if (range < 2.0)
range = 2.0; /* no smoothing */
/* surrounding box */
svg_graph_box(5);
/* find the max IO first */
for (i = 1; i < samples; i++) {
int start;
int stop;
double tot;
start = max(i - ((range / 2) - 1), 0);
stop = min(i + (range / 2), samples - 1);
tot = (double)(blockstat[stop].bi - blockstat[start].bi)
/ (stop - start);
if (tot > max)
max = tot;
tot = (double)(blockstat[stop].bo - blockstat[start].bo)
/ (stop - start);
if (tot > max) {
max = tot;
max_here = i;
}
}
/* plot bo */
for (i = 1; i < samples; i++) {
int start;
int stop;
double tot;
double pbo;
start = max(i - ((range / 2) - 1), 0);
stop = min(i + (range / 2), samples);
tot = (double)(blockstat[stop].bo - blockstat[start].bo)
/ (stop - start);
pbo = tot / max;
if (pbo > 0.001)
svg("<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(sampletime[i - 1] - graph_start),
(arg_scale_y * 5) - (pbo * (arg_scale_y * 5)),
time_to_graph(sampletime[i] - sampletime[i - 1]),
pbo * (arg_scale_y * 5));
/* labels around highest bo value */
if (i == max_here) {
svg(" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n",
time_to_graph(sampletime[i] - graph_start) + 5,
((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))),
max / 1024.0 / (interval / 1000000000.0));
}
}
}
static void svg_cpu_bar(void) {
int i;
svg("<!-- CPU utilization graph -->\n");
svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU utilization</text>\n");
/* surrounding box */
svg_graph_box(5);
/* bars for each sample, proportional to the CPU util. */
for (i = 1; i < samples; i++) {
int c;
double trt;
double ptrt;
ptrt = trt = 0.0;
for (c = 0; c < cpus; c++)
trt += cpustat[c].sample[i].runtime - cpustat[c].sample[i - 1].runtime;
trt = trt / 1000000000.0;
trt = trt / (double)cpus;
if (trt > 0.0)
ptrt = trt / (sampletime[i] - sampletime[i - 1]);
if (ptrt > 1.0)
ptrt = 1.0;
if (ptrt > 0.001) {
svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(sampletime[i - 1] - graph_start),
(arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)),
time_to_graph(sampletime[i] - sampletime[i - 1]),
ptrt * (arg_scale_y * 5));
}
}
}
static void svg_wait_bar(void) {
int i;
svg("<!-- Wait time aggregation box -->\n");
svg("<text class=\"t2\" x=\"5\" y=\"-15\">CPU wait</text>\n");
/* surrounding box */
svg_graph_box(5);
/* bars for each sample, proportional to the CPU util. */
for (i = 1; i < samples; i++) {
int c;
double twt;
double ptwt;
ptwt = twt = 0.0;
for (c = 0; c < cpus; c++)
twt += cpustat[c].sample[i].waittime - cpustat[c].sample[i - 1].waittime;
twt = twt / 1000000000.0;
twt = twt / (double)cpus;
if (twt > 0.0)
ptwt = twt / (sampletime[i] - sampletime[i - 1]);
if (ptwt > 1.0)
ptwt = 1.0;
if (ptwt > 0.001) {
svg("<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(sampletime[i - 1] - graph_start),
((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))),
time_to_graph(sampletime[i] - sampletime[i - 1]),
ptwt * (arg_scale_y * 5));
}
}
}
static void svg_entropy_bar(void) {
int i;
svg("<!-- entropy pool graph -->\n");
svg("<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n");
/* surrounding box */
svg_graph_box(5);
/* bars for each sample, scale 0-4096 */
for (i = 1; i < samples; i++) {
/* svg("<!-- entropy %.03f %i -->\n", sampletime[i], entropy_avail[i]); */
svg("<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(sampletime[i - 1] - graph_start),
((arg_scale_y * 5) - ((entropy_avail[i] / 4096.) * (arg_scale_y * 5))),
time_to_graph(sampletime[i] - sampletime[i - 1]),
(entropy_avail[i] / 4096.) * (arg_scale_y * 5));
}
}
static struct ps_struct *get_next_ps(struct ps_struct *ps) {
/*
* walk the list of processes and return the next one to be
* painted
*/
if (ps == ps_first)
return ps->next_ps;
/* go deep */
if (ps->children)
return ps->children;
/* find siblings */
if (ps->next)
return ps->next;
/* go back for parent siblings */
while (1) {
if (ps->parent)
if (ps->parent->next)
return ps->parent->next;
ps = ps->parent;
if (!ps)
return ps;
}
return NULL;
}
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) */
if (ps->first == ps->last)
return -1;
/* don't filter kthreadd */
if (ps->pid == 2)
return 0;
/* drop stuff that doesn't use any real CPU time */
if (ps->total <= 0.001)
return -1;
return 0;
}
static void svg_do_initcall(int count_only) {
FILE _cleanup_pclose_ *f = NULL;
double t;
char func[256];
int ret;
int usecs;
/* can't plot initcall when disabled or in relative mode */
if (!initcall || arg_relative) {
kcount = 0;
return;
}
if (!count_only) {
svg("<!-- initcall -->\n");
svg("<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n");
/* surrounding box */
svg_graph_box(kcount);
}
kcount = 0;
/*
* Initcall graphing - parses dmesg buffer and displays kernel threads
* This somewhat uses the same methods and scaling to show processes
* but looks a lot simpler. It's overlaid entirely onto the PS graph
* when appropriate.
*/
f = popen("dmesg", "r");
if (!f)
return;
while (!feof(f)) {
int c;
int z = 0;
char l[256];
if (fgets(l, sizeof(l) - 1, f) == NULL)
continue;
c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s",
&t, func, &ret, &usecs);
if (c != 4) {
/* also parse initcalls done by module loading */
c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s",
&t, func, &ret, &usecs);
if (c != 4)
continue;
}
/* chop the +0xXX/0xXX stuff */
while(func[z] != '+')
z++;
func[z] = 0;
if (count_only) {
/* filter out irrelevant stuff */
if (usecs >= 1000)
kcount++;
continue;
}
svg("<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n",
func, t, usecs, ret);
if (usecs < 1000)
continue;
/* rect */
svg(" <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(t - (usecs / 1000000.0)),
ps_to_graph(kcount),
time_to_graph(usecs / 1000000.0),
ps_to_graph(1));
/* label */
svg(" <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n",
time_to_graph(t - (usecs / 1000000.0)) + 5,
ps_to_graph(kcount) + 15,
func,
usecs / 1000000.0);
kcount++;
}
}
static void svg_ps_bars(void) {
struct ps_struct *ps;
int i = 0;
int j = 0;
int w;
int pid;
svg("<!-- Process graph -->\n");
svg("<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n");
/* surrounding box */
svg_graph_box(pcount);
/* pass 2 - ps boxes */
ps = ps_first;
while ((ps = get_next_ps(ps))) {
char _cleanup_free_ *enc_name = NULL;
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("<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid,
ps->ppid, ps->total);
/* it would be nice if we could use exec_start from /proc/pid/sched,
* but it's unreliable and gives bogus numbers */
starttime = sampletime[ps->first];
if (!ps_filter(ps)) {
/* remember where _to_ our children need to draw a line */
ps->pos_x = time_to_graph(starttime - graph_start);
ps->pos_y = ps_to_graph(j+1); /* bottom left corner */
} else {
/* hook children to our parent coords instead */
ps->pos_x = ps->parent->pos_x;
ps->pos_y = ps->parent->pos_y;
/* if this is the last child, we might still need to draw a connecting line */
if ((!ps->next) && (ps->parent))
svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
ps->parent->pos_x,
ps_to_graph(j-1) + 10.0, /* whee, use the last value here */
ps->parent->pos_x,
ps->parent->pos_y);
continue;
}
svg(" <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(starttime - graph_start),
ps_to_graph(j),
time_to_graph(sampletime[ps->last] - starttime),
ps_to_graph(1));
/* paint cpu load over these */
for (t = ps->first + 1; t < ps->last; t++) {
double rt, prt;
double wt, wrt;
/* calculate over interval */
rt = ps->sample[t].runtime - ps->sample[t-1].runtime;
wt = ps->sample[t].waittime - ps->sample[t-1].waittime;
prt = (rt / 1000000000) / (sampletime[t] - sampletime[t-1]);
wrt = (wt / 1000000000) / (sampletime[t] - sampletime[t-1]);
/* this can happen if timekeeping isn't accurate enough */
if (prt > 1.0)
prt = 1.0;
if (wrt > 1.0)
wrt = 1.0;
if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */
continue;
svg(" <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(sampletime[t - 1] - graph_start),
ps_to_graph(j),
time_to_graph(sampletime[t] - sampletime[t - 1]),
ps_to_graph(wrt));
/* draw cpu over wait - TODO figure out how/why run + wait > interval */
svg(" <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n",
time_to_graph(sampletime[t - 1] - graph_start),
ps_to_graph(j + (1.0 - prt)),
time_to_graph(sampletime[t] - sampletime[t - 1]),
ps_to_graph(prt));
}
/* determine where to display the process name */
if (sampletime[ps->last] - sampletime[ps->first] < 1.5)
/* too small to fit label inside the box */
w = ps->last;
else
w = ps->first;
/* text label of process name */
svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan></text>\n",
time_to_graph(sampletime[w] - graph_start) + 5.0,
ps_to_graph(j) + 14.0,
ps->name,
ps->pid,
(ps->sample[ps->last].runtime - ps->sample[ps->first].runtime) / 1000000000.0);
/* paint lines to the parent process */
if (ps->parent) {
/* horizontal part */
svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
time_to_graph(starttime - graph_start),
ps_to_graph(j) + 10.0,
ps->parent->pos_x,
ps_to_graph(j) + 10.0);
/* one vertical line connecting all the horizontal ones up */
if (!ps->next)
svg(" <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
ps->parent->pos_x,
ps_to_graph(j) + 10.0,
ps->parent->pos_x,
ps->parent->pos_y);
}
j++; /* count boxes */
svg("\n");
}
/* last pass - determine when idle */
pid = getpid();
/* make sure we start counting from the point where we actually have
* data: assume that bootchart's first sample is when data started
*/
ps = ps_first;
while (ps->next_ps) {
ps = ps->next_ps;
if (ps->pid == pid)
break;
}
for (i = ps->first; i < samples - (arg_hz / 2); i++) {
double crt;
double brt;
int c;
/* subtract bootchart cpu utilization from total */
crt = 0.0;
for (c = 0; c < cpus; c++)
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":
*
* if for (hz / 2) we've used less CPU than (interval / 2) ...
* defaults to 4.0%, which experimentally, is where atom idles
*/
if ((crt - brt) < (interval / 2.0)) {
idletime = sampletime[i] - graph_start;
svg("\n<!-- idle detected at %.03f seconds -->\n",
idletime);
svg("<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n",
time_to_graph(idletime),
-arg_scale_y,
time_to_graph(idletime),
ps_to_graph(pcount) + arg_scale_y);
svg("<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n",
time_to_graph(idletime) + 5.0,
ps_to_graph(pcount) + arg_scale_y,
idletime);
break;
}
}
}
static void svg_top_ten_cpu(void) {
struct ps_struct *top[10];
struct ps_struct emptyps;
struct ps_struct *ps;
int n, m;
memset(&emptyps, 0, sizeof(struct ps_struct));
for (n=0; n < 10; n++)
top[n] = &emptyps;
/* walk all ps's and setup ptrs */
ps = ps_first;
while ((ps = get_next_ps(ps))) {
for (n = 0; n < 10; n++) {
if (ps->total <= top[n]->total)
continue;
/* cascade insert */
for (m = 9; m > n; m--)
top[m] = top[m-1];
top[n] = ps;
break;
}
}
svg("<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n");
for (n = 0; n < 10; n++)
svg("<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n",
20 + (n * 13),
top[n]->total,
top[n]->name,
top[n]->pid);
}
static void svg_top_ten_pss(void) {
struct ps_struct *top[10];
struct ps_struct emptyps;
struct ps_struct *ps;
int n, m;
memset(&emptyps, 0, sizeof(struct ps_struct));
for (n=0; n < 10; n++)
top[n] = &emptyps;
/* walk all ps's and setup ptrs */
ps = ps_first;
while ((ps = get_next_ps(ps))) {
for (n = 0; n < 10; n++) {
if (ps->pss_max <= top[n]->pss_max)
continue;
/* cascade insert */
for (m = 9; m > n; m--)
top[m] = top[m-1];
top[n] = ps;
break;
}
}
svg("<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n");
for (n = 0; n < 10; n++)
svg("<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n",
20 + (n * 13),
top[n]->pss_max,
top[n]->name,
top[n]->pid);
}
void svg_do(const char *build) {
struct ps_struct *ps;
memset(&str, 0, sizeof(str));
ps = ps_first;
/* count initcall thread count first */
svg_do_initcall(1);
ksize = (kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0);
/* then count processes */
while ((ps = get_next_ps(ps))) {
if (!ps_filter(ps))
pcount++;
else
pfiltered++;
}
psize = ps_to_graph(pcount) + (arg_scale_y * 2);
esize = (arg_entropy ? arg_scale_y * 7 : 0);
/* after this, we can draw the header with proper sizing */
svg_header();
svg("<g transform=\"translate(10,400)\">\n");
svg_io_bi_bar();
svg("</g>\n\n");
svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 7.0));
svg_io_bo_bar();
svg("</g>\n\n");
svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 14.0));
svg_cpu_bar();
svg("</g>\n\n");
svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 21.0));
svg_wait_bar();
svg("</g>\n\n");
if (kcount) {
svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0));
svg_do_initcall(0);
svg("</g>\n\n");
}
svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize);
svg_ps_bars();
svg("</g>\n\n");
svg("<g transform=\"translate(10, 0)\">\n");
svg_title(build);
svg("</g>\n\n");
svg("<g transform=\"translate(10,200)\">\n");
svg_top_ten_cpu();
svg("</g>\n\n");
if (arg_entropy) {
svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize);
svg_entropy_bar();
svg("</g>\n\n");
}
if (arg_pss) {
svg("<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * 28.0) + ksize + psize + esize);
svg_pss_graph();
svg("</g>\n\n");
svg("<g transform=\"translate(410,200)\">\n");
svg_top_ten_pss();
svg("</g>\n\n");
}
/* svg footer */
svg("\n</svg>\n");
}