2 This file is part of GNUnet.
3 Copyright (C) 2011, 2012 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
18 * @file regex/gnunet-regex-simulation-profiler.c
19 * @brief Regex profiler that dumps all DFAs into a database instead of
20 * using the DHT (with cadet).
21 * @author Maximilian Szengel
22 * @author Christophe Genevey
27 #include "gnunet_util_lib.h"
28 #include "regex_internal_lib.h"
29 #include "gnunet_mysql_lib.h"
30 #include "gnunet_my_lib.h"
31 #include <mysql/mysql.h>
34 * MySQL statement to insert an edge.
36 #define INSERT_EDGE_STMT "INSERT IGNORE INTO `%s` "\
37 "(`key`, `label`, `to_key`, `accepting`) "\
38 "VALUES (?, ?, ?, ?);"
41 * MySQL statement to select a key count.
43 #define SELECT_KEY_STMT "SELECT COUNT(*) FROM `%s` "\
44 "WHERE `key` = ? AND `label` = ?;"
47 * Simple struct to keep track of progress, and print a
48 * nice little percentage meter for long running tasks.
53 * Total number of elements.
58 * Intervall for printing percentage.
63 * Number of dots to print.
70 unsigned int completed;
73 * Should the meter be printed?
78 * String to print on startup.
85 * Handle for the progress meter
87 static struct ProgressMeter *meter;
90 * Scan task identifier;
92 static struct GNUNET_SCHEDULER_Task *scan_task;
95 * Global testing status.
102 static struct GNUNET_MYSQL_Context *mysql_ctx;
105 * MySQL prepared statement handle.
107 static struct GNUNET_MYSQL_StatementHandle *stmt_handle;
110 * MySQL prepared statement handle for `key` select.
112 static struct GNUNET_MYSQL_StatementHandle *select_stmt_handle;
117 static char *table_name;
120 * Policy dir containing files that contain policies.
122 static char *policy_dir;
125 * Number of policy files.
127 static unsigned int num_policy_files;
130 * Number of policies.
132 static unsigned int num_policies;
135 * Maximal path compression length.
137 static unsigned int max_path_compression;
140 * Number of merged transitions.
142 static unsigned long long num_merged_transitions;
145 * Number of merged states from different policies.
147 static unsigned long long num_merged_states;
150 * Prefix to add before every regex we're announcing.
152 static char *regex_prefix;
156 * Create a meter to keep track of the progress of some task.
158 * @param total the total number of items to complete
159 * @param start_string a string to prefix the meter with (if printing)
160 * @param print GNUNET_YES to print the meter, GNUNET_NO to count
163 * @return the progress meter
165 static struct ProgressMeter *
166 create_meter (unsigned int total, char *start_string, int print)
168 struct ProgressMeter *ret;
170 ret = GNUNET_new (struct ProgressMeter);
173 ret->modnum = total / 4;
174 if (ret->modnum == 0) /* Divide by zero check */
176 ret->dotnum = (total / 50) + 1;
177 if (start_string != NULL)
178 ret->startup_string = GNUNET_strdup (start_string);
180 ret->startup_string = GNUNET_strdup ("");
187 * Update progress meter (increment by one).
189 * @param meter the meter to update and print info for
191 * @return GNUNET_YES if called the total requested,
192 * GNUNET_NO if more items expected
195 update_meter (struct ProgressMeter *meter)
197 if (meter->print == GNUNET_YES)
199 if (meter->completed % meter->modnum == 0)
201 if (meter->completed == 0)
203 FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string);
206 FPRINTF (stdout, "%d%%",
207 (int) (((float) meter->completed / meter->total) * 100));
209 else if (meter->completed % meter->dotnum == 0)
210 FPRINTF (stdout, "%s", ".");
212 if (meter->completed + 1 == meter->total)
213 FPRINTF (stdout, "%d%%]\n", 100);
218 if (meter->completed == meter->total)
220 if (meter->completed > meter->total)
221 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n");
227 * Reset progress meter.
229 * @param meter the meter to reset
231 * @return #GNUNET_YES if meter reset,
232 * #GNUNET_SYSERR on error
235 reset_meter (struct ProgressMeter *meter)
238 return GNUNET_SYSERR;
240 meter->completed = 0;
246 * Release resources for meter
248 * @param meter the meter to free
251 free_meter (struct ProgressMeter *meter)
253 GNUNET_free_non_null (meter->startup_string);
264 do_shutdown (void *cls)
266 if (NULL != mysql_ctx)
268 GNUNET_MYSQL_context_destroy (mysql_ctx);
280 * Abort task to run on test timed out.
282 * FIXME: this doesn't actually work, it used to cancel
283 * the already running 'scan_task', but now that should
284 * always be NULL and do nothing. We instead need to set
285 * a global variable and abort scan_task internally, not
293 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
294 if (NULL != scan_task)
296 GNUNET_SCHEDULER_cancel (scan_task);
299 result = GNUNET_SYSERR;
300 GNUNET_SCHEDULER_shutdown ();
304 * Iterator over all states that inserts each state into the MySQL db.
306 * @param cls closure.
307 * @param key hash for current state.
308 * @param proof proof for current state.
309 * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not.
310 * @param num_edges number of edges leaving current state.
311 * @param edges edges leaving current state.
314 regex_iterator (void *cls,
315 const struct GNUNET_HashCode *key,
318 unsigned int num_edges,
319 const struct REGEX_BLOCK_Edge *edges)
324 uint32_t iaccepting = (uint32_t)accepting;
327 GNUNET_assert (NULL != mysql_ctx);
329 for (i = 0; i < num_edges; i++)
331 struct GNUNET_MY_QueryParam params_select[] = {
332 GNUNET_MY_query_param_auto_from_type (key),
333 GNUNET_MY_query_param_string (edges[i].label),
334 GNUNET_MY_query_param_end
337 struct GNUNET_MY_ResultSpec results_select[] = {
338 GNUNET_MY_result_spec_uint64 (&total),
339 GNUNET_MY_result_spec_end
343 GNUNET_MY_exec_prepared (mysql_ctx,
347 if (GNUNET_SYSERR == result)
349 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
350 "Error executing prepared mysql select statement\n");
351 GNUNET_SCHEDULER_add_now (&do_abort, NULL);
356 GNUNET_MY_extract_result (select_stmt_handle,
359 if (GNUNET_SYSERR == result)
361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362 "Error extracting result mysql select statement\n");
363 GNUNET_SCHEDULER_add_now (&do_abort, NULL);
367 if (-1 != total && total > 0)
369 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total: %llu (%s, %s)\n",
370 (unsigned long long)total,
371 GNUNET_h2s (key), edges[i].label);
374 struct GNUNET_MY_QueryParam params_stmt[] = {
375 GNUNET_MY_query_param_auto_from_type (&key),
376 GNUNET_MY_query_param_string (edges[i].label),
377 GNUNET_MY_query_param_auto_from_type (&edges[i].destination),
378 GNUNET_MY_query_param_uint32 (&iaccepting),
379 GNUNET_MY_query_param_end
383 GNUNET_MY_exec_prepared (mysql_ctx,
389 char *key_str = GNUNET_strdup (GNUNET_h2s (key));
390 char *to_key_str = GNUNET_strdup (GNUNET_h2s (&edges[i].destination));
392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Merged (%s, %s, %s, %i)\n",
398 GNUNET_free (key_str);
399 GNUNET_free (to_key_str);
400 num_merged_transitions++;
402 else if (-1 != total)
407 if (GNUNET_SYSERR == result || (1 != result && 0 != result))
409 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
410 "Error executing prepared mysql statement for edge: Affected rows: %i, expected 0 or 1!\n",
412 GNUNET_SCHEDULER_add_now (&do_abort, NULL);
418 struct GNUNET_MY_QueryParam params_stmt[] = {
419 GNUNET_MY_query_param_auto_from_type (key),
420 GNUNET_MY_query_param_string (""),
421 GNUNET_MY_query_param_fixed_size (NULL, 0),
422 GNUNET_MY_query_param_uint32 (&iaccepting),
423 GNUNET_MY_query_param_end
427 GNUNET_MY_exec_prepared (mysql_ctx,
431 if (1 != result && 0 != result)
433 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
434 "Error executing prepared mysql statement for edge: Affected rows: %i, expected 0 or 1!\n",
436 GNUNET_SCHEDULER_add_now (&do_abort, NULL);
443 * Announce a regex by creating the DFA and iterating over each state, inserting
444 * each state into a MySQL database.
446 * @param regex regular expression.
447 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure.
450 announce_regex (const char *regex)
452 struct REGEX_INTERNAL_Automaton *dfa;
455 REGEX_INTERNAL_construct_dfa (regex,
457 max_path_compression);
461 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
462 "Failed to create DFA for regex %s\n",
464 GNUNET_SCHEDULER_add_now (&do_abort, NULL);
465 return GNUNET_SYSERR;
467 REGEX_INTERNAL_iterate_all_edges (dfa,
468 ®ex_iterator, NULL);
469 REGEX_INTERNAL_automaton_destroy (dfa);
476 * Function called with a filename.
479 * @param filename complete filename (absolute path)
480 * @return #GNUNET_OK to continue to iterate,
481 * #GNUNET_SYSERR to abort iteration with error!
484 policy_filename_cb (void *cls, const char *filename)
492 GNUNET_assert (NULL != filename);
494 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
495 "Announcing regexes from file %s\n",
498 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
500 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
501 "Could not find policy file %s\n",
506 GNUNET_DISK_file_size (filename, &filesize,
507 GNUNET_YES, GNUNET_YES))
511 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Policy file %s is empty.\n",
515 data = GNUNET_malloc (filesize);
516 if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
519 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
520 "Could not read policy file %s.\n",
525 update_meter (meter);
530 while (offset < (filesize - 1))
533 if (((data[offset] == '\n')) && (buf != &data[offset]))
537 buf = &data[offset + 1];
539 else if ((data[offset] == '\n') || (data[offset] == '\0'))
540 buf = &data[offset + 1];
543 GNUNET_asprintf (®ex, "%s(%s)", regex_prefix, data);
544 GNUNET_assert (NULL != regex);
545 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
546 "Announcing regex: %s\n", regex);
548 if (GNUNET_OK != announce_regex (regex))
550 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
551 "Could not announce regex %s\n",
561 * Iterate over files contained in policy_dir.
566 do_directory_scan (void *cls)
568 struct GNUNET_TIME_Absolute start_time;
569 struct GNUNET_TIME_Relative duration;
572 /* Create an MySQL prepared statement for the inserts */
574 GNUNET_asprintf (&stmt, INSERT_EDGE_STMT, table_name);
575 stmt_handle = GNUNET_MYSQL_statement_prepare (mysql_ctx, stmt);
578 GNUNET_asprintf (&stmt, SELECT_KEY_STMT, table_name);
579 select_stmt_handle = GNUNET_MYSQL_statement_prepare (mysql_ctx, stmt);
582 GNUNET_assert (NULL != stmt_handle);
584 meter = create_meter (num_policy_files,
585 "Announcing policy files\n",
587 start_time = GNUNET_TIME_absolute_get ();
588 GNUNET_DISK_directory_scan (policy_dir,
591 duration = GNUNET_TIME_absolute_get_duration (start_time);
596 printf ("Announced %u files containing %u policies in %s\n"
597 "Duplicate transitions: %llu\nMerged states: %llu\n",
600 GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_NO),
601 num_merged_transitions,
604 GNUNET_SCHEDULER_shutdown ();
609 * Main function that will be run by the scheduler.
612 * @param args remaining command-line arguments
613 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
614 * @param config configuration
620 const struct GNUNET_CONFIGURATION_Handle *config)
625 _("No policy directory specified on command line. Exiting.\n"));
626 result = GNUNET_SYSERR;
630 GNUNET_DISK_directory_test (args[0], GNUNET_YES))
633 _("Specified policies directory does not exist. Exiting.\n"));
634 result = GNUNET_SYSERR;
637 policy_dir = args[0];
639 num_policy_files = GNUNET_DISK_directory_scan (policy_dir,
643 if (NULL == table_name)
645 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
646 "No table name specified, using default \"NFA\".\n");
650 mysql_ctx = GNUNET_MYSQL_context_create (config, "regex-mysql");
651 if (NULL == mysql_ctx)
653 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
654 "Failed to create mysql context\n");
655 result = GNUNET_SYSERR;
660 GNUNET_CONFIGURATION_get_value_string (config,
665 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
668 result = GNUNET_SYSERR;
673 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
675 scan_task = GNUNET_SCHEDULER_add_now (&do_directory_scan, NULL);
682 * @param argc argument count
683 * @param argv argument values
684 * @return 0 on success
687 main (int argc, char *const *argv)
689 struct GNUNET_GETOPT_CommandLineOption options[] = {
691 GNUNET_GETOPT_option_string ('t',
694 gettext_noop ("name of the table to write DFAs"),
697 GNUNET_GETOPT_option_uint ('p',
698 "max-path-compression",
699 "MAX_PATH_COMPRESSION",
700 gettext_noop ("maximum path compression length"),
701 &max_path_compression),
703 GNUNET_GETOPT_OPTION_END
707 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
710 result = GNUNET_SYSERR;
712 GNUNET_PROGRAM_run (argc, argv,
713 "gnunet-regex-simulationprofiler [OPTIONS] policy-dir",
714 _("Profiler for regex library"), options, &run, NULL);
715 if (GNUNET_OK != ret)
717 if (GNUNET_OK != result)