From e3bc247557b3498abf1951bb70f0df9b943f6b67 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Thu, 10 Dec 2020 19:52:41 +0700 Subject: [PATCH] [runtime] Support formatted output in the runtime assertions --- .../runtime/src/main/cpp/KAssert.cpp | 49 ++++++++++--------- kotlin-native/runtime/src/main/cpp/KAssert.h | 14 +++--- .../runtime/src/main/cpp/Porting.cpp | 6 ++- kotlin-native/runtime/src/main/cpp/Porting.h | 2 + .../runtime/src/mm/cpp/ThreadState.cpp | 17 ++----- 5 files changed, 45 insertions(+), 43 deletions(-) diff --git a/kotlin-native/runtime/src/main/cpp/KAssert.cpp b/kotlin-native/runtime/src/main/cpp/KAssert.cpp index ee3ab1fdeaa..224f6cd7336 100644 --- a/kotlin-native/runtime/src/main/cpp/KAssert.cpp +++ b/kotlin-native/runtime/src/main/cpp/KAssert.cpp @@ -1,29 +1,32 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * 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. + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. */ -#include "KAssert.h" +#include #include "Porting.h" -void RuntimeAssertFailed(const char* location, const char* message) { - // TODO: produce stacktrace and such. - char buf[1024]; - if (location != nullptr) - konan::snprintf(buf, sizeof(buf), "%s: runtime assert: %s\n", location, message); - else - konan::snprintf(buf, sizeof(buf), "runtime assert: %s\n", message); - konan::consoleErrorUtf8(buf, konan::strnlen(buf, sizeof(buf))); - konan::abort(); +RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* format, ...) { + char buf[1024]; + int written = -1; + + // Write the title with a source location. + if (location != nullptr) { + written = konan::snprintf(buf, sizeof(buf), "%s: runtime assert: ", location); + } else { + written = konan::snprintf(buf, sizeof(buf), "runtime assert: "); + } + + // Write the message. + if (written >= 0 && static_cast(written) < sizeof(buf)) { + std::va_list args; + va_start(args, format); + konan::vsnprintf(buf + written, sizeof(buf) - written, format, args); + va_end(args); + } + + konan::consoleErrorUtf8(buf, konan::strnlen(buf, sizeof(buf))); + konan::consoleErrorf("\n"); + // TODO: Write the stacktrace. + konan::abort(); } diff --git a/kotlin-native/runtime/src/main/cpp/KAssert.h b/kotlin-native/runtime/src/main/cpp/KAssert.h index aa8edac245b..0f43d854513 100644 --- a/kotlin-native/runtime/src/main/cpp/KAssert.h +++ b/kotlin-native/runtime/src/main/cpp/KAssert.h @@ -25,7 +25,7 @@ #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) -RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* message); +RUNTIME_NORETURN void RuntimeAssertFailed(const char* location, const char* message, ...); namespace internal { @@ -46,9 +46,9 @@ extern "C" const int KonanNeedDebugInfo; #if KONAN_ENABLE_ASSERT // Use RuntimeAssert() in internal state checks, which could be ignored in production. -#define RuntimeAssert(condition, message) \ -if (KonanNeedDebugInfo && (!(condition))) { \ - RuntimeAssertFailed( __FILE__ ":" TOSTRING(__LINE__), message); \ +#define RuntimeAssert(condition, format, ...) \ +if (KonanNeedDebugInfo && (!(condition))) { \ + RuntimeAssertFailed( __FILE__ ":" TOSTRING(__LINE__), format, ##__VA_ARGS__); \ } #else #define RuntimeAssert(condition, message) @@ -56,9 +56,9 @@ if (KonanNeedDebugInfo && (!(condition))) { \ // Use RuntimeCheck() in runtime checks that could fail due to external condition and shall lead // to program termination. Never compiled out. -#define RuntimeCheck(condition, message) \ - if (!(condition)) { \ - RuntimeAssertFailed(nullptr, message); \ +#define RuntimeCheck(condition, format, ...) \ + if (!(condition)) { \ + RuntimeAssertFailed(nullptr, format, ##__VA_ARGS__); \ } #define TODO(...) \ diff --git a/kotlin-native/runtime/src/main/cpp/Porting.cpp b/kotlin-native/runtime/src/main/cpp/Porting.cpp index 953c16cecdd..b2be18cc11c 100644 --- a/kotlin-native/runtime/src/main/cpp/Porting.cpp +++ b/kotlin-native/runtime/src/main/cpp/Porting.cpp @@ -273,11 +273,15 @@ void* memmem(const void *big, size_t bigLen, const void *little, size_t littleLe int snprintf(char* buffer, size_t size, const char* format, ...) { va_list args; va_start(args, format); - int rv = vsnprintf_impl(buffer, size, format, args); + int rv = vsnprintf(buffer, size, format, args); va_end(args); return rv; } +int vsnprintf(char* buffer, size_t size, const char* format, va_list args) { + return vsnprintf_impl(buffer, size, format, args); +} + size_t strnlen(const char* buffer, size_t maxSize) { return ::strnlen(buffer, maxSize); } diff --git a/kotlin-native/runtime/src/main/cpp/Porting.h b/kotlin-native/runtime/src/main/cpp/Porting.h index 59693a538c6..1085e1fb789 100644 --- a/kotlin-native/runtime/src/main/cpp/Porting.h +++ b/kotlin-native/runtime/src/main/cpp/Porting.h @@ -17,6 +17,7 @@ #ifndef RUNTIME_PORTING_H #define RUNTIME_PORTING_H +#include #include #include @@ -46,6 +47,7 @@ void onThreadExit(void (*destructor)(void*), void* destructorParameter); // by C compiler. void* memmem(const void *big, size_t bigLen, const void *little, size_t littleLen); int snprintf(char* buffer, size_t size, const char* format, ...); +int vsnprintf(char* buffer, size_t size, const char* format, va_list args); size_t strnlen(const char* buffer, size_t maxSize); diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp index 57b7c2410dc..c22fcf361c0 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp @@ -23,16 +23,6 @@ const char* stateToString(mm::ThreadState state) noexcept { } } -std::string unexpectedStateMessage(mm::ThreadState expected, mm::ThreadState actual) noexcept { - return std::string("Unexpected thread state. Expected: ") + stateToString(expected) - + ". Actual: " + stateToString(actual); -} - -std::string illegalStateSwitchMessage(mm::ThreadState oldState, mm::ThreadState newState) noexcept { - return std::string("Illegal thread state switch. Old state: ") + stateToString(oldState) - + ". New state: " + stateToString(newState); -} - } // namespace // Switches the state of the current thread to `newState` and returns the previous state. @@ -40,13 +30,16 @@ ALWAYS_INLINE mm::ThreadState mm::SwitchThreadState(ThreadData* threadData, Thre auto oldState = threadData->setState(newState); // TODO(perf): Mesaure the impact of this assert in debug and opt modes. RuntimeAssert(isStateSwitchAllowed(oldState, newState), - illegalStateSwitchMessage(oldState, newState).c_str()); + "Illegal thread state switch. Old state: %s. New state: %s.", + stateToString(oldState), stateToString(newState)); return oldState; } ALWAYS_INLINE void mm::AssertThreadState(ThreadData* threadData, ThreadState expected) noexcept { auto actual = threadData->state(); - RuntimeAssert(actual == expected, unexpectedStateMessage(expected, actual).c_str()); + RuntimeAssert(actual == expected, + "Unexpected thread state. Expected: %s. Actual: %s.", + stateToString(expected), stateToString(actual)); } mm::ThreadStateGuard::ThreadStateGuard(ThreadData* threadData, ThreadState state) noexcept : threadData_(threadData) {