/* Monitors the time annocheck takes running its tools. Copyright (c) 2018 - 2019 Red Hat. This is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; see the file COPYING3. If not, see . It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "annobin-global.h" #include "annocheck.h" #include static bool disabled = true; static unsigned int num_files; static unsigned long long scan_time; static clockid_t clk_id; static struct timespec start_time; static struct timespec section_time; static struct timespec segment_time; static bool first_sec; static bool first_seg; static enum res { SEC, USEC, NSEC } resolution = USEC; static bool timing_start_file (annocheck_data * data) { if (! disabled && clock_gettime (clk_id, & start_time) != 0) { einfo (SYS_WARN, "unable to get time at start of file processing"); disabled = true; return false; } first_sec = true; first_seg = true; return true; } static bool timing_interesting_sec (annocheck_data * data, annocheck_section * sec) { if (disabled || !first_sec) return false; first_sec = false; if (clock_gettime (clk_id, & section_time) != 0) einfo (SYS_WARN, "unable to get time at start of section scan"); /* We do not need any more information from the section, so there is no need to run the checker. */ return false; } static bool timing_interesting_seg (annocheck_data * data, annocheck_segment * seg) { if (!disabled && first_seg) { if (clock_gettime (clk_id, & segment_time) != 0) einfo (SYS_WARN, "unable to get time at start of segment scan"); first_seg = false; } /* We do not need any more information from the segment, so there is no need to run the checker. */ return false; } static unsigned long long int time_diff (struct timespec * end, struct timespec * start) { /* FIXME: Check for end < start ? */ if (end->tv_nsec >= start->tv_nsec) return ((end->tv_sec - start->tv_sec) * 1000000) + (end->tv_nsec - start->tv_nsec); else return ((end->tv_sec - start->tv_sec) * 1000000) - (end->tv_nsec - start->tv_nsec); } static const char * time_print (unsigned long long t) { static char buffer[64]; switch (resolution) { case SEC: sprintf (buffer, "%llu seconds", t / (1000 * 1000)); break; case USEC: sprintf (buffer, "%llu microseconds", t / 1000); break; case NSEC: sprintf (buffer, "%llu nanoseconds", t); break; } return buffer; } static bool timing_end_file (annocheck_data * data) { if (disabled) return true; struct timespec end_time; if (clock_gettime (clk_id, & end_time) != 0) { einfo (SYS_WARN, "unable to get time at end of file scan"); return false; } einfo (INFO, "%s: total time %s, ", data->filename, time_print (time_diff (& end_time, & start_time))); einfo (PARTIAL, "section scan: %s, ", time_print (time_diff (& segment_time, & section_time))); einfo (PARTIAL, "segment scan: %s ", time_print (time_diff (& end_time, & segment_time))); einfo (PARTIAL, "\n"); num_files ++; scan_time += time_diff (& end_time, & start_time); return true; } /* This function is needed so that a data transfer file will be created. */ static void timing_start_scan (uint level, const char * datafile) { num_files = 0; scan_time = 0; clk_id = CLOCK_REALTIME; if (0) ; #ifdef CLOCK_MONOTONIC else if (clock_getres (CLOCK_MONOTONIC, NULL) == 0) clk_id = CLOCK_MONOTONIC; #endif #ifdef CLOCK_PROCESS_CPUTIME_ID else if (clock_getres (CLOCK_PROCESS_CPUTIME_ID, NULL) == 0) clk_id = CLOCK_PROCESS_CPUTIME_ID; #endif /* FIXME: Try other clocks ? */ } static void timing_end_scan (uint level, const char * datafile) { if (disabled) return; FILE * f = fopen (datafile, "r"); if (f != NULL) { unsigned int num = 0; unsigned long long time_taken = 0; einfo (VERBOSE2, "Loading recursed timing data from %s", datafile); if (fscanf (f, "%x %llx\n", & num, & time_taken) != 2) einfo (WARN, "unable to parse the contents of %s", datafile); num_files += num; scan_time += time_taken; fclose (f); } if (level == 0) { einfo (INFO, "%u files processed in %s", num_files, time_print (scan_time)); einfo (VERBOSE2, "Deleting data file %s", datafile); unlink (datafile); } else { einfo (VERBOSE2, "Storing size data in %s", datafile); /* Write the accumulated sizes into the file. */ FILE * f = fopen (datafile, "w"); if (f == NULL) { einfo (WARN, "unable to open datafile %s", datafile); return; } fprintf (f, "%x %llx\n", num_files, scan_time); fclose (f); } } static bool timing_process_arg (const char * arg, const char ** argv, const uint argc, uint * next) { if (streq (arg, "--enable-timing")) disabled = false; else if (streq (arg, "--disable-timing")) disabled = true; else if (streq (arg, "--nsec")) resolution = NSEC; else if (streq (arg, "--usec")) resolution = USEC; else if (streq (arg, "--sec")) resolution = SEC; else return false; return true; } static void timing_usage (void) { einfo (INFO, "Reports the time annocheck's tool take to work"); einfo (INFO, " NOTE: This tool is disabled by default. To enable it use: --enable-timing"); einfo (INFO, " Use --disable-timing to restore the default behaviour"); einfo (INFO, " The resolution of the times reported can be set by the following options:"); } static void timing_version (void) { einfo (INFO, "Version 1.0"); } struct checker timing_checker = { "Timing", timing_start_file, timing_interesting_sec, NULL, /* check_sec */ timing_interesting_seg, NULL, /* check_seg */ timing_end_file, timing_process_arg, timing_usage, timing_version, timing_start_scan, timing_end_scan, NULL /* internal */ }; static __attribute__((constructor)) void timing_register_checker (void) { if (! annocheck_add_checker (& timing_checker, ANNOBIN_VERSION / 100)) disabled = true; }