From 298776c4185d99d2d64f9ff84d1eb662f2f16db7 Mon Sep 17 00:00:00 2001 From: Julius Bünger Date: Sat, 16 Jun 2018 12:47:01 +0200 Subject: statistics-cli: add -t for printing statistics of testbed nodes --- doc/man/gnunet-statistics.1 | 4 + src/statistics/gnunet-statistics.c | 473 ++++++++++++++++++++++++++++++++++--- 2 files changed, 438 insertions(+), 39 deletions(-) diff --git a/doc/man/gnunet-statistics.1 b/doc/man/gnunet-statistics.1 index 36f445a09..eed1c1de8 100644 --- a/doc/man/gnunet-statistics.1 +++ b/doc/man/gnunet-statistics.1 @@ -41,6 +41,10 @@ non\-persistent. Statistics are kept for various subsystems. With this option, the output can be restricted to a particular subsystem only. .B +.IP "\-t TESTBED_PATH, \-\-subsystem=TESTBED_PATH" +When running testbed, you can get statistics of all peers with specefying the +folder containing the data of all testbed nodes like \fBgnunet\-statistics -t /tmp/testbedARtmQv\fP. +.B .IP "\-v, \-\-version" Print GNUnet version number. diff --git a/src/statistics/gnunet-statistics.c b/src/statistics/gnunet-statistics.c index 648fc89bb..2aefcadee 100644 --- a/src/statistics/gnunet-statistics.c +++ b/src/statistics/gnunet-statistics.c @@ -38,6 +38,11 @@ static int ret; */ static char *subsystem; +/** + * The path of the testbed data. + */ +static char *path_testbed; + /** * Set to the specific stat value that we are after (or NULL for all). */ @@ -79,10 +84,192 @@ static unsigned long long set_val; static int set_value; /** - * Handle for pending GET operation. + * @brief Representation of all (testbed) nodes. */ -static struct GNUNET_STATISTICS_GetHandle *gh; +static struct Node { + /** + * @brief Index of the node in this array. + */ + unsigned index_node; + + /** + * @brief Configuration handle for this node + */ + struct GNUNET_CONFIGURATION_Handle *conf; + + /** + * Handle for pending GET operation. + */ + struct GNUNET_STATISTICS_GetHandle *gh; + + /** + * @brief Statistics handle nodes. + */ + struct GNUNET_STATISTICS_Handle *handle; + /** + * @brief Identifier for shutdown task for this node. + */ + struct GNUNET_SCHEDULER_Task *shutdown_task; +} *nodes; +/** + * @brief Number of configurations of all (testbed) nodes. + */ +static unsigned num_nodes; + +/** + * @brief Set of values for a combination of subsystem and name. + */ +struct ValueSet +{ + /** + * @brief Subsystem of the valueset. + */ + char *subsystem; + + /** + * @brief Name of the valueset. + */ + char *name; + + /** + * @brief The values. + */ + uint64_t *values; + + /** + * @brief Persistence of the values. + */ + int is_persistent; +}; + +/** + * @brief Collection of all values (represented with #ValueSet). + */ +static struct GNUNET_CONTAINER_MultiHashMap *values; + +/** + * @brief Number of nodes that have their values ready. + */ +static int num_nodes_ready; + +/** + * @brief Create a new #ValueSet + * + * @param subsystem Subsystem of the valueset. + * @param name Name of the valueset. + * @param num_values Number of values in valueset - number of peers. + * @param is_persistent Persistence status of values. + * + * @return Newly allocated #ValueSet. + */ +static struct ValueSet * +new_value_set (const char *subsystem, + const char *name, + unsigned num_values, + int is_persistent) +{ + struct ValueSet *value_set; + + value_set = GNUNET_new (struct ValueSet); + value_set->subsystem = GNUNET_strdup (subsystem); + value_set->name = GNUNET_strdup (name); + value_set->values = GNUNET_new_array (num_values, uint64_t); + value_set->is_persistent = persistent; + return value_set; +} + +/** + * @brief Print the (collected) values. + * + * Implements #GNUNET_CONTAINER_HashMapIterator. + * + * @param cls Closure - unused + * @param key #GNUNET_HashCode key of #GNUNET_CONTAINER_MultiHashMap iterator - + * unused + * @param value Values represented as #ValueSet. + * + * @return GNUNET_YES - continue iteration. + */ +static int +printer (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get(); + const char *now_str; + struct ValueSet *value_set = value; + + if (quiet == GNUNET_NO) + { + if (GNUNET_YES == watch) + { + now_str = GNUNET_STRINGS_absolute_time_to_string (now); + FPRINTF (stdout, + "%24s %s%12s %50s: ", + now_str, + value_set->is_persistent ? "!" : " ", + value_set->subsystem, + _(value_set->name)); + } + else + { + FPRINTF (stdout, + "%s%12s %50s: ", + value_set->is_persistent ? "!" : " ", + value_set->subsystem, + _(value_set->name)); + } + } + for (unsigned i = 0; i < num_nodes; i++) + { + FPRINTF (stdout, + "%16llu", + (unsigned long long) value_set->values[i]); + } + FPRINTF (stdout, "\n"); + GNUNET_free (value_set->subsystem); + GNUNET_free (value_set->name); + GNUNET_free (value_set->values); + GNUNET_free (value_set); + return GNUNET_YES; +} + +/** + * @brief Called once all statistic values are available. + * + * Implements #GNUNET_STATISTICS_Callback + * + * @param cls Closure - The index of the node. + * @param succes Whether statistics were obtained successfully. + */ +static void +continuation_print (void *cls, + int success) +{ + const unsigned index_node = *(unsigned *) cls; + + nodes[index_node].gh = NULL; + if (GNUNET_OK != success) + { + if (NULL == remote_host) + FPRINTF (stderr, + "%s", + _("Failed to obtain statistics.\n")); + else + FPRINTF (stderr, + _("Failed to obtain statistics from host `%s:%llu'\n"), + remote_host, + remote_port); + ret = 1; + } + num_nodes_ready++; + if (num_nodes_ready == num_nodes) + { + GNUNET_CONTAINER_multihashmap_iterate (values, printer, NULL); + GNUNET_SCHEDULER_shutdown(); + } +} /** * Callback function to process statistic values. @@ -95,11 +282,11 @@ static struct GNUNET_STATISTICS_GetHandle *gh; * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ static int -printer (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) +printer_watch (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) { struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get(); const char *now_str; @@ -135,7 +322,6 @@ printer (void *cls, return GNUNET_OK; } - /** * Function called last by the statistics code. * @@ -147,7 +333,10 @@ static void cleanup (void *cls, int success) { - gh = NULL; + for (unsigned i = 0; i < num_nodes; i++) + { + nodes[i].gh = NULL; + } if (GNUNET_OK != success) { if (NULL == remote_host) @@ -164,6 +353,55 @@ cleanup (void *cls, GNUNET_SCHEDULER_shutdown (); } +/** + * @brief Iterate over statistics values and store them in #values. + * They will be printed once all are available. + * + * @param cls Cosure - Node index. + * @param subsystem Subsystem of the value. + * @param name Name of the value. + * @param value Value itself. + * @param is_persistent Persistence. + * + * @return GNUNET_OK - continue. + */ +static int +collector (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + const unsigned index_node = *(unsigned *) cls; + struct GNUNET_HashCode *key; + struct GNUNET_HashCode hc; + char *subsys_name; + unsigned len_subsys_name; + struct ValueSet *value_set; + + len_subsys_name = strlen (subsystem) + 3 + strlen (name) + 1; + subsys_name = GNUNET_malloc (len_subsys_name); + SPRINTF (subsys_name, "%s---%s", subsystem, name); + key = &hc; + GNUNET_CRYPTO_hash (subsys_name, len_subsys_name, key); + GNUNET_free (subsys_name); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (values, key)) + { + // get + value_set = GNUNET_CONTAINER_multihashmap_get (values, key); + } + else + { + // new + value_set = new_value_set (subsystem, name, num_nodes, is_persistent); + } + // write + value_set->values[index_node] = value; + // put + GNUNET_CONTAINER_multihashmap_put (values, key, value_set, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + return GNUNET_OK; +} /** * Function run on shutdown to clean up. @@ -173,10 +411,30 @@ cleanup (void *cls, static void shutdown_task (void *cls) { - struct GNUNET_STATISTICS_Handle *h = cls; + const unsigned index_node = *(unsigned *) cls; + struct GNUNET_STATISTICS_Handle *h; + struct GNUNET_STATISTICS_GetHandle *gh; + nodes[index_node].shutdown_task = NULL; + if ( (NULL != path_testbed) && + (NULL != nodes[index_node].conf) ) + { + GNUNET_CONFIGURATION_destroy (nodes[index_node].conf); + nodes[index_node].conf = NULL; + } + + h = nodes[index_node].handle; + gh = nodes[index_node].gh; if (NULL == h) + { + num_nodes_ready--; + if (0 == num_nodes_ready) + { + GNUNET_array_grow (nodes, num_nodes, 0); + GNUNET_CONTAINER_multihashmap_destroy (values); + } return; + } if (NULL != gh) { GNUNET_STATISTICS_get_cancel (gh); @@ -186,14 +444,21 @@ shutdown_task (void *cls) (NULL != subsystem) && (NULL != name) ) GNUNET_assert (GNUNET_OK == - GNUNET_STATISTICS_watch_cancel (h, + GNUNET_STATISTICS_watch_cancel (h, subsystem, name, - &printer, - h)); + &printer_watch, + &nodes[index_node].index_node)); GNUNET_STATISTICS_destroy (h, GNUNET_NO); h = NULL; + + num_nodes_ready--; + if (0 == num_nodes_ready) + { + GNUNET_array_grow (nodes, num_nodes, 0); + GNUNET_CONTAINER_multihashmap_destroy (values); + } } @@ -205,8 +470,8 @@ shutdown_task (void *cls) static void main_task (void *cls) { - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct GNUNET_STATISTICS_Handle *h; + unsigned index_node = *(unsigned *) cls; + const struct GNUNET_CONFIGURATION_Handle *cfg = nodes[index_node].conf; if (set_value) { @@ -226,23 +491,23 @@ main_task (void *cls) ret = 1; return; } - h = GNUNET_STATISTICS_create (subsystem, + nodes[index_node].handle = GNUNET_STATISTICS_create (subsystem, cfg); - if (NULL == h) + if (NULL == nodes[index_node].handle) { ret = 1; return; } - GNUNET_STATISTICS_set (h, + GNUNET_STATISTICS_set (nodes[index_node].handle, name, (uint64_t) set_val, persistent); - GNUNET_STATISTICS_destroy (h, + GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_YES); - h = NULL; + nodes[index_node].handle = NULL; return; } - if (NULL == (h = GNUNET_STATISTICS_create ("gnunet-statistics", + if (NULL == (nodes[index_node].handle = GNUNET_STATISTICS_create ("gnunet-statistics", cfg))) { ret = 1; @@ -251,13 +516,13 @@ main_task (void *cls) if (GNUNET_NO == watch) { if (NULL == - (gh = GNUNET_STATISTICS_get (h, - subsystem, - name, - &cleanup, - &printer, - h)) ) - cleanup (h, + (nodes[index_node].gh = GNUNET_STATISTICS_get (nodes[index_node].handle, + subsystem, + name, + &continuation_print, + &collector, + &nodes[index_node].index_node)) ) + cleanup (nodes[index_node].handle, GNUNET_SYSERR); } else @@ -266,30 +531,128 @@ main_task (void *cls) (NULL == name) ) { printf (_("No subsystem or name given\n")); - GNUNET_STATISTICS_destroy (h, + GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_NO); - h = NULL; + nodes[index_node].handle = NULL; ret = 1; return; } if (GNUNET_OK != - GNUNET_STATISTICS_watch (h, + GNUNET_STATISTICS_watch (nodes[index_node].handle, subsystem, name, - &printer, - h)) + &printer_watch, + &nodes[index_node].index_node)) { fprintf (stderr, _("Failed to initialize watch routine\n")); - GNUNET_SCHEDULER_add_now (&shutdown_task, - h); + nodes[index_node].shutdown_task = + GNUNET_SCHEDULER_add_now (&shutdown_task, + &nodes[index_node].index_node); return; } } - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - h); + nodes[index_node].shutdown_task = + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + &nodes[index_node].index_node); } +/** + * @brief Iter over content of a node's directory to check for existence of a + * config file. + * + * Implements #GNUNET_FileNameCallback + * + * @param cls pointer to indicate success + * @param filename filename inside the directory of the potential node + * + * @return to continue iteration or not to + */ +static int +iter_check_config (void *cls, + const char *filename) +{ + if (0 == strncmp (GNUNET_STRINGS_get_short_name (filename), "config", 6)) + { + /* Found the config - stop iteration successfully */ + GNUNET_array_grow (nodes, num_nodes, num_nodes+1); + nodes[num_nodes-1].conf = GNUNET_CONFIGURATION_create(); + nodes[num_nodes-1].index_node = num_nodes-1; + if (GNUNET_OK != GNUNET_CONFIGURATION_load (nodes[num_nodes-1].conf, filename)) + { + FPRINTF (stderr, "Failed loading config `%s'\n", filename); + return GNUNET_SYSERR; + } + return GNUNET_NO; + } + else + { + /* Continue iteration */ + return GNUNET_OK; + } +} + +/** + * @brief Iterates over filenames in testbed directory. + * + * Implements #GNUNET_FileNameCallback + * + * Checks if the file is a directory for a testbed node + * and counts the nodes. + * + * @param cls counter of nodes + * @param filename full path of the file in testbed + * + * @return status whether to continue iteration + */ +static int +iter_testbed_path (void *cls, + const char *filename) +{ + unsigned index_node; + + GNUNET_assert (NULL != filename); + if (1 == SSCANF (GNUNET_STRINGS_get_short_name (filename), + "%u", + &index_node)) + { + if (-1 == GNUNET_DISK_directory_scan (filename, + iter_check_config, + NULL)) + { + /* This is probably no directory for a testbed node + * Go on with iteration */ + return GNUNET_OK; + } + return GNUNET_OK; + } + return GNUNET_OK; +} + +/** + * @brief Count the number of nodes running in the testbed + * + * @param path_testbed path to the testbed data + * + * @return number of running nodes + */ +static int +discover_testbed_nodes (const char *path_testbed) +{ + int num_dir_entries; + + num_dir_entries = GNUNET_DISK_directory_scan (path_testbed, + iter_testbed_path, + NULL); + if (-1 == num_dir_entries) + { + FPRINTF (stderr, + "Failure during scanning directory `%s'\n", + path_testbed); + return -1; + } + return 0; +} /** * Main function that will be run by the scheduler. @@ -361,8 +724,34 @@ run (void *cls, "PORT", remote_port); } - GNUNET_SCHEDULER_add_now (&main_task, - c); + if (NULL == path_testbed) + { + values = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); + GNUNET_array_grow (nodes, num_nodes, 1); + nodes[0].index_node = 0; + nodes[0].conf = c; + GNUNET_SCHEDULER_add_now (&main_task, &nodes[0].index_node); + } + else + { + if (GNUNET_YES == watch) + { + printf (_("Not able to watch testbed nodes (yet - feel free to implement)\n")); + ret = 1; + return; + } + values = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); + if (-1 == discover_testbed_nodes (path_testbed)) + { + return; + } + /* For each config/node collect statistics */ + for (unsigned i = 0; i < num_nodes; i++) + { + GNUNET_SCHEDULER_add_now (&main_task, + &nodes[i].index_node); + } + } } @@ -394,6 +783,12 @@ main (int argc, char *const *argv) gettext_noop ("limit output to the given SUBSYSTEM"), &subsystem), + GNUNET_GETOPT_option_filename ('t', + "testbed", + "TESTBED", + gettext_noop ("path to the folder containing the testbed data"), + &path_testbed), + GNUNET_GETOPT_option_flag ('q', "quiet", gettext_noop ("just print the statistics value"), -- cgit v1.2.3