diff --git a/include/ctrack.hpp b/include/ctrack.hpp index 1794610..ce9b7e6 100644 --- a/include/ctrack.hpp +++ b/include/ctrack.hpp @@ -255,12 +255,30 @@ namespace ctrack { stream << "\n"; } + template + void csvRow(StreamType& stream, const std::vector& row) const { + for (size_t i = 0; i < row.size(); ++i) { + stream << row[i]; + if (i < row.size() - 1) { + stream << ","; + } + } + stream << "\n"; + } + public: BeautifulTable(const std::vector& headerColumns, bool enableColor = false, const ColorScheme& colors = default_colors, const std::vector>& top_header = {}) : header(headerColumns), useColor(enableColor), colors(colors), top_header(top_header) { updateColumnWidths(header); } + enum class ResultFormat { + TABLE, + CSV, + JSON, + DB + }; + void addRow(const std::vector& row) { if (row.size() != header.size()) { throw std::invalid_argument("Row size must match header size"); @@ -269,6 +287,23 @@ namespace ctrack { updateColumnWidths(row); } + template + void output(StreamType& stream, BeautifulTable::ResultFormat resultFormat) const { + switch (resultFormat) { + case BeautifulTable::ResultFormat::TABLE: + print(stream); + break; + case BeautifulTable::ResultFormat::CSV: + csv(stream); + break; + case BeautifulTable::ResultFormat::JSON: + case BeautifulTable::ResultFormat::DB: + default: + std::cerr << "Invalid choice, DB and JSON not yet supported." << std::endl; + break; + } + } + template void print(StreamType& stream) const { if (top_header.size() > 0) { @@ -284,6 +319,17 @@ namespace ctrack { } } + template + void csv(StreamType& stream) const { + if (top_header.size() > 0) { + // csvRow(stream, top_header); + } + csvRow(stream, header); + for (const auto& row : rows) { + csvRow(stream, row); + } + } + template static inline std::string table_string(const T& value) { std::ostringstream oss; @@ -291,12 +337,12 @@ namespace ctrack { return oss.str(); } - static inline std::string table_time(uint_fast64_t nanoseconds) { - return table_time(static_cast(nanoseconds)); + static inline std::string table_time(uint_fast64_t nanoseconds, bool add_unit = true) { + return table_time(static_cast(nanoseconds), add_unit); } - static inline std::string table_time(double nanoseconds) { - const char* units[] = { "ns", "mcs", "ms", "s" }; + static inline std::string table_time(double nanoseconds, bool add_unit = true) { + constexpr std::string_view units[] = { "ns", "mcs", "ms", "s" }; int unit = 0; double value = static_cast(nanoseconds); while (value >= 1000 && unit < 3) { @@ -304,22 +350,33 @@ namespace ctrack { unit++; } std::ostringstream oss; - oss << std::fixed << std::setprecision(2) << value << " " << units[unit]; + oss << std::fixed << std::setprecision(2) << value; + + if (add_unit) { + oss << " " << units[unit]; + } return oss.str(); } - static inline std::string table_percentage(uint_fast64_t value, uint_fast64_t total) { - if (total == 0) { + static inline std::string table_percentage(uint_fast64_t value, uint_fast64_t total, bool add_unit = true) { + if (total == 0 && add_unit) { return "nan%"; } + if (total == 0 && !add_unit) { + return "nan"; + } + // Calculate the percentage double percentage = (static_cast(value) / total) * 100.0; // Format the percentage as a string with 2 decimal places std::ostringstream ss; - ss << std::fixed << std::setprecision(2) << percentage << "%"; + ss << std::fixed << std::setprecision(2) << percentage; + if (add_unit) { + ss << "%"; + } return ss.str(); } @@ -656,30 +713,42 @@ namespace ctrack { }; template - void get_summary_table(StreamType& stream, bool use_color = false) { + void get_summary(StreamType& stream, bool use_color = false, BeautifulTable::ResultFormat resultFormat = BeautifulTable::ResultFormat::TABLE) { + + bool add_unit = resultFormat == BeautifulTable::ResultFormat::TABLE; + BeautifulTable info({ "Start","End","time total","time ctracked","time ctracked %", }, use_color, alternate_colors); - info.addRow({ BeautifulTable::table_timepoint(track_start_time), BeautifulTable::table_timepoint(track_end_time), - BeautifulTable::table_time(time_total), BeautifulTable::table_time(sum_time_active_exclusive), - BeautifulTable::table_percentage(sum_time_active_exclusive, time_total) - }); + info.addRow({ + BeautifulTable::table_timepoint(track_start_time), + BeautifulTable::table_timepoint(track_end_time), + BeautifulTable::table_time(time_total, add_unit), + BeautifulTable::table_time(sum_time_active_exclusive, add_unit), + BeautifulTable::table_percentage(sum_time_active_exclusive, time_total, add_unit) + }); + + info.output(stream, resultFormat); - info.print(stream); BeautifulTable table({ "filename", "function", "line","calls","ae" + center_intervall_str + "%","ae[0-100]%", - "time ae[0-100]" ,"time a[0-100]" }, use_color, alternate_colors); + "time ae[0-100]" ,"time a[0-100]" }, use_color, alternate_colors); for (auto& entry : sorted_events) { - table.addRow({ BeautifulTable::stable_shortenPath(entry->filename), entry->function_name,BeautifulTable::table_string(entry->line), + table.addRow({ + BeautifulTable::stable_shortenPath(entry->filename), entry->function_name, + BeautifulTable::table_string(entry->line), BeautifulTable::table_string(entry->all_cnt), - BeautifulTable::table_percentage(entry->center_time_active_exclusive, time_total), - BeautifulTable::table_percentage(entry->all_time_active_exclusive,time_total), - BeautifulTable::table_time(entry->all_time_active_exclusive), - BeautifulTable::table_time(entry->all_time_active) }); + BeautifulTable::table_percentage(entry->center_time_active_exclusive, time_total, add_unit), + BeautifulTable::table_percentage(entry->all_time_active_exclusive, time_total, add_unit), + BeautifulTable::table_time(entry->all_time_active_exclusive, add_unit), + BeautifulTable::table_time(entry->all_time_active, add_unit) }); } - table.print(stream); + table.output(stream, resultFormat); }; template - void get_detail_table(StreamType& stream, bool use_color = false, bool reverse_vector = false) { + void get_detail(StreamType& stream, bool use_color = false, bool reverse_vector = false, BeautifulTable::ResultFormat resultFormat = BeautifulTable::ResultFormat::TABLE) { + + bool add_unit = resultFormat == BeautifulTable::ResultFormat::TABLE; + if (reverse_vector) { std::reverse(sorted_events.begin(), sorted_events.end()); } @@ -687,21 +756,30 @@ namespace ctrack { auto& entry = sorted_events[i]; BeautifulTable info({ "filename", "function", "line","time acc","sd","cv","calls","threads" }, use_color, default_colors); - info.addRow({ BeautifulTable::stable_shortenPath(entry->filename), entry->function_name,BeautifulTable::table_string(entry->line), - BeautifulTable::table_time(entry->all_time_acc), - BeautifulTable::table_time(sorted_events[i]->all_st),BeautifulTable::table_string(sorted_events[i]->all_cv), - BeautifulTable::table_string(sorted_events[i]->all_cnt), BeautifulTable::table_string(sorted_events[i]->all_thread_cnt) }); + info.addRow({ + BeautifulTable::stable_shortenPath(entry->filename), entry->function_name, + BeautifulTable::table_string(entry->line), + BeautifulTable::table_time(entry->all_time_acc, add_unit), + BeautifulTable::table_time(sorted_events[i]->all_st, add_unit), + BeautifulTable::table_string(sorted_events[i]->all_cv), + BeautifulTable::table_string(sorted_events[i]->all_cnt), + BeautifulTable::table_string(sorted_events[i]->all_thread_cnt) }); BeautifulTable table({ "min", "mean", "min","mean","med","time a","time ae","max","mean","max" }, use_color, default_colors, { {"fastest[0-" + std::to_string(settings.non_center_percent) + "]%",2},{"center" + center_intervall_str + "%",6}, {"slowest[" + std::to_string(100 - settings.non_center_percent) + "-100]%",2} }); - table.addRow({ BeautifulTable::table_time(entry->fastest_min),BeautifulTable::table_time(entry->fastest_mean), - BeautifulTable::table_time(entry->center_min),BeautifulTable::table_time(entry->center_mean), - BeautifulTable::table_time(entry->center_med),BeautifulTable::table_time(entry->center_time_active), - BeautifulTable::table_time(entry->center_time_active_exclusive), - BeautifulTable::table_time(entry->center_max), - BeautifulTable::table_time(entry->slowest_mean),BeautifulTable::table_time(entry->slowest_max) }); + table.addRow({ + BeautifulTable::table_time(entry->fastest_min, add_unit), + BeautifulTable::table_time(entry->fastest_mean, add_unit), + BeautifulTable::table_time(entry->center_min, add_unit), + BeautifulTable::table_time(entry->center_mean, add_unit), + BeautifulTable::table_time(entry->center_med, add_unit), + BeautifulTable::table_time(entry->center_time_active, add_unit), + BeautifulTable::table_time(entry->center_time_active_exclusive, add_unit), + BeautifulTable::table_time(entry->center_max, add_unit), + BeautifulTable::table_time(entry->slowest_mean, add_unit), + BeautifulTable::table_time(entry->slowest_max, add_unit) }); info.print(stream); table.print(stream); @@ -928,18 +1006,24 @@ namespace ctrack { inline void result_print(ctrack_result_settings settings = {}) { auto res = calc_stats_and_clear(settings); std::cout << "Details" << std::endl; - res.get_detail_table(std::cout, true); + res.get_detail(std::cout, true); std::cout << "Summary" << std::endl; - res.get_summary_table(std::cout, true); + res.get_summary(std::cout, true); + } + + inline void result_csv(ctrack_result_settings settings = {}, const std::filesystem::path& path = std::filesystem::path("result.csv")) { + auto res = calc_stats_and_clear(settings); + res.get_summary(std::cout, false, BeautifulTable::ResultFormat::CSV); + // TODO write to file } inline std::string result_as_string(ctrack_result_settings settings = {}) { auto res = calc_stats_and_clear(settings); std::stringstream ss; ss << "Summary\n"; - res.get_summary_table(ss, false); + res.get_summary(ss, false); ss << "Details\n"; - res.get_detail_table(ss, false, true); + res.get_detail(ss, false, true); return ss.str(); } @@ -984,4 +1068,4 @@ namespace ctrack { #define CTRACK_NAME(name) #endif // CTRACK_DISABLE -#endif \ No newline at end of file +#endif