Custom exception reporter. (#2604)

This commit is contained in:
Nikolay Igotti
2019-01-31 16:24:45 +03:00
committed by GitHub
parent 599f188959
commit da652713e6
6 changed files with 95 additions and 11 deletions
+7
View File
@@ -2036,6 +2036,13 @@ task extend_exception(type: RunKonanTest) {
source = "runtime/exceptions/extend0.kt"
}
task custom_hook(type: RunStandaloneKonanTest) {
disabled = (project.testTarget == 'wasm32') // Uses exceptions.
goldValue = "value 42: Error\n"
expectedExitStatus = 1
source = "runtime/exceptions/custom_hook.kt"
}
task runtime_math_exceptions(type: RunKonanTest) {
disabled = (project.testTarget == 'wasm32')
source = "stdlib_external/numbers/MathExceptionTest.kt"
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2018 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.
*/
import kotlin.test.*
import kotlin.native.concurrent.*
fun main(args : Array<String>) {
assertFailsWith<InvalidMutabilityException> {
setUnhandledExceptionHook { _ -> println("wrong") }
}
val x = 42
val old = setUnhandledExceptionHook({
throwable: Throwable -> println("value $x: ${throwable::class.simpleName}")
}.freeze())
assertNull(old)
throw Error("an error")
}
+4 -5
View File
@@ -12,18 +12,17 @@ import kotlin.native.internal.ExportForCppRuntime
@SymbolName("EntryPointSelector")
external fun EntryPointSelector(args: Array<String>)
@SymbolName("OnUnhandledException")
external private fun OnUnhandledException(throwable: Throwable)
@ExportForCppRuntime
private fun Konan_start(args: Array<String>): Int {
try {
EntryPointSelector(args)
// Successfully finished:
return 0
} catch (e: Throwable) {
// TODO: may be add some more info.
print("Uncaught exception from Kotlin's main: ")
e.printStackTrace()
OnUnhandledException(e)
return 1
}
}
+24 -3
View File
@@ -58,6 +58,13 @@ class AutoFree {
}
};
// RuntimeUtiks.kt
extern "C" void ReportUnhandledException(KRef throwable);
extern "C" void ExceptionReporterLaunchpad(KRef reporter, KRef throwable);
KRef currentUnhandledExceptionHook = nullptr;
int32_t currentUnhandledExceptionHookLock = 0;
#if USE_GCC_UNWIND
struct Backtrace {
Backtrace(int count, int skip) : index(0), skipCount(skip) {
@@ -206,10 +213,24 @@ void ThrowException(KRef exception) {
#endif
}
void ReportUnhandledException(KRef e);
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook) {
RETURN_RESULT_OF(SwapRefLocked,
&currentUnhandledExceptionHook, currentUnhandledExceptionHook, hook, &currentUnhandledExceptionHookLock);
}
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef e) {
ReportUnhandledException(e);
void OnUnhandledException(KRef throwable) {
ObjHolder handlerHolder;
auto* handler = SwapRefLocked(&currentUnhandledExceptionHook, currentUnhandledExceptionHook, nullptr,
&currentUnhandledExceptionHookLock, handlerHolder.slot());
if (handler == nullptr) {
ReportUnhandledException(throwable);
} else {
ExceptionReporterLaunchpad(handler, throwable);
}
}
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) {
OnUnhandledException(throwable);
konan::abort();
}
@@ -4,6 +4,9 @@
*/
package kotlin.native
import kotlin.native.concurrent.isFrozen
import kotlin.native.concurrent.InvalidMutabilityException
/**
* Initializes Kotlin runtime for the current thread, if not inited already.
*/
@@ -23,4 +26,27 @@ public class IncorrectDereferenceException : RuntimeException {
constructor() : super()
constructor(message: String) : super(message)
}
}
/**
* Typealias describing custom exception reporting hook.
*/
public typealias ReportUnhandledExceptionHook = Function1<Throwable, Unit>
/**
* Install custom unhandled exception hook. Returns old hook, or null if it was not specified.
* Hook is invoked whenever there's uncaught exception reaching boundaries of the Kotlin world,
* i.e. top level main(), or when Objective-C to Kotlin call not marked with @Throws throws an exception.
* Hook must be a frozen lambda, so that it could be called from any thread/worker.
* Hook is invoked once, and is cleared afterwards, so that memory leak detection works as expected even
* with custom exception hooks.
*/
public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook? {
if (!hook.isFrozen) {
throw InvalidMutabilityException("Unhandled exception hook must be frozen")
}
return setUnhandledExceptionHook0(hook)
}
@SymbolName("Kotlin_setUnhandledExceptionHook")
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?
@@ -86,9 +86,18 @@ internal fun PrintThrowable(throwable: Throwable) {
}
@ExportForCppRuntime
internal fun ReportUnhandledException(e: Throwable) {
internal fun ReportUnhandledException(throwable: Throwable) {
print("Uncaught Kotlin exception: ")
e.printStackTrace()
throwable.printStackTrace()
}
@ExportForCppRuntime
internal fun ExceptionReporterLaunchpad(reporter: (Throwable) -> Unit, throwable: Throwable) {
try {
reporter(throwable)
} catch (t: Throwable) {
ReportUnhandledException(t)
}
}
@ExportForCppRuntime