runtime: add trivial stack traces to exceptions

This commit is contained in:
Svyatoslav Scherbina
2016-12-02 11:23:50 +07:00
committed by SvyatoslavScherbina
parent b7b99d5684
commit f4014b0f29
9 changed files with 91 additions and 12 deletions
+2 -1
View File
@@ -90,7 +90,8 @@ abstract class KonanTest extends DefaultTask {
println "${bcFile.absolutePath} -> ${outputFile.absolutePath}" println "${bcFile.absolutePath} -> ${outputFile.absolutePath}"
println "tool: ${llvmLlc}" println "tool: ${llvmLlc}"
project.exec { project.exec {
commandLine "${llvmLlc}", "-o", "${outputFile.absolutePath}" , "${bcFile}" commandLine "${llvmLlc}", "-o", "${outputFile.absolutePath}" , "${bcFile}",
'-disable-fp-elim' // currently required for stack traces
} }
return outputFile return outputFile
} }
+3 -3
View File
@@ -10,9 +10,9 @@ private fun Konan_start(args: Array<String>) {
main(args) main(args)
} catch (e: Throwable) { } catch (e: Throwable) {
// TODO: Remove .toString() when println is more capable, // TODO: may be add some more info.
// and may be add some more info.
print("Uncaught exception from Kotlin's main: ") print("Uncaught exception from Kotlin's main: ")
println(e.toString()) e.printStackTrace()
// TODO: should exit with non-zero code.
} }
} }
+1 -1
View File
@@ -19,7 +19,7 @@ KRef Kotlin_Array_get(const ArrayHeader* obj, KInt index) {
return *ArrayAddressOfElementAt(obj, 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<uint32_t>(index) >= obj->count_) { if (static_cast<uint32_t>(index) >= obj->count_) {
ThrowArrayIndexOutOfBoundsException(); ThrowArrayIndexOutOfBoundsException();
} }
+49
View File
@@ -1,5 +1,10 @@
#include <execinfo.h>
#include <stdlib.h>
#include <string.h>
#include "Assert.h" #include "Assert.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "Natives.h"
#include "Types.h" #include "Types.h"
class KotlinException { 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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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) { void ThrowException(KRef exception) {
RuntimeAssert(exception != nullptr && IsInstance(exception, theThrowableTypeInfo), RuntimeAssert(exception != nullptr && IsInstance(exception, theThrowableTypeInfo),
"Throwing something non-throwable"); "Throwing something non-throwable");
+3
View File
@@ -7,6 +7,9 @@
extern "C" { extern "C" {
#endif #endif
// Returns current stacktrace as Array<String>.
KArrayRef GetCurrentStackTrace();
// Throws arbitrary exception. // Throws arbitrary exception.
void ThrowException(KRef exception); void ThrowException(KRef exception);
+4
View File
@@ -180,4 +180,8 @@ KString Kotlin_String_subSequence(KString thiz, KInt startIndex, KInt endIndex)
return result; return result;
} }
KArrayRef Kotlin_getCurrentStackTrace() {
return GetCurrentStackTrace();
}
} // extern "C" } // extern "C"
+5 -3
View File
@@ -37,8 +37,8 @@ inline const T* PrimitiveArrayAddressOfElementAt(
return reinterpret_cast<const T*>(obj + 1) + index; return reinterpret_cast<const T*>(obj + 1) + index;
} }
inline KRef* ArrayAddressOfElementAt(ArrayHeader* obj, KInt index) { inline KConstRef* ArrayAddressOfElementAt(ArrayHeader* obj, KInt index) {
return reinterpret_cast<KRef*>(obj + 1) + index; return reinterpret_cast<KConstRef*>(obj + 1) + index;
} }
inline const KRef* ArrayAddressOfElementAt(const ArrayHeader* obj, KInt 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. // TODO: those must be compiler intrinsics afterwards.
ArrayHeader* Kotlin_Array_clone(const ArrayHeader* thiz); ArrayHeader* Kotlin_Array_clone(const ArrayHeader* thiz);
KRef Kotlin_Array_get(const ArrayHeader* thiz, KInt index); 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); KInt Kotlin_Array_getArrayLength(const ArrayHeader* thiz);
ArrayHeader* Kotlin_ByteArray_clone(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); KInt Kotlin_String_getStringLength(KString thiz);
KString Kotlin_String_subSequence(KString thiz, KInt startIndex, KInt endIndex); KString Kotlin_String_subSequence(KString thiz, KInt startIndex, KInt endIndex);
KArrayRef Kotlin_getCurrentStackTrace();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+1
View File
@@ -15,6 +15,7 @@ typedef float KFloat;
typedef double KDouble; typedef double KDouble;
typedef ObjHeader* KRef; typedef ObjHeader* KRef;
typedef ArrayHeader* KArrayRef;
typedef const ObjHeader* KConstRef; typedef const ObjHeader* KConstRef;
typedef const ArrayHeader* KString; typedef const ArrayHeader* KString;
+23 -4
View File
@@ -8,16 +8,35 @@ package kotlin
*/ */
@ExportTypeInfo("theThrowableTypeInfo") @ExportTypeInfo("theThrowableTypeInfo")
public open class Throwable(open val message: String?, open val cause: Throwable?) { public open class Throwable(open val message: String?, open val cause: Throwable?) {
constructor(message: String?) : this(message, null) constructor(message: String?) : this(message, null)
constructor(cause: Throwable?) : this(cause?.toString(), cause) constructor(cause: Throwable?) : this(cause?.toString(), cause)
constructor() : this(null, null) constructor() : this(null, null)
private val stacktrace: Array<String> = 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 { override fun toString(): String {
/* enable, once codegen is improved. val s = "Throwable" // TODO: should be class name
val s = "Throwable" return if (message != null) s + ": " + message.toString() else s
return if (message != null) s + ": " + message.toString() else s */
return "Throwable"
} }
} }
@SymbolName("Kotlin_getCurrentStackTrace")
private external fun getCurrentStackTrace(): Array<String>