diff options
Diffstat (limited to 'src/nse/gnunet-nse-profiler.c')
-rw-r--r-- | src/nse/gnunet-nse-profiler.c | 1264 |
1 files changed, 690 insertions, 574 deletions
diff --git a/src/nse/gnunet-nse-profiler.c b/src/nse/gnunet-nse-profiler.c index 4a4bea5..225a420 100644 --- a/src/nse/gnunet-nse-profiler.c +++ b/src/nse/gnunet-nse-profiler.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2011 Christian Grothoff (and other contributing authors) + (C) 2011, 2012 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -24,35 +24,64 @@ * Generally, the profiler starts a given number of peers, * then churns some off, waits a certain amount of time, then * churns again, and repeats. + * + * TODO: + * - need to enable user to specify topology options + * - need to check for leaks (especially FD leaks) + * - need to TEST */ #include "platform.h" -#include "gnunet_testing_lib.h" +#include "gnunet_testbed_service.h" #include "gnunet_nse_service.h" -#define VERBOSE 3 +/** + * Information we track for a peer in the testbed. + */ struct NSEPeer { + /** + * Prev reference in DLL. + */ struct NSEPeer *prev; + /** + * Next reference in DLL. + */ struct NSEPeer *next; - struct GNUNET_TESTING_Daemon *daemon; + /** + * Handle with testbed. + */ + struct GNUNET_TESTBED_Peer *daemon; - struct GNUNET_NSE_Handle *nse_handle; + /** + * Testbed operation to connect to NSE service. + */ + struct GNUNET_TESTBED_Operation *nse_op; + /** + * Handle to statistics service of the peer. + */ struct GNUNET_STATISTICS_Handle *stats; + + /** + * Testbed operation to connect to statistics service. + */ + struct GNUNET_TESTBED_Operation *stats_op; + /** + * Task scheduled to get statistics from this peer. + */ GNUNET_SCHEDULER_TaskIdentifier stats_task; }; +/** + * Context for the stats task? + */ struct StatsContext { - /** - * Whether or not shoutdown after finishing. - */ - int shutdown; /** * How many messages have peers received during the test. @@ -81,51 +110,80 @@ struct StatsContext }; +/** + * Head of DLL of peers we monitor closely. + */ static struct NSEPeer *peer_head; +/** + * Tail of DLL of peers we monitor closely. + */ static struct NSEPeer *peer_tail; /** - * How long until we give up on connecting the peers? + * Return value from 'main' (0 == success) */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500) - static int ok; /** - * Be verbose + * Be verbose (configuration option) */ static int verbose; /** - * Total number of peers in the test. + * Name of the file with the hosts to run the test over (configuration option) + */ +static char *hosts_file; + +/** + * IP address of this system, as seen by the rest of the system (configuration option) */ -static unsigned long long num_peers; +static char *controller_ip; /** - * Global configuration file + * Maximum number of peers in the test. */ -static struct GNUNET_CONFIGURATION_Handle *testing_cfg; +static unsigned int num_peers; /** - * Total number of currently running peers. + * Total number of rounds to execute. */ -static unsigned long long peers_running; +static unsigned int num_rounds; /** * Current round we are in. */ -static unsigned long long current_round; +static unsigned int current_round; + +/** + * Array of size 'num_rounds' with the requested number of peers in the given round. + */ +static unsigned int *num_peers_in_round; + +/** + * How many peers are running right now? + */ +static unsigned int peers_running; + +/** + * Specification for the numbers of peers to have in each round. + */ +static char *num_peer_spec; + +/** + * Handles to all of the running peers. + */ +static struct GNUNET_TESTBED_Peer **daemons; /** - * Peers desired in the next round. + * Global configuration file */ -static unsigned long long peers_next_round; +static struct GNUNET_CONFIGURATION_Handle *testing_cfg; /** * Maximum number of connections to NSE services. */ -static unsigned long long connection_limit; +static unsigned int connection_limit; /** * Total number of connections in the whole network. @@ -133,14 +191,14 @@ static unsigned long long connection_limit; static unsigned int total_connections; /** - * The currently running peer group. + * File to report results to. */ -static struct GNUNET_TESTING_PeerGroup *pg; +static struct GNUNET_DISK_FileHandle *output_file; /** - * File to report results to. + * Filename to log results to. */ -static struct GNUNET_DISK_FileHandle *output_file; +static char *output_filename; /** * File to log connection info, statistics to. @@ -148,94 +206,106 @@ static struct GNUNET_DISK_FileHandle *output_file; static struct GNUNET_DISK_FileHandle *data_file; /** - * How many data points to capture before triggering next round? + * Filename to log connection info, statistics to. */ -static struct GNUNET_TIME_Relative wait_time; +static char *data_filename; /** - * NSE interval. + * How long to wait before triggering next round? + * Default: 60 s. */ -static struct GNUNET_TIME_Relative interval; +static struct GNUNET_TIME_Relative wait_time = { 60 * 1000 }; /** - * Task called to disconnect peers. + * How often do we query for statistics during a round? + * Default: 1 s. */ -static GNUNET_SCHEDULER_TaskIdentifier disconnect_task; +static struct GNUNET_TIME_Relative interval = { 1000 }; /** - * Task called to shutdown test. + * Name of the file where we write the topology for each round; NULL for + * none. */ -static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle; +static char *topology_file; /** - * Task used to churn the network. + * List of hosts we use for the testbed. */ -static GNUNET_SCHEDULER_TaskIdentifier churn_task; +static struct GNUNET_TESTBED_Host **hosts; -static char *topology_file; +/** + * Size of the 'hosts' array. + */ +static unsigned int num_hosts; /** - * Check whether peers successfully shut down. + * Handle to the master controller. */ -static void -shutdown_callback (void *cls, const char *emsg) -{ - if (emsg != NULL) - { -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown of peers failed!\n"); -#endif - if (ok == 0) - ok = 666; - } - else - { -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All peers successfully shut down!\n"); -#endif - ok = 0; - } -} +static struct GNUNET_TESTBED_Controller *controller; + +/** + * Controller start handle. + */ +static struct GNUNET_TESTBED_ControllerProc *copro; + +/* /\** */ +/* * Testbed handle. */ +/* *\/ */ +/* static struct GNUNET_TESTBED_Testbed *testbed; */ +/** + * Clean up all of the monitoring connections to NSE and + * STATISTICS that we keep to selected peers. + */ static void -shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +close_monitor_connections () { struct NSEPeer *pos; -#if VERBOSE - FPRINTF (stderr, "%s", "Ending test.\n"); -#endif - - if (disconnect_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_NO_TASK; - } while (NULL != (pos = peer_head)) { - if (pos->nse_handle != NULL) - GNUNET_NSE_disconnect (pos->nse_handle); + if (NULL != pos->nse_op) + GNUNET_TESTBED_operation_done (pos->nse_op); + if (NULL != pos->stats_op) + GNUNET_TESTBED_operation_done (pos->stats_op); GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, pos); if (GNUNET_SCHEDULER_NO_TASK != pos->stats_task) - { GNUNET_SCHEDULER_cancel (pos->stats_task); - if (NULL != pos->stats) - GNUNET_STATISTICS_destroy(pos->stats, GNUNET_NO); - } GNUNET_free (pos); } +} + - if (data_file != NULL) +/** + * Task run on shutdown; cleans up everything. + * + * @param cls unused + * @param tc unused + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n"); + close_monitor_connections (); + /* if (NULL != testbed) */ + /* GNUNET_TESTBED_destroy (testbed); */ + if (NULL != controller) + GNUNET_TESTBED_controller_disconnect (controller); + if (NULL != copro) + GNUNET_TESTBED_controller_stop (copro); + while (0 < num_hosts) + GNUNET_TESTBED_host_destroy (hosts[--num_hosts]); + // FIXME: what about closing other files!? + if (NULL != data_file) GNUNET_DISK_file_close (data_file); - GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); } /** * Callback to call when network size estimate is updated. * - * @param cls closure + * @param cls closure with the 'struct NSEPeer' providing the update * @param timestamp server timestamp * @param estimate the value of the current network size estimate * @param std_dev standard deviation (rounded down to nearest integer) @@ -243,32 +313,34 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * */ static void -handle_estimate (void *cls, struct GNUNET_TIME_Absolute timestamp, +handle_estimate (void *cls, + struct GNUNET_TIME_Absolute timestamp, double estimate, double std_dev) { struct NSEPeer *peer = cls; - char *output_buffer; + char output_buffer[512]; size_t size; - if (output_file != NULL) - { - size = - GNUNET_asprintf (&output_buffer, "%s %llu %llu %f %f %f\n", - GNUNET_i2s (&peer->daemon->id), peers_running, - timestamp.abs_value, - GNUNET_NSE_log_estimate_to_n (estimate), estimate, - std_dev); - if (size != GNUNET_DISK_file_write (output_file, output_buffer, size)) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); - GNUNET_free (output_buffer); - } - else - FPRINTF (stderr, - "Received network size estimate from peer %s. Size: %f std.dev. %f\n", - GNUNET_i2s (&peer->daemon->id), estimate, std_dev); - + if (NULL == output_file) + { + FPRINTF (stderr, + "Received network size estimate from peer %p. Size: %f std.dev. %f\n", + peer, estimate, std_dev); + return; + } + size = GNUNET_snprintf (output_buffer, + sizeof (output_buffer), + "%p %llu %llu %f %f %f\n", + peer, peers_running, + timestamp.abs_value, + GNUNET_NSE_log_estimate_to_n (estimate), estimate, + std_dev); + if (size != GNUNET_DISK_file_write (output_file, output_buffer, size)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unable to write to file!\n"); } + /** * Process core statistic values. * @@ -284,29 +356,30 @@ core_stats_iterator (void *cls, const char *subsystem, const char *name, uint64_t value, int is_persistent) { struct NSEPeer *peer = cls; - char *output_buffer; + char output_buffer[512]; size_t size; - if (output_file != NULL) - { - size = - GNUNET_asprintf (&output_buffer, "%s [%s] %s %llu\n", - GNUNET_i2s (&peer->daemon->id), - subsystem, name, value); - if (size != GNUNET_DISK_file_write (output_file, output_buffer, size)) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); - GNUNET_free (output_buffer); - } - else - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "%s -> %s [%s]: %llu\n", - GNUNET_i2s (&peer->daemon->id), subsystem, name, value); - + if (NULL == output_file) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "%p -> %s [%s]: %llu\n", + peer, subsystem, name, value); + return GNUNET_OK; + } + size = + GNUNET_snprintf (output_buffer, + sizeof (output_buffer), + "%p [%s] %s %llu\n", + peer, + subsystem, name, value); + if (size != GNUNET_DISK_file_write (output_file, output_buffer, size)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); return GNUNET_OK; } + /** - * Continuation called by "get_stats" function. + * Continuation called by "get_stats" function once we are done. * * @param cls closure * @param success GNUNET_OK if statistics were @@ -315,32 +388,43 @@ core_stats_iterator (void *cls, const char *subsystem, const char *name, static void core_stats_cont (void *cls, int success); + +/** + * Function invoked periodically to get the statistics. + * + * @param cls 'struct NSEPeer' to get stats from + * @param tc scheduler context + */ static void -core_get_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +core_get_stats (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { struct NSEPeer *peer = cls; - if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) { - GNUNET_STATISTICS_destroy(peer->stats, GNUNET_NO); + GNUNET_TESTBED_operation_done (peer->stats_op); peer->stats = NULL; + peer->stats_op = NULL; return; } - else - { - GNUNET_STATISTICS_get(peer->stats, "core", NULL, - GNUNET_TIME_UNIT_FOREVER_REL, - &core_stats_cont, &core_stats_iterator, peer); - GNUNET_STATISTICS_get(peer->stats, "transport", NULL, - GNUNET_TIME_UNIT_FOREVER_REL, - NULL, &core_stats_iterator, peer); - GNUNET_STATISTICS_get(peer->stats, "nse", NULL, - GNUNET_TIME_UNIT_FOREVER_REL, - NULL, &core_stats_iterator, peer); - } + /* FIXME: code duplication! */ + GNUNET_STATISTICS_get (peer->stats, "core", NULL, + GNUNET_TIME_UNIT_FOREVER_REL, + &core_stats_cont, + &core_stats_iterator, peer); + GNUNET_STATISTICS_get (peer->stats, "transport", NULL, + GNUNET_TIME_UNIT_FOREVER_REL, + NULL, + &core_stats_iterator, peer); + GNUNET_STATISTICS_get (peer->stats, "nse", NULL, + GNUNET_TIME_UNIT_FOREVER_REL, + NULL, + &core_stats_iterator, peer); peer->stats_task = GNUNET_SCHEDULER_NO_TASK; } + /** * Continuation called by "get_stats" function. * @@ -349,149 +433,243 @@ core_get_stats (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * successfully obtained, GNUNET_SYSERR if not. */ static void -core_stats_cont (void *cls, int success) +core_stats_cont (void *cls, + int success) { struct NSEPeer *peer = cls; - peer->stats_task = GNUNET_SCHEDULER_add_delayed (interval, &core_get_stats, - peer); + + if (GNUNET_OK != success) + return; + peer->stats_task = GNUNET_SCHEDULER_add_delayed (interval, + &core_get_stats, peer); } /** + * Adapter function called to establish a connection to + * statistics service. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +statistics_connect_adapter (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + return GNUNET_STATISTICS_create ("<driver>", + cfg); +} + + +/** + * Adapter function called to destroy a connection to + * statistics service. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +statistics_disconnect_adapter (void *cls, + void *op_result) +{ + GNUNET_STATISTICS_destroy (op_result, GNUNET_NO); +} + + +/** + * Function called by testbed once we are connected to stats service. * + * @param cls the 'struct NSEPeer' for which we connected to stats + * @param op connect operation handle + * @param ca_result handle to stats service + * @param emsg error message on failure */ static void -connect_nse_service (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +stat_run (void *cls, + struct GNUNET_TESTBED_Operation *op, + void *ca_result, + const char *emsg) +{ + struct NSEPeer *current_peer = cls; + + if (NULL == ca_result) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to statistics service: %s\n", + emsg); + GNUNET_SCHEDULER_shutdown (); + return; + } + current_peer->stats = ca_result; + GNUNET_STATISTICS_get (current_peer->stats, "core", NULL, + GNUNET_TIME_UNIT_FOREVER_REL, + &core_stats_cont, + &core_stats_iterator, current_peer); + GNUNET_STATISTICS_get (current_peer->stats, "transport", NULL, + GNUNET_TIME_UNIT_FOREVER_REL, + NULL, + &core_stats_iterator, current_peer); + GNUNET_STATISTICS_get (current_peer->stats, "nse", NULL, + GNUNET_TIME_UNIT_FOREVER_REL, + NULL, + &core_stats_iterator, current_peer); +} + + +/** + * Adapter function called to establish a connection to + * NSE service. + * + * @param cls closure (the 'struct NSEPeer') + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +nse_connect_adapter (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct NSEPeer *current_peer = cls; + + return GNUNET_NSE_connect (cfg, &handle_estimate, current_peer); +} + + +/** + * Adapter function called to destroy a connection to + * NSE service. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +nse_disconnect_adapter (void *cls, + void *op_result) +{ + GNUNET_NSE_disconnect (op_result); +} + + +/** + * Task run to connect to the NSE and statistics services to a subset of + * all of the running peers. + */ +static void +connect_nse_service () { struct NSEPeer *current_peer; unsigned int i; -#if VERBOSE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to nse service of peers\n"); -#endif for (i = 0; i < num_peers; i++) { if ((connection_limit > 0) && (num_peers > connection_limit) && - (i % (num_peers / connection_limit) != 0)) + (0 != (i % (num_peers / connection_limit)))) continue; -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "nse-profiler: connecting to nse service of peer %d\n", i); -#endif current_peer = GNUNET_malloc (sizeof (struct NSEPeer)); - current_peer->daemon = GNUNET_TESTING_daemon_get (pg, i); - if (GNUNET_YES == - GNUNET_TESTING_test_daemon_running (GNUNET_TESTING_daemon_get (pg, i))) - { - current_peer->nse_handle = - GNUNET_NSE_connect (current_peer->daemon->cfg, &handle_estimate, - current_peer); - GNUNET_assert (current_peer->nse_handle != NULL); - } - current_peer->stats = GNUNET_STATISTICS_create("profiler", current_peer->daemon->cfg); - GNUNET_STATISTICS_get(current_peer->stats, "core", NULL, - GNUNET_TIME_UNIT_FOREVER_REL, - &core_stats_cont, &core_stats_iterator, current_peer); - GNUNET_STATISTICS_get(current_peer->stats, "transport", NULL, - GNUNET_TIME_UNIT_FOREVER_REL, - NULL, &core_stats_iterator, current_peer); - GNUNET_STATISTICS_get(current_peer->stats, "nse", NULL, - GNUNET_TIME_UNIT_FOREVER_REL, - NULL, &core_stats_iterator, current_peer); + current_peer->daemon = daemons[i]; + current_peer->nse_op + = GNUNET_TESTBED_service_connect (NULL, + current_peer->daemon, + "nse", + NULL, NULL, + &nse_connect_adapter, + &nse_disconnect_adapter, + current_peer); + current_peer->stats_op + = GNUNET_TESTBED_service_connect (NULL, + current_peer->daemon, + "statistics", + &stat_run, current_peer, + &statistics_connect_adapter, + &statistics_disconnect_adapter, + NULL); GNUNET_CONTAINER_DLL_insert (peer_head, peer_tail, current_peer); } } +/** + * Task that starts/stops peers to move to the next round. + * + * @param cls NULL, unused + * @param tc scheduler context (unused) + */ static void -churn_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +next_round (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); /** - * Continuation called by the "get_all" and "get" functions. + * Continuation called by the "get_all" and "get" functions at the + * end of a round. Obtains the final statistics and writes them to + * the file, then either starts the next round, or, if this was the + * last round, terminates the run. * * @param cls struct StatsContext - * @param success GNUNET_OK if statistics were - * successfully obtained, GNUNET_SYSERR if not. + * @param op operation handle + * @param emsg error message, NULL on success */ static void -stats_finished_callback (void *cls, int success) +stats_finished_callback (void *cls, + struct GNUNET_TESTBED_Operation *op, + const char *emsg) { struct StatsContext *stats_context = cls; - char *buf; - int buf_len; + char buf[512]; + size_t buf_len; - if ((GNUNET_OK == success) && (data_file != NULL)) - { - /* Stats lookup successful, write out data */ - buf = NULL; - buf_len = - GNUNET_asprintf (&buf, "TOTAL_NSE_RECEIVED_MESSAGES_%d: %u \n", - stats_context->shutdown, - stats_context->total_nse_received_messages); - if (buf_len > 0) + if (NULL != emsg) { - GNUNET_DISK_file_write (data_file, buf, buf_len); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to get statistics: %s\n", + emsg); + GNUNET_SCHEDULER_shutdown (); + GNUNET_free (stats_context); + return; } - GNUNET_free_non_null (buf); - - buf = NULL; - buf_len = - GNUNET_asprintf (&buf, "TOTAL_NSE_TRANSMITTED_MESSAGES_%d: %u\n", - stats_context->shutdown, - stats_context->total_nse_transmitted_messages); - if (buf_len > 0) + if (NULL != data_file) { + /* Stats lookup successful, write out data */ + buf_len = + GNUNET_snprintf (buf, sizeof (buf), + "TOTAL_NSE_RECEIVED_MESSAGES_%u: %u \n", + current_round, + stats_context->total_nse_received_messages); GNUNET_DISK_file_write (data_file, buf, buf_len); - } - GNUNET_free_non_null (buf); - - buf = NULL; - buf_len = - GNUNET_asprintf (&buf, "TOTAL_NSE_CROSS_%d: %u \n", - stats_context->shutdown, - stats_context->total_nse_cross); - if (buf_len > 0) - { + buf_len = + GNUNET_snprintf (buf, sizeof (buf), + "TOTAL_NSE_TRANSMITTED_MESSAGES_%u: %u\n", + current_round, + stats_context->total_nse_transmitted_messages); + GNUNET_DISK_file_write (data_file, buf, buf_len); + buf_len = + GNUNET_snprintf (buf, sizeof (buf), + "TOTAL_NSE_CROSS_%u: %u \n", + current_round, + stats_context->total_nse_cross); GNUNET_DISK_file_write (data_file, buf, buf_len); - } - GNUNET_free_non_null (buf); - - buf = NULL; - buf_len = - GNUNET_asprintf (&buf, "TOTAL_NSE_EXTRA_%d: %u \n", - stats_context->shutdown, - stats_context->total_nse_extra); - if (buf_len > 0) - { + buf_len = + GNUNET_snprintf (buf, sizeof (buf), + "TOTAL_NSE_EXTRA_%u: %u \n", + current_round, + stats_context->total_nse_extra); GNUNET_DISK_file_write (data_file, buf, buf_len); - } - GNUNET_free_non_null (buf); - - buf = NULL; - buf_len = - GNUNET_asprintf (&buf, "TOTAL_NSE_DISCARDED_%d: %u \n", - stats_context->shutdown, - stats_context->total_discarded); - if (buf_len > 0) - { - GNUNET_DISK_file_write (data_file, buf, buf_len); - } - GNUNET_free_non_null (buf); - - } - - if (GNUNET_YES == stats_context->shutdown) - { - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == shutdown_handle); - shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); - } - else - { - GNUNET_assert (churn_task == GNUNET_SCHEDULER_NO_TASK); - churn_task = GNUNET_SCHEDULER_add_now (&churn_peers, NULL); - } + buf_len = + GNUNET_snprintf (buf, sizeof (buf), + "TOTAL_NSE_DISCARDED_%u: %u \n", + current_round, + stats_context->total_discarded); + GNUNET_DISK_file_write (data_file, buf, buf_len); + } + GNUNET_SCHEDULER_add_now (&next_round, NULL); GNUNET_free (stats_context); } @@ -508,428 +686,366 @@ stats_finished_callback (void *cls, int success) * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration */ static int -statistics_iterator (void *cls, const struct GNUNET_PeerIdentity *peer, +statistics_iterator (void *cls, + const struct GNUNET_TESTBED_Peer *peer, const char *subsystem, const char *name, uint64_t value, int is_persistent) { struct StatsContext *stats_context = cls; + char buf[512]; + size_t buf_len; - if (0 == strcmp (subsystem, "nse")) - { - if (0 == strcmp (name, "# flood messages received")) + if (0 != strcmp (subsystem, "nse")) + return GNUNET_OK; + if (0 == strcmp (name, "# flood messages received")) { stats_context->total_nse_received_messages += value; -#if VERBOSE - if (data_file != NULL) - { - char *buf; - int buf_len; - - buf = NULL; - buf_len = - GNUNET_asprintf (&buf, "%s %u RECEIVED\n", GNUNET_i2s(peer), value); - if (buf_len > 0) - { - GNUNET_DISK_file_write (data_file, buf, buf_len); - } - GNUNET_free_non_null (buf); - } -#endif + if ( (verbose > 1) && + (NULL != data_file) ) + { + buf_len = + GNUNET_snprintf (buf, sizeof (buf), + "%p %u RECEIVED\n", + peer, value); + GNUNET_DISK_file_write (data_file, buf, buf_len); + } } - if (0 == strcmp (name, "# flood messages transmitted")) + if (0 == strcmp (name, "# flood messages transmitted")) { stats_context->total_nse_transmitted_messages += value; -#if VERBOSE - if (data_file != NULL) - { - char *buf; - int buf_len; - - buf = NULL; - buf_len = - GNUNET_asprintf (&buf, "%s %u TRANSMITTED\n", - GNUNET_i2s(peer), value); - if (buf_len > 0) - { - GNUNET_DISK_file_write (data_file, buf, buf_len); - } - GNUNET_free_non_null (buf); - } -#endif - } - if (0 == strcmp (name, "# cross messages")) - { - stats_context->total_nse_cross += value; - } - if (0 == strcmp (name, "# extra messages")) - { - stats_context->total_nse_extra += value; - } - if (0 == strcmp (name, "# flood messages discarded (clock skew too large)")) - { - stats_context->total_discarded += value; + if ( (verbose > 1) && + (NULL != data_file) ) + { + buf_len = + GNUNET_snprintf (buf, sizeof (buf), + "%p %u TRANSMITTED\n", + peer, value); + GNUNET_DISK_file_write (data_file, buf, buf_len); + } } - } + if (0 == strcmp (name, "# cross messages")) + stats_context->total_nse_cross += value; + if (0 == strcmp (name, "# extra messages")) + stats_context->total_nse_extra += value; + if (0 == strcmp (name, "# flood messages discarded (clock skew too large)")) + stats_context->total_discarded += value; return GNUNET_OK; } +/** + * Function called upon completion of the node start/stop operations + * for the current round. Writes the new topology to disk. + */ static void -disconnect_nse_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +write_topology () { - struct NSEPeer *pos; - char *buf; - struct StatsContext *stats_context; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnecting nse service of peers\n"); - disconnect_task = GNUNET_SCHEDULER_NO_TASK; - while (NULL != (pos = peer_head)) - { - if (pos->nse_handle != NULL) - { - GNUNET_NSE_disconnect (pos->nse_handle); - pos->nse_handle = NULL; - } - GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, pos); - if (NULL != pos->stats) - GNUNET_STATISTICS_destroy(pos->stats, GNUNET_NO); - if (GNUNET_SCHEDULER_NO_TASK != pos->stats_task) - GNUNET_SCHEDULER_cancel (pos->stats_task); - GNUNET_free (pos); - } + char temp_output_file[1024]; - GNUNET_asprintf (&buf, "round%llu", current_round); - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_number (testing_cfg, "nse-profiler", buf, - &peers_next_round)) - { - current_round++; - if (current_round == 1) - { - stats_context = GNUNET_malloc (sizeof (struct StatsContext)); - stats_context->shutdown = GNUNET_NO; - GNUNET_TESTING_get_statistics (pg, &stats_finished_callback, - &statistics_iterator, stats_context); - } - else + if (NULL != topology_file) { - GNUNET_assert (churn_task == GNUNET_SCHEDULER_NO_TASK); - churn_task = GNUNET_SCHEDULER_add_now (&churn_peers, NULL); + GNUNET_snprintf (temp_output_file, sizeof (temp_output_file), + "%s_%llu.dot", + topology_file, current_round); + GNUNET_TESTBED_overlay_write_topology_to_file (controller, + temp_output_file); } - } - else /* No more rounds, let's shut it down! */ - { - stats_context = GNUNET_malloc (sizeof (struct StatsContext)); - stats_context->shutdown = GNUNET_YES; - GNUNET_SCHEDULER_cancel (shutdown_handle); - shutdown_handle = GNUNET_SCHEDULER_NO_TASK; - GNUNET_TESTING_get_statistics (pg, &stats_finished_callback, - &statistics_iterator, stats_context); - } - GNUNET_free (buf); } /** - * FIXME. + * We're at the end of a round. Stop monitoring, write total + * number of connections to log and get full stats. Then trigger + * the next round. * - * @param cls unused - * @param emsg NULL on success + * @param cls unused, NULL + * @param tc unused */ static void -topology_output_callback (void *cls, const char *emsg) +finish_round (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - disconnect_task = - GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_nse_peers, NULL); - GNUNET_SCHEDULER_add_now (&connect_nse_service, NULL); + struct StatsContext *stats_context; + char buf[1024]; + size_t buf_len; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Have %u connections\n", + total_connections); + if (NULL != data_file) + { + buf_len = GNUNET_snprintf (buf, sizeof (buf), + "CONNECTIONS_0: %u\n", + total_connections); + GNUNET_DISK_file_write (data_file, buf, buf_len); + } + close_monitor_connections (); + stats_context = GNUNET_malloc (sizeof (struct StatsContext)); + GNUNET_TESTBED_get_statistics (num_peers_in_round[current_round], + daemons, + &statistics_iterator, + &stats_finished_callback, + stats_context); } /** - * FIXME. - * - * @param cls closure - * @param emsg NULL on success + * We have reached the desired number of peers for the current round. + * Run it (by connecting and monitoring a few peers and waiting the + * specified delay before finishing the round). */ static void -churn_callback (void *cls, const char *emsg) +run_round () { - char *temp_output_file; - - if (emsg == NULL) /* Everything is okay! */ - { - peers_running = peers_next_round; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Round %llu, churn finished successfully.\n", current_round); - GNUNET_assert (disconnect_task == GNUNET_SCHEDULER_NO_TASK); - GNUNET_asprintf (&temp_output_file, "%s_%llu.dot", topology_file, - current_round); - GNUNET_TESTING_peergroup_topology_to_file (pg, temp_output_file, - &topology_output_callback, NULL); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Writing topology to file %s\n", - temp_output_file); - GNUNET_free (temp_output_file); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Round %llu, churn FAILED!!\n", - current_round); - GNUNET_SCHEDULER_cancel (shutdown_handle); - shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); - } + write_topology (); + connect_nse_service (); + GNUNET_SCHEDULER_add_delayed (wait_time, + &finish_round, + NULL); } +/** + * Task run at the end of a round. Disconnect from all monitored + * peers; then get statistics from *all* peers. + * + * @param cls NULL, unused + * @param tc unused + */ static void -churn_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +next_round (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - /* peers_running = GNUNET_TESTING_daemons_running(pg); */ - churn_task = GNUNET_SCHEDULER_NO_TASK; - if (peers_next_round == peers_running) - { - /* Nothing to do... */ - GNUNET_SCHEDULER_add_now (&connect_nse_service, NULL); - GNUNET_assert (disconnect_task == GNUNET_SCHEDULER_NO_TASK); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_nse_peers, NULL); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Round %lu, doing nothing!\n", - current_round); - } - else - { - if (peers_next_round > num_peers) + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnecting nse service of peers\n"); + current_round++; + + if (current_round == num_rounds) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Asked to turn on more peers than we have!!\n"); - GNUNET_SCHEDULER_cancel (shutdown_handle); - GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); + /* this was the last round, terminate */ + GNUNET_SCHEDULER_shutdown (); + return; } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Round %llu, turning off %llu peers, turning on %llu peers!\n", - current_round, - (peers_running > - peers_next_round) ? peers_running - peers_next_round : 0, - (peers_next_round > - peers_running) ? peers_next_round - peers_running : 0); - GNUNET_TESTING_daemons_churn (pg, "nse", - (peers_running > - peers_next_round) ? peers_running - - peers_next_round : 0, - (peers_next_round > - peers_running) ? peers_next_round - - peers_running : 0, wait_time, &churn_callback, - NULL); - } + if (num_peers_in_round[current_round] == peers_running) + { + /* no need to churn, just run next round */ + run_round (); + return; + } + + /* start peers if we have too few */ + for (i=peers_running;i<num_peers_in_round[current_round];i++) + GNUNET_TESTBED_peer_start (NULL, daemons[i], NULL, NULL); + + /* stop peers if we have too many */ + for (i=num_peers_in_round[current_round];i<peers_running;i++) + GNUNET_TESTBED_peer_stop (daemons[i], NULL, NULL); } +/** + * Function that will be called whenever something in the + * testbed changes. + * + * @param cls closure, NULL + * @param event information on what is happening + */ static void -nse_started_cb (void *cls, const char *emsg) +master_controller_cb (void *cls, + const struct GNUNET_TESTBED_EventInformation *event) { - GNUNET_SCHEDULER_add_now (&connect_nse_service, NULL); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_nse_peers, NULL); + switch (event->type) + { + case GNUNET_TESTBED_ET_PEER_START: + peers_running++; + if (num_peers_in_round[current_round] == peers_running) + run_round (); + break; + case GNUNET_TESTBED_ET_PEER_STOP: + peers_running--; + if (num_peers_in_round[current_round] == peers_running) + run_round (); + break; + case GNUNET_TESTBED_ET_CONNECT: + total_connections++; + break; + case GNUNET_TESTBED_ET_DISCONNECT: + total_connections--; + break; + default: + break; + } } static void -my_cb (void *cls, const char *emsg) +controller_start_cb (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + int status) { - char *buf; - int buf_len; - - if (emsg != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peergroup callback called with error, aborting test!\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error from testing: `%s'\n"); - ok = 1; - GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); - return; - } -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peer Group started successfully, connecting to NSE service for each peer!\n"); -#endif - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Have %u connections\n", - total_connections); - if (data_file != NULL) - { - buf = NULL; - buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections); - if (buf_len > 0) - GNUNET_DISK_file_write (data_file, buf, buf_len); - GNUNET_free (buf); - } - peers_running = GNUNET_TESTING_daemons_running (pg); - GNUNET_TESTING_daemons_start_service (pg, "nse", wait_time, &nse_started_cb, - NULL); - + if (GNUNET_OK != status) + { + copro = NULL; + GNUNET_SCHEDULER_shutdown (); + return; + } + num_hosts = GNUNET_TESTBED_hosts_load_from_file (hosts_file, + &hosts); + if (0 == num_hosts) + { + fprintf (stderr, + "Failed to read host information from `%s'\n", + hosts_file); + return; + } + controller = GNUNET_TESTBED_controller_connect (cfg, + NULL, + 0 /* mask */, + &master_controller_cb, NULL); + + /* testbed = GNUNET_TESTBED_create (controller, */ + /* num_hosts, hosts, */ + /* num_peers, */ + /* cfg, */ + /* 0 /\* FIXME: topology *\/, */ + /* NULL /\* FIXME: topology options *\/); */ } /** - * Function that will be called whenever two daemons are connected by - * the testing library. + * Actual main function that runs the emulation. * - * @param cls closure - * @param first peer id for first daemon - * @param second peer id for the second daemon - * @param distance distance between the connected peers - * @param first_cfg config for the first daemon - * @param second_cfg config for the second daemon - * @param first_daemon handle for the first daemon - * @param second_daemon handle for the second daemon - * @param emsg error message (NULL on success) + * @param cls unused + * @param args remaining args, unused + * @param cfgfile name of the configuration + * @param cfg configuration handle */ static void -connect_cb (void *cls, const struct GNUNET_PeerIdentity *first, - const struct GNUNET_PeerIdentity *second, uint32_t distance, - const struct GNUNET_CONFIGURATION_Handle *first_cfg, - const struct GNUNET_CONFIGURATION_Handle *second_cfg, - struct GNUNET_TESTING_Daemon *first_daemon, - struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg) -{ - if (emsg == NULL) - total_connections++; -} - - -static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { - char *temp_str; - struct GNUNET_TESTING_Host *hosts; - char *data_filename; + char *tok; + unsigned int num; ok = 1; - //testing_cfg = GNUNET_CONFIGURATION_create (); testing_cfg = GNUNET_CONFIGURATION_dup (cfg); -#if VERBOSE GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting daemons.\n"); - GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing", - "use_progressbars", "YES"); -#endif - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing", - "num_peers", &num_peers)) + if (verbose) + GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing", + "use_progressbars", "YES"); + if (NULL == num_peer_spec) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option TESTING:NUM_PEERS is required!\n"); + fprintf (stderr, "You need to specify the number of peers to run\n"); return; } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (testing_cfg, "nse-profiler", - "WAIT_TIME", &wait_time)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option nse-profiler:wait_time is required!\n"); - return; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (testing_cfg, "nse", - "INTERVAL", &interval)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option nse:interval is required!\n"); - return; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (testing_cfg, "nse-profiler", - "connection_limit", - &connection_limit)) - { - connection_limit = 0; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (testing_cfg, "nse-profiler", - "topology_output_file", - &topology_file)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option nse-profiler:topology_output_file is required!\n"); - return; - } - - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (testing_cfg, "nse-profiler", - "data_output_file", - &data_filename)) - { - data_file = - GNUNET_DISK_file_open (data_filename, - GNUNET_DISK_OPEN_READWRITE | - GNUNET_DISK_OPEN_TRUNCATE | - GNUNET_DISK_OPEN_CREATE, - GNUNET_DISK_PERM_USER_READ | - GNUNET_DISK_PERM_USER_WRITE); - if (data_file == NULL) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", - data_filename); - GNUNET_free (data_filename); - } - - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_string (cfg, "nse-profiler", "output_file", - &temp_str)) - { - output_file = - GNUNET_DISK_file_open (temp_str, - GNUNET_DISK_OPEN_READWRITE | - GNUNET_DISK_OPEN_CREATE, - GNUNET_DISK_PERM_USER_READ | - GNUNET_DISK_PERM_USER_WRITE); - if (output_file == NULL) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", - temp_str); - } - GNUNET_free_non_null (temp_str); - - hosts = GNUNET_TESTING_hosts_load (testing_cfg); - - pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT, - &connect_cb, &my_cb, NULL, hosts); - GNUNET_assert (pg != NULL); - shutdown_handle = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, - &shutdown_task, NULL); + for (tok = strtok (num_peer_spec, ","); NULL != tok; tok = strtok (NULL, ",")) + { + if (1 != sscanf (tok, "%u", &num)) + { + fprintf (stderr, "You need to specify numbers, not `%s'\n", tok); + return; + } + if (0 == num) + { + fprintf (stderr, "Refusing to run a round with 0 peers\n"); + return; + } + GNUNET_array_grow (num_peers_in_round, num_rounds, num); + num_peers = GNUNET_MAX (num_peers, num); + } + if (0 == num_peers) + { + fprintf (stderr, "Refusing to run a testbed with no rounds\n"); + return; + } + daemons = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Peer*) * num_peers); + if ( (NULL != data_filename) && + (NULL == (data_file = + GNUNET_DISK_file_open (data_filename, + GNUNET_DISK_OPEN_READWRITE | + GNUNET_DISK_OPEN_TRUNCATE | + GNUNET_DISK_OPEN_CREATE, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE))) ) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + data_filename); + + if ( (NULL != output_filename) && + (NULL == (output_file = + GNUNET_DISK_file_open (output_filename, + GNUNET_DISK_OPEN_READWRITE | + GNUNET_DISK_OPEN_CREATE, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE))) ) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", + output_filename); + + if (NULL == + (copro = GNUNET_TESTBED_controller_start (controller_ip, NULL, + cfg, + &controller_start_cb, NULL))) + { + fprintf (stderr, + "Failed to start controller\n"); + return; + } + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, NULL); } - /** - * nse-profiler command line options + * Main function. + * + * @return 0 on success */ -static struct GNUNET_GETOPT_CommandLineOption options[] = { - {'V', "verbose", NULL, - gettext_noop ("be verbose (print progress information)"), - 0, &GNUNET_GETOPT_set_one, &verbose}, - GNUNET_GETOPT_OPTION_END -}; - - int -main (int argc, char *argv[]) +main (int argc, char *const *argv) { - GNUNET_log_setup ("nse-profiler", -#if VERBOSE - "DEBUG", -#else - "WARNING", -#endif - NULL); - GNUNET_PROGRAM_run (argc, argv, "nse-profiler", - gettext_noop - ("Measure quality and performance of the NSE service."), - options, &run, NULL); -#if REMOVE_DIR - GNUNET_DISK_directory_remove ("/tmp/nse-profiler"); -#endif + static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'C', "connections", "COUNT", + gettext_noop ("limit to the number of connections to NSE services, 0 for none"), + 1, &GNUNET_GETOPT_set_string, &num_peer_spec}, + {'d', "details", "FILENAME", + gettext_noop ("name of the file for writing connection information and statistics"), + 1, &GNUNET_GETOPT_set_string, &data_filename}, + {'H', "hosts", "FILENAME", + gettext_noop ("name of the file with the login information for the testbed"), + 1, &GNUNET_GETOPT_set_string, &hosts_file}, + {'i', "ip", "CONTROLLER_IP", + gettext_noop ("IP address of this system as seen by the rest of the testbed"), + 1, &GNUNET_GETOPT_set_string, &controller_ip}, + {'I', "interval", "DELAY", + gettext_noop ("delay between queries to statistics during a round"), + 1, &GNUNET_GETOPT_set_relative_time, &interval}, + {'t', "topology", "FILENAME", + gettext_noop ("prefix of the filenames we use for writing the topology for each round"), + 1, &GNUNET_GETOPT_set_string, &topology_file}, + {'o', "output", "FILENAME", + gettext_noop ("name of the file for writing the main results"), + 1, &GNUNET_GETOPT_set_string, &output_filename}, + {'p', "peers", "NETWORKSIZESPEC", + gettext_noop ("Number of peers to run in each round, separated by commas"), + 1, &GNUNET_GETOPT_set_string, &num_peer_spec}, + {'V', "verbose", NULL, + gettext_noop ("be verbose (print progress information)"), + 0, &GNUNET_GETOPT_increment_value, &verbose}, + {'w', "wait", "DELAY", + gettext_noop ("delay between rounds"), + 1, &GNUNET_GETOPT_set_relative_time, &wait_time}, + GNUNET_GETOPT_OPTION_END + }; + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + GNUNET_log_setup ("nse-profiler", "WARNING", NULL); + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, "nse-profiler", + gettext_noop + ("Measure quality and performance of the NSE service."), + options, &run, NULL)) + ok = 1; return ok; } |