1*1a3d31e3SAndroid Build Coastguard Worker /*
2*1a3d31e3SAndroid Build Coastguard Worker * Copyright (C) 2012 Fusion-io
3*1a3d31e3SAndroid Build Coastguard Worker *
4*1a3d31e3SAndroid Build Coastguard Worker * This program is free software; you can redistribute it and/or
5*1a3d31e3SAndroid Build Coastguard Worker * modify it under the terms of the GNU General Public
6*1a3d31e3SAndroid Build Coastguard Worker * License v2 as published by the Free Software Foundation.
7*1a3d31e3SAndroid Build Coastguard Worker *
8*1a3d31e3SAndroid Build Coastguard Worker * This program is distributed in the hope that it will be useful,
9*1a3d31e3SAndroid Build Coastguard Worker * but WITHOUT ANY WARRANTY; without even the implied warranty of
10*1a3d31e3SAndroid Build Coastguard Worker * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11*1a3d31e3SAndroid Build Coastguard Worker * GNU General Public License for more details.
12*1a3d31e3SAndroid Build Coastguard Worker *
13*1a3d31e3SAndroid Build Coastguard Worker * You should have received a copy of the GNU General Public License
14*1a3d31e3SAndroid Build Coastguard Worker * along with this program; if not, write to the Free Software
15*1a3d31e3SAndroid Build Coastguard Worker * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*1a3d31e3SAndroid Build Coastguard Worker *
17*1a3d31e3SAndroid Build Coastguard Worker * Parts of this file were imported from Jens Axboe's blktrace sources (also GPL)
18*1a3d31e3SAndroid Build Coastguard Worker */
19*1a3d31e3SAndroid Build Coastguard Worker #include <sys/types.h>
20*1a3d31e3SAndroid Build Coastguard Worker #include <sys/stat.h>
21*1a3d31e3SAndroid Build Coastguard Worker #include <fcntl.h>
22*1a3d31e3SAndroid Build Coastguard Worker #include <unistd.h>
23*1a3d31e3SAndroid Build Coastguard Worker #include <stdlib.h>
24*1a3d31e3SAndroid Build Coastguard Worker #include <stdio.h>
25*1a3d31e3SAndroid Build Coastguard Worker #include <math.h>
26*1a3d31e3SAndroid Build Coastguard Worker #include <inttypes.h>
27*1a3d31e3SAndroid Build Coastguard Worker #include <string.h>
28*1a3d31e3SAndroid Build Coastguard Worker #include <asm/types.h>
29*1a3d31e3SAndroid Build Coastguard Worker #include <errno.h>
30*1a3d31e3SAndroid Build Coastguard Worker #include <sys/mman.h>
31*1a3d31e3SAndroid Build Coastguard Worker #include <time.h>
32*1a3d31e3SAndroid Build Coastguard Worker #include <math.h>
33*1a3d31e3SAndroid Build Coastguard Worker
34*1a3d31e3SAndroid Build Coastguard Worker #include "plot.h"
35*1a3d31e3SAndroid Build Coastguard Worker
36*1a3d31e3SAndroid Build Coastguard Worker static int io_graph_scale = 8;
37*1a3d31e3SAndroid Build Coastguard Worker static int graph_width = 700;
38*1a3d31e3SAndroid Build Coastguard Worker static int graph_height = 250;
39*1a3d31e3SAndroid Build Coastguard Worker static int graph_circle_extra = 30;
40*1a3d31e3SAndroid Build Coastguard Worker static int graph_inner_x_margin = 2;
41*1a3d31e3SAndroid Build Coastguard Worker static int graph_inner_y_margin = 2;
42*1a3d31e3SAndroid Build Coastguard Worker static int graph_tick_len = 5;
43*1a3d31e3SAndroid Build Coastguard Worker static int graph_left_pad = 120;
44*1a3d31e3SAndroid Build Coastguard Worker static int tick_label_pad = 16;
45*1a3d31e3SAndroid Build Coastguard Worker static int tick_font_size = 15;
46*1a3d31e3SAndroid Build Coastguard Worker static char *font_family = "sans-serif";
47*1a3d31e3SAndroid Build Coastguard Worker
48*1a3d31e3SAndroid Build Coastguard Worker /* this is the title for the whole page */
49*1a3d31e3SAndroid Build Coastguard Worker static int plot_title_height = 50;
50*1a3d31e3SAndroid Build Coastguard Worker static int plot_title_font_size = 25;
51*1a3d31e3SAndroid Build Coastguard Worker
52*1a3d31e3SAndroid Build Coastguard Worker /* this is the label at the top of each plot */
53*1a3d31e3SAndroid Build Coastguard Worker static int plot_label_height = 60;
54*1a3d31e3SAndroid Build Coastguard Worker static int plot_label_font_size = 20;
55*1a3d31e3SAndroid Build Coastguard Worker
56*1a3d31e3SAndroid Build Coastguard Worker /* label for each axis is slightly smaller */
57*1a3d31e3SAndroid Build Coastguard Worker static int axis_label_font_size = 16;
58*1a3d31e3SAndroid Build Coastguard Worker
59*1a3d31e3SAndroid Build Coastguard Worker int legend_x_off = 45;
60*1a3d31e3SAndroid Build Coastguard Worker int legend_y_off = -10;
61*1a3d31e3SAndroid Build Coastguard Worker int legend_font_size = 15;
62*1a3d31e3SAndroid Build Coastguard Worker int legend_width = 80;
63*1a3d31e3SAndroid Build Coastguard Worker
64*1a3d31e3SAndroid Build Coastguard Worker static int rolling_avg_secs = 0;
65*1a3d31e3SAndroid Build Coastguard Worker
66*1a3d31e3SAndroid Build Coastguard Worker static int line_len = 1024;
67*1a3d31e3SAndroid Build Coastguard Worker static char line[1024];
68*1a3d31e3SAndroid Build Coastguard Worker
69*1a3d31e3SAndroid Build Coastguard Worker static int final_height = 0;
70*1a3d31e3SAndroid Build Coastguard Worker static int final_width = 0;
71*1a3d31e3SAndroid Build Coastguard Worker
72*1a3d31e3SAndroid Build Coastguard Worker static char *colors[] = {
73*1a3d31e3SAndroid Build Coastguard Worker "blue", "darkgreen",
74*1a3d31e3SAndroid Build Coastguard Worker "red",
75*1a3d31e3SAndroid Build Coastguard Worker "darkviolet",
76*1a3d31e3SAndroid Build Coastguard Worker "orange",
77*1a3d31e3SAndroid Build Coastguard Worker "aqua",
78*1a3d31e3SAndroid Build Coastguard Worker "brown", "#00FF00",
79*1a3d31e3SAndroid Build Coastguard Worker "yellow", "coral",
80*1a3d31e3SAndroid Build Coastguard Worker "black", "darkred",
81*1a3d31e3SAndroid Build Coastguard Worker "fuchsia", "crimson",
82*1a3d31e3SAndroid Build Coastguard Worker NULL };
83*1a3d31e3SAndroid Build Coastguard Worker
84*1a3d31e3SAndroid Build Coastguard Worker extern unsigned int longest_proc_name;
85*1a3d31e3SAndroid Build Coastguard Worker
pick_color(void)86*1a3d31e3SAndroid Build Coastguard Worker char *pick_color(void)
87*1a3d31e3SAndroid Build Coastguard Worker {
88*1a3d31e3SAndroid Build Coastguard Worker static int color_index;
89*1a3d31e3SAndroid Build Coastguard Worker char *ret = colors[color_index];
90*1a3d31e3SAndroid Build Coastguard Worker
91*1a3d31e3SAndroid Build Coastguard Worker if (!ret) {
92*1a3d31e3SAndroid Build Coastguard Worker color_index = 0;
93*1a3d31e3SAndroid Build Coastguard Worker ret = colors[color_index];
94*1a3d31e3SAndroid Build Coastguard Worker }
95*1a3d31e3SAndroid Build Coastguard Worker color_index++;
96*1a3d31e3SAndroid Build Coastguard Worker return ret;
97*1a3d31e3SAndroid Build Coastguard Worker }
98*1a3d31e3SAndroid Build Coastguard Worker
pick_fio_color(void)99*1a3d31e3SAndroid Build Coastguard Worker char *pick_fio_color(void)
100*1a3d31e3SAndroid Build Coastguard Worker {
101*1a3d31e3SAndroid Build Coastguard Worker static int fio_color_index;
102*1a3d31e3SAndroid Build Coastguard Worker char *ret = colors[fio_color_index];
103*1a3d31e3SAndroid Build Coastguard Worker
104*1a3d31e3SAndroid Build Coastguard Worker if (!ret) {
105*1a3d31e3SAndroid Build Coastguard Worker fio_color_index = 0;
106*1a3d31e3SAndroid Build Coastguard Worker ret = colors[fio_color_index];
107*1a3d31e3SAndroid Build Coastguard Worker }
108*1a3d31e3SAndroid Build Coastguard Worker fio_color_index += 2;
109*1a3d31e3SAndroid Build Coastguard Worker return ret;
110*1a3d31e3SAndroid Build Coastguard Worker }
111*1a3d31e3SAndroid Build Coastguard Worker
112*1a3d31e3SAndroid Build Coastguard Worker static int cpu_color_index;
113*1a3d31e3SAndroid Build Coastguard Worker
pick_cpu_color(void)114*1a3d31e3SAndroid Build Coastguard Worker char *pick_cpu_color(void)
115*1a3d31e3SAndroid Build Coastguard Worker {
116*1a3d31e3SAndroid Build Coastguard Worker char *ret = colors[cpu_color_index];
117*1a3d31e3SAndroid Build Coastguard Worker if (!ret) {
118*1a3d31e3SAndroid Build Coastguard Worker cpu_color_index = 0;
119*1a3d31e3SAndroid Build Coastguard Worker ret = colors[cpu_color_index];
120*1a3d31e3SAndroid Build Coastguard Worker }
121*1a3d31e3SAndroid Build Coastguard Worker cpu_color_index++;
122*1a3d31e3SAndroid Build Coastguard Worker return ret;
123*1a3d31e3SAndroid Build Coastguard Worker }
124*1a3d31e3SAndroid Build Coastguard Worker
reset_cpu_color(void)125*1a3d31e3SAndroid Build Coastguard Worker void reset_cpu_color(void)
126*1a3d31e3SAndroid Build Coastguard Worker {
127*1a3d31e3SAndroid Build Coastguard Worker cpu_color_index = 0;
128*1a3d31e3SAndroid Build Coastguard Worker }
129*1a3d31e3SAndroid Build Coastguard Worker
alloc_line_data(unsigned int min_seconds,unsigned int max_seconds,unsigned int stop_seconds)130*1a3d31e3SAndroid Build Coastguard Worker struct graph_line_data *alloc_line_data(unsigned int min_seconds,
131*1a3d31e3SAndroid Build Coastguard Worker unsigned int max_seconds,
132*1a3d31e3SAndroid Build Coastguard Worker unsigned int stop_seconds)
133*1a3d31e3SAndroid Build Coastguard Worker {
134*1a3d31e3SAndroid Build Coastguard Worker int size = sizeof(struct graph_line_data) + (stop_seconds + 1) * sizeof(struct graph_line_pair);
135*1a3d31e3SAndroid Build Coastguard Worker struct graph_line_data *gld;
136*1a3d31e3SAndroid Build Coastguard Worker
137*1a3d31e3SAndroid Build Coastguard Worker gld = calloc(1, size);
138*1a3d31e3SAndroid Build Coastguard Worker if (!gld) {
139*1a3d31e3SAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate memory for graph data\n");
140*1a3d31e3SAndroid Build Coastguard Worker exit(1);
141*1a3d31e3SAndroid Build Coastguard Worker }
142*1a3d31e3SAndroid Build Coastguard Worker gld->min_seconds = min_seconds;
143*1a3d31e3SAndroid Build Coastguard Worker gld->max_seconds = max_seconds;
144*1a3d31e3SAndroid Build Coastguard Worker gld->stop_seconds = stop_seconds;
145*1a3d31e3SAndroid Build Coastguard Worker return gld;
146*1a3d31e3SAndroid Build Coastguard Worker }
147*1a3d31e3SAndroid Build Coastguard Worker
alloc_dot_data(unsigned int min_seconds,unsigned int max_seconds,u64 min_offset,u64 max_offset,unsigned int stop_seconds,char * color,char * label)148*1a3d31e3SAndroid Build Coastguard Worker struct graph_dot_data *alloc_dot_data(unsigned int min_seconds,
149*1a3d31e3SAndroid Build Coastguard Worker unsigned int max_seconds,
150*1a3d31e3SAndroid Build Coastguard Worker u64 min_offset, u64 max_offset,
151*1a3d31e3SAndroid Build Coastguard Worker unsigned int stop_seconds,
152*1a3d31e3SAndroid Build Coastguard Worker char *color, char *label)
153*1a3d31e3SAndroid Build Coastguard Worker {
154*1a3d31e3SAndroid Build Coastguard Worker int size;
155*1a3d31e3SAndroid Build Coastguard Worker int arr_size;
156*1a3d31e3SAndroid Build Coastguard Worker int rows = graph_height * io_graph_scale;
157*1a3d31e3SAndroid Build Coastguard Worker int cols = graph_width;
158*1a3d31e3SAndroid Build Coastguard Worker struct graph_dot_data *gdd;
159*1a3d31e3SAndroid Build Coastguard Worker
160*1a3d31e3SAndroid Build Coastguard Worker size = sizeof(struct graph_dot_data);
161*1a3d31e3SAndroid Build Coastguard Worker
162*1a3d31e3SAndroid Build Coastguard Worker /* the number of bits */
163*1a3d31e3SAndroid Build Coastguard Worker arr_size = (rows + 1) * cols;
164*1a3d31e3SAndroid Build Coastguard Worker
165*1a3d31e3SAndroid Build Coastguard Worker /* the number of bytes */
166*1a3d31e3SAndroid Build Coastguard Worker arr_size = (arr_size + 7) / 8;
167*1a3d31e3SAndroid Build Coastguard Worker
168*1a3d31e3SAndroid Build Coastguard Worker gdd = calloc(1, size + arr_size);
169*1a3d31e3SAndroid Build Coastguard Worker if (!gdd) {
170*1a3d31e3SAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate memory for graph data\n");
171*1a3d31e3SAndroid Build Coastguard Worker exit(1);
172*1a3d31e3SAndroid Build Coastguard Worker }
173*1a3d31e3SAndroid Build Coastguard Worker gdd->min_seconds = min_seconds;
174*1a3d31e3SAndroid Build Coastguard Worker gdd->max_seconds = max_seconds;
175*1a3d31e3SAndroid Build Coastguard Worker gdd->stop_seconds = stop_seconds;
176*1a3d31e3SAndroid Build Coastguard Worker gdd->rows = rows;
177*1a3d31e3SAndroid Build Coastguard Worker gdd->cols = cols;
178*1a3d31e3SAndroid Build Coastguard Worker gdd->min_offset = min_offset;
179*1a3d31e3SAndroid Build Coastguard Worker gdd->max_offset = max_offset;
180*1a3d31e3SAndroid Build Coastguard Worker gdd->color = color;
181*1a3d31e3SAndroid Build Coastguard Worker gdd->label = label;
182*1a3d31e3SAndroid Build Coastguard Worker
183*1a3d31e3SAndroid Build Coastguard Worker if (strlen(label) > longest_proc_name)
184*1a3d31e3SAndroid Build Coastguard Worker longest_proc_name = strlen(label);
185*1a3d31e3SAndroid Build Coastguard Worker
186*1a3d31e3SAndroid Build Coastguard Worker return gdd;
187*1a3d31e3SAndroid Build Coastguard Worker }
188*1a3d31e3SAndroid Build Coastguard Worker
set_gdd_bit(struct graph_dot_data * gdd,u64 offset,double bytes,double time)189*1a3d31e3SAndroid Build Coastguard Worker void set_gdd_bit(struct graph_dot_data *gdd, u64 offset, double bytes, double time)
190*1a3d31e3SAndroid Build Coastguard Worker {
191*1a3d31e3SAndroid Build Coastguard Worker double bytes_per_row = (double)(gdd->max_offset - gdd->min_offset + 1) / gdd->rows;
192*1a3d31e3SAndroid Build Coastguard Worker double secs_per_col = (double)(gdd->max_seconds - gdd->min_seconds) / gdd->cols;
193*1a3d31e3SAndroid Build Coastguard Worker double col;
194*1a3d31e3SAndroid Build Coastguard Worker double row;
195*1a3d31e3SAndroid Build Coastguard Worker int col_int;
196*1a3d31e3SAndroid Build Coastguard Worker int row_int;
197*1a3d31e3SAndroid Build Coastguard Worker int bit_index;
198*1a3d31e3SAndroid Build Coastguard Worker int arr_index;
199*1a3d31e3SAndroid Build Coastguard Worker int bit_mod;
200*1a3d31e3SAndroid Build Coastguard Worker double mod = bytes_per_row;
201*1a3d31e3SAndroid Build Coastguard Worker
202*1a3d31e3SAndroid Build Coastguard Worker if (offset > gdd->max_offset || offset < gdd->min_offset)
203*1a3d31e3SAndroid Build Coastguard Worker return;
204*1a3d31e3SAndroid Build Coastguard Worker time = time / 1000000000.0;
205*1a3d31e3SAndroid Build Coastguard Worker if (time < gdd->min_seconds || time > gdd->max_seconds)
206*1a3d31e3SAndroid Build Coastguard Worker return;
207*1a3d31e3SAndroid Build Coastguard Worker gdd->total_ios++;
208*1a3d31e3SAndroid Build Coastguard Worker while (bytes > 0 && offset <= gdd->max_offset) {
209*1a3d31e3SAndroid Build Coastguard Worker row = (double)(offset - gdd->min_offset) / bytes_per_row;
210*1a3d31e3SAndroid Build Coastguard Worker col = (time - gdd->min_seconds) / secs_per_col;
211*1a3d31e3SAndroid Build Coastguard Worker
212*1a3d31e3SAndroid Build Coastguard Worker col_int = floor(col);
213*1a3d31e3SAndroid Build Coastguard Worker row_int = floor(row);
214*1a3d31e3SAndroid Build Coastguard Worker bit_index = row_int * gdd->cols + col_int;
215*1a3d31e3SAndroid Build Coastguard Worker arr_index = bit_index / 8;
216*1a3d31e3SAndroid Build Coastguard Worker bit_mod = bit_index % 8;
217*1a3d31e3SAndroid Build Coastguard Worker
218*1a3d31e3SAndroid Build Coastguard Worker gdd->data[arr_index] |= 1 << bit_mod;
219*1a3d31e3SAndroid Build Coastguard Worker offset += mod;
220*1a3d31e3SAndroid Build Coastguard Worker bytes -= mod;
221*1a3d31e3SAndroid Build Coastguard Worker }
222*1a3d31e3SAndroid Build Coastguard Worker }
223*1a3d31e3SAndroid Build Coastguard Worker
rolling_avg(struct graph_line_pair * data,int index,int distance)224*1a3d31e3SAndroid Build Coastguard Worker static double rolling_avg(struct graph_line_pair *data, int index, int distance)
225*1a3d31e3SAndroid Build Coastguard Worker {
226*1a3d31e3SAndroid Build Coastguard Worker double sum = 0;
227*1a3d31e3SAndroid Build Coastguard Worker int start;
228*1a3d31e3SAndroid Build Coastguard Worker
229*1a3d31e3SAndroid Build Coastguard Worker if (distance < 0)
230*1a3d31e3SAndroid Build Coastguard Worker distance = 1;
231*1a3d31e3SAndroid Build Coastguard Worker if (distance > index) {
232*1a3d31e3SAndroid Build Coastguard Worker start = 0;
233*1a3d31e3SAndroid Build Coastguard Worker } else {
234*1a3d31e3SAndroid Build Coastguard Worker start = index - distance;
235*1a3d31e3SAndroid Build Coastguard Worker }
236*1a3d31e3SAndroid Build Coastguard Worker distance = 0;
237*1a3d31e3SAndroid Build Coastguard Worker while (start <= index) {
238*1a3d31e3SAndroid Build Coastguard Worker double avg;
239*1a3d31e3SAndroid Build Coastguard Worker
240*1a3d31e3SAndroid Build Coastguard Worker if (data[start].count)
241*1a3d31e3SAndroid Build Coastguard Worker avg = ((double)data[start].sum) / data[start].count;
242*1a3d31e3SAndroid Build Coastguard Worker else
243*1a3d31e3SAndroid Build Coastguard Worker avg= 0;
244*1a3d31e3SAndroid Build Coastguard Worker
245*1a3d31e3SAndroid Build Coastguard Worker sum += avg;
246*1a3d31e3SAndroid Build Coastguard Worker distance++;
247*1a3d31e3SAndroid Build Coastguard Worker start++;
248*1a3d31e3SAndroid Build Coastguard Worker }
249*1a3d31e3SAndroid Build Coastguard Worker return sum / distance;
250*1a3d31e3SAndroid Build Coastguard Worker }
251*1a3d31e3SAndroid Build Coastguard Worker
write_check(int fd,char * buf,size_t size)252*1a3d31e3SAndroid Build Coastguard Worker static void write_check(int fd, char *buf, size_t size)
253*1a3d31e3SAndroid Build Coastguard Worker {
254*1a3d31e3SAndroid Build Coastguard Worker ssize_t ret;
255*1a3d31e3SAndroid Build Coastguard Worker
256*1a3d31e3SAndroid Build Coastguard Worker ret = write(fd, buf, size);
257*1a3d31e3SAndroid Build Coastguard Worker if (ret != (ssize_t)size) {
258*1a3d31e3SAndroid Build Coastguard Worker if (ret < 0)
259*1a3d31e3SAndroid Build Coastguard Worker perror("write failed");
260*1a3d31e3SAndroid Build Coastguard Worker else
261*1a3d31e3SAndroid Build Coastguard Worker fprintf(stderr, "error: short write\n");
262*1a3d31e3SAndroid Build Coastguard Worker exit(1);
263*1a3d31e3SAndroid Build Coastguard Worker }
264*1a3d31e3SAndroid Build Coastguard Worker }
265*1a3d31e3SAndroid Build Coastguard Worker
write_svg_header(int fd)266*1a3d31e3SAndroid Build Coastguard Worker void write_svg_header(int fd)
267*1a3d31e3SAndroid Build Coastguard Worker {
268*1a3d31e3SAndroid Build Coastguard Worker char *spaces = " \n";
269*1a3d31e3SAndroid Build Coastguard Worker char *header = "<svg xmlns=\"http://www.w3.org/2000/svg\">\n";
270*1a3d31e3SAndroid Build Coastguard Worker char *filter1 ="<filter id=\"shadow\">\n "
271*1a3d31e3SAndroid Build Coastguard Worker "<feOffset result=\"offOut\" in=\"SourceAlpha\" dx=\"4\" dy=\"4\" />\n "
272*1a3d31e3SAndroid Build Coastguard Worker "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"2\" />\n "
273*1a3d31e3SAndroid Build Coastguard Worker "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n "
274*1a3d31e3SAndroid Build Coastguard Worker "</filter>\n";
275*1a3d31e3SAndroid Build Coastguard Worker char *filter2 ="<filter id=\"textshadow\" x=\"0\" y=\"0\" width=\"200%\" height=\"200%\">\n "
276*1a3d31e3SAndroid Build Coastguard Worker "<feOffset result=\"offOut\" in=\"SourceAlpha\" dx=\"1\" dy=\"1\" />\n "
277*1a3d31e3SAndroid Build Coastguard Worker "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"1.5\" />\n "
278*1a3d31e3SAndroid Build Coastguard Worker "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n "
279*1a3d31e3SAndroid Build Coastguard Worker "</filter>\n";
280*1a3d31e3SAndroid Build Coastguard Worker char *filter3 ="<filter id=\"labelshadow\" x=\"0\" y=\"0\" width=\"200%\" height=\"200%\">\n "
281*1a3d31e3SAndroid Build Coastguard Worker "<feOffset result=\"offOut\" in=\"SourceGraphic\" dx=\"3\" dy=\"3\" />\n "
282*1a3d31e3SAndroid Build Coastguard Worker "<feColorMatrix result=\"matrixOut\" in=\"offOut\" type=\"matrix\" "
283*1a3d31e3SAndroid Build Coastguard Worker "values=\"0.2 0 0 0 0 0 0.2 0 0 0 0 0 0.2 0 0 0 0 0 1 0\" /> "
284*1a3d31e3SAndroid Build Coastguard Worker "<feGaussianBlur result=\"blurOut\" in=\"offOut\" stdDeviation=\"2\" />\n "
285*1a3d31e3SAndroid Build Coastguard Worker "<feBlend in=\"SourceGraphic\" in2=\"blurOut\" mode=\"normal\" />\n "
286*1a3d31e3SAndroid Build Coastguard Worker "</filter>\n";
287*1a3d31e3SAndroid Build Coastguard Worker char *defs_start = "<defs>\n";
288*1a3d31e3SAndroid Build Coastguard Worker char *defs_close = "</defs>\n";
289*1a3d31e3SAndroid Build Coastguard Worker final_width = 0;
290*1a3d31e3SAndroid Build Coastguard Worker final_height = 0;
291*1a3d31e3SAndroid Build Coastguard Worker
292*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, header, strlen(header));
293*1a3d31e3SAndroid Build Coastguard Worker /* write a bunch of spaces so we can stuff in the width and height later */
294*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, spaces, strlen(spaces));
295*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, spaces, strlen(spaces));
296*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, spaces, strlen(spaces));
297*1a3d31e3SAndroid Build Coastguard Worker
298*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, defs_start, strlen(defs_start));
299*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, filter1, strlen(filter1));
300*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, filter2, strlen(filter2));
301*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, filter3, strlen(filter3));
302*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, defs_close, strlen(defs_close));
303*1a3d31e3SAndroid Build Coastguard Worker }
304*1a3d31e3SAndroid Build Coastguard Worker
305*1a3d31e3SAndroid Build Coastguard Worker /* svg y offset for the traditional 0,0 (bottom left corner) of the plot */
axis_y(void)306*1a3d31e3SAndroid Build Coastguard Worker static int axis_y(void)
307*1a3d31e3SAndroid Build Coastguard Worker {
308*1a3d31e3SAndroid Build Coastguard Worker return plot_label_height + graph_height + graph_inner_y_margin;
309*1a3d31e3SAndroid Build Coastguard Worker }
310*1a3d31e3SAndroid Build Coastguard Worker
311*1a3d31e3SAndroid Build Coastguard Worker /* this gives you the correct pixel for a given offset from the bottom left y axis */
axis_y_off_double(double y)312*1a3d31e3SAndroid Build Coastguard Worker static double axis_y_off_double(double y)
313*1a3d31e3SAndroid Build Coastguard Worker {
314*1a3d31e3SAndroid Build Coastguard Worker return plot_label_height + graph_height - y;
315*1a3d31e3SAndroid Build Coastguard Worker }
316*1a3d31e3SAndroid Build Coastguard Worker
axis_y_off(int y)317*1a3d31e3SAndroid Build Coastguard Worker static int axis_y_off(int y)
318*1a3d31e3SAndroid Build Coastguard Worker {
319*1a3d31e3SAndroid Build Coastguard Worker return axis_y_off_double(y);
320*1a3d31e3SAndroid Build Coastguard Worker }
321*1a3d31e3SAndroid Build Coastguard Worker
322*1a3d31e3SAndroid Build Coastguard Worker /* svg x axis offset from 0 */
axis_x(void)323*1a3d31e3SAndroid Build Coastguard Worker static int axis_x(void)
324*1a3d31e3SAndroid Build Coastguard Worker {
325*1a3d31e3SAndroid Build Coastguard Worker return graph_left_pad;
326*1a3d31e3SAndroid Build Coastguard Worker }
327*1a3d31e3SAndroid Build Coastguard Worker
328*1a3d31e3SAndroid Build Coastguard Worker /* the correct pixel for a given X offset */
axis_x_off_double(double x)329*1a3d31e3SAndroid Build Coastguard Worker static double axis_x_off_double(double x)
330*1a3d31e3SAndroid Build Coastguard Worker {
331*1a3d31e3SAndroid Build Coastguard Worker return graph_left_pad + graph_inner_x_margin + x;
332*1a3d31e3SAndroid Build Coastguard Worker }
333*1a3d31e3SAndroid Build Coastguard Worker
axis_x_off(int x)334*1a3d31e3SAndroid Build Coastguard Worker static int axis_x_off(int x)
335*1a3d31e3SAndroid Build Coastguard Worker {
336*1a3d31e3SAndroid Build Coastguard Worker return (int)axis_x_off_double(x);
337*1a3d31e3SAndroid Build Coastguard Worker }
338*1a3d31e3SAndroid Build Coastguard Worker
339*1a3d31e3SAndroid Build Coastguard Worker /*
340*1a3d31e3SAndroid Build Coastguard Worker * this draws a backing rectangle for the plot and it
341*1a3d31e3SAndroid Build Coastguard Worker * also creates a new svg element so our offsets can
342*1a3d31e3SAndroid Build Coastguard Worker * be relative to this one plot.
343*1a3d31e3SAndroid Build Coastguard Worker */
setup_axis(struct plot * plot)344*1a3d31e3SAndroid Build Coastguard Worker void setup_axis(struct plot *plot)
345*1a3d31e3SAndroid Build Coastguard Worker {
346*1a3d31e3SAndroid Build Coastguard Worker int len;
347*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;
348*1a3d31e3SAndroid Build Coastguard Worker int bump_height = tick_font_size * 3 + axis_label_font_size;
349*1a3d31e3SAndroid Build Coastguard Worker int local_legend_width = legend_width;
350*1a3d31e3SAndroid Build Coastguard Worker
351*1a3d31e3SAndroid Build Coastguard Worker if (plot->no_legend)
352*1a3d31e3SAndroid Build Coastguard Worker local_legend_width = 0;
353*1a3d31e3SAndroid Build Coastguard Worker
354*1a3d31e3SAndroid Build Coastguard Worker plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + local_legend_width;
355*1a3d31e3SAndroid Build Coastguard Worker plot->total_height = axis_y() + tick_label_pad + tick_font_size;
356*1a3d31e3SAndroid Build Coastguard Worker
357*1a3d31e3SAndroid Build Coastguard Worker if (plot->add_xlabel)
358*1a3d31e3SAndroid Build Coastguard Worker plot->total_height += bump_height;
359*1a3d31e3SAndroid Build Coastguard Worker
360*1a3d31e3SAndroid Build Coastguard Worker /* backing rect */
361*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
362*1a3d31e3SAndroid Build Coastguard Worker "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
363*1a3d31e3SAndroid Build Coastguard Worker plot->start_x_offset,
364*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset, plot->total_width + 40,
365*1a3d31e3SAndroid Build Coastguard Worker plot->total_height + 20);
366*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
367*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
368*1a3d31e3SAndroid Build Coastguard Worker
369*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
370*1a3d31e3SAndroid Build Coastguard Worker "filter=\"url(#shadow)\" "
371*1a3d31e3SAndroid Build Coastguard Worker "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
372*1a3d31e3SAndroid Build Coastguard Worker plot->start_x_offset + 15,
373*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset, plot->total_width, plot->total_height);
374*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
375*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
376*1a3d31e3SAndroid Build Coastguard Worker plot->total_height += 20;
377*1a3d31e3SAndroid Build Coastguard Worker plot->total_width += 20;
378*1a3d31e3SAndroid Build Coastguard Worker
379*1a3d31e3SAndroid Build Coastguard Worker if (plot->total_height + plot->start_y_offset > final_height)
380*1a3d31e3SAndroid Build Coastguard Worker final_height = plot->total_height + plot->start_y_offset;
381*1a3d31e3SAndroid Build Coastguard Worker if (plot->start_x_offset + plot->total_width + 40 > final_width)
382*1a3d31e3SAndroid Build Coastguard Worker final_width = plot->start_x_offset + plot->total_width + 40;
383*1a3d31e3SAndroid Build Coastguard Worker
384*1a3d31e3SAndroid Build Coastguard Worker /* create an svg object for all our coords to be relative against */
385*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\">\n", plot->start_x_offset, plot->start_y_offset);
386*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, strlen(line));
387*1a3d31e3SAndroid Build Coastguard Worker
388*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, 1024, "<path d=\"M%d %d h %d V %d H %d Z\" stroke=\"black\" stroke-width=\"2\" fill=\"none\"/>\n",
389*1a3d31e3SAndroid Build Coastguard Worker axis_x(), axis_y(),
390*1a3d31e3SAndroid Build Coastguard Worker graph_width + graph_inner_x_margin * 2, axis_y_off(graph_height) - graph_inner_y_margin,
391*1a3d31e3SAndroid Build Coastguard Worker axis_x());
392*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
393*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
394*1a3d31e3SAndroid Build Coastguard Worker }
395*1a3d31e3SAndroid Build Coastguard Worker
396*1a3d31e3SAndroid Build Coastguard Worker /*
397*1a3d31e3SAndroid Build Coastguard Worker * this draws a backing rectangle for the plot and it
398*1a3d31e3SAndroid Build Coastguard Worker * also creates a new svg element so our offsets can
399*1a3d31e3SAndroid Build Coastguard Worker * be relative to this one plot.
400*1a3d31e3SAndroid Build Coastguard Worker */
setup_axis_spindle(struct plot * plot)401*1a3d31e3SAndroid Build Coastguard Worker void setup_axis_spindle(struct plot *plot)
402*1a3d31e3SAndroid Build Coastguard Worker {
403*1a3d31e3SAndroid Build Coastguard Worker int len;
404*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;
405*1a3d31e3SAndroid Build Coastguard Worker int bump_height = tick_font_size * 3 + axis_label_font_size;
406*1a3d31e3SAndroid Build Coastguard Worker
407*1a3d31e3SAndroid Build Coastguard Worker legend_x_off = -60;
408*1a3d31e3SAndroid Build Coastguard Worker
409*1a3d31e3SAndroid Build Coastguard Worker plot->total_width = axis_x_off(graph_width) + legend_width;
410*1a3d31e3SAndroid Build Coastguard Worker plot->total_height = axis_y() + tick_label_pad + tick_font_size;
411*1a3d31e3SAndroid Build Coastguard Worker
412*1a3d31e3SAndroid Build Coastguard Worker if (plot->add_xlabel)
413*1a3d31e3SAndroid Build Coastguard Worker plot->total_height += bump_height;
414*1a3d31e3SAndroid Build Coastguard Worker
415*1a3d31e3SAndroid Build Coastguard Worker /* backing rect */
416*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
417*1a3d31e3SAndroid Build Coastguard Worker "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
418*1a3d31e3SAndroid Build Coastguard Worker plot->start_x_offset,
419*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset, plot->total_width + 10,
420*1a3d31e3SAndroid Build Coastguard Worker plot->total_height + 20);
421*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
422*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
423*1a3d31e3SAndroid Build Coastguard Worker
424*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" "
425*1a3d31e3SAndroid Build Coastguard Worker "filter=\"url(#shadow)\" "
426*1a3d31e3SAndroid Build Coastguard Worker "height=\"%d\" fill=\"white\" stroke=\"none\"/>",
427*1a3d31e3SAndroid Build Coastguard Worker plot->start_x_offset + 15,
428*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset, plot->total_width - 30,
429*1a3d31e3SAndroid Build Coastguard Worker plot->total_height);
430*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
431*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
432*1a3d31e3SAndroid Build Coastguard Worker plot->total_height += 20;
433*1a3d31e3SAndroid Build Coastguard Worker
434*1a3d31e3SAndroid Build Coastguard Worker if (plot->total_height + plot->start_y_offset > final_height)
435*1a3d31e3SAndroid Build Coastguard Worker final_height = plot->total_height + plot->start_y_offset;
436*1a3d31e3SAndroid Build Coastguard Worker if (plot->start_x_offset + plot->total_width + 40 > final_width)
437*1a3d31e3SAndroid Build Coastguard Worker final_width = plot->start_x_offset + plot->total_width + 40;
438*1a3d31e3SAndroid Build Coastguard Worker
439*1a3d31e3SAndroid Build Coastguard Worker /* create an svg object for all our coords to be relative against */
440*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<svg x=\"%d\" y=\"%d\">\n", plot->start_x_offset, plot->start_y_offset);
441*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, strlen(line));
442*1a3d31e3SAndroid Build Coastguard Worker
443*1a3d31e3SAndroid Build Coastguard Worker }
444*1a3d31e3SAndroid Build Coastguard Worker
445*1a3d31e3SAndroid Build Coastguard Worker /* draw a plot title. This should be done only once,
446*1a3d31e3SAndroid Build Coastguard Worker * and it bumps the plot width/height numbers by
447*1a3d31e3SAndroid Build Coastguard Worker * what it draws.
448*1a3d31e3SAndroid Build Coastguard Worker *
449*1a3d31e3SAndroid Build Coastguard Worker * Call this before setting up the first axis
450*1a3d31e3SAndroid Build Coastguard Worker */
set_plot_title(struct plot * plot,char * title)451*1a3d31e3SAndroid Build Coastguard Worker void set_plot_title(struct plot *plot, char *title)
452*1a3d31e3SAndroid Build Coastguard Worker {
453*1a3d31e3SAndroid Build Coastguard Worker int len;
454*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;
455*1a3d31e3SAndroid Build Coastguard Worker
456*1a3d31e3SAndroid Build Coastguard Worker plot->total_height = plot_title_height;
457*1a3d31e3SAndroid Build Coastguard Worker plot->total_width = axis_x_off(graph_width) + graph_left_pad / 2 + legend_width;
458*1a3d31e3SAndroid Build Coastguard Worker
459*1a3d31e3SAndroid Build Coastguard Worker /* backing rect */
460*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"0\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"white\" stroke=\"none\"/>",
461*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset, plot->total_width + 40, plot_title_height + 20);
462*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
463*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
464*1a3d31e3SAndroid Build Coastguard Worker
465*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
466*1a3d31e3SAndroid Build Coastguard Worker "font-weight=\"bold\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
467*1a3d31e3SAndroid Build Coastguard Worker axis_x_off(graph_width / 2),
468*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset + plot_title_height / 2,
469*1a3d31e3SAndroid Build Coastguard Worker font_family, plot_title_font_size, "middle", title);
470*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset += plot_title_height;
471*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
472*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
473*1a3d31e3SAndroid Build Coastguard Worker }
474*1a3d31e3SAndroid Build Coastguard Worker
475*1a3d31e3SAndroid Build Coastguard Worker #define TICK_MINI_STEPS 3
476*1a3d31e3SAndroid Build Coastguard Worker
find_step(double first,double last,int num_ticks)477*1a3d31e3SAndroid Build Coastguard Worker static double find_step(double first, double last, int num_ticks)
478*1a3d31e3SAndroid Build Coastguard Worker {
479*1a3d31e3SAndroid Build Coastguard Worker int mini_step[TICK_MINI_STEPS] = { 1, 2, 5 };
480*1a3d31e3SAndroid Build Coastguard Worker int cur_mini_step = 0;
481*1a3d31e3SAndroid Build Coastguard Worker double step = (last - first) / num_ticks;
482*1a3d31e3SAndroid Build Coastguard Worker double log10 = log(10);
483*1a3d31e3SAndroid Build Coastguard Worker
484*1a3d31e3SAndroid Build Coastguard Worker /* Round to power of 10 */
485*1a3d31e3SAndroid Build Coastguard Worker step = exp(floor(log(step) / log10) * log10);
486*1a3d31e3SAndroid Build Coastguard Worker /* Scale down step to provide enough ticks */
487*1a3d31e3SAndroid Build Coastguard Worker while (cur_mini_step < TICK_MINI_STEPS
488*1a3d31e3SAndroid Build Coastguard Worker && (last - first) / (step * mini_step[cur_mini_step]) > num_ticks)
489*1a3d31e3SAndroid Build Coastguard Worker cur_mini_step++;
490*1a3d31e3SAndroid Build Coastguard Worker
491*1a3d31e3SAndroid Build Coastguard Worker if (cur_mini_step > 0)
492*1a3d31e3SAndroid Build Coastguard Worker step *= mini_step[cur_mini_step - 1];
493*1a3d31e3SAndroid Build Coastguard Worker
494*1a3d31e3SAndroid Build Coastguard Worker return step;
495*1a3d31e3SAndroid Build Coastguard Worker }
496*1a3d31e3SAndroid Build Coastguard Worker
497*1a3d31e3SAndroid Build Coastguard Worker /*
498*1a3d31e3SAndroid Build Coastguard Worker * create evenly spread out ticks along the xaxis. if tick only is set
499*1a3d31e3SAndroid Build Coastguard Worker * this just makes the ticks, otherwise it labels each tick as it goes
500*1a3d31e3SAndroid Build Coastguard Worker */
set_xticks(struct plot * plot,int num_ticks,int first,int last)501*1a3d31e3SAndroid Build Coastguard Worker void set_xticks(struct plot *plot, int num_ticks, int first, int last)
502*1a3d31e3SAndroid Build Coastguard Worker {
503*1a3d31e3SAndroid Build Coastguard Worker int pixels_per_tick;
504*1a3d31e3SAndroid Build Coastguard Worker double step;
505*1a3d31e3SAndroid Build Coastguard Worker int i;
506*1a3d31e3SAndroid Build Coastguard Worker int tick_y = axis_y_off(graph_tick_len) + graph_inner_y_margin;
507*1a3d31e3SAndroid Build Coastguard Worker int tick_x = axis_x();
508*1a3d31e3SAndroid Build Coastguard Worker int tick_only = plot->add_xlabel == 0;
509*1a3d31e3SAndroid Build Coastguard Worker
510*1a3d31e3SAndroid Build Coastguard Worker int text_y = axis_y() + tick_label_pad;
511*1a3d31e3SAndroid Build Coastguard Worker
512*1a3d31e3SAndroid Build Coastguard Worker char *middle = "middle";
513*1a3d31e3SAndroid Build Coastguard Worker char *start = "start";
514*1a3d31e3SAndroid Build Coastguard Worker
515*1a3d31e3SAndroid Build Coastguard Worker step = find_step(first, last, num_ticks);
516*1a3d31e3SAndroid Build Coastguard Worker /*
517*1a3d31e3SAndroid Build Coastguard Worker * We don't want last two ticks to be too close together so subtract
518*1a3d31e3SAndroid Build Coastguard Worker * 20% of the step from the interval
519*1a3d31e3SAndroid Build Coastguard Worker */
520*1a3d31e3SAndroid Build Coastguard Worker num_ticks = (double)(last - first - step) / step + 1;
521*1a3d31e3SAndroid Build Coastguard Worker pixels_per_tick = graph_width * step / (double)(last - first);
522*1a3d31e3SAndroid Build Coastguard Worker
523*1a3d31e3SAndroid Build Coastguard Worker for (i = 0; i < num_ticks; i++) {
524*1a3d31e3SAndroid Build Coastguard Worker char *anchor;
525*1a3d31e3SAndroid Build Coastguard Worker if (i != 0) {
526*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"2\" height=\"%d\" style=\"stroke:none;fill:black;\"/>\n",
527*1a3d31e3SAndroid Build Coastguard Worker tick_x, tick_y, graph_tick_len);
528*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
529*1a3d31e3SAndroid Build Coastguard Worker anchor = middle;
530*1a3d31e3SAndroid Build Coastguard Worker } else {
531*1a3d31e3SAndroid Build Coastguard Worker anchor = start;
532*1a3d31e3SAndroid Build Coastguard Worker }
533*1a3d31e3SAndroid Build Coastguard Worker
534*1a3d31e3SAndroid Build Coastguard Worker if (!tick_only) {
535*1a3d31e3SAndroid Build Coastguard Worker if (step >= 1)
536*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
537*1a3d31e3SAndroid Build Coastguard Worker "fill=\"black\" style=\"text-anchor: %s\">%d</text>\n",
538*1a3d31e3SAndroid Build Coastguard Worker tick_x, text_y, font_family, tick_font_size, anchor,
539*1a3d31e3SAndroid Build Coastguard Worker (int)(first + step * i));
540*1a3d31e3SAndroid Build Coastguard Worker else
541*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
542*1a3d31e3SAndroid Build Coastguard Worker "fill=\"black\" style=\"text-anchor: %s\">%.2f</text>\n",
543*1a3d31e3SAndroid Build Coastguard Worker tick_x, text_y, font_family, tick_font_size, anchor,
544*1a3d31e3SAndroid Build Coastguard Worker first + step * i);
545*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
546*1a3d31e3SAndroid Build Coastguard Worker }
547*1a3d31e3SAndroid Build Coastguard Worker tick_x += pixels_per_tick;
548*1a3d31e3SAndroid Build Coastguard Worker }
549*1a3d31e3SAndroid Build Coastguard Worker
550*1a3d31e3SAndroid Build Coastguard Worker if (!tick_only) {
551*1a3d31e3SAndroid Build Coastguard Worker if (step >= 1)
552*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
553*1a3d31e3SAndroid Build Coastguard Worker "fill=\"black\" style=\"text-anchor: middle\">%d</text>\n",
554*1a3d31e3SAndroid Build Coastguard Worker axis_x_off(graph_width - 2),
555*1a3d31e3SAndroid Build Coastguard Worker text_y, font_family, tick_font_size, last);
556*1a3d31e3SAndroid Build Coastguard Worker else
557*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
558*1a3d31e3SAndroid Build Coastguard Worker "fill=\"black\" style=\"text-anchor: middle\">%.2f</text>\n",
559*1a3d31e3SAndroid Build Coastguard Worker axis_x_off(graph_width - 2),
560*1a3d31e3SAndroid Build Coastguard Worker text_y, font_family, tick_font_size, (double)last);
561*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
562*1a3d31e3SAndroid Build Coastguard Worker }
563*1a3d31e3SAndroid Build Coastguard Worker }
564*1a3d31e3SAndroid Build Coastguard Worker
set_ylabel(struct plot * plot,char * label)565*1a3d31e3SAndroid Build Coastguard Worker void set_ylabel(struct plot *plot, char *label)
566*1a3d31e3SAndroid Build Coastguard Worker {
567*1a3d31e3SAndroid Build Coastguard Worker int len;
568*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;
569*1a3d31e3SAndroid Build Coastguard Worker
570*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" "
571*1a3d31e3SAndroid Build Coastguard Worker "transform=\"rotate(-90 %d %d)\" font-weight=\"bold\" "
572*1a3d31e3SAndroid Build Coastguard Worker "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
573*1a3d31e3SAndroid Build Coastguard Worker graph_left_pad / 2 - axis_label_font_size,
574*1a3d31e3SAndroid Build Coastguard Worker axis_y_off(graph_height / 2),
575*1a3d31e3SAndroid Build Coastguard Worker font_family,
576*1a3d31e3SAndroid Build Coastguard Worker graph_left_pad / 2 - axis_label_font_size,
577*1a3d31e3SAndroid Build Coastguard Worker (int)axis_y_off(graph_height / 2),
578*1a3d31e3SAndroid Build Coastguard Worker axis_label_font_size, "middle", label);
579*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
580*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
581*1a3d31e3SAndroid Build Coastguard Worker }
582*1a3d31e3SAndroid Build Coastguard Worker
set_xlabel(struct plot * plot,char * label)583*1a3d31e3SAndroid Build Coastguard Worker void set_xlabel(struct plot *plot, char *label)
584*1a3d31e3SAndroid Build Coastguard Worker {
585*1a3d31e3SAndroid Build Coastguard Worker int len;
586*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;
587*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" "
588*1a3d31e3SAndroid Build Coastguard Worker "font-weight=\"bold\" "
589*1a3d31e3SAndroid Build Coastguard Worker "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
590*1a3d31e3SAndroid Build Coastguard Worker axis_x_off(graph_width / 2),
591*1a3d31e3SAndroid Build Coastguard Worker axis_y() + tick_font_size * 3 + axis_label_font_size / 2,
592*1a3d31e3SAndroid Build Coastguard Worker font_family,
593*1a3d31e3SAndroid Build Coastguard Worker axis_label_font_size, "middle", label);
594*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
595*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
596*1a3d31e3SAndroid Build Coastguard Worker
597*1a3d31e3SAndroid Build Coastguard Worker }
598*1a3d31e3SAndroid Build Coastguard Worker
599*1a3d31e3SAndroid Build Coastguard Worker /*
600*1a3d31e3SAndroid Build Coastguard Worker * create evenly spread out ticks along the y axis.
601*1a3d31e3SAndroid Build Coastguard Worker * The ticks are labeled as it goes
602*1a3d31e3SAndroid Build Coastguard Worker */
set_yticks(struct plot * plot,int num_ticks,int first,int last,char * units)603*1a3d31e3SAndroid Build Coastguard Worker void set_yticks(struct plot *plot, int num_ticks, int first, int last, char *units)
604*1a3d31e3SAndroid Build Coastguard Worker {
605*1a3d31e3SAndroid Build Coastguard Worker int pixels_per_tick = graph_height / num_ticks;
606*1a3d31e3SAndroid Build Coastguard Worker int step = (last - first) / num_ticks;
607*1a3d31e3SAndroid Build Coastguard Worker int i;
608*1a3d31e3SAndroid Build Coastguard Worker int tick_y = 0;
609*1a3d31e3SAndroid Build Coastguard Worker int text_x = axis_x() - 6;
610*1a3d31e3SAndroid Build Coastguard Worker int tick_x = axis_x();
611*1a3d31e3SAndroid Build Coastguard Worker char *anchor = "end";
612*1a3d31e3SAndroid Build Coastguard Worker
613*1a3d31e3SAndroid Build Coastguard Worker for (i = 0; i < num_ticks; i++) {
614*1a3d31e3SAndroid Build Coastguard Worker if (i != 0) {
615*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
616*1a3d31e3SAndroid Build Coastguard Worker "style=\"stroke:lightgray;stroke-width:2;stroke-dasharray:9,12;\"/>\n",
617*1a3d31e3SAndroid Build Coastguard Worker tick_x, axis_y_off(tick_y),
618*1a3d31e3SAndroid Build Coastguard Worker axis_x_off(graph_width), axis_y_off(tick_y));
619*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
620*1a3d31e3SAndroid Build Coastguard Worker }
621*1a3d31e3SAndroid Build Coastguard Worker
622*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
623*1a3d31e3SAndroid Build Coastguard Worker "fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n",
624*1a3d31e3SAndroid Build Coastguard Worker text_x,
625*1a3d31e3SAndroid Build Coastguard Worker axis_y_off(tick_y - tick_font_size / 2),
626*1a3d31e3SAndroid Build Coastguard Worker font_family, tick_font_size, anchor, first + step * i, units);
627*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
628*1a3d31e3SAndroid Build Coastguard Worker tick_y += pixels_per_tick;
629*1a3d31e3SAndroid Build Coastguard Worker }
630*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
631*1a3d31e3SAndroid Build Coastguard Worker "fill=\"black\" style=\"text-anchor: %s\">%d%s</text>\n",
632*1a3d31e3SAndroid Build Coastguard Worker text_x, axis_y_off(graph_height), font_family, tick_font_size, anchor, last, units);
633*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
634*1a3d31e3SAndroid Build Coastguard Worker }
635*1a3d31e3SAndroid Build Coastguard Worker
set_plot_label(struct plot * plot,char * label)636*1a3d31e3SAndroid Build Coastguard Worker void set_plot_label(struct plot *plot, char *label)
637*1a3d31e3SAndroid Build Coastguard Worker {
638*1a3d31e3SAndroid Build Coastguard Worker int len;
639*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;
640*1a3d31e3SAndroid Build Coastguard Worker
641*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<text x=\"%d\" y=\"%d\" font-family=\"%s\" "
642*1a3d31e3SAndroid Build Coastguard Worker "font-size=\"%d\" fill=\"black\" style=\"text-anchor: %s\">%s</text>\n",
643*1a3d31e3SAndroid Build Coastguard Worker axis_x() + graph_width / 2,
644*1a3d31e3SAndroid Build Coastguard Worker plot_label_height / 2,
645*1a3d31e3SAndroid Build Coastguard Worker font_family, plot_label_font_size, "middle", label);
646*1a3d31e3SAndroid Build Coastguard Worker len = strlen(line);
647*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, len);
648*1a3d31e3SAndroid Build Coastguard Worker }
649*1a3d31e3SAndroid Build Coastguard Worker
close_svg(int fd)650*1a3d31e3SAndroid Build Coastguard Worker static void close_svg(int fd)
651*1a3d31e3SAndroid Build Coastguard Worker {
652*1a3d31e3SAndroid Build Coastguard Worker char *close_line = "</svg>\n";
653*1a3d31e3SAndroid Build Coastguard Worker
654*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, close_line, strlen(close_line));
655*1a3d31e3SAndroid Build Coastguard Worker }
656*1a3d31e3SAndroid Build Coastguard Worker
close_plot(struct plot * plot)657*1a3d31e3SAndroid Build Coastguard Worker int close_plot(struct plot *plot)
658*1a3d31e3SAndroid Build Coastguard Worker {
659*1a3d31e3SAndroid Build Coastguard Worker close_svg(plot->fd);
660*1a3d31e3SAndroid Build Coastguard Worker if (plot->direction == PLOT_DOWN)
661*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset += plot->total_height;
662*1a3d31e3SAndroid Build Coastguard Worker else if (plot->direction == PLOT_ACROSS)
663*1a3d31e3SAndroid Build Coastguard Worker plot->start_x_offset += plot->total_width;
664*1a3d31e3SAndroid Build Coastguard Worker return 0;
665*1a3d31e3SAndroid Build Coastguard Worker }
666*1a3d31e3SAndroid Build Coastguard Worker
alloc_plot(void)667*1a3d31e3SAndroid Build Coastguard Worker struct plot *alloc_plot(void)
668*1a3d31e3SAndroid Build Coastguard Worker {
669*1a3d31e3SAndroid Build Coastguard Worker struct plot *plot;
670*1a3d31e3SAndroid Build Coastguard Worker plot = calloc(1, sizeof(*plot));
671*1a3d31e3SAndroid Build Coastguard Worker if (!plot) {
672*1a3d31e3SAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate memory %s\n", strerror(errno));
673*1a3d31e3SAndroid Build Coastguard Worker exit(1);
674*1a3d31e3SAndroid Build Coastguard Worker }
675*1a3d31e3SAndroid Build Coastguard Worker plot->fd = 0;
676*1a3d31e3SAndroid Build Coastguard Worker return plot;
677*1a3d31e3SAndroid Build Coastguard Worker }
678*1a3d31e3SAndroid Build Coastguard Worker
close_plot_file(struct plot * plot)679*1a3d31e3SAndroid Build Coastguard Worker int close_plot_file(struct plot *plot)
680*1a3d31e3SAndroid Build Coastguard Worker {
681*1a3d31e3SAndroid Build Coastguard Worker int ret;
682*1a3d31e3SAndroid Build Coastguard Worker ret = lseek(plot->fd, 0, SEEK_SET);
683*1a3d31e3SAndroid Build Coastguard Worker if (ret == (off_t)-1) {
684*1a3d31e3SAndroid Build Coastguard Worker perror("seek");
685*1a3d31e3SAndroid Build Coastguard Worker exit(1);
686*1a3d31e3SAndroid Build Coastguard Worker }
687*1a3d31e3SAndroid Build Coastguard Worker final_width = ((final_width + 1) / 2) * 2;
688*1a3d31e3SAndroid Build Coastguard Worker final_height = ((final_height + 1) / 2) * 2;
689*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<svg xmlns=\"http://www.w3.org/2000/svg\" "
690*1a3d31e3SAndroid Build Coastguard Worker "width=\"%d\" height=\"%d\">\n",
691*1a3d31e3SAndroid Build Coastguard Worker final_width, final_height);
692*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
693*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"0\" y=\"0\" width=\"%d\" "
694*1a3d31e3SAndroid Build Coastguard Worker "height=\"%d\" fill=\"white\"/>\n", final_width, final_height);
695*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
696*1a3d31e3SAndroid Build Coastguard Worker close(plot->fd);
697*1a3d31e3SAndroid Build Coastguard Worker plot->fd = 0;
698*1a3d31e3SAndroid Build Coastguard Worker return 0;
699*1a3d31e3SAndroid Build Coastguard Worker }
700*1a3d31e3SAndroid Build Coastguard Worker
set_plot_output(struct plot * plot,char * filename)701*1a3d31e3SAndroid Build Coastguard Worker void set_plot_output(struct plot *plot, char *filename)
702*1a3d31e3SAndroid Build Coastguard Worker {
703*1a3d31e3SAndroid Build Coastguard Worker int fd;
704*1a3d31e3SAndroid Build Coastguard Worker
705*1a3d31e3SAndroid Build Coastguard Worker if (plot->fd)
706*1a3d31e3SAndroid Build Coastguard Worker close_plot_file(plot);
707*1a3d31e3SAndroid Build Coastguard Worker fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
708*1a3d31e3SAndroid Build Coastguard Worker if (fd < 0) {
709*1a3d31e3SAndroid Build Coastguard Worker fprintf(stderr, "Unable to open output file %s err %s\n", filename, strerror(errno));
710*1a3d31e3SAndroid Build Coastguard Worker exit(1);
711*1a3d31e3SAndroid Build Coastguard Worker }
712*1a3d31e3SAndroid Build Coastguard Worker plot->fd = fd;
713*1a3d31e3SAndroid Build Coastguard Worker plot->start_y_offset = plot->start_x_offset = 0;
714*1a3d31e3SAndroid Build Coastguard Worker write_svg_header(fd);
715*1a3d31e3SAndroid Build Coastguard Worker }
716*1a3d31e3SAndroid Build Coastguard Worker
717*1a3d31e3SAndroid Build Coastguard Worker char *byte_unit_names[] = { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "unobtainium" };
718*1a3d31e3SAndroid Build Coastguard Worker int MAX_BYTE_UNIT_SCALE = 9;
719*1a3d31e3SAndroid Build Coastguard Worker
720*1a3d31e3SAndroid Build Coastguard Worker char *time_unit_names[] = { "n", "u", "m", "s" };
721*1a3d31e3SAndroid Build Coastguard Worker int MAX_TIME_UNIT_SCALE = 3;
722*1a3d31e3SAndroid Build Coastguard Worker
scale_line_graph_bytes(u64 * max,char ** units,u64 factor)723*1a3d31e3SAndroid Build Coastguard Worker void scale_line_graph_bytes(u64 *max, char **units, u64 factor)
724*1a3d31e3SAndroid Build Coastguard Worker {
725*1a3d31e3SAndroid Build Coastguard Worker int scale = 0;
726*1a3d31e3SAndroid Build Coastguard Worker u64 val = *max;
727*1a3d31e3SAndroid Build Coastguard Worker u64 div = 1;
728*1a3d31e3SAndroid Build Coastguard Worker while (val > factor * 64) {
729*1a3d31e3SAndroid Build Coastguard Worker val /= factor;
730*1a3d31e3SAndroid Build Coastguard Worker scale++;
731*1a3d31e3SAndroid Build Coastguard Worker div *= factor;
732*1a3d31e3SAndroid Build Coastguard Worker }
733*1a3d31e3SAndroid Build Coastguard Worker *units = byte_unit_names[scale];
734*1a3d31e3SAndroid Build Coastguard Worker if (scale == 0)
735*1a3d31e3SAndroid Build Coastguard Worker return;
736*1a3d31e3SAndroid Build Coastguard Worker
737*1a3d31e3SAndroid Build Coastguard Worker if (scale > MAX_BYTE_UNIT_SCALE)
738*1a3d31e3SAndroid Build Coastguard Worker scale = MAX_BYTE_UNIT_SCALE;
739*1a3d31e3SAndroid Build Coastguard Worker
740*1a3d31e3SAndroid Build Coastguard Worker *max /= div;
741*1a3d31e3SAndroid Build Coastguard Worker }
742*1a3d31e3SAndroid Build Coastguard Worker
scale_line_graph_time(u64 * max,char ** units)743*1a3d31e3SAndroid Build Coastguard Worker void scale_line_graph_time(u64 *max, char **units)
744*1a3d31e3SAndroid Build Coastguard Worker {
745*1a3d31e3SAndroid Build Coastguard Worker int scale = 0;
746*1a3d31e3SAndroid Build Coastguard Worker u64 val = *max;
747*1a3d31e3SAndroid Build Coastguard Worker u64 div = 1;
748*1a3d31e3SAndroid Build Coastguard Worker while (val > 1000 * 10) {
749*1a3d31e3SAndroid Build Coastguard Worker val /= 1000;
750*1a3d31e3SAndroid Build Coastguard Worker scale++;
751*1a3d31e3SAndroid Build Coastguard Worker div *= 1000;
752*1a3d31e3SAndroid Build Coastguard Worker if (scale == MAX_TIME_UNIT_SCALE)
753*1a3d31e3SAndroid Build Coastguard Worker break;
754*1a3d31e3SAndroid Build Coastguard Worker }
755*1a3d31e3SAndroid Build Coastguard Worker *units = time_unit_names[scale];
756*1a3d31e3SAndroid Build Coastguard Worker if (scale == 0)
757*1a3d31e3SAndroid Build Coastguard Worker return;
758*1a3d31e3SAndroid Build Coastguard Worker
759*1a3d31e3SAndroid Build Coastguard Worker *max /= div;
760*1a3d31e3SAndroid Build Coastguard Worker }
761*1a3d31e3SAndroid Build Coastguard Worker
rolling_span(struct graph_line_data * gld)762*1a3d31e3SAndroid Build Coastguard Worker static int rolling_span(struct graph_line_data *gld)
763*1a3d31e3SAndroid Build Coastguard Worker {
764*1a3d31e3SAndroid Build Coastguard Worker if (rolling_avg_secs)
765*1a3d31e3SAndroid Build Coastguard Worker return rolling_avg_secs;
766*1a3d31e3SAndroid Build Coastguard Worker return (gld->stop_seconds - gld->min_seconds) / 25;
767*1a3d31e3SAndroid Build Coastguard Worker }
768*1a3d31e3SAndroid Build Coastguard Worker
769*1a3d31e3SAndroid Build Coastguard Worker
line_graph_roll_avg_max(struct graph_line_data * gld)770*1a3d31e3SAndroid Build Coastguard Worker double line_graph_roll_avg_max(struct graph_line_data *gld)
771*1a3d31e3SAndroid Build Coastguard Worker {
772*1a3d31e3SAndroid Build Coastguard Worker unsigned int i;
773*1a3d31e3SAndroid Build Coastguard Worker int rolling;
774*1a3d31e3SAndroid Build Coastguard Worker double avg, max = 0;
775*1a3d31e3SAndroid Build Coastguard Worker
776*1a3d31e3SAndroid Build Coastguard Worker rolling = rolling_span(gld);
777*1a3d31e3SAndroid Build Coastguard Worker for (i = gld->min_seconds; i < gld->stop_seconds; i++) {
778*1a3d31e3SAndroid Build Coastguard Worker avg = rolling_avg(gld->data, i, rolling);
779*1a3d31e3SAndroid Build Coastguard Worker if (avg > max)
780*1a3d31e3SAndroid Build Coastguard Worker max = avg;
781*1a3d31e3SAndroid Build Coastguard Worker }
782*1a3d31e3SAndroid Build Coastguard Worker return max;
783*1a3d31e3SAndroid Build Coastguard Worker }
784*1a3d31e3SAndroid Build Coastguard Worker
svg_line_graph(struct plot * plot,struct graph_line_data * gld,char * color,int thresh1,int thresh2)785*1a3d31e3SAndroid Build Coastguard Worker int svg_line_graph(struct plot *plot, struct graph_line_data *gld, char *color, int thresh1, int thresh2)
786*1a3d31e3SAndroid Build Coastguard Worker {
787*1a3d31e3SAndroid Build Coastguard Worker unsigned int i;
788*1a3d31e3SAndroid Build Coastguard Worker double val;
789*1a3d31e3SAndroid Build Coastguard Worker double avg;
790*1a3d31e3SAndroid Build Coastguard Worker int rolling;
791*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;
792*1a3d31e3SAndroid Build Coastguard Worker char *start = "<path d=\"";
793*1a3d31e3SAndroid Build Coastguard Worker double yscale = ((double)gld->max) / graph_height;
794*1a3d31e3SAndroid Build Coastguard Worker double xscale = (double)(gld->max_seconds - gld->min_seconds - 1) / graph_width;
795*1a3d31e3SAndroid Build Coastguard Worker char c = 'M';
796*1a3d31e3SAndroid Build Coastguard Worker double x;
797*1a3d31e3SAndroid Build Coastguard Worker int printed_header = 0;
798*1a3d31e3SAndroid Build Coastguard Worker int printed_lines = 0;
799*1a3d31e3SAndroid Build Coastguard Worker
800*1a3d31e3SAndroid Build Coastguard Worker if (thresh1 && thresh2)
801*1a3d31e3SAndroid Build Coastguard Worker rolling = 0;
802*1a3d31e3SAndroid Build Coastguard Worker else
803*1a3d31e3SAndroid Build Coastguard Worker rolling = rolling_span(gld);
804*1a3d31e3SAndroid Build Coastguard Worker
805*1a3d31e3SAndroid Build Coastguard Worker for (i = gld->min_seconds; i < gld->stop_seconds; i++) {
806*1a3d31e3SAndroid Build Coastguard Worker avg = rolling_avg(gld->data, i, rolling);
807*1a3d31e3SAndroid Build Coastguard Worker if (yscale == 0)
808*1a3d31e3SAndroid Build Coastguard Worker val = 0;
809*1a3d31e3SAndroid Build Coastguard Worker else
810*1a3d31e3SAndroid Build Coastguard Worker val = avg / yscale;
811*1a3d31e3SAndroid Build Coastguard Worker
812*1a3d31e3SAndroid Build Coastguard Worker if (val > graph_height)
813*1a3d31e3SAndroid Build Coastguard Worker val = graph_height;
814*1a3d31e3SAndroid Build Coastguard Worker if (val < 0)
815*1a3d31e3SAndroid Build Coastguard Worker val = 0;
816*1a3d31e3SAndroid Build Coastguard Worker
817*1a3d31e3SAndroid Build Coastguard Worker x = (double)(i - gld->min_seconds) / xscale;
818*1a3d31e3SAndroid Build Coastguard Worker if (!thresh1 && !thresh2) {
819*1a3d31e3SAndroid Build Coastguard Worker if (!printed_header) {
820*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, start, strlen(start));
821*1a3d31e3SAndroid Build Coastguard Worker printed_header = 1;
822*1a3d31e3SAndroid Build Coastguard Worker }
823*1a3d31e3SAndroid Build Coastguard Worker
824*1a3d31e3SAndroid Build Coastguard Worker /* in full line mode, everything in the graph is connected */
825*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "%c %d %d ", c, axis_x_off(x), axis_y_off(val));
826*1a3d31e3SAndroid Build Coastguard Worker c = 'L';
827*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, strlen(line));
828*1a3d31e3SAndroid Build Coastguard Worker printed_lines = 1;
829*1a3d31e3SAndroid Build Coastguard Worker } else if (avg > thresh1 || avg > thresh2) {
830*1a3d31e3SAndroid Build Coastguard Worker int len = 10;
831*1a3d31e3SAndroid Build Coastguard Worker if (!printed_header) {
832*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, start, strlen(start));
833*1a3d31e3SAndroid Build Coastguard Worker printed_header = 1;
834*1a3d31e3SAndroid Build Coastguard Worker }
835*1a3d31e3SAndroid Build Coastguard Worker
836*1a3d31e3SAndroid Build Coastguard Worker /* otherwise, we just print a bar up there to show this one data point */
837*1a3d31e3SAndroid Build Coastguard Worker if (i >= gld->stop_seconds - 2)
838*1a3d31e3SAndroid Build Coastguard Worker len = -10;
839*1a3d31e3SAndroid Build Coastguard Worker
840*1a3d31e3SAndroid Build Coastguard Worker /*
841*1a3d31e3SAndroid Build Coastguard Worker * we don't use the rolling averages here to show high
842*1a3d31e3SAndroid Build Coastguard Worker * points in the data
843*1a3d31e3SAndroid Build Coastguard Worker */
844*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "M %d %d h %d ", axis_x_off(x),
845*1a3d31e3SAndroid Build Coastguard Worker axis_y_off(val), len);
846*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, strlen(line));
847*1a3d31e3SAndroid Build Coastguard Worker printed_lines = 1;
848*1a3d31e3SAndroid Build Coastguard Worker }
849*1a3d31e3SAndroid Build Coastguard Worker
850*1a3d31e3SAndroid Build Coastguard Worker }
851*1a3d31e3SAndroid Build Coastguard Worker if (printed_lines) {
852*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "\" fill=\"none\" stroke=\"%s\" stroke-width=\"2\"/>\n", color);
853*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, strlen(line));
854*1a3d31e3SAndroid Build Coastguard Worker }
855*1a3d31e3SAndroid Build Coastguard Worker if (plot->timeline)
856*1a3d31e3SAndroid Build Coastguard Worker svg_write_time_line(plot, plot->timeline);
857*1a3d31e3SAndroid Build Coastguard Worker
858*1a3d31e3SAndroid Build Coastguard Worker return 0;
859*1a3d31e3SAndroid Build Coastguard Worker }
860*1a3d31e3SAndroid Build Coastguard Worker
svg_write_time_line(struct plot * plot,int col)861*1a3d31e3SAndroid Build Coastguard Worker void svg_write_time_line(struct plot *plot, int col)
862*1a3d31e3SAndroid Build Coastguard Worker {
863*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
864*1a3d31e3SAndroid Build Coastguard Worker "style=\"stroke:black;stroke-width:2;\"/>\n",
865*1a3d31e3SAndroid Build Coastguard Worker axis_x_off(col), axis_y_off(0),
866*1a3d31e3SAndroid Build Coastguard Worker axis_x_off(col), axis_y_off(graph_height));
867*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
868*1a3d31e3SAndroid Build Coastguard Worker }
869*1a3d31e3SAndroid Build Coastguard Worker
svg_add_io(int fd,double row,double col,double width,double height,char * color)870*1a3d31e3SAndroid Build Coastguard Worker static void svg_add_io(int fd, double row, double col, double width, double height, char *color)
871*1a3d31e3SAndroid Build Coastguard Worker {
872*1a3d31e3SAndroid Build Coastguard Worker float rx = 0;
873*1a3d31e3SAndroid Build Coastguard Worker
874*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"%.2f\" y=\"%.2f\" width=\"%.1f\" height=\"%.1f\" "
875*1a3d31e3SAndroid Build Coastguard Worker "rx=\"%.2f\" style=\"stroke:none;fill:%s;stroke-width:0\"/>\n",
876*1a3d31e3SAndroid Build Coastguard Worker axis_x_off_double(col), axis_y_off_double(row), width, height, rx, color);
877*1a3d31e3SAndroid Build Coastguard Worker write_check(fd, line, strlen(line));
878*1a3d31e3SAndroid Build Coastguard Worker }
879*1a3d31e3SAndroid Build Coastguard Worker
svg_io_graph_movie_array(struct plot * plot,struct pid_plot_history * pph)880*1a3d31e3SAndroid Build Coastguard Worker int svg_io_graph_movie_array(struct plot *plot, struct pid_plot_history *pph)
881*1a3d31e3SAndroid Build Coastguard Worker {
882*1a3d31e3SAndroid Build Coastguard Worker double cell_index;
883*1a3d31e3SAndroid Build Coastguard Worker double movie_row;
884*1a3d31e3SAndroid Build Coastguard Worker double movie_col;
885*1a3d31e3SAndroid Build Coastguard Worker int i;
886*1a3d31e3SAndroid Build Coastguard Worker
887*1a3d31e3SAndroid Build Coastguard Worker for (i = 0; i < pph->num_used; i++) {
888*1a3d31e3SAndroid Build Coastguard Worker cell_index = pph->history[i];
889*1a3d31e3SAndroid Build Coastguard Worker movie_row = floor(cell_index / graph_width);
890*1a3d31e3SAndroid Build Coastguard Worker movie_col = cell_index - movie_row * graph_width;
891*1a3d31e3SAndroid Build Coastguard Worker svg_add_io(plot->fd, movie_row, movie_col, 4, 4, pph->color);
892*1a3d31e3SAndroid Build Coastguard Worker }
893*1a3d31e3SAndroid Build Coastguard Worker return 0;
894*1a3d31e3SAndroid Build Coastguard Worker }
895*1a3d31e3SAndroid Build Coastguard Worker
896*1a3d31e3SAndroid Build Coastguard Worker static float spindle_steps = 0;
897*1a3d31e3SAndroid Build Coastguard Worker
rewind_spindle_steps(int num)898*1a3d31e3SAndroid Build Coastguard Worker void rewind_spindle_steps(int num)
899*1a3d31e3SAndroid Build Coastguard Worker {
900*1a3d31e3SAndroid Build Coastguard Worker spindle_steps -= num * 0.01;
901*1a3d31e3SAndroid Build Coastguard Worker }
902*1a3d31e3SAndroid Build Coastguard Worker
svg_io_graph_movie_array_spindle(struct plot * plot,struct pid_plot_history * pph)903*1a3d31e3SAndroid Build Coastguard Worker int svg_io_graph_movie_array_spindle(struct plot *plot, struct pid_plot_history *pph)
904*1a3d31e3SAndroid Build Coastguard Worker {
905*1a3d31e3SAndroid Build Coastguard Worker double cell_index;
906*1a3d31e3SAndroid Build Coastguard Worker int i;
907*1a3d31e3SAndroid Build Coastguard Worker int num_circles = 0;
908*1a3d31e3SAndroid Build Coastguard Worker double cells_per_circle;
909*1a3d31e3SAndroid Build Coastguard Worker double circle_num;
910*1a3d31e3SAndroid Build Coastguard Worker double degrees_per_cell;
911*1a3d31e3SAndroid Build Coastguard Worker double rot;
912*1a3d31e3SAndroid Build Coastguard Worker double center_x;
913*1a3d31e3SAndroid Build Coastguard Worker double center_y;
914*1a3d31e3SAndroid Build Coastguard Worker double graph_width_extra = graph_width + graph_circle_extra;
915*1a3d31e3SAndroid Build Coastguard Worker double graph_height_extra = graph_height + graph_circle_extra;
916*1a3d31e3SAndroid Build Coastguard Worker double radius;;
917*1a3d31e3SAndroid Build Coastguard Worker
918*1a3d31e3SAndroid Build Coastguard Worker if (graph_width_extra > graph_height_extra)
919*1a3d31e3SAndroid Build Coastguard Worker graph_width_extra = graph_height_extra;
920*1a3d31e3SAndroid Build Coastguard Worker
921*1a3d31e3SAndroid Build Coastguard Worker if (graph_width_extra < graph_height_extra)
922*1a3d31e3SAndroid Build Coastguard Worker graph_height_extra = graph_width_extra;
923*1a3d31e3SAndroid Build Coastguard Worker
924*1a3d31e3SAndroid Build Coastguard Worker radius = graph_width_extra;
925*1a3d31e3SAndroid Build Coastguard Worker
926*1a3d31e3SAndroid Build Coastguard Worker center_x = axis_x_off_double(graph_width_extra / 2);
927*1a3d31e3SAndroid Build Coastguard Worker center_y = axis_y_off_double(graph_height_extra / 2);
928*1a3d31e3SAndroid Build Coastguard Worker
929*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<g transform=\"rotate(%.4f, %.2f, %.2f)\"> "
930*1a3d31e3SAndroid Build Coastguard Worker "<circle cx=\"%.2f\" cy=\"%.2f\" "
931*1a3d31e3SAndroid Build Coastguard Worker "stroke=\"black\" stroke-width=\"6\" "
932*1a3d31e3SAndroid Build Coastguard Worker "r=\"%.2f\" fill=\"none\"/>\n",
933*1a3d31e3SAndroid Build Coastguard Worker spindle_steps * 1.2, center_x, center_y, center_x, center_y, graph_width_extra / 2);
934*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
935*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<circle cx=\"%.2f\" cy=\"%.2f\" "
936*1a3d31e3SAndroid Build Coastguard Worker "stroke=\"none\" fill=\"red\" r=\"%.2f\"/>\n</g>\n",
937*1a3d31e3SAndroid Build Coastguard Worker axis_x_off_double(graph_width_extra), center_y, 4.5);
938*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
939*1a3d31e3SAndroid Build Coastguard Worker spindle_steps += 0.01;
940*1a3d31e3SAndroid Build Coastguard Worker
941*1a3d31e3SAndroid Build Coastguard Worker radius = floor(radius / 2);
942*1a3d31e3SAndroid Build Coastguard Worker num_circles = radius / 4 - 3;
943*1a3d31e3SAndroid Build Coastguard Worker cells_per_circle = pph->history_max / num_circles;
944*1a3d31e3SAndroid Build Coastguard Worker degrees_per_cell = 360 / cells_per_circle;
945*1a3d31e3SAndroid Build Coastguard Worker
946*1a3d31e3SAndroid Build Coastguard Worker for (i = 0; i < pph->num_used; i++) {
947*1a3d31e3SAndroid Build Coastguard Worker cell_index = pph->history[i];
948*1a3d31e3SAndroid Build Coastguard Worker circle_num = floor(cell_index / cells_per_circle);
949*1a3d31e3SAndroid Build Coastguard Worker rot = cell_index - circle_num * cells_per_circle;
950*1a3d31e3SAndroid Build Coastguard Worker circle_num = num_circles - circle_num;
951*1a3d31e3SAndroid Build Coastguard Worker radius = circle_num * 4;
952*1a3d31e3SAndroid Build Coastguard Worker
953*1a3d31e3SAndroid Build Coastguard Worker rot = rot * degrees_per_cell;
954*1a3d31e3SAndroid Build Coastguard Worker rot -= spindle_steps;
955*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<path transform=\"rotate(%.4f, %.2f, %.2f)\" "
956*1a3d31e3SAndroid Build Coastguard Worker "d=\"M %.2f %.2f a %.2f %.2f 0 0 1 0 5\" "
957*1a3d31e3SAndroid Build Coastguard Worker "stroke=\"%s\" stroke-width=\"4\"/>\n",
958*1a3d31e3SAndroid Build Coastguard Worker -rot, center_x, center_y,
959*1a3d31e3SAndroid Build Coastguard Worker axis_x_off_double(graph_width_extra / 2 + radius) + 8, center_y,
960*1a3d31e3SAndroid Build Coastguard Worker radius, radius, pph->color);
961*1a3d31e3SAndroid Build Coastguard Worker
962*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
963*1a3d31e3SAndroid Build Coastguard Worker }
964*1a3d31e3SAndroid Build Coastguard Worker return 0;
965*1a3d31e3SAndroid Build Coastguard Worker }
966*1a3d31e3SAndroid Build Coastguard Worker
add_plot_history(struct pid_plot_history * pph,double val)967*1a3d31e3SAndroid Build Coastguard Worker static int add_plot_history(struct pid_plot_history *pph, double val)
968*1a3d31e3SAndroid Build Coastguard Worker {
969*1a3d31e3SAndroid Build Coastguard Worker if (pph->num_used == pph->history_len) {
970*1a3d31e3SAndroid Build Coastguard Worker pph->history_len += 4096;
971*1a3d31e3SAndroid Build Coastguard Worker pph->history = realloc(pph->history,
972*1a3d31e3SAndroid Build Coastguard Worker pph->history_len * sizeof(double));
973*1a3d31e3SAndroid Build Coastguard Worker if (!pph->history) {
974*1a3d31e3SAndroid Build Coastguard Worker perror("Unable to allocate memory");
975*1a3d31e3SAndroid Build Coastguard Worker exit(1);
976*1a3d31e3SAndroid Build Coastguard Worker }
977*1a3d31e3SAndroid Build Coastguard Worker }
978*1a3d31e3SAndroid Build Coastguard Worker pph->history[pph->num_used++] = val;
979*1a3d31e3SAndroid Build Coastguard Worker return 0;
980*1a3d31e3SAndroid Build Coastguard Worker }
981*1a3d31e3SAndroid Build Coastguard Worker
svg_io_graph_movie(struct graph_dot_data * gdd,struct pid_plot_history * pph,int col)982*1a3d31e3SAndroid Build Coastguard Worker int svg_io_graph_movie(struct graph_dot_data *gdd, struct pid_plot_history *pph, int col)
983*1a3d31e3SAndroid Build Coastguard Worker {
984*1a3d31e3SAndroid Build Coastguard Worker int row = 0;
985*1a3d31e3SAndroid Build Coastguard Worker int arr_index;
986*1a3d31e3SAndroid Build Coastguard Worker unsigned char val;
987*1a3d31e3SAndroid Build Coastguard Worker int bit_index;
988*1a3d31e3SAndroid Build Coastguard Worker int bit_mod;
989*1a3d31e3SAndroid Build Coastguard Worker double blocks_per_row = (gdd->max_offset - gdd->min_offset + 1) / gdd->rows;
990*1a3d31e3SAndroid Build Coastguard Worker double movie_blocks_per_cell = (gdd->max_offset - gdd->min_offset + 1) / (graph_width * graph_height);
991*1a3d31e3SAndroid Build Coastguard Worker double cell_index;
992*1a3d31e3SAndroid Build Coastguard Worker int margin_orig = graph_inner_y_margin;
993*1a3d31e3SAndroid Build Coastguard Worker
994*1a3d31e3SAndroid Build Coastguard Worker graph_inner_y_margin += 5;
995*1a3d31e3SAndroid Build Coastguard Worker pph->history_max = (gdd->max_offset - gdd->min_offset + 1) / movie_blocks_per_cell;
996*1a3d31e3SAndroid Build Coastguard Worker
997*1a3d31e3SAndroid Build Coastguard Worker for (row = gdd->rows - 1; row >= 0; row--) {
998*1a3d31e3SAndroid Build Coastguard Worker bit_index = row * gdd->cols + col;
999*1a3d31e3SAndroid Build Coastguard Worker arr_index = bit_index / 8;
1000*1a3d31e3SAndroid Build Coastguard Worker bit_mod = bit_index % 8;
1001*1a3d31e3SAndroid Build Coastguard Worker
1002*1a3d31e3SAndroid Build Coastguard Worker if (arr_index < 0)
1003*1a3d31e3SAndroid Build Coastguard Worker continue;
1004*1a3d31e3SAndroid Build Coastguard Worker val = gdd->data[arr_index];
1005*1a3d31e3SAndroid Build Coastguard Worker if (val & (1 << bit_mod)) {
1006*1a3d31e3SAndroid Build Coastguard Worker /* in bytes, linear offset from the start of the drive */
1007*1a3d31e3SAndroid Build Coastguard Worker cell_index = (double)row * blocks_per_row;
1008*1a3d31e3SAndroid Build Coastguard Worker
1009*1a3d31e3SAndroid Build Coastguard Worker /* a cell number in the graph */
1010*1a3d31e3SAndroid Build Coastguard Worker cell_index /= movie_blocks_per_cell;
1011*1a3d31e3SAndroid Build Coastguard Worker
1012*1a3d31e3SAndroid Build Coastguard Worker add_plot_history(pph, cell_index);
1013*1a3d31e3SAndroid Build Coastguard Worker }
1014*1a3d31e3SAndroid Build Coastguard Worker }
1015*1a3d31e3SAndroid Build Coastguard Worker graph_inner_y_margin = margin_orig;
1016*1a3d31e3SAndroid Build Coastguard Worker return 0;
1017*1a3d31e3SAndroid Build Coastguard Worker }
1018*1a3d31e3SAndroid Build Coastguard Worker
svg_io_graph(struct plot * plot,struct graph_dot_data * gdd)1019*1a3d31e3SAndroid Build Coastguard Worker int svg_io_graph(struct plot *plot, struct graph_dot_data *gdd)
1020*1a3d31e3SAndroid Build Coastguard Worker {
1021*1a3d31e3SAndroid Build Coastguard Worker int fd = plot->fd;;
1022*1a3d31e3SAndroid Build Coastguard Worker int col = 0;
1023*1a3d31e3SAndroid Build Coastguard Worker int row = 0;
1024*1a3d31e3SAndroid Build Coastguard Worker int arr_index;
1025*1a3d31e3SAndroid Build Coastguard Worker unsigned char val;
1026*1a3d31e3SAndroid Build Coastguard Worker int bit_index;
1027*1a3d31e3SAndroid Build Coastguard Worker int bit_mod;
1028*1a3d31e3SAndroid Build Coastguard Worker
1029*1a3d31e3SAndroid Build Coastguard Worker for (row = gdd->rows - 1; row >= 0; row--) {
1030*1a3d31e3SAndroid Build Coastguard Worker for (col = 0; col < gdd->cols; col++) {
1031*1a3d31e3SAndroid Build Coastguard Worker bit_index = row * gdd->cols + col;
1032*1a3d31e3SAndroid Build Coastguard Worker arr_index = bit_index / 8;
1033*1a3d31e3SAndroid Build Coastguard Worker bit_mod = bit_index % 8;
1034*1a3d31e3SAndroid Build Coastguard Worker
1035*1a3d31e3SAndroid Build Coastguard Worker if (arr_index < 0)
1036*1a3d31e3SAndroid Build Coastguard Worker continue;
1037*1a3d31e3SAndroid Build Coastguard Worker val = gdd->data[arr_index];
1038*1a3d31e3SAndroid Build Coastguard Worker if (val & (1 << bit_mod))
1039*1a3d31e3SAndroid Build Coastguard Worker svg_add_io(fd, floor(row / io_graph_scale), col, 1.5, 1.5, gdd->color);
1040*1a3d31e3SAndroid Build Coastguard Worker }
1041*1a3d31e3SAndroid Build Coastguard Worker }
1042*1a3d31e3SAndroid Build Coastguard Worker return 0;
1043*1a3d31e3SAndroid Build Coastguard Worker }
1044*1a3d31e3SAndroid Build Coastguard Worker
svg_alloc_legend(struct plot * plot,int num_lines)1045*1a3d31e3SAndroid Build Coastguard Worker void svg_alloc_legend(struct plot *plot, int num_lines)
1046*1a3d31e3SAndroid Build Coastguard Worker {
1047*1a3d31e3SAndroid Build Coastguard Worker char **lines = calloc(num_lines, sizeof(char *));
1048*1a3d31e3SAndroid Build Coastguard Worker plot->legend_index = 0;
1049*1a3d31e3SAndroid Build Coastguard Worker plot->legend_lines = lines;
1050*1a3d31e3SAndroid Build Coastguard Worker plot->num_legend_lines = num_lines;
1051*1a3d31e3SAndroid Build Coastguard Worker }
1052*1a3d31e3SAndroid Build Coastguard Worker
svg_free_legend(struct plot * plot)1053*1a3d31e3SAndroid Build Coastguard Worker void svg_free_legend(struct plot *plot)
1054*1a3d31e3SAndroid Build Coastguard Worker {
1055*1a3d31e3SAndroid Build Coastguard Worker int i;
1056*1a3d31e3SAndroid Build Coastguard Worker for (i = 0; i < plot->legend_index; i++)
1057*1a3d31e3SAndroid Build Coastguard Worker free(plot->legend_lines[i]);
1058*1a3d31e3SAndroid Build Coastguard Worker free(plot->legend_lines);
1059*1a3d31e3SAndroid Build Coastguard Worker plot->legend_lines = NULL;
1060*1a3d31e3SAndroid Build Coastguard Worker plot->legend_index = 0;
1061*1a3d31e3SAndroid Build Coastguard Worker }
1062*1a3d31e3SAndroid Build Coastguard Worker
svg_write_legend(struct plot * plot)1063*1a3d31e3SAndroid Build Coastguard Worker void svg_write_legend(struct plot *plot)
1064*1a3d31e3SAndroid Build Coastguard Worker {
1065*1a3d31e3SAndroid Build Coastguard Worker int legend_line_x = axis_x_off(graph_width) + legend_x_off;
1066*1a3d31e3SAndroid Build Coastguard Worker int legend_line_y = axis_y_off(graph_height) + legend_y_off;
1067*1a3d31e3SAndroid Build Coastguard Worker int i;
1068*1a3d31e3SAndroid Build Coastguard Worker
1069*1a3d31e3SAndroid Build Coastguard Worker if (plot->legend_index == 0)
1070*1a3d31e3SAndroid Build Coastguard Worker return;
1071*1a3d31e3SAndroid Build Coastguard Worker
1072*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "
1073*1a3d31e3SAndroid Build Coastguard Worker "fill=\"white\" filter=\"url(#shadow)\"/>\n",
1074*1a3d31e3SAndroid Build Coastguard Worker legend_line_x - 15,
1075*1a3d31e3SAndroid Build Coastguard Worker legend_line_y - 12,
1076*1a3d31e3SAndroid Build Coastguard Worker legend_width,
1077*1a3d31e3SAndroid Build Coastguard Worker plot->legend_index * legend_font_size + legend_font_size / 2 + 12);
1078*1a3d31e3SAndroid Build Coastguard Worker
1079*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, line, strlen(line));
1080*1a3d31e3SAndroid Build Coastguard Worker for (i = 0; i < plot->legend_index; i++) {
1081*1a3d31e3SAndroid Build Coastguard Worker write_check(plot->fd, plot->legend_lines[i],
1082*1a3d31e3SAndroid Build Coastguard Worker strlen(plot->legend_lines[i]));
1083*1a3d31e3SAndroid Build Coastguard Worker free(plot->legend_lines[i]);
1084*1a3d31e3SAndroid Build Coastguard Worker }
1085*1a3d31e3SAndroid Build Coastguard Worker free(plot->legend_lines);
1086*1a3d31e3SAndroid Build Coastguard Worker plot->legend_lines = NULL;
1087*1a3d31e3SAndroid Build Coastguard Worker plot->legend_index = 0;
1088*1a3d31e3SAndroid Build Coastguard Worker }
1089*1a3d31e3SAndroid Build Coastguard Worker
svg_add_legend(struct plot * plot,char * text,char * extra,char * color)1090*1a3d31e3SAndroid Build Coastguard Worker void svg_add_legend(struct plot *plot, char *text, char *extra, char *color)
1091*1a3d31e3SAndroid Build Coastguard Worker {
1092*1a3d31e3SAndroid Build Coastguard Worker int legend_line_x = axis_x_off(graph_width) + legend_x_off;
1093*1a3d31e3SAndroid Build Coastguard Worker int legend_line_y = axis_y_off(graph_height) + legend_y_off;
1094*1a3d31e3SAndroid Build Coastguard Worker
1095*1a3d31e3SAndroid Build Coastguard Worker if (!text && (!extra || strlen(extra) == 0))
1096*1a3d31e3SAndroid Build Coastguard Worker return;
1097*1a3d31e3SAndroid Build Coastguard Worker
1098*1a3d31e3SAndroid Build Coastguard Worker legend_line_y += plot->legend_index * legend_font_size + legend_font_size / 2;
1099*1a3d31e3SAndroid Build Coastguard Worker snprintf(line, line_len, "<path d=\"M %d %d h 8\" stroke=\"%s\" stroke-width=\"8\" "
1100*1a3d31e3SAndroid Build Coastguard Worker "filter=\"url(#labelshadow)\"/> "
1101*1a3d31e3SAndroid Build Coastguard Worker "<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d\" "
1102*1a3d31e3SAndroid Build Coastguard Worker "fill=\"black\" style=\"text-anchor: left\">%s%s</text>\n",
1103*1a3d31e3SAndroid Build Coastguard Worker legend_line_x, legend_line_y,
1104*1a3d31e3SAndroid Build Coastguard Worker color, legend_line_x + 13,
1105*1a3d31e3SAndroid Build Coastguard Worker legend_line_y + 4, font_family, legend_font_size,
1106*1a3d31e3SAndroid Build Coastguard Worker text, extra);
1107*1a3d31e3SAndroid Build Coastguard Worker
1108*1a3d31e3SAndroid Build Coastguard Worker plot->legend_lines[plot->legend_index++] = strdup(line);
1109*1a3d31e3SAndroid Build Coastguard Worker }
1110*1a3d31e3SAndroid Build Coastguard Worker
set_legend_width(int longest_str)1111*1a3d31e3SAndroid Build Coastguard Worker void set_legend_width(int longest_str)
1112*1a3d31e3SAndroid Build Coastguard Worker {
1113*1a3d31e3SAndroid Build Coastguard Worker if (longest_str)
1114*1a3d31e3SAndroid Build Coastguard Worker legend_width = longest_str * (legend_font_size * 3 / 4) + 25;
1115*1a3d31e3SAndroid Build Coastguard Worker else
1116*1a3d31e3SAndroid Build Coastguard Worker legend_width = 0;
1117*1a3d31e3SAndroid Build Coastguard Worker }
1118*1a3d31e3SAndroid Build Coastguard Worker
set_rolling_avg(int rolling)1119*1a3d31e3SAndroid Build Coastguard Worker void set_rolling_avg(int rolling)
1120*1a3d31e3SAndroid Build Coastguard Worker {
1121*1a3d31e3SAndroid Build Coastguard Worker rolling_avg_secs = rolling;
1122*1a3d31e3SAndroid Build Coastguard Worker }
1123*1a3d31e3SAndroid Build Coastguard Worker
set_io_graph_scale(int scale)1124*1a3d31e3SAndroid Build Coastguard Worker void set_io_graph_scale(int scale)
1125*1a3d31e3SAndroid Build Coastguard Worker {
1126*1a3d31e3SAndroid Build Coastguard Worker io_graph_scale = scale;
1127*1a3d31e3SAndroid Build Coastguard Worker }
1128*1a3d31e3SAndroid Build Coastguard Worker
set_graph_size(int width,int height)1129*1a3d31e3SAndroid Build Coastguard Worker void set_graph_size(int width, int height)
1130*1a3d31e3SAndroid Build Coastguard Worker {
1131*1a3d31e3SAndroid Build Coastguard Worker graph_width = width;
1132*1a3d31e3SAndroid Build Coastguard Worker graph_height = height;
1133*1a3d31e3SAndroid Build Coastguard Worker }
1134*1a3d31e3SAndroid Build Coastguard Worker
get_graph_size(int * width,int * height)1135*1a3d31e3SAndroid Build Coastguard Worker void get_graph_size(int *width, int *height)
1136*1a3d31e3SAndroid Build Coastguard Worker {
1137*1a3d31e3SAndroid Build Coastguard Worker *width = graph_width;
1138*1a3d31e3SAndroid Build Coastguard Worker *height = graph_height;
1139*1a3d31e3SAndroid Build Coastguard Worker }
1140*1a3d31e3SAndroid Build Coastguard Worker
set_graph_height(int h)1141*1a3d31e3SAndroid Build Coastguard Worker void set_graph_height(int h)
1142*1a3d31e3SAndroid Build Coastguard Worker {
1143*1a3d31e3SAndroid Build Coastguard Worker graph_height = h;
1144*1a3d31e3SAndroid Build Coastguard Worker }
set_graph_width(int w)1145*1a3d31e3SAndroid Build Coastguard Worker void set_graph_width(int w)
1146*1a3d31e3SAndroid Build Coastguard Worker {
1147*1a3d31e3SAndroid Build Coastguard Worker graph_width = w;
1148*1a3d31e3SAndroid Build Coastguard Worker }
1149