ART: Add support for runtime debug checks
Add support for runtime-switchable debug flags. These flags are only
enabled in debug mode, and a constexpr false in release mode. This
allows to distinguish fast from slow checks, for example.
To simplify usage, two macros should be used. DECLARE_RUNTIME_DEBUG_FLAG
defines a static class member or constexpr as the flag. DEFINE_RUNTIME_DEBUG_FLAG
defines the static storage and registers it, if needed. See the
documentation in logging.h.
As is, runtime checks are disabled by default. Pass -XX:SlowDebug=true
to enable them on startup.
Add logging_test. Fix up tests and scripts to enable slow debug checks.
Bug: 35644369
Test: m test-art-host
Change-Id: Icc1cb0bb921d863514c55abef794c5c96f8af801
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 20f95c0..9b110eb 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -528,6 +528,7 @@
"base/hash_set_test.cc",
"base/hex_dump_test.cc",
"base/histogram_test.cc",
+ "base/logging_test.cc",
"base/mutex_test.cc",
"base/safe_copy_test.cc",
"base/scoped_flock_test.cc",
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index adfd7d3..2be9067 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -34,6 +34,55 @@
namespace art {
+// We test here that the runtime-debug-checks are actually a no-op constexpr false in release
+// builds, as we can't check that in gtests (which are always debug).
+
+#ifdef NDEBUG
+namespace {
+DECLARE_RUNTIME_DEBUG_FLAG(kTestForConstexpr);
+static_assert(!kTestForConstexpr, "Issue with DECLARE_RUNTIME_DEBUG_FLAG in NDEBUG.");
+}
+#endif
+
+// Implementation of runtime debug flags. This should be compile-time optimized away in release
+// builds.
+namespace {
+bool gSlowEnabled = false; // Default for slow flags is "off."
+
+// Use a function with a static to ensure our vector storage doesn't have initialization order
+// issues.
+std::vector<bool*>& GetFlagPtrs() {
+ static std::vector<bool*> g_flag_ptrs;
+ return g_flag_ptrs;
+}
+
+bool RegisterRuntimeDebugFlagImpl(bool* flag_ptr) {
+ GetFlagPtrs().push_back(flag_ptr);
+ return gSlowEnabled;
+}
+
+void SetRuntimeDebugFlagsEnabledImpl(bool enabled) {
+ gSlowEnabled = enabled;
+ for (bool* flag_ptr : GetFlagPtrs()) {
+ *flag_ptr = enabled;
+ }
+}
+
+} // namespace
+
+bool RegisterRuntimeDebugFlag(bool* flag_ptr) {
+ if (kIsDebugBuild) {
+ return RegisterRuntimeDebugFlagImpl(flag_ptr);
+ }
+ return false;
+}
+
+void SetRuntimeDebugFlagsEnabled(bool enabled) {
+ if (kIsDebugBuild) {
+ SetRuntimeDebugFlagsEnabledImpl(enabled);
+ }
+}
+
LogVerbosity gLogVerbosity;
unsigned int gAborting = 0;
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 7a9184e..d8954e5 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -62,6 +62,43 @@
// Global log verbosity setting, initialized by InitLogging.
extern LogVerbosity gLogVerbosity;
+// Runtime debug flags are flags that have a runtime component, that is, their value can be changed.
+// This is meant to implement fast vs slow debug builds, in that certain debug flags can be turned
+// on and off. To that effect, expose two macros to help implement and globally drive these flags:
+//
+// In the header, declare a (class) flag like this:
+//
+// class C {
+// DECLARE_RUNTIME_DEBUG_FLAG(kFlag);
+// };
+//
+// This will declare a flag kFlag that is a constexpr false in release builds, and a static field
+// in debug builds. Usage is than uniform as C::kFlag.
+//
+// In the cc file, define the flag like this:
+//
+// DEFINE_RUNTIME_DEBUG_FLAG(C, kFlag);
+//
+// This will define the static storage, as necessary, and register the flag with the runtime
+// infrastructure to toggle the value.
+
+#ifdef NDEBUG
+#define DECLARE_RUNTIME_DEBUG_FLAG(x) \
+ static constexpr bool x = false;
+// Note: the static_assert in the following only works for public flags. Fix this when we cross
+// the line at some point.
+#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \
+ static_assert(!C::x, "Unexpected enabled flag in release build");
+#else
+#define DECLARE_RUNTIME_DEBUG_FLAG(x) \
+ static bool x;
+#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \
+ bool C::x = RegisterRuntimeDebugFlag(&C::x);
+#endif // NDEBUG
+
+bool RegisterRuntimeDebugFlag(bool* runtime_debug_flag);
+void SetRuntimeDebugFlagsEnabled(bool enabled);
+
// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive
// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown
// makes forward progress.
diff --git a/runtime/base/logging_test.cc b/runtime/base/logging_test.cc
new file mode 100644
index 0000000..d380b9e
--- /dev/null
+++ b/runtime/base/logging_test.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logging.h"
+
+#include <type_traits>
+
+#include "android-base/logging.h"
+#include "base/bit_utils.h"
+#include "base/macros.h"
+#include "common_runtime_test.h"
+
+namespace art {
+
+static void SimpleAborter(const char* msg) {
+ LOG(FATAL_WITHOUT_ABORT) << msg;
+ _exit(1);
+}
+
+class LoggingTest : public CommonRuntimeTest {
+ protected:
+ void PostRuntimeCreate() OVERRIDE {
+ // In our abort tests we really don't want the runtime to create a real dump.
+ android::base::SetAborter(SimpleAborter);
+ }
+};
+
+#ifdef NDEBUG
+#error Unexpected NDEBUG
+#endif
+
+class TestClass {
+ public:
+ DECLARE_RUNTIME_DEBUG_FLAG(kFlag);
+};
+DEFINE_RUNTIME_DEBUG_FLAG(TestClass, kFlag);
+
+TEST_F(LoggingTest, DECL_DEF) {
+ SetRuntimeDebugFlagsEnabled(true);
+ EXPECT_TRUE(TestClass::kFlag);
+
+ SetRuntimeDebugFlagsEnabled(false);
+ EXPECT_FALSE(TestClass::kFlag);
+}
+
+} // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f925994..5a4a26a 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -404,6 +404,7 @@
options.push_back(std::make_pair("-Xcheck:jni", nullptr));
options.push_back(std::make_pair(min_heap_string, nullptr));
options.push_back(std::make_pair(max_heap_string, nullptr));
+ options.push_back(std::make_pair("-XX:SlowDebug=true", nullptr));
callbacks_.reset(new NoopCompilerCallbacks());
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index abb6f8c..b1eb506 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -18,6 +18,7 @@
#include <sstream>
+#include "base/logging.h"
#include "base/stringpiece.h"
#include "debugger.h"
#include "gc/heap.h"
@@ -306,6 +307,10 @@
.Define("-XX:ThreadSuspendTimeout=_") // in ms
.WithType<MillisecondsToNanoseconds>() // store as ns
.IntoKey(M::ThreadSuspendTimeout)
+ .Define("-XX:SlowDebug=_")
+ .WithType<bool>()
+ .WithValueMap({{"false", false}, {"true", true}})
+ .IntoKey(M::SlowDebug)
.Ignore({
"-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
"-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
@@ -517,6 +522,8 @@
MaybeOverrideVerbosity();
+ SetRuntimeDebugFlagsEnabled(args.Get(M::SlowDebug));
+
// -Xprofile:
Trace::SetDefaultClockSource(args.GetOrDefault(M::ProfileClock));
@@ -704,6 +711,7 @@
UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n");
UsageMessage(stream, " -XX:LargeObjectThreshold=N\n");
UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n");
+ UsageMessage(stream, " -XX:SlowDebug={false,true}\n");
UsageMessage(stream, " -Xmethod-trace\n");
UsageMessage(stream, " -Xmethod-trace-file:filename");
UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n");
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index cfc681f..3d23e20 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -142,4 +142,6 @@
// Runtime::Abort.
RUNTIME_OPTIONS_KEY (void (*)(), HookAbort, nullptr)
+RUNTIME_OPTIONS_KEY (bool, SlowDebug, false)
+
#undef RUNTIME_OPTIONS_KEY
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index c44fb97..d24edcc 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -79,6 +79,9 @@
# build step (e.g. dex2oat) were finished writing.
SYNC_BEFORE_RUN="n"
+# When running a debug build, we want to run with all checks.
+ANDROID_FLAGS="${ANDROID_FLAGS} -XX:SlowDebug=true"
+
while true; do
if [ "x$1" = "x--quiet" ]; then
QUIET="y"
diff --git a/tools/art b/tools/art
index 0bc08f0..2e5df91 100644
--- a/tools/art
+++ b/tools/art
@@ -24,6 +24,7 @@
LIBART=libart.so
JIT_PROFILE="no"
VERBOSE="no"
+EXTRA_OPTIONS=""
# Follow all sym links to get the program name.
if [ z"$BASH_SOURCE" != z ]; then
@@ -147,6 +148,8 @@
;& # Fallthrough
--debug)
LIBART="libartd.so"
+ # Expect that debug mode wants all checks.
+ EXTRA_OPTIONS="${EXTRA_OPTIONS} -XX:SlowDebug=true"
;;
--gdb)
LIBART="libartd.so"
@@ -203,7 +206,6 @@
LIBDIR="$(find_libdir $ART_BINARY_PATH)"
LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
-EXTRA_OPTIONS=""
# If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own,
# and ensure we delete it at the end.
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index f742767..225fb39 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -154,7 +154,7 @@
if [[ $debug == "yes" ]]; then
art="$art -d"
art_debugee="$art_debugee -d"
- vm_args="$vm_args --vm-arg -XXlib:libartd.so"
+ vm_args="$vm_args --vm-arg -XXlib:libartd.so --vm-arg -XX:SlowDebug=true"
fi
if [[ $verbose == "yes" ]]; then
# Enable JDWP logs in the debuggee.
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 8b3df3a..6dcc23a 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -127,7 +127,7 @@
elif [[ "$1" == "--debug" ]]; then
# Remove the --debug from the arguments.
vogar_args=${vogar_args/$1}
- vogar_args="$vogar_args --vm-arg -XXlib:libartd.so"
+ vogar_args="$vogar_args --vm-arg -XXlib:libartd.so --vm-arg -XX:SlowDebug=true"
debug=true
shift
elif [[ "$1" == "-Xgc:gcstress" ]]; then