diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 1103dd90cba..75db75ef105 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -90,7 +90,8 @@ abstract class KonanTest extends DefaultTask { println "${bcFile.absolutePath} -> ${outputFile.absolutePath}" println "tool: ${llvmLlc}" project.exec { - commandLine "${llvmLlc}", "-o", "${outputFile.absolutePath}" , "${bcFile}" + commandLine "${llvmLlc}", "-o", "${outputFile.absolutePath}" , "${bcFile}", + '-disable-fp-elim' // currently required for stack traces } return outputFile } diff --git a/runtime/src/launcher/kotlin/konan/start.kt b/runtime/src/launcher/kotlin/konan/start.kt index e5132fcb708..fa750bb3da7 100644 --- a/runtime/src/launcher/kotlin/konan/start.kt +++ b/runtime/src/launcher/kotlin/konan/start.kt @@ -10,9 +10,9 @@ private fun Konan_start(args: Array) { main(args) } catch (e: Throwable) { - // TODO: Remove .toString() when println is more capable, - // and may be add some more info. + // TODO: may be add some more info. print("Uncaught exception from Kotlin's main: ") - println(e.toString()) + e.printStackTrace() + // TODO: should exit with non-zero code. } } diff --git a/runtime/src/main/cpp/Arrays.cpp b/runtime/src/main/cpp/Arrays.cpp index 00769bcbc11..e80984fddd6 100644 --- a/runtime/src/main/cpp/Arrays.cpp +++ b/runtime/src/main/cpp/Arrays.cpp @@ -19,7 +19,7 @@ KRef Kotlin_Array_get(const ArrayHeader* obj, KInt index) { return *ArrayAddressOfElementAt(obj, index); } -void Kotlin_Array_set(ArrayHeader* obj, KInt index, KRef value) { +void Kotlin_Array_set(ArrayHeader* obj, KInt index, KConstRef value) { if (static_cast(index) >= obj->count_) { ThrowArrayIndexOutOfBoundsException(); } diff --git a/runtime/src/main/cpp/Exceptions.cpp b/runtime/src/main/cpp/Exceptions.cpp index 62882da5a91..f97d6b76d33 100644 --- a/runtime/src/main/cpp/Exceptions.cpp +++ b/runtime/src/main/cpp/Exceptions.cpp @@ -1,5 +1,10 @@ +#include +#include +#include + #include "Assert.h" #include "Exceptions.h" +#include "Natives.h" #include "Types.h" class KotlinException { @@ -16,10 +21,54 @@ class KotlinException { }; }; +// TODO: it seems to be very common case; does C++ std library provide something like this? +class AutoFree { + private: + void* mem_; + + public: + AutoFree(void* mem): mem_(mem) {} + + ~AutoFree() { + free(mem_); + } +}; + +// TODO: this method ignores the encoding +KString CreateKotlinStringFromCString(const char* str) { + int32_t length = strlen(str); + ArrayHeader* result = ArrayContainer(theStringTypeInfo, length).GetPlace(); + memcpy( + ByteArrayAddressOfElementAt(result, 0), + str, length); + return result; +} + #ifdef __cplusplus extern "C" { #endif +// TODO: this implementation is just a hack, e.g. the result is inexact; +// however it is better to have an inexact stacktrace than not to have any. +KArrayRef GetCurrentStackTrace() { + const int maxSize = 32; + void* buffer[maxSize]; + + int size = backtrace(buffer, maxSize); + char** symbols = backtrace_symbols(buffer, size); + RuntimeAssert(symbols != nullptr, "Not enough memory to retrieve the stacktrace"); + + AutoFree autoFree(symbols); + KArrayRef result = AllocArrayInstance(theArrayTypeInfo, SCOPE_GLOBAL, size); + + for (int i = 0; i < size; ++i) { + KString symbol = CreateKotlinStringFromCString(symbols[i]); + Kotlin_Array_set(result, i, symbol); + } + + return result; +} + void ThrowException(KRef exception) { RuntimeAssert(exception != nullptr && IsInstance(exception, theThrowableTypeInfo), "Throwing something non-throwable"); diff --git a/runtime/src/main/cpp/Exceptions.h b/runtime/src/main/cpp/Exceptions.h index 563e144e827..d97a8972bb1 100644 --- a/runtime/src/main/cpp/Exceptions.h +++ b/runtime/src/main/cpp/Exceptions.h @@ -7,6 +7,9 @@ extern "C" { #endif +// Returns current stacktrace as Array. +KArrayRef GetCurrentStackTrace(); + // Throws arbitrary exception. void ThrowException(KRef exception); diff --git a/runtime/src/main/cpp/Natives.cpp b/runtime/src/main/cpp/Natives.cpp index baae7e3ba37..6c1c7c80686 100644 --- a/runtime/src/main/cpp/Natives.cpp +++ b/runtime/src/main/cpp/Natives.cpp @@ -180,4 +180,8 @@ KString Kotlin_String_subSequence(KString thiz, KInt startIndex, KInt endIndex) return result; } +KArrayRef Kotlin_getCurrentStackTrace() { + return GetCurrentStackTrace(); +} + } // extern "C" diff --git a/runtime/src/main/cpp/Natives.h b/runtime/src/main/cpp/Natives.h index c1b9dc9b190..cba6f64c036 100644 --- a/runtime/src/main/cpp/Natives.h +++ b/runtime/src/main/cpp/Natives.h @@ -37,8 +37,8 @@ inline const T* PrimitiveArrayAddressOfElementAt( return reinterpret_cast(obj + 1) + index; } -inline KRef* ArrayAddressOfElementAt(ArrayHeader* obj, KInt index) { - return reinterpret_cast(obj + 1) + index; +inline KConstRef* ArrayAddressOfElementAt(ArrayHeader* obj, KInt index) { + return reinterpret_cast(obj + 1) + index; } inline const KRef* ArrayAddressOfElementAt(const ArrayHeader* obj, KInt index) { @@ -60,7 +60,7 @@ KString Kotlin_Any_toString(KConstRef thiz); // TODO: those must be compiler intrinsics afterwards. ArrayHeader* Kotlin_Array_clone(const ArrayHeader* thiz); KRef Kotlin_Array_get(const ArrayHeader* thiz, KInt index); -void Kotlin_Array_set(ArrayHeader* thiz, KInt index, KRef value); +void Kotlin_Array_set(ArrayHeader* thiz, KInt index, KConstRef value); KInt Kotlin_Array_getArrayLength(const ArrayHeader* thiz); ArrayHeader* Kotlin_ByteArray_clone(const ArrayHeader* thiz); @@ -98,6 +98,8 @@ KString Kotlin_String_plusImpl(KString thiz, KString other); KInt Kotlin_String_getStringLength(KString thiz); KString Kotlin_String_subSequence(KString thiz, KInt startIndex, KInt endIndex); +KArrayRef Kotlin_getCurrentStackTrace(); + #ifdef __cplusplus } #endif diff --git a/runtime/src/main/cpp/Types.h b/runtime/src/main/cpp/Types.h index dde452d8707..a46addf5b9f 100644 --- a/runtime/src/main/cpp/Types.h +++ b/runtime/src/main/cpp/Types.h @@ -15,6 +15,7 @@ typedef float KFloat; typedef double KDouble; typedef ObjHeader* KRef; +typedef ArrayHeader* KArrayRef; typedef const ObjHeader* KConstRef; typedef const ArrayHeader* KString; diff --git a/runtime/src/main/kotlin/kotlin/Throwable.kt b/runtime/src/main/kotlin/kotlin/Throwable.kt index 3b6e1cb3505..cbcdd9f001d 100644 --- a/runtime/src/main/kotlin/kotlin/Throwable.kt +++ b/runtime/src/main/kotlin/kotlin/Throwable.kt @@ -8,16 +8,35 @@ package kotlin */ @ExportTypeInfo("theThrowableTypeInfo") public open class Throwable(open val message: String?, open val cause: Throwable?) { + constructor(message: String?) : this(message, null) constructor(cause: Throwable?) : this(cause?.toString(), cause) constructor() : this(null, null) + private val stacktrace: Array = getCurrentStackTrace() + + fun printStackTrace() { + println(this.toString()) + for (element in stacktrace) { + println(" at " + element) + } + + this.cause?.printEnclosedStackTrace(this) + } + + private fun printEnclosedStackTrace(enclosing: Throwable) { + // TODO: should skip common stack frames + print("Caused by: ") + this.printStackTrace() + } + override fun toString(): String { - /* enable, once codegen is improved. - val s = "Throwable" - return if (message != null) s + ": " + message.toString() else s */ - return "Throwable" + val s = "Throwable" // TODO: should be class name + return if (message != null) s + ": " + message.toString() else s } } + +@SymbolName("Kotlin_getCurrentStackTrace") +private external fun getCurrentStackTrace(): Array \ No newline at end of file