/** * \brief Periodically prints statistics about proccessing speed * * @param config output manager's configuration * @return 0; */ static void *statistics_thread(void* config) { struct output_manager_config *conf = (struct output_manager_config *) config; time_t begin = time(NULL), time_now, diff_time; uint64_t pkts, last_pkts, records, last_records, diff_pkts, diff_records; pkts = last_pkts = records = last_records = diff_pkts = diff_records = 0; uint64_t lost_records, last_lost_records, diff_lost_records; lost_records = last_lost_records = diff_lost_records = 0; /* create statistics config */ conf->stats.total_cpu = 0; conf->stats.threads = NULL; conf->stats.cpus = sysconf(_SC_NPROCESSORS_ONLN); snprintf(conf->stats.tasks_dir, MAX_DIR_LEN, "/proc/%d/task/", getpid()); // Create file handle in case statistics file has been specified in config FILE *stat_out_file = NULL; xmlNode *node = conf->plugins_config->collector_node; while (node != NULL) { // Skip processing this node in case it's a comment if (node->type == XML_COMMENT_NODE) { node = node->next; continue; } // Jump to collectingProcess tree, since 'statisticsFile' belongs to it (if present) if (xmlStrcmp(node->name, (const xmlChar *) "collectingProcess") == 0) { node = node->xmlChildrenNode; } if (xmlStrcmp(node->name, (const xmlChar *) "statisticsFile") == 0) { char *stat_out_file_path = (char *) xmlNodeGetContent(node->xmlChildrenNode); if (stat_out_file_path && strlen(stat_out_file_path) > 0) { /* Consists of the following elements: * strlen(stat_out_file_path) - length of supplied path * 1 - dot (.) * 5 - we assume that a PID has a maximum length of 5 digits * 1 - null-terminating character */ int max_len = strlen(stat_out_file_path) + 1 + 5 + 1; char buf[max_len]; // snprintf ensures null-termination if (max_len != 0), which is always true snprintf(buf, max_len, "%s.%d", stat_out_file_path, getpid()); stat_out_file = fopen(buf, "w"); } else { MSG_ERROR(msg_module, "Configuration error: 'statisticsFile' node has no value"); } xmlFree(stat_out_file_path); // No need to continue tree traversal, because 'statisticsFile' is only node to look for break; } node = node->next; } /* Catch SIGUSR1 */ signal(SIGUSR1, sig_handler); /* set thread name */ prctl(PR_SET_NAME, "ipfixcol:stats", 0, 0, 0); while (conf->stat_interval) { sleep(conf->stat_interval); /* killed by output manager*/ if (conf->stats.done) { break; } /* Compute time */ time_now = time(NULL); diff_time = time_now - begin; /* Update and save packets counter */ pkts = conf->packets; diff_pkts = pkts - last_pkts; last_pkts = pkts; /* Update and save data records counter */ records = conf->data_records; diff_records = records - last_records; last_records = records; /* Collect lost data record counts from Data Managers */ lost_records = conf->lost_data_records; diff_lost_records = lost_records - last_lost_records; last_lost_records = lost_records; /* print info */ MSG_INFO(stat_module, "Time: %lu", time_now); MSG_INFO(stat_module, "%15s %15s %15s %15s %15s %15s %20s", "total time", "total packets", "tot. data rec.", "lost data rec.", "packets/s", "data records/s", "lost data records/s"); MSG_INFO(stat_module, "%15lu %15lu %15lu %15lu %15lu %15lu %20lu", diff_time, pkts, records, lost_records, diff_pkts/conf->stat_interval, diff_records/conf->stat_interval, diff_lost_records/conf->stat_interval); if (stat_out_file) { rewind(stat_out_file); // Move to beginning of file fprintf(stat_out_file, "%s=%lu\n", "TIME", time_now); fprintf(stat_out_file, "%s=%lu\n", "RUN_TIME", diff_time); fprintf(stat_out_file, "%s=%lu\n", "PACKETS", pkts); fprintf(stat_out_file, "%s=%lu\n", "DATA_REC", records); fprintf(stat_out_file, "%s=%lu\n", "LOST_DATA_REC", lost_records); fprintf(stat_out_file, "%s=%lu\n", "PACKETS_SEC", diff_pkts/conf->stat_interval); fprintf(stat_out_file, "%s=%lu\n", "DATA_REC_SEC", diff_records/conf->stat_interval); fprintf(stat_out_file, "%s=%lu\n", "LOST_DATA_REC_SEC", diff_lost_records/conf->stat_interval); fflush(stat_out_file); } /* print cpu usage by threads */ statistics_print_cpu(&(conf->stats), stat_out_file); /* print buffers usage */ statistics_print_buffers(conf, stat_out_file); MSG_INFO(stat_module, ""); } if (stat_out_file) { fclose(stat_out_file); } return NULL; }
/** * \brief Periodically prints statistics about proccessing speed * * @param config Output Manager configuration * @return 0; */ static void *statistics_thread(void* config) { struct output_manager_config *conf = (struct output_manager_config *) config; time_t begin = time(NULL), time_now, run_time; /* Create statistics config */ conf->stats.total_cpu = 0; conf->stats.threads = NULL; conf->stats.cpus = sysconf(_SC_NPROCESSORS_ONLN); snprintf(conf->stats.tasks_dir, MAX_DIR_LEN, "/proc/%d/task/", getpid()); /* If statistics are enabled (using -S), printing them to console is default */ uint8_t print_stat_to_file = 0; uint8_t print_stat_to_console = 1; /* Process XML config */ char *full_stat_out_file_path = NULL; xmlNode *node = conf->plugins_config->collector_node; while (node != NULL) { /* Skip processing this node in case it's a comment */ if (node->type == XML_COMMENT_NODE) { node = node->next; continue; } /* Jump to collectingProcess tree, since 'statisticsFile' belongs to it (if present) */ if (xmlStrcmp(node->name, (const xmlChar *) "collectingProcess") == 0) { node = node->xmlChildrenNode; } if (xmlStrcmp(node->name, (const xmlChar *) "statisticsFile") == 0) { /* Disable printing statistics to console when printing to file */ print_stat_to_console = 0; char *stat_out_file_path = (char *) xmlNodeGetContent(node->xmlChildrenNode); if (stat_out_file_path && strlen(stat_out_file_path) > 0) { print_stat_to_file = 1; /* Determine statistics file path, i.e., full path minus basename */ char stat_out_path[strlen(stat_out_file_path) + 1]; strncpy_safe(stat_out_path, stat_out_file_path, strlen(stat_out_file_path) - strlen(basename(stat_out_file_path))); /* Check whether statistics file path exists */ DIR *stat_out_dir; if ((stat_out_dir = opendir(stat_out_path)) == NULL) { MSG_ERROR(msg_module, "Statistics file directory '%s' does not exist", stat_out_path); xmlFree(stat_out_file_path); /* Skip to next XML node in configuration */ node = node->next; continue; } else { closedir(stat_out_dir); } /* Clean up previous statistics files */ char glob_path[strlen(stat_out_file_path) + 2]; // +2 is for wildcard (*) and null-terminating char strncpy_safe(glob_path, stat_out_file_path, strlen(stat_out_file_path) + 1); glob_path[strlen(stat_out_file_path)] = '*'; glob_path[strlen(stat_out_file_path) + 1] = '\0'; /* Search for files using glob */ int glob_ret, glob_flags = 0; glob_t glob_results; if ((glob_ret = glob(glob_path, glob_flags, NULL, &glob_results)) == 0) { uint16_t i, stat_files_deleted = 0; for (i = 0; i < glob_results.gl_pathc; ++i) { if ((remove(glob_results.gl_pathv[i])) == 0) { ++stat_files_deleted; } else { MSG_ERROR(msg_module, "An error occurred while deleting statistics file '%s'", glob_results.gl_pathv[i]); } } MSG_INFO(msg_module, "Cleaned up %u old statistics file(s)", stat_files_deleted); globfree(&glob_results); } else if (glob_ret == GLOB_NOMATCH) { /* No matching files/directories found; do nothing */ } else { /* Another glob error occurred; do nothing */ } /* Consists of the following elements: * strlen(stat_out_file_path) - length of supplied path * 1 - dot (.) * 5 - we assume that a PID has a maximum length of 5 digits * 1 - null-terminating character */ uint8_t max_len = strlen(stat_out_file_path) + 1 + 5 + 1; full_stat_out_file_path = calloc(max_len, sizeof(char)); /* snprintf ensures null-termination if (max_len != 0), which is always true */ snprintf(full_stat_out_file_path, max_len, "%s.%d", stat_out_file_path, getpid()); } else { MSG_ERROR(msg_module, "Configuration error: 'statisticsFile' node has no value"); } xmlFree(stat_out_file_path); /* No need to continue tree traversal, because 'statisticsFile' is only node to look for */ break; } node = node->next; } /* Set thread name */ prctl(PR_SET_NAME, "ipfixcol:stats", 0, 0, 0); FILE *stat_out_file = NULL; while (conf->stat_interval && !conf->stats.done) { /* stats.done can be set by Output Manager */ /* Sleep */ struct timespec tv; tv.tv_sec = conf->stat_interval; tv.tv_nsec = 0; /* Reconfiguration (SIGUSR1) cause sleep interruption */ while (nanosleep(&tv, &tv) == -1 && errno == EINTR && !conf->stats.done); /* Compute time */ time_now = time(NULL); run_time = time_now - begin; /* Print info */ if (print_stat_to_file) { if (full_stat_out_file_path) { stat_out_file = fopen(full_stat_out_file_path, "w"); if (!stat_out_file) { MSG_ERROR(msg_module, "Could not open statistics file '%s'; stopping statistics thread", full_stat_out_file_path); break; } fprintf(stat_out_file, "%s=%lu\n", "TIME", time_now); fprintf(stat_out_file, "%s=%lu\n", "RUN_TIME", run_time); } else { MSG_ERROR(msg_module, "Could not determine statistics file path; stopping statistics thread"); break; } } else if (print_stat_to_console) { MSG_ALWAYS("Statistics", NULL); MSG_ALWAYS(" | Time: %lu, run time: %lu", time_now, run_time); MSG_ALWAYS(" | %10s %15s %15s %15s %15s %15s %20s", "ODID", "packets", "data rec.", "lost data rec.", "packets/s", "data records/s", "lost data records/s"); } /* Variables for calculating sums */ uint64_t packets_total = 0; uint64_t data_records_total = 0; uint64_t lost_data_records_total = 0; /* Variables for calculating delta (to previous measurement) */ uint32_t delta_packets; uint32_t delta_data_records; uint32_t delta_lost_data_records; struct input_info_node *aux_node = input_info_list; uint8_t aux_node_count = 0; while (aux_node) { delta_packets = aux_node->input_info->packets - aux_node->last_packets; delta_data_records = aux_node->input_info->data_records - aux_node->last_data_records; delta_lost_data_records = aux_node->input_info->sequence_number - aux_node->input_info->data_records - aux_node->last_lost_data_records; if (print_stat_to_file) { fprintf(stat_out_file, "%s_%u=%" PRIu64 "\n", "PACKETS", aux_node->input_info->odid, aux_node->input_info->packets); fprintf(stat_out_file, "%s_%u=%" PRIu64 "\n", "DATA_REC", aux_node->input_info->odid, aux_node->input_info->data_records); fprintf(stat_out_file, "%s_%u=%" PRIu64 "\n", "LOST_DATA_REC", aux_node->input_info->odid, aux_node->input_info->sequence_number - aux_node->input_info->data_records); fprintf(stat_out_file, "%s_%u=%u\n", "PACKETS_SEC", aux_node->input_info->odid, delta_packets / conf->stat_interval); fprintf(stat_out_file, "%s_%u=%u\n", "DATA_REC_SEC", aux_node->input_info->odid, delta_data_records / conf->stat_interval); fprintf(stat_out_file, "%s_%u=%u\n", "LOST_DATA_REC_SEC", aux_node->input_info->odid, delta_lost_data_records / conf->stat_interval); } else if (print_stat_to_console) { MSG_ALWAYS(" | %10u %15lu %15lu %15lu %15u %15u %20u", aux_node->input_info->odid, aux_node->input_info->packets, aux_node->input_info->data_records, aux_node->input_info->sequence_number - aux_node->input_info->data_records, delta_packets / conf->stat_interval, delta_data_records / conf->stat_interval, delta_lost_data_records / conf->stat_interval); } /* Add values per ODID to sum */ packets_total += aux_node->input_info->packets; data_records_total += aux_node->input_info->data_records; lost_data_records_total += aux_node->input_info->sequence_number - aux_node->input_info->data_records; /* Update 'last' values */ aux_node->last_packets = aux_node->input_info->packets; aux_node->last_data_records = aux_node->input_info->data_records; aux_node->last_lost_data_records = aux_node->input_info->sequence_number - aux_node->input_info->data_records; aux_node = aux_node->next; ++aux_node_count; } if (print_stat_to_console && aux_node_count > 1) { /* Print totals row, but only in case there is more than one ODID */ MSG_ALWAYS(" | %s", "----------------------------------------------------------"); MSG_ALWAYS(" | %10s %15lu %15lu %15lu", "Total:", packets_total, data_records_total, lost_data_records_total); } /* Print CPU usage by threads */ statistics_print_cpu(&(conf->stats), stat_out_file); /* Print buffer usage */ statistics_print_buffers(conf, stat_out_file); /* Flush input stream and close file */ if (print_stat_to_file) { fflush(stat_out_file); fclose(stat_out_file); } } if (print_stat_to_file) { free(full_stat_out_file_path); } return NULL; }