diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2021-11-10 12:06:36 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-11-10 12:06:36 +0000 |
| commit | 5cb23725ae64731242e8d425958b0bf4dacf6025 (patch) | |
| tree | ff790300e0aee092b3b8f37d27bd04bb91a2683e /src/main.cpp | |
| parent | 4a552e632696f7dd78ef86b59eedbfef25378f86 (diff) | |
| parent | 8c5c45a04c2a525e048538d764cc35f44239230b (diff) | |
Merge pull request #1289 from Kelimion/timings-export
Add functionality to export build timings.
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 277 |
1 files changed, 216 insertions, 61 deletions
diff --git a/src/main.cpp b/src/main.cpp index c8af38211..61288f3c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -592,6 +592,8 @@ enum BuildFlagKind { BuildFlag_ShowUnused, BuildFlag_ShowUnusedWithLocation, BuildFlag_ShowMoreTimings, + BuildFlag_ExportTimings, + BuildFlag_ExportTimingsFile, BuildFlag_ShowSystemCalls, BuildFlag_ThreadCount, BuildFlag_KeepTempFiles, @@ -740,6 +742,8 @@ bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check); add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check); add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all); @@ -855,9 +859,9 @@ bool parse_build_flags(Array<String> args) { } else { ok = true; switch (bf.param_kind) { - default: + default: { ok = false; - break; + } break; case BuildFlagParam_Boolean: { if (str_eq_ignore_case(param, str_lit("t")) || str_eq_ignore_case(param, str_lit("true")) || @@ -871,12 +875,12 @@ bool parse_build_flags(Array<String> args) { gb_printf_err("Invalid flag parameter for '%.*s' : '%.*s'\n", LIT(name), LIT(param)); } } break; - case BuildFlagParam_Integer: + case BuildFlagParam_Integer: { value = exact_value_integer_from_string(param); - break; - case BuildFlagParam_Float: + } break; + case BuildFlagParam_Float: { value = exact_value_float_from_string(param); - break; + } break; case BuildFlagParam_String: { value = exact_value_string(param); if (value.kind == ExactValue_String) { @@ -932,7 +936,6 @@ bool parse_build_flags(Array<String> args) { case BuildFlag_Help: build_context.show_help = true; break; - case BuildFlag_OutFile: { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; @@ -945,7 +948,7 @@ bool parse_build_flags(Array<String> args) { } break; } - case BuildFlag_OptimizationLevel: + case BuildFlag_OptimizationLevel: { GB_ASSERT(value.kind == ExactValue_Integer); if (set_flags[BuildFlag_OptimizationMode]) { gb_printf_err("Mixture of -opt and -o is not allowed\n"); @@ -954,7 +957,8 @@ bool parse_build_flags(Array<String> args) { } build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer); break; - case BuildFlag_OptimizationMode: + } + case BuildFlag_OptimizationMode: { GB_ASSERT(value.kind == ExactValue_String); if (set_flags[BuildFlag_OptimizationLevel]) { gb_printf_err("Mixture of -opt and -o is not allowed\n"); @@ -977,28 +981,65 @@ bool parse_build_flags(Array<String> args) { bad_flags = true; } break; - case BuildFlag_ShowTimings: + } + case BuildFlag_ShowTimings: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; break; - case BuildFlag_ShowUnused: + } + case BuildFlag_ShowUnused: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_unused = true; break; - case BuildFlag_ShowUnusedWithLocation: + } + case BuildFlag_ShowUnusedWithLocation: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_unused = true; build_context.show_unused_with_location = true; break; + } case BuildFlag_ShowMoreTimings: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; build_context.show_more_timings = true; break; - case BuildFlag_ShowSystemCalls: + case BuildFlag_ExportTimings: { + GB_ASSERT(value.kind == ExactValue_String); + /* + NOTE(Jeroen): `build_context.export_timings_format == 0` means the option wasn't used. + */ + if (value.value_string == "json") { + build_context.export_timings_format = TimingsExportJson; + } else if (value.value_string == "csv") { + build_context.export_timings_format = TimingsExportCSV; + } else { + gb_printf_err("Invalid export format for -export-timings:<string>, got %.*s\n", LIT(value.value_string)); + gb_printf_err("Valid export formats:\n"); + gb_printf_err("\tjson\n"); + gb_printf_err("\tcsv\n"); + bad_flags = true; + } + + break; + } + case BuildFlag_ExportTimingsFile: { + GB_ASSERT(value.kind == ExactValue_String); + + String export_path = string_trim_whitespace(value.value_string); + if (is_build_flag_path_valid(export_path)) { + build_context.export_timings_file = path_to_full_path(heap_allocator(), export_path); + } else { + gb_printf_err("Invalid -export-timings-file path, got %.*s\n", LIT(export_path)); + bad_flags = true; + } + + break; + } + case BuildFlag_ShowSystemCalls: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_system_calls = true; break; + } case BuildFlag_ThreadCount: { GB_ASSERT(value.kind == ExactValue_Integer); isize count = cast(isize)big_int_to_i64(&value.value_integer); @@ -1010,11 +1051,11 @@ bool parse_build_flags(Array<String> args) { } break; } - case BuildFlag_KeepTempFiles: + case BuildFlag_KeepTempFiles: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.keep_temp_files = true; break; - + } case BuildFlag_Collection: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; @@ -1078,8 +1119,6 @@ bool parse_build_flags(Array<String> args) { // NOTE(bill): Allow for multiple library collections continue; } - - case BuildFlag_Define: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; @@ -1219,84 +1258,74 @@ bool parse_build_flags(Array<String> args) { case BuildFlag_Debug: build_context.ODIN_DEBUG = true; break; - case BuildFlag_DisableAssert: build_context.ODIN_DISABLE_ASSERT = true; break; - case BuildFlag_NoBoundsCheck: build_context.no_bounds_check = true; break; - case BuildFlag_NoDynamicLiterals: build_context.no_dynamic_literals = true; break; - case BuildFlag_NoCRT: build_context.no_crt = true; break; - case BuildFlag_NoEntryPoint: build_context.no_entry_point = true; break; - case BuildFlag_UseLLD: build_context.use_lld = true; break; - case BuildFlag_UseSeparateModules: build_context.use_separate_modules = true; break; - - case BuildFlag_ThreadedChecker: + case BuildFlag_ThreadedChecker: { #if defined(DEFAULT_TO_THREADED_CHECKER) gb_printf_err("-threaded-checker is the default on this platform\n"); bad_flags = true; #endif build_context.threaded_checker = true; break; - - case BuildFlag_NoThreadedChecker: + } + case BuildFlag_NoThreadedChecker: { #if !defined(DEFAULT_TO_THREADED_CHECKER) gb_printf_err("-no-threaded-checker is the default on this platform\n"); bad_flags = true; #endif build_context.threaded_checker = false; break; - + } case BuildFlag_ShowDebugMessages: build_context.show_debug_messages = true; break; - case BuildFlag_Vet: build_context.vet = true; break; - case BuildFlag_VetExtra: + case BuildFlag_VetExtra: { build_context.vet = true; build_context.vet_extra = true; break; - - case BuildFlag_UseLLVMApi: + } + case BuildFlag_UseLLVMApi: { gb_printf_err("-llvm-api flag is not required any more\n"); bad_flags = true; break; - + } case BuildFlag_IgnoreUnknownAttributes: build_context.ignore_unknown_attributes = true; break; - - case BuildFlag_ExtraLinkerFlags: + case BuildFlag_ExtraLinkerFlags: { GB_ASSERT(value.kind == ExactValue_String); build_context.extra_linker_flags = value.value_string; break; - - case BuildFlag_Microarch: + } + case BuildFlag_Microarch: { GB_ASSERT(value.kind == ExactValue_String); build_context.microarch = value.value_string; string_to_lower(&build_context.microarch); break; - - case BuildFlag_TestName: + } + case BuildFlag_TestName: { GB_ASSERT(value.kind == ExactValue_String); { String name = value.value_string; @@ -1310,35 +1339,33 @@ bool parse_build_flags(Array<String> args) { // NOTE(bill): Allow for multiple -test-name continue; } - + } case BuildFlag_DisallowDo: build_context.disallow_do = true; break; - case BuildFlag_DefaultToNilAllocator: build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; break; - - case BuildFlag_InsertSemicolon: + case BuildFlag_InsertSemicolon: { gb_printf_err("-insert-semicolon flag is not required any more\n"); bad_flags = true; break; - - case BuildFlag_StrictStyle: + } + case BuildFlag_StrictStyle: { if (build_context.strict_style_init_only) { gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n"); } build_context.strict_style = true; break; - case BuildFlag_StrictStyleInitOnly: + } + case BuildFlag_StrictStyleInitOnly: { if (build_context.strict_style) { gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n"); } build_context.strict_style_init_only = true; break; - - - case BuildFlag_Compact: + } + case BuildFlag_Compact: { if (!build_context.query_data_set_settings.ok) { gb_printf_err("Invalid use of -compact flag, only allowed with 'odin query'\n"); bad_flags = true; @@ -1346,8 +1373,8 @@ bool parse_build_flags(Array<String> args) { build_context.query_data_set_settings.compact = true; } break; - - case BuildFlag_GlobalDefinitions: + } + case BuildFlag_GlobalDefinitions: { if (!build_context.query_data_set_settings.ok) { gb_printf_err("Invalid use of -global-definitions flag, only allowed with 'odin query'\n"); bad_flags = true; @@ -1358,7 +1385,8 @@ bool parse_build_flags(Array<String> args) { build_context.query_data_set_settings.kind = QueryDataSet_GlobalDefinitions; } break; - case BuildFlag_GoToDefinitions: + } + case BuildFlag_GoToDefinitions: { if (!build_context.query_data_set_settings.ok) { gb_printf_err("Invalid use of -go-to-definitions flag, only allowed with 'odin query'\n"); bad_flags = true; @@ -1369,7 +1397,7 @@ bool parse_build_flags(Array<String> args) { build_context.query_data_set_settings.kind = QueryDataSet_GoToDefinitions; } break; - + } case BuildFlag_Short: build_context.cmd_doc_flags |= CmdDocFlag_Short; break; @@ -1379,7 +1407,7 @@ bool parse_build_flags(Array<String> args) { case BuildFlag_DocFormat: build_context.cmd_doc_flags |= CmdDocFlag_DocFormat; break; - case BuildFlag_IgnoreWarnings: + case BuildFlag_IgnoreWarnings: { if (build_context.warnings_as_errors) { gb_printf_err("-ignore-warnings cannot be used with -warnings-as-errors\n"); bad_flags = true; @@ -1387,7 +1415,8 @@ bool parse_build_flags(Array<String> args) { build_context.ignore_warnings = true; } break; - case BuildFlag_WarningsAsErrors: + } + case BuildFlag_WarningsAsErrors: { if (build_context.ignore_warnings) { gb_printf_err("-warnings-as-errors cannot be used with -ignore-warnings\n"); bad_flags = true; @@ -1395,21 +1424,19 @@ bool parse_build_flags(Array<String> args) { build_context.warnings_as_errors = true; } break; - + } case BuildFlag_VerboseErrors: build_context.show_error_line = true; break; - case BuildFlag_InternalIgnoreLazy: build_context.ignore_lazy = true; break; - #if defined(GB_SYSTEM_WINDOWS) - case BuildFlag_IgnoreVsSearch: + case BuildFlag_IgnoreVsSearch: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.ignore_microsoft_magic = true; break; - + } case BuildFlag_ResourceFile: { GB_ASSERT(value.kind == ExactValue_String); String path = value.value_string; @@ -1496,6 +1523,19 @@ bool parse_build_flags(Array<String> args) { } } + if ((!(build_context.export_timings_format == TimingsExportUnspecified)) && (build_context.export_timings_file.len == 0)) { + gb_printf_err("`-export-timings:<format>` requires `-export-timings-file:<filename>` to be specified as well\n"); + bad_flags = true; + } else if ((build_context.export_timings_format == TimingsExportUnspecified) && (build_context.export_timings_file.len > 0)) { + gb_printf_err("`-export-timings-file:<filename>` requires `-export-timings:<format>` to be specified as well\n"); + bad_flags = true; + } + + if (build_context.export_timings_format && !(build_context.show_timings || build_context.show_more_timings)) { + gb_printf_err("`-export-timings:<format>` requires `-show-timings` or `-show-more-timings` to be present\n"); + bad_flags = true; + } + if (build_context.query_data_set_settings.ok) { if (build_context.query_data_set_settings.kind == QueryDataSet_Invalid) { gb_printf_err("'odin query' requires a flag determining the kind of query data set to be returned\n"); @@ -1508,6 +1548,104 @@ bool parse_build_flags(Array<String> args) { return !bad_flags; } +void timings_export_all(Timings *t, Checker *c, bool timings_are_finalized = false) { + GB_ASSERT((!(build_context.export_timings_format == TimingsExportUnspecified) && build_context.export_timings_file.len > 0)); + + /* + NOTE(Jeroen): Whether we call `timings_print_all()`, then `timings_export_all()`, the other way around, + or just one of them, we only need to stop the clock once. + */ + if (!timings_are_finalized) { + timings__stop_current_section(t); + t->total.finish = time_stamp_time_now(); + } + + TimingUnit unit = TimingUnit_Millisecond; + + /* + Prepare file for export. + */ + gbFile f = {}; + char * fileName = (char *)build_context.export_timings_file.text; + gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName); + if (err != gbFileError_None) { + gb_printf_err("Failed to export timings to: %s\n", fileName); + gb_exit(1); + return; + } else { + gb_printf("\nExporting timings to '%s'... ", fileName); + } + defer (gb_file_close(&f)); + + if (build_context.export_timings_format == TimingsExportJson) { + /* + JSON export + */ + Parser *p = c->parser; + isize lines = p->total_line_count; + isize tokens = p->total_token_count; + isize files = 0; + isize packages = p->packages.count; + isize total_file_size = 0; + for_array(i, p->packages) { + files += p->packages[i]->files.count; + for_array(j, p->packages[i]->files) { + AstFile *file = p->packages[i]->files[j]; + total_file_size += file->tokenizer.end - file->tokenizer.start; + } + } + + gb_fprintf(&f, "{\n"); + gb_fprintf(&f, "\t\"totals\": [\n"); + + gb_fprintf(&f, "\t\t{\"name\": \"total_packages\", \"count\": %td},\n", packages); + gb_fprintf(&f, "\t\t{\"name\": \"total_files\", \"count\": %td},\n", files); + gb_fprintf(&f, "\t\t{\"name\": \"total_lines\", \"count\": %td},\n", lines); + gb_fprintf(&f, "\t\t{\"name\": \"total_tokens\", \"count\": %td},\n", tokens); + gb_fprintf(&f, "\t\t{\"name\": \"total_file_size\", \"count\": %td},\n", total_file_size); + + gb_fprintf(&f, "\t],\n"); + + gb_fprintf(&f, "\t\"timings\": [\n"); + + t->total_time_seconds = time_stamp_as_s(t->total, t->freq); + f64 total_time = time_stamp(t->total, t->freq, unit); + + gb_fprintf(&f, "\t\t{\"name\": \"%.*s\", \"millis\": %.3f},\n", + LIT(t->total.label), total_time); + + for_array(i, t->sections) { + TimeStamp ts = t->sections[i]; + f64 section_time = time_stamp(ts, t->freq, unit); + gb_fprintf(&f, "\t\t{\"name\": \"%.*s\", \"millis\": %.3f},\n", + LIT(ts.label), section_time); + } + + gb_fprintf(&f, "\t],\n"); + + gb_fprintf(&f, "}\n"); + } else if (build_context.export_timings_format == TimingsExportCSV) { + /* + CSV export + */ + t->total_time_seconds = time_stamp_as_s(t->total, t->freq); + f64 total_time = time_stamp(t->total, t->freq, unit); + + /* + CSV doesn't really like floating point values. Cast to `int`. + */ + gb_fprintf(&f, "\"%.*s\", %d\n", LIT(t->total.label), int(total_time)); + + for_array(i, t->sections) { + TimeStamp ts = t->sections[i]; + f64 section_time = time_stamp(ts, t->freq, unit); + gb_fprintf(&f, "\"%.*s\", %d\n", LIT(ts.label), int(section_time)); + } + } + + gb_printf("Done.\n"); +} + void show_timings(Checker *c, Timings *t) { Parser *p = c->parser; isize lines = p->total_line_count; @@ -1528,6 +1666,11 @@ void show_timings(Checker *c, Timings *t) { } timings_print_all(t); + + if (!(build_context.export_timings_format == TimingsExportUnspecified)) { + timings_export_all(t, c, true); + } + if (build_context.show_debug_messages && build_context.show_more_timings) { { gb_printf("\n"); @@ -1739,6 +1882,18 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds"); print_usage_line(0, ""); + print_usage_line(1, "-export-timings:<format>"); + print_usage_line(2, "Export timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`"); + print_usage_line(2, "Available options:"); + print_usage_line(3, "-export-timings:json Export compile time stats to JSON"); + print_usage_line(3, "-export-timings:csv Export compile time stats to CSV"); + print_usage_line(0, ""); + + print_usage_line(1, "-export-timings-file:<filename>"); + print_usage_line(2, "Specify the filename for `-export-timings`"); + print_usage_line(2, "Example: -export-timings-file:timings.json"); + print_usage_line(0, ""); + print_usage_line(1, "-thread-count:<integer>"); print_usage_line(2, "Override the number of threads the compiler will use to compile with"); print_usage_line(2, "Example: -thread-count:2"); |