From 2380be2ae17c7e2c92a555723fd71eaae0b29a0d Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Tue, 6 Jan 2026 13:56:08 -0800 Subject: [PATCH] Add flag to dump the raw SemIR in the event of a crash. (#6558) --- toolchain/check/check.cpp | 22 ++++++++++++++++++++++ toolchain/check/check.h | 4 ++++ toolchain/docs/adding_features.md | 8 ++++++++ toolchain/driver/compile_subcommand.cpp | 11 +++++++++++ toolchain/driver/compile_subcommand.h | 2 ++ 5 files changed, 47 insertions(+) diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 035384b6e821..cbb4b8112207 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -9,6 +9,7 @@ #include "common/check.h" #include "common/map.h" +#include "common/pretty_stack_trace_function.h" #include "toolchain/check/check_unit.h" #include "toolchain/check/context.h" #include "toolchain/check/cpp/import.h" @@ -417,6 +418,27 @@ auto CheckParseTrees( &unit, tree_and_subtrees_getters.Get(unit.sem_ir->check_ir_id())); })); + // Dump the raw SemIR in the event of a crash. We dump it to a separate file + // to keep the stack trace manageable. + PrettyStackTraceFunction sem_ir_dumper([&](llvm::raw_ostream& output) { + if (!options.sem_ir_crash_dump.empty()) { + output << "Dumping raw SemIR to " << options.sem_ir_crash_dump << "\n"; + std::error_code error_code; + llvm::raw_fd_ostream sem_ir_dump(options.sem_ir_crash_dump, error_code); + if (error_code) { + output << "Raw SemIR dump failed: " << error_code.category().name() + << ":" << error_code.value() << ": " << error_code.message() + << "\n"; + } else { + for (const auto& unit_info : unit_infos) { + if (unit_info.is_checked && unit_info.unit->sem_ir != nullptr) { + unit_info.unit->sem_ir->Print(sem_ir_dump); + } + } + } + } + }); + Map api_map = BuildApiMapAndDiagnosePackaging(unit_infos); diff --git a/toolchain/check/check.h b/toolchain/check/check.h index 91472c15b5d9..df7f237f669d 100644 --- a/toolchain/check/check.h +++ b/toolchain/check/check.h @@ -70,6 +70,10 @@ struct CheckParseTreesOptions { // When dumping raw SemIR, whether to include builtins. bool dump_raw_sem_ir_builtins = false; + + // If not empty, a raw SemIR dump should be written to this path in the event + // of a crash. + llvm::StringRef sem_ir_crash_dump; }; // Checks a group of parse trees. This will use imports to decide the order of diff --git a/toolchain/docs/adding_features.md b/toolchain/docs/adding_features.md index 2b71b5c5a195..1bb623493bc5 100644 --- a/toolchain/docs/adding_features.md +++ b/toolchain/docs/adding_features.md @@ -628,6 +628,10 @@ minimal context for how the current function is reached, we use LLVM's `PrettyStackTrace` to include details about the state stack. The state stack will be above the function stack in crash output. +You can also use the `--sem-ir-crash-dump=path/to/file` flag to get a raw SemIR +dump in the event of a crash in the check phase. This can be particularly useful +for interpreting IDs you encounter during interactive debugging. + #### Dumping objects in interactive debuggers We provide namespace-scoped `Dump` functions in several components, such as @@ -638,6 +642,10 @@ regarding support. Objects which inherit from `Printable` also have `Dump` member functions, but these will lack contextual information. +IDs are dumped in hexadecimal, so it's often convenient to set your interactive +debugger to print integers in hexadecimal as well. In LLDB you can do this with +`type format add --format hex int`. + #### Dumping prelude files By default, prelude files are excluded from dumps by diff --git a/toolchain/driver/compile_subcommand.cpp b/toolchain/driver/compile_subcommand.cpp index 48faae45428e..ad5cb6932564 100644 --- a/toolchain/driver/compile_subcommand.cpp +++ b/toolchain/driver/compile_subcommand.cpp @@ -369,6 +369,16 @@ Whether to run the LLVM verifier on modules. arg_b.Default(true); arg_b.Set(&run_llvm_verifier); }); + b.AddStringOption( + { + .name = "sem-ir-crash-dump", + .value_name = "PATH", + .help = R"""( +Where to write a dump of the raw SemIR emitted so far, in the event of a crash +in the check phase. If empty, the dump is not written. +)""", + }, + [&](auto& arg_b) { arg_b.Set(&sem_ir_crash_dump); }); } static constexpr CommandLine::CommandInfo SubcommandInfo = { @@ -1240,6 +1250,7 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult { options.raw_dump_stream = driver_env.output_stream; options.dump_raw_sem_ir_builtins = options_.builtin_sem_ir; } + options.sem_ir_crash_dump = options_.sem_ir_crash_dump; } Check::CheckParseTrees(check_units, cache.tree_and_subtrees_getters(), driver_env.fs, options, clang_invocation); diff --git a/toolchain/driver/compile_subcommand.h b/toolchain/driver/compile_subcommand.h index f467924de1d1..71d52cd54d39 100644 --- a/toolchain/driver/compile_subcommand.h +++ b/toolchain/driver/compile_subcommand.h @@ -68,6 +68,8 @@ struct CompileOptions { bool run_llvm_verifier = true; llvm::SmallVector exclude_dump_file_prefixes; + + llvm::StringRef sem_ir_crash_dump; }; // Implements the compile subcommand of the driver.