aboutsummaryrefslogtreecommitdiff
path: root/src/statistics/gnunet-statistics-gtk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/statistics/gnunet-statistics-gtk.c')
-rw-r--r--src/statistics/gnunet-statistics-gtk.c383
1 files changed, 376 insertions, 7 deletions
diff --git a/src/statistics/gnunet-statistics-gtk.c b/src/statistics/gnunet-statistics-gtk.c
index 0bea069e..fd1d72c7 100644
--- a/src/statistics/gnunet-statistics-gtk.c
+++ b/src/statistics/gnunet-statistics-gtk.c
@@ -24,16 +24,83 @@
24 * @author Christian Grothoff 24 * @author Christian Grothoff
25 */ 25 */
26#include "gnunet_gtk.h" 26#include "gnunet_gtk.h"
27
28#include <gnunet/gnunet_util_lib.h>
27#include <gnunet/gnunet_statistics_service.h> 29#include <gnunet/gnunet_statistics_service.h>
28 30
31#define MAX_HISTORY 1280
32
29 33
30/** 34/**
31 * Information we track for each peer outside of the model. 35 * Information about a value we received.
32 */ 36 */
33struct Statistics 37struct HistoricValue
34{ 38{
39 /**
40 * A value we recorded.
41 */
42 uint64_t value;
43
44 /**
45 * Time when the value was recorded.
46 */
47 struct GNUNET_TIME_Absolute timestamp;
48};
35 49
36 50
51/**
52 * Historic information we recorded for a value.
53 */
54struct ValueHistory
55{
56
57 /**
58 * Name of the subsystem.
59 */
60 char *subsystem;
61
62 /**
63 * Name of the statistic.
64 */
65 char *name;
66
67 /**
68 * Recent values for this number.
69 */
70 struct HistoricValue history[MAX_HISTORY];
71
72 /**
73 * Last offset we wrote to in history.
74 */
75 unsigned int last_history_offset;
76
77 /**
78 * Number of valid entries in the history.
79 */
80 unsigned int history_size;
81};
82
83
84/**
85 * Information about how to plot certain values.
86 */
87struct PlotInfo
88{
89 /**
90 * Subsystem originating the value to plot.
91 */
92 const char *subsystem;
93
94 /**
95 * Name of the value to plot.
96 */
97 const char *name;
98
99 /**
100 * Name of color to use when plotting.
101 */
102 const char *color_name;
103
37}; 104};
38 105
39 106
@@ -52,15 +119,107 @@ static int tray_only;
52 */ 119 */
53static struct GNUNET_STATISTICS_Handle *statistics; 120static struct GNUNET_STATISTICS_Handle *statistics;
54 121
122/**
123 * Map from keys (subsystem+value) to 'struct ValueHistory'.
124 */
125static struct GNUNET_CONTAINER_MultiHashMap *stat_map;
126
127/**
128 * Task that refreshes connection graphic.
129 */
130static GNUNET_SCHEDULER_TaskIdentifier connection_task;
55 131
132/**
133 * Pixmap where we have the connection image.
134 */
135static GdkPixmap *connection_pixmap;
56 136
57/** 137/**
58 * Get cfg. 138 * How often do we refresh?
59 */ 139 */
60static const struct GNUNET_CONFIGURATION_Handle * 140static struct GNUNET_TIME_Relative refresh_delay;
61get_configuration () 141
142
143/**
144 * Obtain key for 'stat_map' for a given subsystem and name.
145 *
146 * @param subsystem subsystem
147 * @param name name
148 * @param key set to the hash map key
149 */
150static void
151get_key (const char *subsystem,
152 const char *name,
153 GNUNET_HashCode *key)
154{
155 GNUNET_HashCode h1;
156 GNUNET_HashCode h2;
157
158 GNUNET_CRYPTO_hash (subsystem, strlen(subsystem), &h1);
159 GNUNET_CRYPTO_hash (name, strlen(name), &h2);
160 GNUNET_CRYPTO_hash_xor (&h1, &h2, key);
161}
162
163
164/**
165 * Callback function to process statistic values.
166 *
167 * @param cls the 'struct ValueHistory' to update
168 * @param subsystem name of subsystem that created the statistic
169 * @param name the name of the datum
170 * @param value the current value
171 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
172 * @return GNUNET_OK to continue
173 */
174static int
175process_value_update (void *cls, const char *subsystem,
176 const char *name, uint64_t value,
177 int is_persistent)
178{
179 struct ValueHistory *vh = cls;
180 GNUNET_HashCode key;
181
182 get_key (subsystem, name, &key);
183 if (++vh->last_history_offset == MAX_HISTORY)
184 vh->last_history_offset = 0;
185 if (vh->history_size < MAX_HISTORY)
186 vh->history_size++;
187 vh->history[vh->last_history_offset].value = value;
188 vh->history[vh->last_history_offset].timestamp = GNUNET_TIME_absolute_get ();
189 return GNUNET_OK;
190}
191
192
193/**
194 * Begin monitoring a particular value.
195 *
196 * @param subsystem name of subsystem that created the statistic
197 * @param name the name of the datum
198 */
199static void
200monitor (const char *subsystem,
201 const char *name)
62{ 202{
63 return GNUNET_GTK_main_loop_get_configuration (ml); 203 GNUNET_HashCode key;
204 struct ValueHistory *vh;
205
206 get_key (subsystem, name, &key);
207 vh = GNUNET_CONTAINER_multihashmap_get (stat_map, &key);
208 if (NULL != vh)
209 return;
210 vh = GNUNET_malloc (sizeof (struct ValueHistory));
211 vh->subsystem = GNUNET_strdup (subsystem);
212 vh->name = GNUNET_strdup (name);
213 GNUNET_assert (GNUNET_OK ==
214 GNUNET_CONTAINER_multihashmap_put (stat_map,
215 &key,
216 vh,
217 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
218 GNUNET_STATISTICS_watch (statistics,
219 subsystem,
220 name,
221 &process_value_update,
222 vh);
64} 223}
65 224
66 225
@@ -78,6 +237,179 @@ get_object (const char *name)
78 237
79 238
80/** 239/**
240 * Actually draw a plot based on collected data.
241 *
242 * @param widget size and style information for the plot
243 * @param info what to draw
244 * @return pixmap with the drawing
245 */
246static GdkPixmap *
247create_plot (GtkWidget *widget,
248 const struct PlotInfo *info)
249{
250 GdkPixmap *ret;
251 GdkGC *gc;
252 unsigned int i;
253 GNUNET_HashCode key;
254 struct ValueHistory *vh;
255 GdkColor color;
256 GdkColormap *colormap;
257
258 ret = gdk_pixmap_new (widget->window,
259 widget->allocation.width,
260 widget->allocation.height,
261 gtk_widget_get_visual (widget)->depth);
262 colormap = gdk_window_get_colormap (widget->window);
263 gc = gdk_gc_new (widget->window);
264 gdk_gc_copy (gc, widget->style->white_gc);
265 gdk_color_parse ("black", &color);
266 gdk_colormap_alloc_color (colormap, &color, TRUE, TRUE);
267 gdk_gc_set_foreground (gc, &color);
268 gdk_draw_rectangle (GDK_DRAWABLE (ret),
269 widget->style->black_gc,
270 TRUE, 0, 0,
271 widget->allocation.width,
272 widget->allocation.height);
273 for (i=0; NULL != info[i].subsystem; i++)
274 {
275 get_key (info[i].subsystem, info[i].name, &key);
276 vh = GNUNET_CONTAINER_multihashmap_get (stat_map, &key);
277 if (NULL == vh)
278 continue;
279 gdk_color_parse (info[i].color_name, &color);
280 gdk_colormap_alloc_color (colormap, &color, TRUE, TRUE);
281 gdk_gc_set_foreground (gc, &color);
282
283
284
285 }
286
287 // FIXME
288 return ret;
289}
290
291
292/**
293 * Redraw the 'connection_pixmap'.
294 *
295 * @param widget where to render it to in the end (also for size to use)
296 */
297static void
298plot_connection_graph (GtkWidget *widget)
299{
300 static const struct PlotInfo connection_data[] =
301 {
302 { "core", "entries in session map", "white" },
303 { NULL, NULL, NULL}
304 };
305 if (NULL != connection_pixmap)
306 {
307 gdk_pixmap_unref (connection_pixmap);
308 connection_pixmap = NULL;
309 }
310 connection_pixmap = create_plot (widget,
311 connection_data);
312}
313
314
315/**
316 * Part of the connection graph got re-exposed; refresh the area.
317 *
318 * @param widget the drawing area of the connection graph
319 * @param event expose event
320 * @param data_ptr unused
321 * @return FALSE
322 */
323gint
324GNUNET_STATISTICS_connection_graph_expose (GtkWidget * widget,
325 GdkEventExpose * event,
326 gpointer data_ptr)
327{
328 gdk_draw_pixmap (widget->window,
329 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
330 connection_pixmap,
331 event->area.x, event->area.y,
332 event->area.x, event->area.y,
333 event->area.width, event->area.height);
334 return FALSE;
335}
336
337
338/**
339 * The window size was changed, forcing us to re-draw the connection
340 * graph.
341 *
342 * @param widget the drawing area of the connection graph
343 * @param event configure event
344 * @param data_ptr unused
345 * @return TRUE
346 */
347gint
348GNUNET_STATISTICS_connection_graph_configure (GtkWidget * widget,
349 GdkEventConfigure * event,
350 gpointer data_ptr)
351{
352 plot_connection_graph (widget);
353 gdk_draw_pixmap (widget->window,
354 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
355 connection_pixmap,
356 0, 0,
357 0, 0,
358 widget->allocation.width, widget->allocation.height);
359 return TRUE;
360}
361
362
363/**
364 * Refresh the 'connections' graphic.
365 *
366 * @param cls closure
367 * @param tc scheduler context
368 */
369static void
370refresh_connections (void *cls,
371 const struct GNUNET_SCHEDULER_TaskContext *tc)
372{
373 GtkWidget *area;
374
375 connection_task = GNUNET_SCHEDULER_add_delayed (refresh_delay,
376 &refresh_connections,
377 NULL);
378 area = GTK_WIDGET (get_object ("GNUNET_STATISTICS_GTK_main_notebook_connectivity_drawingarea"));
379 plot_connection_graph (area);
380 gdk_draw_pixmap (area->window,
381 area->style->fg_gc[GTK_WIDGET_STATE (area)],
382 connection_pixmap,
383 0, 0,
384 0, 0,
385 area->allocation.width,
386 area->allocation.height);
387}
388
389
390/**
391 * Free entries in the value history.
392 *
393 * @param cls unused
394 * @param key unused
395 * @param value 'struct ValueHistory' to free
396 * @return GNUNET_OK (continue iteration)
397 */
398static int
399free_history (void *cls,
400 const GNUNET_HashCode *key,
401 void *value)
402{
403 struct ValueHistory *vh = value;
404
405 GNUNET_free (vh->subsystem);
406 GNUNET_free (vh->name);
407 GNUNET_free (vh);
408 return GNUNET_OK;
409}
410
411
412/**
81 * Task run on shutdown. 413 * Task run on shutdown.
82 * 414 *
83 * @param cls unused 415 * @param cls unused
@@ -88,6 +420,9 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
88{ 420{
89 GNUNET_STATISTICS_destroy (statistics, GNUNET_NO); 421 GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
90 statistics = NULL; 422 statistics = NULL;
423 GNUNET_CONTAINER_multihashmap_iterate (stat_map, &free_history, NULL);
424 GNUNET_CONTAINER_multihashmap_destroy (stat_map);
425 stat_map = NULL;
91} 426}
92 427
93 428
@@ -99,6 +434,16 @@ GNUNET_STATISTICS_GTK_quit_cb (GObject * object, gpointer user_data)
99{ 434{
100 GNUNET_GTK_tray_icon_destroy (); 435 GNUNET_GTK_tray_icon_destroy ();
101 GNUNET_GTK_main_loop_quit (ml); 436 GNUNET_GTK_main_loop_quit (ml);
437 if (connection_task != GNUNET_SCHEDULER_NO_TASK)
438 {
439 GNUNET_SCHEDULER_cancel (connection_task);
440 connection_task = GNUNET_SCHEDULER_NO_TASK;
441 }
442 if (NULL != connection_pixmap)
443 {
444 gdk_pixmap_unref (connection_pixmap);
445 connection_pixmap = NULL;
446 }
102 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); 447 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
103} 448}
104 449
@@ -110,19 +455,40 @@ GNUNET_STATISTICS_GTK_quit_cb (GObject * object, gpointer user_data)
110static void 455static void
111run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 456run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
112{ 457{
458 static const struct
459 {
460 const char *subsystem;
461 const char *name;
462 } moma[] =
463 {
464 { "core", "entries in session map" },
465 { NULL, NULL }
466 };
113 GtkWidget *main_window; 467 GtkWidget *main_window;
468 unsigned int i;
114 469
115 ml = cls; 470 ml = cls;
116 statistics = GNUNET_STATISTICS_create ("gnunet-statistics-gtk", 471 statistics = GNUNET_STATISTICS_create ("gnunet-statistics-gtk",
117 get_configuration()); 472 GNUNET_GTK_main_loop_get_configuration (ml));
118 if (NULL == statistics) 473 if (NULL == statistics)
119 { 474 {
120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 475 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
121 _("Failed to initiate connection with statistics service\n")); 476 _("Failed to initiate connection with statistics service\n"));
122 return; 477 return;
123 } 478 }
479 stat_map = GNUNET_CONTAINER_multihashmap_create (128);
124 GNUNET_GTK_set_icon_search_path (); 480 GNUNET_GTK_set_icon_search_path ();
125 GNUNET_GTK_setup_nls (); 481 GNUNET_GTK_setup_nls ();
482 refresh_delay = GNUNET_TIME_UNIT_SECONDS; /* fixme: make option / cmd-line option */
483
484 i = 0;
485 while (moma[i].subsystem != NULL)
486 {
487 monitor (moma[i].subsystem,
488 moma[i].name);
489 i++;
490 }
491
126 /* setup main window */ 492 /* setup main window */
127 main_window = GTK_WIDGET (get_object ("GNUNET_STATISTICS_GTK_main_window")); 493 main_window = GTK_WIDGET (get_object ("GNUNET_STATISTICS_GTK_main_window"));
128 gtk_window_maximize (GTK_WINDOW (main_window)); 494 gtk_window_maximize (GTK_WINDOW (main_window));
@@ -130,6 +496,9 @@ run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
130 "gnunet-gtk" /* FIXME: different icon? */ , 496 "gnunet-gtk" /* FIXME: different icon? */ ,
131 "gnunet-statistics-gtk"); 497 "gnunet-statistics-gtk");
132 498
499 /* FIXME: only schedule this task if the respective tab is open!? */
500 connection_task = GNUNET_SCHEDULER_add_now (&refresh_connections, NULL);
501
133 /* make GUI visible */ 502 /* make GUI visible */
134 if (!tray_only) 503 if (!tray_only)
135 { 504 {