runtime: add trivial stack traces to exceptions
This commit is contained in:
committed by
SvyatoslavScherbina
parent
b7b99d5684
commit
f4014b0f29
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user