diff options
Diffstat (limited to 'src/statistics/gnunet-statistics-gtk.c')
-rw-r--r-- | src/statistics/gnunet-statistics-gtk.c | 383 |
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 | */ |
33 | struct Statistics | 37 | struct 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 | */ | ||
54 | struct 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 | */ | ||
87 | struct 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 | */ |
53 | static struct GNUNET_STATISTICS_Handle *statistics; | 120 | static struct GNUNET_STATISTICS_Handle *statistics; |
54 | 121 | ||
122 | /** | ||
123 | * Map from keys (subsystem+value) to 'struct ValueHistory'. | ||
124 | */ | ||
125 | static struct GNUNET_CONTAINER_MultiHashMap *stat_map; | ||
126 | |||
127 | /** | ||
128 | * Task that refreshes connection graphic. | ||
129 | */ | ||
130 | static GNUNET_SCHEDULER_TaskIdentifier connection_task; | ||
55 | 131 | ||
132 | /** | ||
133 | * Pixmap where we have the connection image. | ||
134 | */ | ||
135 | static GdkPixmap *connection_pixmap; | ||
56 | 136 | ||
57 | /** | 137 | /** |
58 | * Get cfg. | 138 | * How often do we refresh? |
59 | */ | 139 | */ |
60 | static const struct GNUNET_CONFIGURATION_Handle * | 140 | static struct GNUNET_TIME_Relative refresh_delay; |
61 | get_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 | */ | ||
150 | static void | ||
151 | get_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 | */ | ||
174 | static int | ||
175 | process_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 | */ | ||
199 | static void | ||
200 | monitor (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 | */ | ||
246 | static GdkPixmap * | ||
247 | create_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 | */ | ||
297 | static void | ||
298 | plot_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 | */ | ||
323 | gint | ||
324 | GNUNET_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 | */ | ||
347 | gint | ||
348 | GNUNET_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 | */ | ||
369 | static void | ||
370 | refresh_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 | */ | ||
398 | static int | ||
399 | free_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) | |||
110 | static void | 455 | static void |
111 | run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 456 | run (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 | { |