aboutsummaryrefslogtreecommitdiff
path: root/src/statistics/gnunet-service-statistics.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2014-12-23 22:08:47 +0000
committerChristian Grothoff <christian@grothoff.org>2014-12-23 22:08:47 +0000
commitf3575010be1a53abe6a27b5e5c4d2d60f46ba805 (patch)
treeb8685067df250ac92e78414df3063315fc15adc0 /src/statistics/gnunet-service-statistics.c
parent912d1dc85990d15f2ce7fcfb837dc37cd327833b (diff)
downloadgnunet-f3575010be1a53abe6a27b5e5c4d2d60f46ba805.tar.gz
gnunet-f3575010be1a53abe6a27b5e5c4d2d60f46ba805.zip
-cleaning up stats code, use separate structures per subsystem for faster processing
Diffstat (limited to 'src/statistics/gnunet-service-statistics.c')
-rw-r--r--src/statistics/gnunet-service-statistics.c624
1 files changed, 422 insertions, 202 deletions
diff --git a/src/statistics/gnunet-service-statistics.c b/src/statistics/gnunet-service-statistics.c
index 4d8251cf4..dbf1ecd14 100644
--- a/src/statistics/gnunet-service-statistics.c
+++ b/src/statistics/gnunet-service-statistics.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2009, 2010, 2012 Christian Grothoff (and other contributing authors) 3 (C) 2009, 2010, 2012, 2014 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published 6 it under the terms of the GNU General Public License as published
@@ -76,21 +76,11 @@ struct WatchEntry
76 76
77 77
78/** 78/**
79 * Client entry. 79 * We keep the statistics organized by subsystem for faster
80 * lookup during SET operations.
80 */ 81 */
81struct ClientEntry 82struct SubsystemEntry;
82{
83 /**
84 * Corresponding server handle.
85 */
86 struct GNUNET_SERVER_Client *client;
87 83
88 /**
89 * Maximum watch ID used by this client so far.
90 */
91 uint32_t max_wid;
92
93};
94 84
95/** 85/**
96 * Entry in the statistics list. 86 * Entry in the statistics list.
@@ -103,21 +93,20 @@ struct StatsEntry
103 struct StatsEntry *next; 93 struct StatsEntry *next;
104 94
105 /** 95 /**
106 * Name of the service, points into the middle of @e msg. 96 * This is a linked list.
107 */ 97 */
108 const char *service; 98 struct StatsEntry *prev;
109 99
110 /** 100 /**
111 * Name for the value, points into the middle of @e msg. 101 * Subsystem this entry belongs to.
112 */ 102 */
113 const char *name; 103 struct SubsystemEntry *subsystem;
114 104
115 /** 105 /**
116 * Message that can be used to set this value, 106 * Name for the value stored by this entry, allocated at the end of
117 * stored at the end of the memory used by
118 * this struct. 107 * this struct.
119 */ 108 */
120 struct GNUNET_STATISTICS_SetMessage *msg; 109 const char *name;
121 110
122 /** 111 /**
123 * Watch context for changes to this value, or NULL for none. 112 * Watch context for changes to this value, or NULL for none.
@@ -146,21 +135,85 @@ struct StatsEntry
146 135
147 /** 136 /**
148 * Is this value set? 137 * Is this value set?
149 * #GNUNET_NO : value is n/a, #GNUNET_YES: value is valid 138 * #GNUNET_NO: value is n/a, #GNUNET_YES: value is valid
150 */ 139 */
151 int set; 140 int set;
152 141
153}; 142};
154 143
144
145/**
146 * We keep the statistics organized by subsystem for faster
147 * lookup during SET operations.
148 */
149struct SubsystemEntry
150{
151 /**
152 * Subsystems are kept in a DLL.
153 */
154 struct SubsystemEntry *next;
155
156 /**
157 * Subsystems are kept in a DLL.
158 */
159 struct SubsystemEntry *prev;
160
161 /**
162 * Head of list of values kept for this subsystem.
163 */
164 struct StatsEntry *stat_head;
165
166 /**
167 * Tail of list of values kept for this subsystem.
168 */
169 struct StatsEntry *stat_tail;
170
171 /**
172 * Name of the subsystem this entry is for, allocated at
173 * the end of this struct, do not free().
174 */
175 const char *service;
176
177};
178
179
180/**
181 * Client entry.
182 */
183struct ClientEntry
184{
185 /**
186 * Corresponding server handle.
187 */
188 struct GNUNET_SERVER_Client *client;
189
190 /**
191 * Which subsystem is this client writing to (SET/UPDATE)?
192 */
193 struct SubsystemEntry *subsystem;
194
195 /**
196 * Maximum watch ID used by this client so far.
197 */
198 uint32_t max_wid;
199
200};
201
202
155/** 203/**
156 * Our configuration. 204 * Our configuration.
157 */ 205 */
158static const struct GNUNET_CONFIGURATION_Handle *cfg; 206static const struct GNUNET_CONFIGURATION_Handle *cfg;
159 207
160/** 208/**
161 * Linked list of our active statistics. 209 * Head of linked list of subsystems with active statistics.
162 */ 210 */
163static struct StatsEntry *start; 211static struct SubsystemEntry *sub_head;
212
213/**
214 * Tail of linked list of subsystems with active statistics.
215 */
216static struct SubsystemEntry *sub_tail;
164 217
165/** 218/**
166 * Number of connected clients. 219 * Number of connected clients.
@@ -196,7 +249,8 @@ static int in_shutdown;
196 * @param msg message to inject 249 * @param msg message to inject
197 */ 250 */
198static int 251static int
199inject_message (void *cls, void *client, 252inject_message (void *cls,
253 void *client,
200 const struct GNUNET_MessageHeader *msg) 254 const struct GNUNET_MessageHeader *msg)
201{ 255{
202 struct GNUNET_SERVER_Handle *server = cls; 256 struct GNUNET_SERVER_Handle *server = cls;
@@ -220,7 +274,6 @@ load (struct GNUNET_SERVER_Handle *server)
220 uint64_t fsize; 274 uint64_t fsize;
221 char *buf; 275 char *buf;
222 struct GNUNET_SERVER_MessageStreamTokenizer *mst; 276 struct GNUNET_SERVER_MessageStreamTokenizer *mst;
223 char *emsg;
224 277
225 if (GNUNET_OK != 278 if (GNUNET_OK !=
226 GNUNET_CONFIGURATION_get_value_filename (cfg, 279 GNUNET_CONFIGURATION_get_value_filename (cfg,
@@ -234,7 +287,10 @@ load (struct GNUNET_SERVER_Handle *server)
234 return; 287 return;
235 } 288 }
236 if ( (GNUNET_OK != 289 if ( (GNUNET_OK !=
237 GNUNET_DISK_file_size (fn, &fsize, GNUNET_NO, GNUNET_YES)) || 290 GNUNET_DISK_file_size (fn,
291 &fsize,
292 GNUNET_NO,
293 GNUNET_YES)) ||
238 (0 == fsize) ) 294 (0 == fsize) )
239 { 295 {
240 GNUNET_free (fn); 296 GNUNET_free (fn);
@@ -248,26 +304,36 @@ load (struct GNUNET_SERVER_Handle *server)
248 GNUNET_free (fn); 304 GNUNET_free (fn);
249 return; 305 return;
250 } 306 }
251 if (GNUNET_OK != GNUNET_BIO_read (rh, fn, buf, fsize)) 307 if (GNUNET_OK !=
308 GNUNET_BIO_read (rh,
309 fn,
310 buf,
311 fsize))
252 { 312 {
253 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", fn); 313 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
254 GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, &emsg)); 314 "read",
315 fn);
316 GNUNET_break (GNUNET_OK ==
317 GNUNET_BIO_read_close (rh, NULL));
255 GNUNET_free (buf); 318 GNUNET_free (buf);
256 GNUNET_free_non_null (emsg);
257 GNUNET_free (fn); 319 GNUNET_free (fn);
258 return; 320 return;
259 } 321 }
260 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 322 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
261 _("Loading %llu bytes of statistics from `%s'\n"), 323 _("Loading %llu bytes of statistics from `%s'\n"),
262 fsize, fn); 324 fsize, fn);
263 mst = GNUNET_SERVER_mst_create (&inject_message, server); 325 mst = GNUNET_SERVER_mst_create (&inject_message,
326 server);
264 GNUNET_break (GNUNET_OK == 327 GNUNET_break (GNUNET_OK ==
265 GNUNET_SERVER_mst_receive (mst, NULL, buf, fsize, 328 GNUNET_SERVER_mst_receive (mst, NULL,
266 GNUNET_YES, GNUNET_NO)); 329 buf, fsize,
330 GNUNET_YES,
331 GNUNET_NO));
267 GNUNET_SERVER_mst_destroy (mst); 332 GNUNET_SERVER_mst_destroy (mst);
268 GNUNET_free (buf); 333 GNUNET_free (buf);
269 GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, &emsg)); 334 GNUNET_break (GNUNET_OK ==
270 GNUNET_free_non_null (emsg); 335 GNUNET_BIO_read_close (rh,
336 NULL));
271 GNUNET_free (fn); 337 GNUNET_free (fn);
272} 338}
273 339
@@ -278,11 +344,15 @@ load (struct GNUNET_SERVER_Handle *server)
278static void 344static void
279save () 345save ()
280{ 346{
347 struct SubsystemEntry *se;
281 struct StatsEntry *pos; 348 struct StatsEntry *pos;
282 char *fn; 349 char *fn;
283 struct GNUNET_BIO_WriteHandle *wh; 350 struct GNUNET_BIO_WriteHandle *wh;
284 uint16_t size; 351 uint16_t size;
285 unsigned long long total; 352 unsigned long long total;
353 size_t nlen;
354 size_t slen;
355 struct GNUNET_STATISTICS_SetMessage *msg;
286 356
287 if (GNUNET_OK != 357 if (GNUNET_OK !=
288 GNUNET_CONFIGURATION_get_value_filename (cfg, 358 GNUNET_CONFIGURATION_get_value_filename (cfg,
@@ -298,33 +368,72 @@ save ()
298 (void) GNUNET_DISK_directory_create_for_file (fn); 368 (void) GNUNET_DISK_directory_create_for_file (fn);
299 wh = GNUNET_BIO_write_open (fn); 369 wh = GNUNET_BIO_write_open (fn);
300 total = 0; 370 total = 0;
301 while (NULL != (pos = start)) 371 while (NULL != (se = sub_head))
302 { 372 {
303 start = pos->next; 373 GNUNET_CONTAINER_DLL_remove (sub_head,
304 if ((pos->persistent) && (NULL != wh)) 374 sub_tail,
375 se);
376 slen = strlen (se->service) + 1;
377 while (NULL != (pos = se->stat_head))
305 { 378 {
306 size = htons (pos->msg->header.size); 379 GNUNET_CONTAINER_DLL_remove (se->stat_head,
307 if (GNUNET_OK != GNUNET_BIO_write (wh, pos->msg, size)) 380 se->stat_tail,
381 pos);
382 if ((pos->persistent) && (NULL != wh))
308 { 383 {
309 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn); 384 nlen = strlen (pos->name) + 1;
310 if (GNUNET_OK != GNUNET_BIO_write_close (wh)) 385 size = sizeof (struct GNUNET_STATISTICS_SetMessage) + nlen + slen;
311 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); 386 GNUNET_assert (size < UINT16_MAX);
312 wh = NULL; 387 msg = GNUNET_malloc (size);
388
389 msg->header.size = htons ((uint16_t) size);
390 msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
391 GNUNET_assert (nlen + slen ==
392 GNUNET_STRINGS_buffer_fill ((char *) &msg[1],
393 nlen + slen,
394 2,
395 se->service,
396 pos->name));
397 msg->flags = htonl (pos->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0);
398 msg->value = GNUNET_htonll (pos->value);
399 if (GNUNET_OK != GNUNET_BIO_write (wh,
400 msg,
401 size))
402 {
403 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
404 "write",
405 fn);
406 if (GNUNET_OK != GNUNET_BIO_write_close (wh))
407 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
408 "close",
409 fn);
410 wh = NULL;
411 }
412 else
413 {
414 total += size;
415 }
416 GNUNET_free (msg);
313 } 417 }
314 else 418 GNUNET_free (pos);
315 total += size;
316 } 419 }
317 GNUNET_free (pos); 420 GNUNET_free (se);
318 } 421 }
319 if (NULL != wh) 422 if (NULL != wh)
320 { 423 {
321 if (GNUNET_OK != GNUNET_BIO_write_close (wh)) 424 if (GNUNET_OK !=
322 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); 425 GNUNET_BIO_write_close (wh))
323 if (total == 0) 426 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
324 GNUNET_break (0 == UNLINK (fn)); 427 "close",
428 fn);
429 if (0 == total)
430 GNUNET_break (0 ==
431 UNLINK (fn));
325 else 432 else
326 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 433 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
327 _("Wrote %llu bytes of statistics to `%s'\n"), total, fn); 434 _("Wrote %llu bytes of statistics to `%s'\n"),
435 total,
436 fn);
328 } 437 }
329 GNUNET_free_non_null (fn); 438 GNUNET_free_non_null (fn);
330} 439}
@@ -343,9 +452,9 @@ transmit (struct GNUNET_SERVER_Client *client,
343 struct GNUNET_STATISTICS_ReplyMessage *m; 452 struct GNUNET_STATISTICS_ReplyMessage *m;
344 size_t size; 453 size_t size;
345 454
346 size = 455 size = sizeof (struct GNUNET_STATISTICS_ReplyMessage) +
347 sizeof (struct GNUNET_STATISTICS_ReplyMessage) + strlen (e->service) + 1 + 456 strlen (e->subsystem->service) + 1 +
348 strlen (e->name) + 1; 457 strlen (e->name) + 1;
349 GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE); 458 GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
350 m = GNUNET_malloc (size); 459 m = GNUNET_malloc (size);
351 m->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_VALUE); 460 m->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_VALUE);
@@ -356,11 +465,14 @@ transmit (struct GNUNET_SERVER_Client *client,
356 m->value = GNUNET_htonll (e->value); 465 m->value = GNUNET_htonll (e->value);
357 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage); 466 size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage);
358 GNUNET_assert (size == 467 GNUNET_assert (size ==
359 GNUNET_STRINGS_buffer_fill ((char *) &m[1], size, 2, 468 GNUNET_STRINGS_buffer_fill ((char *) &m[1],
360 e->service, e->name)); 469 size,
470 2,
471 e->subsystem->service,
472 e->name));
361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 473 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362 "Transmitting value for `%s:%s' (%d): %llu\n", 474 "Transmitting value for `%s:%s' (%d): %llu\n",
363 e->service, 475 e->subsystem->service,
364 e->name, 476 e->name,
365 e->persistent, 477 e->persistent,
366 e->value); 478 e->value);
@@ -371,24 +483,6 @@ transmit (struct GNUNET_SERVER_Client *client,
371 483
372 484
373/** 485/**
374 * Does this entry match the request?
375 *
376 * @param e an entry
377 * @param service name of service to match
378 * @param name value to match
379 * @return 1 if they match, 0 if not
380 */
381static int
382matches (const struct StatsEntry *e,
383 const char *service,
384 const char *name)
385{
386 return ((0 == strlen (service)) || (0 == strcmp (service, e->service))) &&
387 ((0 == strlen (name)) || (0 == strcmp (name, e->name)));
388}
389
390
391/**
392 * Find a client entry for the given client handle, or create one. 486 * Find a client entry for the given client handle, or create one.
393 * 487 *
394 * @param client handle to match 488 * @param client handle to match
@@ -399,7 +493,6 @@ make_client_entry (struct GNUNET_SERVER_Client *client)
399{ 493{
400 struct ClientEntry *ce; 494 struct ClientEntry *ce;
401 495
402 GNUNET_assert (NULL != client);
403 ce = GNUNET_SERVER_client_get_user_context (client, 496 ce = GNUNET_SERVER_client_get_user_context (client,
404 struct ClientEntry); 497 struct ClientEntry);
405 if (NULL != ce) 498 if (NULL != ce)
@@ -433,33 +526,54 @@ handle_get (void *cls,
433 const struct GNUNET_MessageHeader *message) 526 const struct GNUNET_MessageHeader *message)
434{ 527{
435 struct GNUNET_MessageHeader end; 528 struct GNUNET_MessageHeader end;
436 char *service; 529 const char *service;
437 char *name; 530 const char *name;
531 size_t slen;
532 size_t nlen;
533 struct SubsystemEntry *se;
438 struct StatsEntry *pos; 534 struct StatsEntry *pos;
439 size_t size; 535 size_t size;
440 536
441 if ( (NULL != client) && 537 if (NULL == make_client_entry (client))
442 (NULL == make_client_entry (client)) )
443 return; /* new client during shutdown */ 538 return; /* new client during shutdown */
444 size = ntohs (message->size) - sizeof (struct GNUNET_MessageHeader); 539 size = ntohs (message->size) - sizeof (struct GNUNET_MessageHeader);
445 if (size != 540 if (size !=
446 GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], size, 2, 541 GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
447 &service, &name)) 542 size,
543 2,
544 &service,
545 &name))
448 { 546 {
449 GNUNET_break (0); 547 GNUNET_break (0);
450 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); 548 GNUNET_SERVER_receive_done (client,
549 GNUNET_SYSERR);
451 return; 550 return;
452 } 551 }
552 slen = strlen (service);
553 nlen = strlen (name);
453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454 "Received request for statistics on `%s:%s'\n", 555 "Received request for statistics on `%s:%s'\n",
455 strlen (service) ? service : "*", 556 slen ? service : "*",
456 strlen (name) ? name : "*"); 557 nlen ? name : "*");
457 for (pos = start; NULL != pos; pos = pos->next) 558 for (se = sub_head; NULL != se; se = se->next)
458 if (matches (pos, service, name)) 559 {
560 if (! ( (0 == slen) ||
561 (0 == strcmp (service, se->service))) )
562 continue;
563 for (pos = se->stat_head; NULL != pos; pos = pos->next)
564 {
565 if (! ( (0 == nlen) ||
566 (0 == strcmp (name, pos->name))) )
567 continue;
459 transmit (client, pos); 568 transmit (client, pos);
569 }
570 }
460 end.size = htons (sizeof (struct GNUNET_MessageHeader)); 571 end.size = htons (sizeof (struct GNUNET_MessageHeader));
461 end.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_END); 572 end.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_END);
462 GNUNET_SERVER_notification_context_unicast (nc, client, &end, GNUNET_NO); 573 GNUNET_SERVER_notification_context_unicast (nc,
574 client,
575 &end,
576 GNUNET_NO);
463 GNUNET_SERVER_receive_done (client, GNUNET_OK); 577 GNUNET_SERVER_receive_done (client, GNUNET_OK);
464} 578}
465 579
@@ -489,11 +603,13 @@ notify_change (struct StatsEntry *se)
489 wvm.header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE); 603 wvm.header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE);
490 wvm.header.size = 604 wvm.header.size =
491 htons (sizeof (struct GNUNET_STATISTICS_WatchValueMessage)); 605 htons (sizeof (struct GNUNET_STATISTICS_WatchValueMessage));
492 wvm.flags = htonl (se->persistent ? GNUNET_STATISTICS_PERSIST_BIT : 0); 606 wvm.flags = htonl (se->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0);
493 wvm.wid = htonl (pos->wid); 607 wvm.wid = htonl (pos->wid);
494 wvm.reserved = htonl (0); 608 wvm.reserved = htonl (0);
495 wvm.value = GNUNET_htonll (se->value); 609 wvm.value = GNUNET_htonll (se->value);
496 GNUNET_SERVER_notification_context_unicast (nc, pos->client, &wvm.header, 610 GNUNET_SERVER_notification_context_unicast (nc,
611 pos->client,
612 &wvm.header,
497 GNUNET_NO); 613 GNUNET_NO);
498 pos->last_value = se->value; 614 pos->last_value = se->value;
499 } 615 }
@@ -501,6 +617,77 @@ notify_change (struct StatsEntry *se)
501 617
502 618
503/** 619/**
620 * Find the subsystem entry of the given name for the specified client.
621 *
622 * @param ce client looking for the subsystem, may contain a hint
623 * to find the entry faster, can be NULL
624 * @param service name of the subsystem to look for
625 * @return subsystem entry, never NULL (subsystem entry is created if necessary)
626 */
627static struct SubsystemEntry *
628find_subsystem_entry (struct ClientEntry *ce,
629 const char *service)
630{
631 size_t slen;
632 struct SubsystemEntry *se;
633
634 if (NULL != ce)
635 se = ce->subsystem;
636 else
637 se = NULL;
638 if ( (NULL == se) ||
639 (0 != strcmp (service,
640 se->service)) )
641 {
642 for (se = sub_head; NULL != se; se = se->next)
643 if (0 == strcmp (service,
644 se->service))
645 break;
646 if (NULL != ce)
647 ce->subsystem = se;
648 }
649 if (NULL != se)
650 return se;
651 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
652 "Allocating new subsystem entry `%s'\n",
653 service);
654 slen = strlen (service) + 1;
655 se = GNUNET_malloc (sizeof (struct SubsystemEntry) +
656 slen);
657 memcpy (&se[1],
658 service,
659 slen);
660 se->service = (const char *) &se[1];
661 GNUNET_CONTAINER_DLL_insert (sub_head,
662 sub_tail,
663 se);
664 if (NULL != ce)
665 ce->subsystem = se;
666 return se;
667}
668
669
670/**
671 * Find the statistics entry of the given subsystem.
672 *
673 * @param subsystem subsystem to look in
674 * @param name name of the entry to look for
675 * @return statistis entry, or NULL if not found
676 */
677static struct StatsEntry *
678find_stat_entry (struct SubsystemEntry *se,
679 const char *name)
680{
681 struct StatsEntry *pos;
682
683 for (pos = se->stat_head; NULL != pos; pos = pos->next)
684 if (0 == strcmp (name, pos->name))
685 return pos;
686 return NULL;
687}
688
689
690/**
504 * Handle SET-message. 691 * Handle SET-message.
505 * 692 *
506 * @param cls closure 693 * @param cls closure
@@ -512,21 +699,24 @@ handle_set (void *cls,
512 struct GNUNET_SERVER_Client *client, 699 struct GNUNET_SERVER_Client *client,
513 const struct GNUNET_MessageHeader *message) 700 const struct GNUNET_MessageHeader *message)
514{ 701{
515 char *service; 702 const char *service;
516 char *name; 703 const char *name;
704 size_t nlen;
517 uint16_t msize; 705 uint16_t msize;
518 uint16_t size; 706 uint16_t size;
519 const struct GNUNET_STATISTICS_SetMessage *msg; 707 const struct GNUNET_STATISTICS_SetMessage *msg;
708 struct SubsystemEntry *se;
709 struct ClientEntry *ce;
520 struct StatsEntry *pos; 710 struct StatsEntry *pos;
521 struct StatsEntry *prev;
522 uint32_t flags; 711 uint32_t flags;
523 uint64_t value; 712 uint64_t value;
524 int64_t delta; 713 int64_t delta;
525 int changed; 714 int changed;
526 int initial_set; 715 int initial_set;
527 716
717 ce = NULL;
528 if ( (NULL != client) && 718 if ( (NULL != client) &&
529 (NULL == make_client_entry (client)) ) 719 (NULL == (ce = make_client_entry (client))) )
530 return; /* new client during shutdown */ 720 return; /* new client during shutdown */
531 msize = ntohs (message->size); 721 msize = ntohs (message->size);
532 if (msize < sizeof (struct GNUNET_STATISTICS_SetMessage)) 722 if (msize < sizeof (struct GNUNET_STATISTICS_SetMessage))
@@ -537,77 +727,91 @@ handle_set (void *cls,
537 } 727 }
538 size = msize - sizeof (struct GNUNET_STATISTICS_SetMessage); 728 size = msize - sizeof (struct GNUNET_STATISTICS_SetMessage);
539 msg = (const struct GNUNET_STATISTICS_SetMessage *) message; 729 msg = (const struct GNUNET_STATISTICS_SetMessage *) message;
540
541 if (size != 730 if (size !=
542 GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], size, 2, &service, 731 GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1],
732 size,
733 2,
734 &service,
543 &name)) 735 &name))
544 { 736 {
545 GNUNET_break (0); 737 GNUNET_break (0);
546 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); 738 GNUNET_SERVER_receive_done (client,
739 GNUNET_SYSERR);
547 return; 740 return;
548 } 741 }
742 se = find_subsystem_entry (ce, service);
549 flags = ntohl (msg->flags); 743 flags = ntohl (msg->flags);
550 value = GNUNET_ntohll (msg->value); 744 value = GNUNET_ntohll (msg->value);
551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552 "Received request to update statistic on `%s:%s' (%u) to/by %llu\n", 746 "Received request to update statistic on `%s:%s' (%u) to/by %llu\n",
553 service, name, (unsigned int) flags, (unsigned long long) value); 747 service,
554 pos = start; 748 name,
555 prev = NULL; 749 (unsigned int) flags,
556 while (pos != NULL) 750 (unsigned long long) value);
751 pos = find_stat_entry (se, name);
752 if (NULL != pos)
557 { 753 {
558 if (matches (pos, service, name)) 754 initial_set = 0;
755 if (0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE))
559 { 756 {
560 initial_set = 0; 757 changed = (pos->value != value);
561 if ((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0) 758 pos->value = value;
759 }
760 else
761 {
762 delta = (int64_t) value;
763 if ((delta < 0) && (pos->value < -delta))
562 { 764 {
563 changed = (pos->value != value); 765 changed = (0 != pos->value);
564 pos->value = value; 766 pos->value = 0;
565 } 767 }
566 else 768 else
567 { 769 {
568 delta = (int64_t) value; 770 changed = (0 != delta);
569 if ((delta < 0) && (pos->value < -delta)) 771 GNUNET_break ( (delta <= 0) ||
570 { 772 (pos->value + delta > pos->value) );
571 changed = (pos->value != 0); 773 pos->value += delta;
572 pos->value = 0;
573 }
574 else
575 {
576 changed = (delta != 0);
577 GNUNET_break ((delta <= 0) || (pos->value + delta > pos->value));
578 pos->value += delta;
579 }
580 }
581 if (GNUNET_NO == pos->set)
582 {
583 pos->set = GNUNET_YES;
584 initial_set = 1;
585 }
586 pos->msg->value = GNUNET_htonll (pos->value);
587 pos->msg->flags = msg->flags;
588 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
589 if (prev != NULL)
590 {
591 /* move to front for faster setting next time! */
592 prev->next = pos->next;
593 pos->next = start;
594 start = pos;
595 } 774 }
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
597 "Statistic `%s:%s' updated to value %llu.\n", service, name,
598 pos->value);
599 if ((changed) || (1 == initial_set))
600 notify_change (pos);
601 GNUNET_SERVER_receive_done (client, GNUNET_OK);
602 return;
603 } 775 }
604 prev = pos; 776 if (GNUNET_NO == pos->set)
605 pos = pos->next; 777 {
778 pos->set = GNUNET_YES;
779 initial_set = 1;
780 }
781 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
782 if (pos != se->stat_head)
783 {
784 /* move to front for faster setting next time! */
785 GNUNET_CONTAINER_DLL_remove (se->stat_head,
786 se->stat_tail,
787 pos);
788 GNUNET_CONTAINER_DLL_insert (se->stat_head,
789 se->stat_tail,
790 pos);
791 }
792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
793 "Statistic `%s:%s' updated to value %llu (%d).\n",
794 service,
795 name,
796 pos->value,
797 pos->persistent);
798 if ( (changed) ||
799 (1 == initial_set) )
800 notify_change (pos);
801 GNUNET_SERVER_receive_done (client,
802 GNUNET_OK);
803 return;
606 } 804 }
607 pos = GNUNET_malloc (sizeof (struct StatsEntry) + msize); 805 /* not found, create a new entry */
608 pos->next = start; 806 nlen = strlen (name) + 1;
609 if (((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0) || 807 pos = GNUNET_malloc (sizeof (struct StatsEntry) + nlen);
610 (0 < (int64_t) GNUNET_ntohll (msg->value))) 808 memcpy (&pos[1],
809 name,
810 nlen);
811 pos->name = (const char *) &pos[1];
812 pos->subsystem = se;
813 if ( (0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) ||
814 (0 < (int64_t) GNUNET_ntohll (msg->value)) )
611 { 815 {
612 pos->value = GNUNET_ntohll (msg->value); 816 pos->value = GNUNET_ntohll (msg->value);
613 pos->set = GNUNET_YES; 817 pos->set = GNUNET_YES;
@@ -618,18 +822,16 @@ handle_set (void *cls,
618 } 822 }
619 pos->uid = uidgen++; 823 pos->uid = uidgen++;
620 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); 824 pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT));
621 pos->msg = (void *) &pos[1]; 825 GNUNET_CONTAINER_DLL_insert (se->stat_head,
622 memcpy (pos->msg, message, ntohs (message->size)); 826 se->stat_tail,
623 pos->service = (const char *) &pos->msg[1]; 827 pos);
624 pos->name = &pos->service[strlen (pos->service) + 1];
625
626 start = pos;
627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 828 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
628 "New statistic on `%s:%s' with value %llu created.\n", 829 "New statistic on `%s:%s' with value %llu created.\n",
629 service, 830 service,
630 name, 831 name,
631 pos->value); 832 pos->value);
632 GNUNET_SERVER_receive_done (client, GNUNET_OK); 833 GNUNET_SERVER_receive_done (client,
834 GNUNET_OK);
633} 835}
634 836
635 837
@@ -645,18 +847,20 @@ handle_watch (void *cls,
645 struct GNUNET_SERVER_Client *client, 847 struct GNUNET_SERVER_Client *client,
646 const struct GNUNET_MessageHeader *message) 848 const struct GNUNET_MessageHeader *message)
647{ 849{
648 char *service; 850 const char *service;
649 char *name; 851 const char *name;
650 uint16_t msize; 852 uint16_t msize;
651 uint16_t size; 853 uint16_t size;
854 struct SubsystemEntry *se;
652 struct StatsEntry *pos; 855 struct StatsEntry *pos;
653 struct ClientEntry *ce; 856 struct ClientEntry *ce;
654 struct WatchEntry *we; 857 struct WatchEntry *we;
655 size_t slen; 858 size_t nlen;
656 859
657 if (NULL == nc) 860 if (NULL == nc)
658 { 861 {
659 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); 862 GNUNET_SERVER_receive_done (client,
863 GNUNET_SYSERR);
660 return; 864 return;
661 } 865 }
662 GNUNET_SERVER_client_mark_monitor (client); 866 GNUNET_SERVER_client_mark_monitor (client);
@@ -670,8 +874,11 @@ handle_watch (void *cls,
670 } 874 }
671 size = msize - sizeof (struct GNUNET_MessageHeader); 875 size = msize - sizeof (struct GNUNET_MessageHeader);
672 if (size != 876 if (size !=
673 GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], size, 2, 877 GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1],
674 &service, &name)) 878 size,
879 2,
880 &service,
881 &name))
675 { 882 {
676 GNUNET_break (0); 883 GNUNET_break (0);
677 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); 884 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
@@ -681,27 +888,21 @@ handle_watch (void *cls,
681 "Received request to watch statistic on `%s:%s'\n", 888 "Received request to watch statistic on `%s:%s'\n",
682 service, 889 service,
683 name); 890 name);
684 for (pos = start; NULL != pos; pos = pos->next) 891 se = find_subsystem_entry (ce, service);
685 if (matches (pos, service, name)) 892 pos = find_stat_entry (se, name);
686 break; 893 if (NULL == pos)
687 if (pos == NULL)
688 { 894 {
689 pos = 895 nlen = strlen (name) + 1;
690 GNUNET_malloc (sizeof (struct StatsEntry) + 896 pos = GNUNET_malloc (sizeof (struct StatsEntry) +
691 sizeof (struct GNUNET_STATISTICS_SetMessage) + size); 897 nlen);
692 pos->next = start; 898 memcpy (&pos[1], name, nlen);
899 pos->name = (const char *) &pos[1];
900 pos->subsystem = se;
901 GNUNET_CONTAINER_DLL_insert (se->stat_head,
902 se->stat_tail,
903 pos);
693 pos->uid = uidgen++; 904 pos->uid = uidgen++;
694 pos->set = GNUNET_NO; 905 pos->set = GNUNET_NO;
695 pos->msg = (void *) &pos[1];
696 pos->msg->header.size =
697 htons (sizeof (struct GNUNET_STATISTICS_SetMessage) + size);
698 pos->msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET);
699 pos->service = (const char *) &pos->msg[1];
700 slen = strlen (service) + 1;
701 memcpy ((void *) pos->service, service, slen);
702 pos->name = &pos->service[slen];
703 memcpy ((void *) pos->name, name, strlen (name) + 1);
704 start = pos;
705 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 906 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
706 "New statistic on `%s:%s' with value %llu created.\n", 907 "New statistic on `%s:%s' with value %llu created.\n",
707 service, 908 service,
@@ -715,7 +916,7 @@ handle_watch (void *cls,
715 GNUNET_CONTAINER_DLL_insert (pos->we_head, 916 GNUNET_CONTAINER_DLL_insert (pos->we_head,
716 pos->we_tail, 917 pos->we_tail,
717 we); 918 we);
718 if (pos->value != 0) 919 if (0 != pos->value)
719 notify_change (pos); 920 notify_change (pos);
720 GNUNET_SERVER_receive_done (client, GNUNET_OK); 921 GNUNET_SERVER_receive_done (client, GNUNET_OK);
721} 922}
@@ -728,7 +929,8 @@ static void
728do_shutdown () 929do_shutdown ()
729{ 930{
730 struct WatchEntry *we; 931 struct WatchEntry *we;
731 struct StatsEntry *se; 932 struct StatsEntry *pos;
933 struct SubsystemEntry *se;
732 934
733 if (NULL == nc) 935 if (NULL == nc)
734 return; 936 return;
@@ -736,15 +938,25 @@ do_shutdown ()
736 GNUNET_SERVER_notification_context_destroy (nc); 938 GNUNET_SERVER_notification_context_destroy (nc);
737 nc = NULL; 939 nc = NULL;
738 GNUNET_assert (0 == client_count); 940 GNUNET_assert (0 == client_count);
739 while (NULL != (se = start)) 941 while (NULL != (se = sub_head))
740 { 942 {
741 start = se->next; 943 GNUNET_CONTAINER_DLL_remove (sub_head,
742 while (NULL != (we = se->we_head)) 944 sub_tail,
945 se);
946 while (NULL != (pos = se->stat_head))
743 { 947 {
744 GNUNET_CONTAINER_DLL_remove (se->we_head, 948 GNUNET_CONTAINER_DLL_remove (se->stat_head,
745 se->we_tail, 949 se->stat_tail,
746 we); 950 pos);
747 GNUNET_free (we); 951 while (NULL != (we = pos->we_head))
952 {
953 GNUNET_break (0);
954 GNUNET_CONTAINER_DLL_remove (pos->we_head,
955 pos->we_tail,
956 we);
957 GNUNET_free (we);
958 }
959 GNUNET_free (pos);
748 } 960 }
749 GNUNET_free (se); 961 GNUNET_free (se);
750 } 962 }
@@ -781,7 +993,8 @@ handle_client_disconnect (void *cls,
781 struct ClientEntry *ce; 993 struct ClientEntry *ce;
782 struct WatchEntry *we; 994 struct WatchEntry *we;
783 struct WatchEntry *wen; 995 struct WatchEntry *wen;
784 struct StatsEntry *se; 996 struct StatsEntry *pos;
997 struct SubsystemEntry *se;
785 998
786 if (NULL == client) 999 if (NULL == client)
787 return; 1000 return;
@@ -792,19 +1005,22 @@ handle_client_disconnect (void *cls,
792 GNUNET_SERVER_client_set_user_context (client, 1005 GNUNET_SERVER_client_set_user_context (client,
793 NULL); 1006 NULL);
794 client_count--; 1007 client_count--;
795 se = start; 1008 for (se = sub_head; NULL != se; se = se->next)
796 while (NULL != se)
797 { 1009 {
798 wen = se->we_head; 1010 for (pos = se->stat_head; NULL != pos; pos = pos->next)
799 while (NULL != (we = wen))
800 { 1011 {
801 wen = we->next; 1012 wen = pos->we_head;
802 if (we->client != client) 1013 while (NULL != (we = wen))
803 continue; 1014 {
804 GNUNET_CONTAINER_DLL_remove (se->we_head, se->we_tail, we); 1015 wen = we->next;
805 GNUNET_free (we); 1016 if (we->client != client)
1017 continue;
1018 GNUNET_CONTAINER_DLL_remove (pos->we_head,
1019 pos->we_tail,
1020 we);
1021 GNUNET_free (we);
1022 }
806 } 1023 }
807 se = se->next;
808 } 1024 }
809 if ( (0 == client_count) && 1025 if ( (0 == client_count) &&
810 (GNUNET_YES == in_shutdown) ) 1026 (GNUNET_YES == in_shutdown) )
@@ -832,9 +1048,12 @@ run (void *cls,
832 }; 1048 };
833 cfg = c; 1049 cfg = c;
834 srv = server; 1050 srv = server;
835 GNUNET_SERVER_add_handlers (server, handlers); 1051 GNUNET_SERVER_add_handlers (server,
1052 handlers);
836 nc = GNUNET_SERVER_notification_context_create (server, 16); 1053 nc = GNUNET_SERVER_notification_context_create (server, 16);
837 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); 1054 GNUNET_SERVER_disconnect_notify (server,
1055 &handle_client_disconnect,
1056 NULL);
838 load (server); 1057 load (server);
839 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, 1058 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
840 &shutdown_task, 1059 &shutdown_task,
@@ -854,7 +1073,8 @@ main (int argc, char *const *argv)
854{ 1073{
855 return (GNUNET_OK == 1074 return (GNUNET_OK ==
856 GNUNET_SERVICE_run (argc, argv, "statistics", 1075 GNUNET_SERVICE_run (argc, argv, "statistics",
857 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, &run, NULL)) ? 0 : 1; 1076 GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN,
1077 &run, NULL)) ? 0 : 1;
858} 1078}
859 1079
860#ifdef LINUX 1080#ifdef LINUX