graph.c revision a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2
1af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron/*
2af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron * gfio - gui front end for fio - the flexible io tester
3af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *
4af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *
6af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron * The license below covers all files distributed with fio unless otherwise
7af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron * noted in the file itself.
8af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *
9af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  This program is free software; you can redistribute it and/or modify
10af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  it under the terms of the GNU General Public License version 2 as
11af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  published by the Free Software Foundation.
12af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *
13af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  This program is distributed in the hope that it will be useful,
14af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  GNU General Public License for more details.
17af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *
18af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  You should have received a copy of the GNU General Public License
19af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  along with this program; if not, write to the Free Software
20af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron *
22af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron */
23af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron#include <string.h>
24af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron#include <malloc.h>
25af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron#include <math.h>
26af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron#include <assert.h>
2793e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe#include <stdlib.h>
28af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
29af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron#include <cairo.h>
30af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron#include <gtk/gtk.h>
31af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
32af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron#include "tickmarks.h"
3309ad20ff60eb8c11edf407c4060062dae187f5e7Stephen M. Cameron#include "graph.h"
34b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe#include "flist.h"
35b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe#include "lib/prio_tree.h"
36b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
37b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe/*
38b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe * Allowable difference to show tooltip
39b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe */
40b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe#define TOOLTIP_DELTA	0.08
41af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
42af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstruct xyvalue {
43af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double x, y;
44af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron};
45af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
46cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboeenum {
47cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	GV_F_ON_PRIO	= 1,
488dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	GV_F_PRIO_SKIP	= 2,
49cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe};
50cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
51af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstruct graph_value {
52cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head list;
53b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	struct prio_tree_node node;
54cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head alias;
55cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	unsigned int flags;
5693e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	char *tooltip;
57af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	void *value;
58af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron};
59af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
60af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstruct graph_label {
61cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head list;
62af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	char *label;
63cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head value_list;
64b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	struct prio_tree_root prio_tree;
65af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double r, g, b;
6601a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	int hide;
67af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	int value_count;
68af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph *parent;
69af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron};
70af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
71b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboestruct tick_value {
72b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	unsigned int offset;
73b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double value;
74b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe};
75b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
76af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstruct graph {
77af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	char *title;
78af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	char *xtitle;
79af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	char *ytitle;
8087d5f276b14f42b09062d499ebb3f524e3aa86f3Jens Axboe	unsigned int xdim, ydim;
8157f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron	double xoffset, yoffset;
82cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head label_list;
83af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	int per_label_limit;
84f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	const char *font;
857175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	graph_axis_unit_change_callback x_axis_unit_change_callback;
867175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	graph_axis_unit_change_callback y_axis_unit_change_callback;
87d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	unsigned int base_offset;
8801a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	unsigned int dont_graph_all_zeroes;
89def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	double left_extra;
90def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	double right_extra;
91def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	double top_extra;
92def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	double bottom_extra;
93b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
94b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double xtick_zero;
95b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double xtick_delta;
96b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double xtick_zero_val;
97b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	double xtick_one_val;
98b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double ytick_zero;
99b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double ytick_delta;
100b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double ytick_zero_val;
101b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	double ytick_one_val;
102af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron};
103af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
1043ea48b883bcac5ad0c119205805b741af45427e3Stephen M. Cameronvoid graph_set_size(struct graph *g, unsigned int xdim, unsigned int ydim)
1053ea48b883bcac5ad0c119205805b741af45427e3Stephen M. Cameron{
1063ea48b883bcac5ad0c119205805b741af45427e3Stephen M. Cameron	g->xdim = xdim;
1073ea48b883bcac5ad0c119205805b741af45427e3Stephen M. Cameron	g->ydim = ydim;
1083ea48b883bcac5ad0c119205805b741af45427e3Stephen M. Cameron}
1093ea48b883bcac5ad0c119205805b741af45427e3Stephen M. Cameron
11057f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameronvoid graph_set_position(struct graph *g, double xoffset, double yoffset)
11157f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron{
11257f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron	g->xoffset = xoffset;
11357f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron	g->yoffset = yoffset;
11457f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron}
11557f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron
116f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboestruct graph *graph_new(unsigned int xdim, unsigned int ydim, const char *font)
117af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
118af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph *g;
119af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
120af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	g = calloc(1, sizeof(*g));
121cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	INIT_FLIST_HEAD(&g->label_list);
1223ea48b883bcac5ad0c119205805b741af45427e3Stephen M. Cameron	graph_set_size(g, xdim, ydim);
123af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	g->per_label_limit = -1;
124f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	g->font = font;
125f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	if (!g->font)
126a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2Jens Axboe		g->font = GRAPH_DEFAULT_FONT;
127af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return g;
128af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
129af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
130a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2Jens Axboevoid graph_set_font(struct graph *g, const char *font)
131a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2Jens Axboe{
132a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2Jens Axboe	g->font = font;
133a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2Jens Axboe}
134a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2Jens Axboe
1357175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameronvoid graph_x_axis_unit_change_notify(struct graph *g, graph_axis_unit_change_callback f)
1367175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron{
1377175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	g->x_axis_unit_change_callback = f;
1387175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron}
1397175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron
1407175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameronvoid graph_y_axis_unit_change_notify(struct graph *g, graph_axis_unit_change_callback f)
1417175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron{
1427175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	g->y_axis_unit_change_callback = f;
1437175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron}
1447175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron
145cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic int count_labels(struct graph *g)
146af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
147cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
148af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	int count = 0;
149af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
150cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &g->label_list)
151af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		count++;
152cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
153af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return count;
154af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
155af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
156cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic int count_values(struct graph_label *l)
157af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
158cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
159af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	int count = 0;
160af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
161cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &l->value_list)
162af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		count++;
163cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
164af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return count;
165af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
166af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
167af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Camerontypedef double (*double_comparator)(double a, double b);
168af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
169af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic double mindouble(double a, double b)
170af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
171af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return a < b ? a : b;
172af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
173af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
174af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic double maxdouble(double a, double b)
175af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
176af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return a < b ? b : a;
177af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
178af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
179cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic double find_double_values(struct graph_label *l, double_comparator cmp)
180af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
181cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
182af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double answer, tmp;
183cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	int first = 1;
184af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
18501a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	if (flist_empty(&l->value_list))
18601a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		return 0.0;
18701a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
188cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &l->value_list) {
189cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		struct graph_value *i;
190cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
191cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(entry, struct graph_value, list);
192cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		tmp = *(double *) i->value;
193af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		if (first) {
194af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			answer = tmp;
195af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			first = 0;
196af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		} else {
197af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			answer = cmp(answer, tmp);
198af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		}
199af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
200af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return answer;
201af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
202af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
203cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic double find_double_data(struct graph *g, double_comparator cmp)
204af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
205cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
206af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_label *i;
207af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	int first = 1;
208af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double answer, tmp;
209af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
21001a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	if (flist_empty(&g->label_list))
21101a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		return 0.0;
21201a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
213cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &g->label_list) {
214cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(entry, struct graph_label, list);
215cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		tmp = find_double_values(i, cmp);
216af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		if (first) {
217af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			answer = tmp;
218af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			first = 0;
219af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		} else {
220af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			answer = cmp(tmp, answer);
221af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		}
222af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
223af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return answer;
224af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
225af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
226cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic double find_min_data(struct graph *g)
227af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
228cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	return find_double_data(g, mindouble);
229af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
230af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
231cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic double find_max_data(struct graph *g)
232af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
233cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	return find_double_data(g, maxdouble);
234af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
235af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
236af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic void draw_bars(struct graph *bg, cairo_t *cr, struct graph_label *lb,
237af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			double label_offset, double bar_width,
238af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			double mindata, double maxdata)
239af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
240cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
241af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double x1, y1, x2, y2;
242af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	int bar_num = 0;
243af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double domain, range, v;
244af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
245af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	domain = (maxdata - mindata);
246af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	range = (double) bg->ydim * 0.80; /* FIXME */
247af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_stroke(cr);
248cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &lb->value_list) {
249cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		struct graph_value *i;
250cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
251cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(entry, struct graph_value, list);
252af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
253af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		x1 = label_offset + (double) bar_num * bar_width + (bar_width * 0.05);
254af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		x2 = x1 + bar_width * 0.90;
255af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		y2 = bg->ydim * 0.90;
256af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		v = *(double *) i->value;
257af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		y1 = y2 - (((v - mindata) / domain) * range);
258af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_move_to(cr, x1, y1);
259af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_line_to(cr, x1, y2);
260af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_line_to(cr, x2, y2);
261af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_line_to(cr, x2, y1);
262af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_close_path(cr);
263af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_fill(cr);
264af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
265af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		bar_num++;
266af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
267af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
268af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
26910e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameronstatic void draw_aligned_text(struct graph *g, cairo_t *cr, double x, double y,
27010e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			       double fontsize, const char *text, int alignment)
271af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
27210e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron#define CENTERED 0
27310e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron#define LEFT_JUSTIFIED 1
27410e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron#define RIGHT_JUSTIFIED 2
27510e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron
27610e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	double factor, direction;
277af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_text_extents_t extents;
278af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
27910e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	switch(alignment) {
28010e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron		case CENTERED:
28110e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			direction = -1.0;
28210e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			factor = 0.5;
28310e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			break;
28410e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron		case RIGHT_JUSTIFIED:
28510e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			direction = -1.0;
28610e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			factor = 1.0;
28710e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			break;
28810e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron		case LEFT_JUSTIFIED:
28910e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron		default:
29010e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			direction = 1.0;
29110e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			factor = 1.0;
29210e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			break;
29310e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	}
294a1e7972d96ce1482aa43c2fcafd81d6c7f3c44d2Jens Axboe	cairo_select_font_face(cr, g->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
295af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
296af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_set_font_size(cr, fontsize);
297af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_text_extents(cr, text, &extents);
29810e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	x = x + direction * (factor * extents.width  + extents.x_bearing);
299af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	y = y - (extents.height / 2 + extents.y_bearing);
300af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
301af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_move_to(cr, x, y);
302af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_show_text(cr, text);
303af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
304af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
30510e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameronstatic inline void draw_centered_text(struct graph *g, cairo_t *cr, double x, double y,
30610e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron			       double fontsize, const char *text)
30710e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron{
30810e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	draw_aligned_text(g, cr, x, y, fontsize, text, CENTERED);
30910e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron}
31010e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron
31110e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameronstatic inline void draw_right_justified_text(struct graph *g, cairo_t *cr,
31210e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron				double x, double y,
31310e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron				double fontsize, const char *text)
31410e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron{
31510e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	draw_aligned_text(g, cr, x, y, fontsize, text, RIGHT_JUSTIFIED);
31610e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron}
31710e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron
31810e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameronstatic inline void draw_left_justified_text(struct graph *g, cairo_t *cr,
31910e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron				double x, double y,
32010e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron				double fontsize, const char *text)
32110e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron{
32210e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	draw_aligned_text(g, cr, x, y, fontsize, text, LEFT_JUSTIFIED);
32310e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron}
32410e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron
325f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboestatic void draw_vertical_centered_text(struct graph *g, cairo_t *cr, double x,
326f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe					double y, double fontsize,
327f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe					const char *text)
328af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
329af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double sx, sy;
330af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_text_extents_t extents;
331af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
332f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	cairo_select_font_face(cr, g->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
333af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
334af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_set_font_size(cr, fontsize);
335af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_text_extents(cr, text, &extents);
336af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	sx = x;
337af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	sy = y;
338af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	y = y + (extents.width / 2.0 + extents.x_bearing);
339af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	x = x - (extents.height / 2.0 + extents.y_bearing);
340af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
341af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_move_to(cr, x, y);
342af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_save(cr);
343af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_translate(cr, -sx, -sy);
344af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_rotate(cr, -90.0 * M_PI / 180.0);
345af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_translate(cr, sx, sy);
346af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_show_text(cr, text);
347af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_restore(cr);
348af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
349af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
350af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic void graph_draw_common(struct graph *g, cairo_t *cr,
351af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double *x1, double *y1, double *x2, double *y2)
352af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
3534e14f0181e1c0cc59a55601011ba7bc73e4c5b20Jens Axboe	cairo_set_source_rgb(cr, 0, 0, 0);
3544e14f0181e1c0cc59a55601011ba7bc73e4c5b20Jens Axboe	cairo_set_line_width (cr, 0.8);
355af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
3569ede9c9c00f5457564677833cc44d436b7c936c4Jens Axboe	*x1 = 0.10 * g->xdim;
3576bf860089760c72ea22bc74e929080c15cd4c835Stephen M. Cameron	*x2 = 0.95 * g->xdim;
3586bf860089760c72ea22bc74e929080c15cd4c835Stephen M. Cameron	*y1 = 0.10 * g->ydim;
3596bf860089760c72ea22bc74e929080c15cd4c835Stephen M. Cameron	*y2 = 0.90 * g->ydim;
360af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
361af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_move_to(cr, *x1, *y1);
362af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_line_to(cr, *x1, *y2);
363af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_line_to(cr, *x2, *y2);
364af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_line_to(cr, *x2, *y1);
365af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_line_to(cr, *x1, *y1);
366af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_stroke(cr);
367af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
368f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	draw_centered_text(g, cr, g->xdim / 2, g->ydim / 20, 20.0, g->title);
369f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	draw_centered_text(g, cr, g->xdim / 2, g->ydim * 0.97, 14.0, g->xtitle);
370f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	draw_vertical_centered_text(g, cr, g->xdim * 0.02, g->ydim / 2, 14.0, g->ytitle);
371af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_stroke(cr);
372af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
373af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
374af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic void graph_draw_x_ticks(struct graph *g, cairo_t *cr,
375af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double x1, double y1, double x2, double y2,
376d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	double minx, double maxx, int nticks, int add_tm_text)
377af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
378af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct tickmark *tm;
379af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double tx;
3807175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	int i, power_of_ten;
381af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	static double dash[] = { 1.0, 2.0 };
382af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
3837175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	nticks = calc_tickmarks(minx, maxx, nticks, &tm, &power_of_ten,
384d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		g->x_axis_unit_change_callback == NULL, g->base_offset);
3857175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	if (g->x_axis_unit_change_callback)
3867175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron		g->x_axis_unit_change_callback(g, power_of_ten);
387af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
388af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	for (i = 0; i < nticks; i++) {
389af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		tx = (((tm[i].value) - minx) / (maxx - minx)) * (x2 - x1) + x1;
390d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
391b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		/*
392b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		 * Update tick delta
393b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		 */
394b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		if (!i) {
395b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			g->xtick_zero = tx;
396b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			g->xtick_zero_val = tm[0].value;
397b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		} else if (i == 1) {
398b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			g->xtick_delta = (tm[1].value - tm[0].value) / (tx - g->xtick_zero);
399b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe			g->xtick_one_val = tm[1].value;
400b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		}
401b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
402d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		/* really tx < yx || tx > x2, but protect against rounding */
403d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		if (x1 - tx > 0.01 || tx - x2 > 0.01)
404af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			continue;
405af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
406af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		/* Draw tick mark */
407f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe		cairo_set_line_width(cr, 0.8);
408af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_move_to(cr, tx, y2);
409af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_line_to(cr, tx, y2 + (y2 - y1) * 0.03);
410af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
411af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
412af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		/* draw grid lines */
413af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_save(cr);
414af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_set_dash(cr, dash, 2, 2.0);
415af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_set_line_width(cr, 0.5);
416af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_move_to(cr, tx, y1);
417af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_line_to(cr, tx, y2);
418af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
419af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_restore(cr);
420af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
421d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		if (!add_tm_text)
422d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe			continue;
423d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
424af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		/* draw tickmark label */
425f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe		draw_centered_text(g, cr, tx, y2 * 1.04, 12.0, tm[i].string);
426af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
427af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
428af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
429af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
430d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboestatic double graph_draw_y_ticks(struct graph *g, cairo_t *cr,
431af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double x1, double y1, double x2, double y2,
432d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	double miny, double maxy, int nticks, int add_tm_text)
433af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
434af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct tickmark *tm;
435af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double ty;
4367175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	int i, power_of_ten;
437af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	static double dash[] = { 2.0, 2.0 };
438af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
4397175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	nticks = calc_tickmarks(miny, maxy, nticks, &tm, &power_of_ten,
440d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		g->y_axis_unit_change_callback == NULL, g->base_offset);
4417175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron	if (g->y_axis_unit_change_callback)
4427175d91deff20b1408450c231b2b445ea28f7f29Stephen M. Cameron		g->y_axis_unit_change_callback(g, power_of_ten);
443af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
444d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	/*
445d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	 * Use highest tickmark as top of graph, not highest value. Otherwise
446d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	 * it's impossible to see what the max value is, if the graph is
447d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	 * fairly flat.
448d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	 */
449d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	maxy = tm[nticks - 1].value;
450d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
451af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	for (i = 0; i < nticks; i++) {
452af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		ty = y2 - (((tm[i].value) - miny) / (maxy - miny)) * (y2 - y1);
453d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
454b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		/*
455b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		 * Update tick delta
456b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		 */
457b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		if (!i) {
458b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			g->ytick_zero = ty;
459b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			g->ytick_zero_val = tm[0].value;
460b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		} else if (i == 1) {
461b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			g->ytick_delta = (tm[1].value - tm[0].value) / (ty - g->ytick_zero);
462b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe			g->ytick_one_val = tm[1].value;
463b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		}
464b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
465d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		/* really ty < y1 || ty > y2, but protect against rounding */
466d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		if (y1 - ty > 0.01 || ty - y2 > 0.01)
467af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			continue;
468d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
469af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		/* draw tick mark */
470af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_move_to(cr, x1, ty);
471af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_line_to(cr, x1 - (x2 - x1) * 0.02, ty);
472af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
473af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
474af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		/* draw grid lines */
475af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_save(cr);
476af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_set_dash(cr, dash, 2, 2.0);
477af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_set_line_width(cr, 0.5);
478af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_move_to(cr, x1, ty);
479af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_line_to(cr, x2, ty);
480af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
481af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_restore(cr);
482af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
483d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe		if (!add_tm_text)
484d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe			continue;
485d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
486af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		/* draw tickmark label */
48710e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron		draw_right_justified_text(g, cr, x1 - (x2 - x1) * 0.025, ty, 12.0, tm[i].string);
488af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
489af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
490d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
491d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	/*
492d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	 * Return new max to use
493d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	 */
494d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	return maxy;
495af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
496af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
497af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronvoid bar_graph_draw(struct graph *bg, cairo_t *cr)
498af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
499af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double x1, y1, x2, y2;
500af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double space_per_label, bar_width;
501af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double label_offset, mindata, maxdata;
502af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	int i, nlabels;
503af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_label *lb;
504cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
505af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
506af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_save(cr);
50757f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron	cairo_translate(cr, bg->xoffset, bg->yoffset);
508af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	graph_draw_common(bg, cr, &x1, &y1, &x2, &y2);
509af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
510cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	nlabels = count_labels(bg);
511d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	space_per_label = (x2 - x1) / (double) nlabels;
512af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
513bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe	/*
514bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe	 * Start bars at 0 unless we have negative values, otherwise we
515bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe	 * present a skewed picture comparing label X and X+1.
516bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe	 */
517cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	mindata = find_min_data(bg);
518bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe	if (mindata > 0)
519bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe		mindata = 0;
520bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe
521cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	maxdata = find_max_data(bg);
522af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
523af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	if (fabs(maxdata - mindata) < 1e-20) {
524f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe		draw_centered_text(bg, cr,
525af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			x1 + (x2 - x1) / 2.0,
526af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			y1 + (y2 - y1) / 2.0, 20.0, "No good data");
527af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		return;
528af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
529af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
530bb39379fa3eb5e5c42c68e2e146561c49e3309f2Jens Axboe	maxdata = graph_draw_y_ticks(bg, cr, x1, y1, x2, y2, mindata, maxdata, 10, 1);
531af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	i = 0;
532cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &bg->label_list) {
533af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		int nvalues;
534cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
535cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		lb = flist_entry(entry, struct graph_label, list);
536cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		nvalues = count_values(lb);
537af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		bar_width = (space_per_label - space_per_label * 0.2) / (double) nvalues;
538af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		label_offset = bg->xdim * 0.1 + space_per_label * (double) i + space_per_label * 0.1;
539af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		draw_bars(bg, cr, lb, label_offset, bar_width, mindata, maxdata);
540af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		// draw_centered_text(cr, label_offset + (bar_width / 2.0 + bar_width * 0.1), bg->ydim * 0.93,
541f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe		draw_centered_text(bg, cr, x1 + space_per_label * (i + 0.5), bg->ydim * 0.93,
542af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			12.0, lb->label);
543af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		i++;
544af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
545af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_stroke(cr);
546af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_restore(cr);
547af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
548af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
549af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Camerontypedef double (*xy_value_extractor)(struct graph_value *v);
550af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
551af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic double getx(struct graph_value *v)
552af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
553af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct xyvalue *xy = v->value;
554af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return xy->x;
555af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
556af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
557af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic double gety(struct graph_value *v)
558af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
559af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct xyvalue *xy = v->value;
560af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return xy->y;
561af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
562af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
563af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic double find_xy_value(struct graph *g, xy_value_extractor getvalue, double_comparator cmp)
564af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
56510e54cdcc78dc173ee5ce4ee20cfd4f5fef97173Stephen M. Cameron	double tmp, answer = 0.0;
566af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_label *i;
567af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_value *j;
568cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *jentry, *entry;
569d582bf70449c1ebbd96f9afa3b2e37dcc7dfb11fStephen M. Cameron	int first = 1;
570af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
571cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &g->label_list) {
572cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(entry, struct graph_label, list);
573cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
574cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		flist_for_each(jentry, &i->value_list) {
575cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			j = flist_entry(jentry, struct graph_value, list);
576af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			tmp = getvalue(j);
577d582bf70449c1ebbd96f9afa3b2e37dcc7dfb11fStephen M. Cameron			if (first) {
578d582bf70449c1ebbd96f9afa3b2e37dcc7dfb11fStephen M. Cameron				first = 0;
579d582bf70449c1ebbd96f9afa3b2e37dcc7dfb11fStephen M. Cameron				answer = tmp;
580d582bf70449c1ebbd96f9afa3b2e37dcc7dfb11fStephen M. Cameron			}
581af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			answer = cmp(tmp, answer);
582af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		}
583cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	}
584cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
585af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return answer;
58601a947f067b1a03add33e645ece73ac19d8257ddJens Axboe}
587af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
588af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronvoid line_graph_draw(struct graph *g, cairo_t *cr)
589af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
590af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double x1, y1, x2, y2;
591def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	double minx, miny, maxx, maxy, gminx, gminy, gmaxx, gmaxy;
592def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	double tx, ty, top_extra, bottom_extra, left_extra, right_extra;
593af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_label *i;
594af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_value *j;
5959ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron	int good_data = 1, first = 1;
596cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry, *lentry;
597af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
598af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_save(cr);
59957f9d28e010b52fea5f41245e8fcb998367d3bcdStephen M. Cameron	cairo_translate(cr, g->xoffset, g->yoffset);
600af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	graph_draw_common(g, cr, &x1, &y1, &x2, &y2);
601af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
602af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	minx = find_xy_value(g, getx, mindouble);
603af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	maxx = find_xy_value(g, getx, maxdouble);
604af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	miny = find_xy_value(g, gety, mindouble);
6055aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe
6065aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe	/*
6075aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe	 * Start graphs at zero, unless we have a value below. Otherwise
6085aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe	 * it's hard to visually compare the read and write graph, since
6095aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe	 * the lowest valued one will be the floor of the graph view.
6105aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe	 */
6115aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe	if (miny > 0)
6125aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe		miny = 0;
6135aec66804d2a6d60aee8d973680c69b13c1d34a3Jens Axboe
614af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	maxy = find_xy_value(g, gety, maxdouble);
615af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
616af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	if (fabs(maxx - minx) < 1e-20 || fabs(maxy - miny) < 1e-20) {
6179ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron		good_data = 0;
6189ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron		minx = 0.0;
6199ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron		miny = 0.0;
6209ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron		maxx = 10.0;
6219ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron		maxy = 100.0;
622af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
623af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
624def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	top_extra = 0.0;
625def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	bottom_extra = 0.0;
626def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	left_extra = 0.0;
627def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	right_extra = 0.0;
628def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron
629def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	if (g->top_extra > 0.001)
630def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron		top_extra = fabs(maxy - miny) * g->top_extra;
631def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	if (g->bottom_extra > 0.001)
632def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron		bottom_extra = fabs(maxy - miny) * g->bottom_extra;
633def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	if (g->left_extra > 0.001)
634def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron		left_extra = fabs(maxx - minx) * g->left_extra;
635def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	if (g->right_extra > 0.001)
636def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron		right_extra = fabs(maxx - minx) * g->right_extra;
637def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron
638def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	gminx = minx - left_extra;
639def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	gmaxx = maxx + right_extra;
640def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	gminy = miny - bottom_extra;
641def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	gmaxy = maxy + top_extra;
642def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron
643d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	graph_draw_x_ticks(g, cr, x1, y1, x2, y2, gminx, gmaxx, 10, good_data);
644d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	gmaxy = graph_draw_y_ticks(g, cr, x1, y1, x2, y2, gminy, gmaxy, 10, good_data);
645af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
6469ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron	if (!good_data)
6479ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron		goto skip_data;
6489ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron
649f3e8440f75f98ced28cdd19ba785718e734cf7c5Jens Axboe	cairo_set_line_width(cr, 1.5);
650cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(lentry, &g->label_list) {
651cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(lentry, struct graph_label, list);
652af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		first = 1;
65301a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		if (i->hide || i->r < 0) /* invisible data */
654cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron			continue;
655b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
656af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_set_source_rgb(cr, i->r, i->g, i->b);
657cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		flist_for_each(entry, &i->value_list) {
658cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			j = flist_entry(entry, struct graph_value, list);
659def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron			tx = ((getx(j) - gminx) / (gmaxx - gminx)) * (x2 - x1) + x1;
660def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron			ty = y2 - ((gety(j) - gminy) / (gmaxy - gminy)) * (y2 - y1);
661af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			if (first) {
662af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron				cairo_move_to(cr, tx, ty);
663af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron				first = 0;
664af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			} else {
665af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron				cairo_line_to(cr, tx, ty);
666af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			}
667af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		}
668af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		cairo_stroke(cr);
669af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
6709ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameron
6719ce9cfbd6ab2a821aa31cedf64ec3e8c0729a4fcStephen M. Cameronskip_data:
672af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	cairo_restore(cr);
673af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
674af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
675af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic void setstring(char **str, const char *value)
676af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
677b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	free(*str);
678af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	*str = strdup(value);
679af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
680af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
681af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronvoid graph_title(struct graph *bg, const char *title)
682af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
683af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	setstring(&bg->title, title);
684af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
685af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
686af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronvoid graph_x_title(struct graph *bg, const char *title)
687af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
688af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	setstring(&bg->xtitle, title);
689af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
690af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
691af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronvoid graph_y_title(struct graph *bg, const char *title)
692af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
693af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	setstring(&bg->ytitle, title);
694af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
695af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
696af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronstatic struct graph_label *graph_find_label(struct graph *bg,
697af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron				const char *label)
698af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
699cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
700af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_label *i;
701af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
702cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &bg->label_list) {
703cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(entry, struct graph_label, list);
704cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
705af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron		if (strcmp(label, i->label) == 0)
706af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron			return i;
707cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	}
708cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
709af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return NULL;
710af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
711af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
7128dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboegraph_label_t graph_add_label(struct graph *bg, const char *label)
713af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
714af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_label *i;
715af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
716af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	i = graph_find_label(bg, label);
717af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	if (i)
7188dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe		return i; /* already present. */
719af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	i = calloc(1, sizeof(*i));
720cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	INIT_FLIST_HEAD(&i->value_list);
721af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	i->parent = bg;
722af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	setstring(&i->label, label);
723cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_add_tail(&i->list, &bg->label_list);
724b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	INIT_PRIO_TREE_ROOT(&i->prio_tree);
7258dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	return i;
726af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
727af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
728cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic void __graph_value_drop(struct graph_label *l, struct graph_value *v)
729cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe{
730d0db819d9c5abe904f85a3b21a95023fef007f20Jens Axboe	flist_del_init(&v->list);
731cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	if (v->tooltip)
732cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		free(v->tooltip);
733cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	free(v->value);
734cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	free(v);
735cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	l->value_count--;
736cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe}
737cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
738cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic void graph_value_drop(struct graph_label *l, struct graph_value *v)
739cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe{
7408dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	if (v->flags & GV_F_PRIO_SKIP) {
7418dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe		__graph_value_drop(l, v);
7428dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe		return;
7438dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	}
7448dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe
745cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	/*
746cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	 * Find head, the guy that's on the prio tree
747cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	 */
748cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	while (!(v->flags & GV_F_ON_PRIO)) {
749cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		assert(!flist_empty(&v->alias));
750cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		v = flist_entry(v->alias.next, struct graph_value, alias);
751cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	}
752cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
753cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	prio_tree_remove(&l->prio_tree, &v->node);
754cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
755cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	/*
756cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	 * Free aliases
757cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	 */
758d0db819d9c5abe904f85a3b21a95023fef007f20Jens Axboe	while (!flist_empty(&v->alias)) {
759cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		struct graph_value *a;
760cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
761d0db819d9c5abe904f85a3b21a95023fef007f20Jens Axboe		a = flist_entry(v->alias.next, struct graph_value, alias);
762d0db819d9c5abe904f85a3b21a95023fef007f20Jens Axboe		flist_del_init(&a->alias);
763cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
764cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		__graph_value_drop(l, a);
765cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	}
766cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
767cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	__graph_value_drop(l, v);
768cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe}
769cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
770b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboestatic void graph_label_add_value(struct graph_label *i, void *value,
771b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe				  const char *tooltip)
772af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
773b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	struct graph *g = i->parent;
774af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct graph_value *x;
775af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
776af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	x = malloc(sizeof(*x));
7773ca30e63c16734837336380b6f40b85e97579167Jens Axboe	memset(x, 0, sizeof(*x));
778cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	INIT_FLIST_HEAD(&x->alias);
779cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	INIT_FLIST_HEAD(&x->list);
780cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_add_tail(&x->list, &i->value_list);
781af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	i->value_count++;
782cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	x->value = value;
783b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
784f00e43f2953d71cfc0fb21d704a473632d0b1ee4Jens Axboe	if (tooltip) {
785b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		double xval = getx(x);
786b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		double minx = xval - (g->xtick_one_val * TOOLTIP_DELTA);
787b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		double maxx = xval + (g->xtick_one_val * TOOLTIP_DELTA);
7884c0cd537f392e8a824d0080675f07b84c355af20Jens Axboe		struct prio_tree_node *ret;
789b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
7905fb8d7912ebf6fabb9ad3a61c4cd316126af602eJens Axboe		/*
7915fb8d7912ebf6fabb9ad3a61c4cd316126af602eJens Axboe		 * use msec to avoid dropping too much precision when
7925fb8d7912ebf6fabb9ad3a61c4cd316126af602eJens Axboe		 * storing as an integer.
7935fb8d7912ebf6fabb9ad3a61c4cd316126af602eJens Axboe		 */
794b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		minx = minx * 1000.0;
795b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		maxx = maxx * 1000.0;
796b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe
7973ca30e63c16734837336380b6f40b85e97579167Jens Axboe		INIT_PRIO_TREE_NODE(&x->node);
798b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		x->node.start = minx;
799b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		x->node.last = maxx;
800cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		x->tooltip = strdup(tooltip);
801b552682725e8269e4977d3a11317ec27c1720ea2Jens Axboe		if (x->node.last == x->node.start) {
802b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe			x->node.last += fabs(g->xtick_delta);
803b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe			if (x->node.last == x->node.start)
804b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe				x->node.last++;
805b552682725e8269e4977d3a11317ec27c1720ea2Jens Axboe		}
806b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
8074c0cd537f392e8a824d0080675f07b84c355af20Jens Axboe		/*
8084c0cd537f392e8a824d0080675f07b84c355af20Jens Axboe		 * If ret != &x->node, we have an alias. Since the values
8094c0cd537f392e8a824d0080675f07b84c355af20Jens Axboe		 * should be identical, we can drop it
8104c0cd537f392e8a824d0080675f07b84c355af20Jens Axboe		 */
8114c0cd537f392e8a824d0080675f07b84c355af20Jens Axboe		ret = prio_tree_insert(&i->prio_tree, &x->node);
812cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		if (ret != &x->node) {
813cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			struct graph_value *alias;
814cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
815cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			alias = container_of(ret, struct graph_value, node);
816cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			flist_add_tail(&x->alias, &alias->alias);
817bd10a0694df75c5ae0257ac2f6c8b51061859321Jens Axboe		} else
818cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			x->flags = GV_F_ON_PRIO;
8198dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	} else
8208dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe		x->flags = GV_F_PRIO_SKIP;
821af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
822b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	if (g->per_label_limit != -1 &&
823b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		i->value_count > g->per_label_limit) {
824c148daed3da130062a7575d5667ca0e044927153Jens Axboe		int to_drop = 1;
825c148daed3da130062a7575d5667ca0e044927153Jens Axboe
826c148daed3da130062a7575d5667ca0e044927153Jens Axboe		/*
827c148daed3da130062a7575d5667ca0e044927153Jens Axboe		 * If the limit was dynamically reduced, making us more
828c148daed3da130062a7575d5667ca0e044927153Jens Axboe		 * than 1 entry ahead after adding this one, drop two
829c148daed3da130062a7575d5667ca0e044927153Jens Axboe		 * entries. This will make us (eventually) reach the
830c148daed3da130062a7575d5667ca0e044927153Jens Axboe		 * specified limit.
831c148daed3da130062a7575d5667ca0e044927153Jens Axboe		 */
832b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		if (i->value_count - g->per_label_limit >= 2)
833c148daed3da130062a7575d5667ca0e044927153Jens Axboe			to_drop = 2;
834c148daed3da130062a7575d5667ca0e044927153Jens Axboe
835cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		while (to_drop-- && !flist_empty(&i->value_list)) {
836cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			x = flist_entry(i->value_list.next, struct graph_value, list);
837cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			graph_value_drop(i, x);
838cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
839cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			/*
840cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			 * If we have aliases, we could drop > 1 above.
841cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			 */
842cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			if (i->value_count <= g->per_label_limit)
843cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				break;
844c148daed3da130062a7575d5667ca0e044927153Jens Axboe		}
845af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
846af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
847af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
8488dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboeint graph_add_data(struct graph *bg, graph_label_t label, const double value)
849af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
8508dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	struct graph_label *i = label;
851af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double *d;
852af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
853af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	d = malloc(sizeof(*d));
854af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	*d = value;
855af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
856b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	graph_label_add_value(i, d, NULL);
857af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return 0;
858af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
859af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
86001a947f067b1a03add33e645ece73ac19d8257ddJens Axboestatic int graph_nonzero_y(struct graph_label *l)
86101a947f067b1a03add33e645ece73ac19d8257ddJens Axboe{
86201a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	struct flist_head *entry;
86301a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
86401a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	flist_for_each(entry, &l->value_list) {
86501a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		struct graph_value *v;
86601a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
86701a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		v = flist_entry(entry, struct graph_value, list);
86801a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		if (gety(v) != 0.0)
86901a947f067b1a03add33e645ece73ac19d8257ddJens Axboe			return 1;
87001a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	}
87101a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
87201a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	return 0;
87301a947f067b1a03add33e645ece73ac19d8257ddJens Axboe}
87401a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
8758dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboeint graph_add_xy_data(struct graph *bg, graph_label_t label,
87693e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe		      const double x, const double y, const char *tooltip)
877af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
8788dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	struct graph_label *i = label;
879af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	struct xyvalue *xy;
880af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
88101a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	if (bg->dont_graph_all_zeroes && y == 0.0 && !graph_nonzero_y(i))
88201a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		i->hide = 1;
88301a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	else
88401a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		i->hide = 0;
88501a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
88601a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	xy = malloc(sizeof(*xy));
88701a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	xy->x = x;
88801a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	xy->y = y;
88901a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
890b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	graph_label_add_value(i, xy, tooltip);
891af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	return 0;
892af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
893af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
894cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic void graph_free_values(struct graph_label *l)
895af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
896cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct graph_value *i;
897af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
898cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	while (!flist_empty(&l->value_list)) {
899cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(l->value_list.next, struct graph_value, list);
900cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		graph_value_drop(l, i);
901af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
902af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
903af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
904cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboestatic void graph_free_labels(struct graph *g)
905af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
906cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct graph_label *i;
907af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
908cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	while (!flist_empty(&g->label_list)) {
909cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(g->label_list.next, struct graph_label, list);
910cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		flist_del(&i->list);
911cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		graph_free_values(i);
912b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		free(i);
913af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	}
914af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
915af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
9168dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboevoid graph_set_color(struct graph *gr, graph_label_t label, double red,
9178dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe		     double green, double blue)
918af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
9198dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	struct graph_label *i = label;
920af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	double r, g, b;
921af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
922cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron	if (red < 0.0) { /* invisible color */
923cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		r = -1.0;
924cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		g = -1.0;
925cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		b = -1.0;
926cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron	} else {
927cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		r = fabs(red);
928cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		g = fabs(green);
929cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		b = fabs(blue);
930cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron
931cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		if (r > 1.0)
932cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron			r = 1.0;
933cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		if (g > 1.0)
934cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron			g = 1.0;
935cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron		if (b > 1.0)
936b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			b = 1.0;
937cae0872709f690086f896f7327e136c7db7ba567Stephen M. Cameron	}
938af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
9398dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	i->r = r;
9408dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	i->g = g;
9418dfd6071e1a4fd3966c0a77dbb7d719c52433b54Jens Axboe	i->b = b;
942af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
943af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
944af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronvoid graph_free(struct graph *bg)
945af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
946b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	free(bg->title);
947b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	free(bg->xtitle);
948b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	free(bg->ytitle);
949cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	graph_free_labels(bg);
950af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
951af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
952af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron/* For each line in the line graph, up to per_label_limit segments may
953af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron * be added.  After that, adding more data to the end of the line
954af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron * causes data to drop off of the front of the line.
955af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron */
956af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameronvoid line_graph_set_data_count_limit(struct graph *g, int per_label_limit)
957af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron{
958af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron	g->per_label_limit = per_label_limit;
959af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron}
960af58ef32b7398d791168af54aa4aab0b23192f90Stephen M. Cameron
961def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameronvoid graph_add_extra_space(struct graph *g, double left_percent, double right_percent,
962def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron                                double top_percent, double bottom_percent)
963def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron{
964def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	g->left_extra = left_percent;
965def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	g->right_extra = right_percent;
966def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	g->top_extra = top_percent;
967def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron	g->bottom_extra = bottom_percent;
968def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron}
969def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron
970d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe/*
971d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe * Normally values are logged in a base unit of 0, but for other purposes
972d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe * it makes more sense to log in higher unit. For instance for bandwidth
973d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe * purposes, you may want to log in KB/sec (or MB/sec) rather than bytes/sec.
974d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe */
975d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboevoid graph_set_base_offset(struct graph *g, unsigned int base_offset)
976d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe{
977d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe	g->base_offset = base_offset;
978d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe}
979d8fbeefb67641e9f63088b329de78a26a69fdbaeJens Axboe
98093e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboeint graph_has_tooltips(struct graph *g)
98193e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe{
982cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
98393e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	struct graph_label *i;
98493e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
985cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &g->label_list) {
986cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(entry, struct graph_label, list);
987cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
988cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		if (!prio_tree_empty(&i->prio_tree))
98993e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe			return 1;
990cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	}
99193e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
99293e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	return 0;
99393e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe}
99493e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
99593e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboeint graph_contains_xy(struct graph *g, int x, int y)
99693e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe{
99793e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	int first_x = g->xoffset;
99893e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	int last_x = g->xoffset + g->xdim;
99993e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	int first_y = g->yoffset;
100093e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	int last_y = g->yoffset + g->ydim;
100193e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
100293e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	return (x >= first_x && x <= last_x) && (y >= first_y && y <= last_y);
100393e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe}
1004def0ac29a7eb92282cf7f208b229261bb254ca00Stephen M. Cameron
1005b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboeconst char *graph_find_tooltip(struct graph *g, int ix, int iy)
100693e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe{
1007b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double x = ix, y = iy;
1008b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	struct prio_tree_iter iter;
1009b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	struct prio_tree_node *n;
1010b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	struct graph_value *best = NULL;
1011cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	struct flist_head *entry;
1012b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	double best_delta;
1013b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	double maxy, miny;
101493e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
1015b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	x -= g->xoffset;
1016b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	y -= g->yoffset;
101793e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
1018b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	x = g->xtick_zero_val + ((x - g->xtick_zero) * g->xtick_delta);
1019b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	y = g->ytick_zero_val + ((y - g->ytick_zero) * g->ytick_delta);
102093e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
1021b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	x = x * 1000.0;
1022b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	maxy = y + (g->ytick_one_val * TOOLTIP_DELTA);
1023b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe	miny = y - (g->ytick_one_val * TOOLTIP_DELTA);
1024b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	best_delta = UINT_MAX;
1025cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	flist_for_each(entry, &g->label_list) {
1026cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		struct graph_label *i;
1027cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
1028cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe		i = flist_entry(entry, struct graph_label, list);
102901a947f067b1a03add33e645ece73ac19d8257ddJens Axboe		if (i->hide)
103001a947f067b1a03add33e645ece73ac19d8257ddJens Axboe			continue;
1031cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
103208d7d5a80f3698f9a5b362831762c23e99fd9a22Jens Axboe		INIT_PRIO_TREE_ITER(&iter);
1033b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe		prio_tree_iter_init(&iter, &i->prio_tree, x, x);
1034b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
1035b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		n = prio_tree_next(&iter);
1036b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		if (!n)
1037b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			continue;
1038b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
1039b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		do {
1040cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			struct graph_value *v, *rootv;
1041b7a69ad89e65cb4f38ccf164ae7c00bb4efe0ef5Jens Axboe			double yval, ydiff;
1042b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
1043b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe			v = container_of(n, struct graph_value, node);
1044cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			rootv = v;
1045cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			do {
1046cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				yval = gety(v);
1047cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				ydiff = fabs(yval - y);
1048cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe
1049cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				/*
1050cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				 * zero delta, or within or match critera, break
1051cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				 */
1052cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				if (ydiff < best_delta) {
1053cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe					best_delta = ydiff;
1054cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe					if (!best_delta ||
1055cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe					    (yval >= miny && yval <= maxy)) {
1056cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe						best = v;
1057cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe						break;
1058cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe					}
1059b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe				}
1060cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe				if (!flist_empty(&v->alias))
1061cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe					v = flist_entry(v->alias.next, struct graph_value, alias);
1062cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe			} while (v != rootv);
1063b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		} while ((n = prio_tree_next(&iter)) != NULL);
1064b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
1065b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		/*
1066b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		 * If we got matches in one label, don't check others.
1067b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		 */
1068d0db819d9c5abe904f85a3b21a95023fef007f20Jens Axboe		if (best)
1069d0db819d9c5abe904f85a3b21a95023fef007f20Jens Axboe			break;
1070cdae5ff83147bedec73318e347e39d4c388da2b9Jens Axboe	}
1071b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe
1072b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe	if (best)
1073b65c7ec44355b9bc4d6c5ee6613dfb895dace3d6Jens Axboe		return best->tooltip;
107493e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe
107593e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe	return NULL;
107693e2db2bdbedbd6954bb0e0632514cae659fc30eJens Axboe}
107701a947f067b1a03add33e645ece73ac19d8257ddJens Axboe
107801a947f067b1a03add33e645ece73ac19d8257ddJens Axboevoid graph_set_graph_all_zeroes(struct graph *g, unsigned int set)
107901a947f067b1a03add33e645ece73ac19d8257ddJens Axboe{
108001a947f067b1a03add33e645ece73ac19d8257ddJens Axboe	g->dont_graph_all_zeroes = !set;
108101a947f067b1a03add33e645ece73ac19d8257ddJens Axboe}
1082