Custom exception reporter. (#2604)
This commit is contained in:
@@ -2036,6 +2036,13 @@ task extend_exception(type: RunKonanTest) {
|
|||||||
source = "runtime/exceptions/extend0.kt"
|
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) {
|
task runtime_math_exceptions(type: RunKonanTest) {
|
||||||
disabled = (project.testTarget == 'wasm32')
|
disabled = (project.testTarget == 'wasm32')
|
||||||
source = "stdlib_external/numbers/MathExceptionTest.kt"
|
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")
|
||||||
|
}
|
||||||
@@ -12,18 +12,17 @@ import kotlin.native.internal.ExportForCppRuntime
|
|||||||
@SymbolName("EntryPointSelector")
|
@SymbolName("EntryPointSelector")
|
||||||
external fun EntryPointSelector(args: Array<String>)
|
external fun EntryPointSelector(args: Array<String>)
|
||||||
|
|
||||||
|
@SymbolName("OnUnhandledException")
|
||||||
|
external private fun OnUnhandledException(throwable: Throwable)
|
||||||
|
|
||||||
@ExportForCppRuntime
|
@ExportForCppRuntime
|
||||||
private fun Konan_start(args: Array<String>): Int {
|
private fun Konan_start(args: Array<String>): Int {
|
||||||
try {
|
try {
|
||||||
EntryPointSelector(args)
|
EntryPointSelector(args)
|
||||||
|
|
||||||
// Successfully finished:
|
// Successfully finished:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
// TODO: may be add some more info.
|
OnUnhandledException(e)
|
||||||
print("Uncaught exception from Kotlin's main: ")
|
|
||||||
e.printStackTrace()
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
#if USE_GCC_UNWIND
|
||||||
struct Backtrace {
|
struct Backtrace {
|
||||||
Backtrace(int count, int skip) : index(0), skipCount(skip) {
|
Backtrace(int count, int skip) : index(0), skipCount(skip) {
|
||||||
@@ -206,10 +213,24 @@ void ThrowException(KRef exception) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportUnhandledException(KRef e);
|
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook) {
|
||||||
|
RETURN_RESULT_OF(SwapRefLocked,
|
||||||
|
¤tUnhandledExceptionHook, currentUnhandledExceptionHook, hook, ¤tUnhandledExceptionHookLock);
|
||||||
|
}
|
||||||
|
|
||||||
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef e) {
|
void OnUnhandledException(KRef throwable) {
|
||||||
ReportUnhandledException(e);
|
ObjHolder handlerHolder;
|
||||||
|
auto* handler = SwapRefLocked(¤tUnhandledExceptionHook, currentUnhandledExceptionHook, nullptr,
|
||||||
|
¤tUnhandledExceptionHookLock, handlerHolder.slot());
|
||||||
|
if (handler == nullptr) {
|
||||||
|
ReportUnhandledException(throwable);
|
||||||
|
} else {
|
||||||
|
ExceptionReporterLaunchpad(handler, throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) {
|
||||||
|
OnUnhandledException(throwable);
|
||||||
konan::abort();
|
konan::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
package kotlin.native
|
package kotlin.native
|
||||||
|
|
||||||
|
import kotlin.native.concurrent.isFrozen
|
||||||
|
import kotlin.native.concurrent.InvalidMutabilityException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes Kotlin runtime for the current thread, if not inited already.
|
* Initializes Kotlin runtime for the current thread, if not inited already.
|
||||||
*/
|
*/
|
||||||
@@ -23,4 +26,27 @@ public class IncorrectDereferenceException : RuntimeException {
|
|||||||
constructor() : super()
|
constructor() : super()
|
||||||
|
|
||||||
constructor(message: String) : super(message)
|
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
|
@ExportForCppRuntime
|
||||||
internal fun ReportUnhandledException(e: Throwable) {
|
internal fun ReportUnhandledException(throwable: Throwable) {
|
||||||
print("Uncaught Kotlin exception: ")
|
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
|
@ExportForCppRuntime
|
||||||
|
|||||||
Reference in New Issue
Block a user