Skip to content

Commit 58fba07

Browse files
zygoloidjonmeow
andauthored
Add a flag to make CHECK failures non-fatal for debugging. (#4835)
`toolchain/autoupdate_testdata.py --allow-check-fail` can now be used to perform an autoupdate even if some `CARBON_CHECK`s are failing. What this does will depend on how the toolchain behaves after the `CHECK` failure, and of course there's no guarantees there, but this can be useful if it's easier to debug the `CHECK` failure by looking at the produced SemIR. Internally, this uses `bazel build --config=non-fatal-checks`, which in turn specifies a `--per_file_copt` for `check_internal.cpp`. The intent here is that the rebuild required to enable or disable this mode is as small as reasonably possible. This mode is not compatible with `-c opt`, as it's important that check failure calls are `[[noreturn]]` in `-c opt` mode. --------- Co-authored-by: Jon Ross-Perkins <[email protected]>
1 parent 1670baf commit 58fba07

File tree

4 files changed

+63
-19
lines changed

4 files changed

+63
-19
lines changed

.bazelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ build:clang-tidy --action_env=PATH --host_action_env=PATH
2828
# not firing in our normal builds.
2929
build:clang-tidy --copt=-Wno-unknown-pragmas
3030

31+
# --config=non-fatal-checks makes CHECK failures not terminate compilation.
32+
build:non-fatal-checks --per_file_copt=common/check_internal.cpp@-DCARBON_NON_FATAL_CHECKS
33+
3134
# Provide an alias for controlling the `carbon_*` Bazel rules' configuration. We
3235
# enable use of the target config here to make our build and tests more
3336
# efficient, see the documentation in //bazel/carbon_rules/BUILD for details.

common/check_internal.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@
99

1010
namespace Carbon::Internal {
1111

12-
// Prints the buffered message.
13-
static auto PrintAfterStackTrace(void* str) -> void {
14-
llvm::errs() << reinterpret_cast<char*>(str);
15-
}
16-
1712
auto CheckFailImpl(const char* kind, const char* file, int line,
1813
const char* condition_str, llvm::StringRef extra_message)
1914
-> void {
@@ -23,15 +18,29 @@ auto CheckFailImpl(const char* kind, const char* file, int line,
2318
llvm::StringRef(condition_str).empty() ? "" : ": ", condition_str,
2419
extra_message.empty() ? "" : ": ", extra_message);
2520

21+
// This macro is defined by `--config=non-fatal-checks`.
22+
#ifdef CARBON_NON_FATAL_CHECKS
23+
#ifdef NDEBUG
24+
#error "--config=non-fatal-checks is incompatible with -c opt"
25+
#endif
26+
// TODO: It'd be nice to print the LLVM PrettyStackTrace, but LLVM doesn't
27+
// expose functionality to do so.
28+
llvm::sys::PrintStackTrace(llvm::errs());
29+
30+
llvm::errs() << message;
31+
#else
2632
// Register another signal handler to print the message. This is because we
2733
// want it at the bottom of output, after LLVM's builtin stack output, rather
2834
// than the top.
29-
llvm::sys::AddSignalHandler(PrintAfterStackTrace,
30-
const_cast<char*>(message.c_str()));
35+
llvm::sys::AddSignalHandler(
36+
[](void* str) { llvm::errs() << reinterpret_cast<char*>(str); },
37+
const_cast<char*>(message.c_str()));
38+
3139
// It's useful to exit the program with `std::abort()` for integration with
3240
// debuggers and other tools. We also assume LLVM's exit handling is
3341
// installed, which will stack trace on `std::abort()`.
3442
std::abort();
43+
#endif
3544
}
3645

3746
} // namespace Carbon::Internal

common/check_internal.h

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,22 @@ CheckCondition(bool condition)
3131
// Implements the check failure message printing.
3232
//
3333
// This is out-of-line and will arrange to stop the program, print any debugging
34-
// information and this string.
34+
// information and this string. In `!NDEBUG` mode (`dbg` and `fastbuild`), check
35+
// failures can be made non-fatal by a build flag, so this is not `[[noreturn]]`
36+
// in that case.
3537
//
3638
// This API uses `const char*` C string arguments rather than `llvm::StringRef`
3739
// because we know that these are available as C strings and passing them that
3840
// way lets the code size of calling it be smaller: it only needs to materialize
3941
// a single pointer argument for each. The runtime cost of re-computing the size
4042
// should be minimal. The extra message however might not be compile-time
4143
// guaranteed to be a C string so we use a normal `StringRef` there.
42-
[[noreturn]] auto CheckFailImpl(const char* kind, const char* file, int line,
43-
const char* condition_str,
44-
llvm::StringRef extra_message) -> void;
44+
#ifdef NDEBUG
45+
[[noreturn]]
46+
#endif
47+
auto CheckFailImpl(const char* kind, const char* file, int line,
48+
const char* condition_str, llvm::StringRef extra_message)
49+
-> void;
4550

4651
// Allow converting format values; the default behaviour is to just pass them
4752
// through.
@@ -77,8 +82,11 @@ auto ConvertFormatValue(T&& t) {
7782
// `llvm::formatv` which handles all of the formatting of output.
7883
template <TemplateString Kind, TemplateString File, int Line,
7984
TemplateString ConditionStr, TemplateString FormatStr, typename... Ts>
80-
[[noreturn, gnu::cold, clang::noinline]] auto CheckFail(Ts&&... values)
81-
-> void {
85+
#ifdef NDEBUG
86+
[[noreturn]]
87+
#endif
88+
[[gnu::cold, clang::noinline]] auto
89+
CheckFail(Ts&&... values) -> void {
8290
if constexpr (llvm::StringRef(FormatStr).empty()) {
8391
// Skip the format string rendering if empty. Note that we don't skip it
8492
// even if there are no values as we want to have consistent handling of
@@ -135,9 +143,10 @@ template <TemplateString Kind, TemplateString File, int Line,
135143
//
136144
// Similar to the check failure macro, but tags the message as a fatal one and
137145
// leaves the stringified condition empty.
138-
#define CARBON_INTERNAL_FATAL(...) \
139-
CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \
140-
"FATAL", __FILE__, __LINE__, "" __VA_OPT__(, ) __VA_ARGS__)
146+
#define CARBON_INTERNAL_FATAL(...) \
147+
(CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \
148+
"FATAL", __FILE__, __LINE__, "" __VA_OPT__(, ) __VA_ARGS__), \
149+
CARBON_INTERNAL_FATAL_NORETURN_SUFFIX())
141150

142151
#ifdef NDEBUG
143152
// For `DCHECK` in optimized builds we have a dead check that we want to
@@ -153,6 +162,11 @@ template <TemplateString Kind, TemplateString File, int Line,
153162

154163
#define CARBON_INTERNAL_DEAD_DCHECK_IMPL_FORMAT(format_str, ...) \
155164
Carbon::Internal::CheckFail<"", "", 0, "", "">(__VA_ARGS__)
165+
166+
// The CheckFail function itself is noreturn in NDEBUG.
167+
#define CARBON_INTERNAL_FATAL_NORETURN_SUFFIX() void()
168+
#else
169+
#define CARBON_INTERNAL_FATAL_NORETURN_SUFFIX() std::abort()
156170
#endif
157171

158172
#endif // CARBON_COMMON_CHECK_INTERNAL_H_

toolchain/autoupdate_testdata.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
99
"""
1010

11+
import argparse
1112
import re
1213
import subprocess
1314
import sys
@@ -16,6 +17,7 @@
1617

1718
def main() -> None:
1819
bazel = str(Path(__file__).parents[1] / "scripts" / "run_bazel.py")
20+
configs = []
1921
# Use the most recently used build mode, or `fastbuild` if missing
2022
# `bazel-bin`.
2123
build_mode = "fastbuild"
@@ -37,11 +39,26 @@ def main() -> None:
3739
else:
3840
exit(f"Build mode not found in `bazel-bin` symlink: {link}")
3941

42+
# Parse arguments.
43+
parser = argparse.ArgumentParser(__doc__)
44+
parser.add_argument("--non-fatal-checks", action="store_true")
45+
parser.add_argument("files", nargs="*")
46+
args = parser.parse_args()
47+
48+
if args.allow_check_fail:
49+
if build_mode == "opt":
50+
exit(
51+
"`--non-fatal-checks` is incompatible with inferred "
52+
"`-c opt` build mode"
53+
)
54+
configs.append("--config=non-fatal-checks")
55+
4056
argv = [
4157
bazel,
4258
"run",
4359
"-c",
4460
build_mode,
61+
*configs,
4562
"--experimental_convenience_symlinks=ignore",
4663
"--ui_event_filters=-info,-stdout,-stderr,-finish",
4764
"//toolchain/testing:file_test",
@@ -50,18 +67,19 @@ def main() -> None:
5067
]
5168
# Support specifying tests to update, such as:
5269
# ./autoupdate_testdata.py lex/**/*
53-
if len(sys.argv) > 1:
70+
if args.files:
5471
repo_root = Path(__file__).parents[1]
5572
file_tests = []
5673
# Filter down to just test files.
57-
for f in sys.argv[1:]:
74+
for f in args.files:
5875
if f.endswith(".carbon"):
5976
path = str(Path(f).resolve().relative_to(repo_root))
6077
if path.count("/testdata/"):
6178
file_tests.append(path)
6279
if not file_tests:
6380
sys.exit(
64-
f"Args do not seem to be test files; for example, {sys.argv[1]}"
81+
"Args do not seem to be test files; for example, "
82+
f"{args.files[0]}"
6583
)
6684
argv.append("--file_tests=" + ",".join(file_tests))
6785
# Provide an empty stdin so that the driver tests that read from stdin

0 commit comments

Comments
 (0)