Lazy stacktrace (#2036)

* stringify stacktrace lazily

* Introduce NativePtrArray

* Added Throwable::getStackTrace
This commit is contained in:
Sergey Bogolepov
2018-09-12 20:27:01 +07:00
committed by GitHub
parent da2736337e
commit 20d3520717
10 changed files with 146 additions and 116 deletions
@@ -97,7 +97,8 @@ internal val arrayTypes = setOf(
"kotlin.FloatArray",
"kotlin.DoubleArray",
"kotlin.BooleanArray",
"kotlin.native.ImmutableBlob"
"kotlin.native.ImmutableBlob",
"kotlin.native.internal.NativePtrArray"
)
@@ -109,7 +109,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
"kotlin.DoubleArray" to LLVMDoubleType()!!,
"kotlin.BooleanArray" to LLVMInt8Type()!!,
"kotlin.String" to LLVMInt16Type()!!,
"kotlin.native.ImmutableBlob" to LLVMInt8Type()!!
"kotlin.native.ImmutableBlob" to LLVMInt8Type()!!,
"kotlin.native.internal.NativePtrArray" to kInt8Ptr
)
// Keep in sync with Konan_RuntimeType.
+47 -78
View File
@@ -48,6 +48,26 @@ inline void copyImpl(KConstRef thiz, KInt fromIndex,
count * sizeof(T));
}
template <class T>
inline void PrimitiveArraySet(KRef thiz, KInt index, T value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<T>(array, index) = value;
}
template <class T>
inline T PrimitiveArrayGet(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<T>(array, index);
}
} // namespace
extern "C" {
@@ -278,20 +298,11 @@ void Kotlin_ByteArray_setDoubleAt(KRef thiz, KInt index, KDouble value) {
}
KChar Kotlin_CharArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<KChar>(array, index);
return PrimitiveArrayGet<KChar>(thiz, index);
}
void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<KChar>(array, index) = value;
PrimitiveArraySet(thiz, index, value);
}
OBJ_GETTER(Kotlin_CharArray_copyOf, KConstRef thiz, KInt newSize) {
@@ -315,20 +326,11 @@ KInt Kotlin_CharArray_getArrayLength(KConstRef thiz) {
}
KShort Kotlin_ShortArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<KShort>(array, index);
return PrimitiveArrayGet<KShort>(thiz, index);
}
void Kotlin_ShortArray_set(KRef thiz, KInt index, KShort value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<KShort>(array, index) = value;
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_ShortArray_getArrayLength(KConstRef thiz) {
@@ -337,20 +339,11 @@ KInt Kotlin_ShortArray_getArrayLength(KConstRef thiz) {
}
KInt Kotlin_IntArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<KInt>(array, index);
return PrimitiveArrayGet<KInt>(thiz, index);
}
void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<KInt>(array, index) = value;
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_IntArray_getArrayLength(KConstRef thiz) {
@@ -410,21 +403,11 @@ void Kotlin_BooleanArray_copyImpl(KConstRef thiz, KInt fromIndex,
}
KLong Kotlin_LongArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<KLong>(array, index);
return PrimitiveArrayGet<KLong>(thiz, index);
}
void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<KLong>(array, index) = value;
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_LongArray_getArrayLength(KConstRef thiz) {
@@ -433,20 +416,11 @@ KInt Kotlin_LongArray_getArrayLength(KConstRef thiz) {
}
KFloat Kotlin_FloatArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<KFloat>(array, index);
return PrimitiveArrayGet<KFloat>(thiz, index);
}
void Kotlin_FloatArray_set(KRef thiz, KInt index, KFloat value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<KFloat>(array, index) = value;
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_FloatArray_getArrayLength(KConstRef thiz) {
@@ -455,20 +429,11 @@ KInt Kotlin_FloatArray_getArrayLength(KConstRef thiz) {
}
KDouble Kotlin_DoubleArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<KDouble>(array, index);
return PrimitiveArrayGet<KDouble>(thiz, index);
}
void Kotlin_DoubleArray_set(KRef thiz, KInt index, KDouble value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<KDouble>(array, index) = value;
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_DoubleArray_getArrayLength(KConstRef thiz) {
@@ -477,20 +442,11 @@ KInt Kotlin_DoubleArray_getArrayLength(KConstRef thiz) {
}
KBoolean Kotlin_BooleanArray_get(KConstRef thiz, KInt index) {
const ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
return *PrimitiveArrayAddressOfElementAt<KBoolean>(array, index);
return PrimitiveArrayGet<KBoolean>(thiz, index);
}
void Kotlin_BooleanArray_set(KRef thiz, KInt index, KBoolean value) {
ArrayHeader* array = thiz->array();
if (static_cast<uint32_t>(index) >= array->count_) {
ThrowArrayIndexOutOfBoundsException();
}
mutabilityCheck(thiz);
*PrimitiveArrayAddressOfElementAt<KBoolean>(array, index) = value;
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_BooleanArray_getArrayLength(KConstRef thiz) {
@@ -498,6 +454,19 @@ KInt Kotlin_BooleanArray_getArrayLength(KConstRef thiz) {
return array->count_;
}
KNativePtr Kotlin_NativePtrArray_get(KConstRef thiz, KInt index) {
return PrimitiveArrayGet<KNativePtr>(thiz, index);
}
void Kotlin_NativePtrArray_set(KRef thiz, KInt index, KNativePtr value) {
PrimitiveArraySet(thiz, index, value);
}
KInt Kotlin_NativePtrArray_getArrayLength(KConstRef thiz) {
const ArrayHeader* array = thiz->array();
return array->count_;
}
OBJ_GETTER(Kotlin_ImmutableBlob_toByteArray, KConstRef thiz, KInt start, KInt count) {
const ArrayHeader* array = thiz->array();
if (start < 0 || count < 0 || start > array->count_ - count) {
+51 -33
View File
@@ -60,17 +60,17 @@ class AutoFree {
#if USE_GCC_UNWIND
struct Backtrace {
Backtrace(int count, int skip) : index(0), skipCount(skip) {
auto result = AllocArrayInstance(
theArrayTypeInfo, count - skipCount, arrayHolder.slot());
uint32_t size = count - skipCount;
if (size < 0) {
size = 0;
}
auto result = AllocArrayInstance(theNativePtrArrayTypeInfo, size, arrayHolder.slot());
// TODO: throw cached OOME?
RuntimeCheck(result != nullptr, "Cannot create backtrace array");
}
void setNextElement(const char* element) {
auto result = CreateStringFromCString(
element, ArrayAddressOfElementAt(obj()->array(), index++));
// TODO: throw cached OOME?
RuntimeCheck(result != nullptr, "Cannot create backtrace array element");
void setNextElement(_Unwind_Ptr element) {
Kotlin_NativePtrArray_set(obj(), index++, (KNativePtr) element);
}
ObjHeader* obj() { return arrayHolder.obj(); }
@@ -100,17 +100,8 @@ _Unwind_Reason_Code unwindCallback(
#else
_Unwind_Ptr address = _Unwind_GetIP(context);
#endif
backtrace->setNextElement(address);
char symbol[512];
if (!AddressToSymbol((const void*)address, symbol, sizeof(symbol))) {
// Make empty string:
symbol[0] = '\0';
}
char line[512];
konan::snprintf(line, sizeof(line) - 1, "%s (%p)",
symbol, (void*)(intptr_t)address);
backtrace->setNextElement(line);
return _URC_NO_REASON;
}
#endif
@@ -123,44 +114,71 @@ extern "C" {
// however it is better to have an inexact stacktrace than not to have any.
OBJ_GETTER0(GetCurrentStackTrace) {
#if OMIT_BACKTRACE
ObjHeader* result = AllocArrayInstance(theArrayTypeInfo, 1, OBJ_RESULT);
ArrayHeader* array = result->array();
CreateStringFromCString("<UNIMPLEMENTED>", ArrayAddressOfElementAt(array, 0));
return result;
return AllocArrayInstance(theNativePtrArrayTypeInfo, 0, OBJ_RESULT);
#else
// Skips first 3 elements as irrelevant.
constexpr int kSkipFrames = 3;
#if USE_GCC_UNWIND
int depth = 0;
_Unwind_Backtrace(depthCountCallback, &depth);
if (depth < kSkipFrames)
return AllocArrayInstance(theArrayTypeInfo, 0, OBJ_RESULT);
Backtrace result(depth, kSkipFrames);
_Unwind_Backtrace(unwindCallback, &result);
if (result.obj()->array()->count_ > 0) {
_Unwind_Backtrace(unwindCallback, &result);
}
RETURN_OBJ(result.obj());
#else
const int maxSize = 32;
void* buffer[maxSize];
int size = backtrace(buffer, maxSize);
char** symbols = backtrace_symbols(buffer, size);
RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace");
if (size < kSkipFrames)
return AllocArrayInstance(theArrayTypeInfo, 0, OBJ_RESULT);
AutoFree autoFree(symbols);
return AllocArrayInstance(theNativePtrArrayTypeInfo, 0, OBJ_RESULT);
ObjHolder resultHolder;
ObjHeader* result = AllocArrayInstance(
theArrayTypeInfo, size - kSkipFrames, resultHolder.slot());
ArrayHeader* array = result->array();
ObjHeader* result = AllocArrayInstance(theNativePtrArrayTypeInfo, size - kSkipFrames, resultHolder.slot());
for (int index = kSkipFrames; index < size; ++index) {
CreateStringFromCString(
symbols[index], ArrayAddressOfElementAt(array, index - kSkipFrames));
Kotlin_NativePtrArray_set(result, index - kSkipFrames, buffer[index]);
}
RETURN_OBJ(result);
#endif
#endif // !OMIT_BACKTRACE
}
OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace) {
#if OMIT_BACKTRACE
ObjHeader* result = AllocArrayInstance(theArrayTypeInfo, 1, OBJ_RESULT);
CreateStringFromCString("<UNIMPLEMENTED>", ArrayAddressOfElementAt(result->array(), 0));
return result;
#else
uint32_t size = stackTrace->array()->count_;
ObjHolder resultHolder;
ObjHeader* strings = AllocArrayInstance(theArrayTypeInfo, size, resultHolder.slot());
#if USE_GCC_UNWIND
for (int index = 0; index < size; ++index) {
KNativePtr address = Kotlin_NativePtrArray_get(stackTrace, index);
char symbol[512];
if (!AddressToSymbol((const void*) address, symbol, sizeof(symbol))) {
// Make empty string:
symbol[0] = '\0';
}
char line[512];
konan::snprintf(line, sizeof(line) - 1, "%s (%p)", symbol, (void*)(intptr_t)address);
CreateStringFromCString(line, ArrayAddressOfElementAt(strings->array(), index));
}
#else
if (size > 0) {
char **symbols = backtrace_symbols(PrimitiveArrayAddressOfElementAt<KNativePtr>(stackTrace->array(), 0), size);
RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace");
AutoFree autoFree(symbols);
for (int index = 0; index < size; ++index) {
CreateStringFromCString(symbols[index], ArrayAddressOfElementAt(strings->array(), index));
}
}
#endif
RETURN_OBJ(strings);
#endif // !OMIT_BACKTRACE
}
void ThrowException(KRef exception) {
RuntimeAssert(exception != nullptr && IsInstance(exception, theThrowableTypeInfo),
"Throwing something non-throwable");
+2
View File
@@ -26,6 +26,8 @@ extern "C" {
// Returns current stacktrace as Array<String>.
OBJ_GETTER0(GetCurrentStackTrace);
OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace);
// Throws arbitrary exception.
void ThrowException(KRef exception);
+4
View File
@@ -43,6 +43,10 @@ OBJ_GETTER0(Kotlin_getCurrentStackTrace) {
RETURN_RESULT_OF0(GetCurrentStackTrace);
}
OBJ_GETTER(Kotlin_getStackTraceStrings, KConstRef stackTrace) {
RETURN_RESULT_OF(GetStackTraceStrings, stackTrace);
}
// TODO: consider handling it with compiler magic instead.
OBJ_GETTER0(Kotlin_native_internal_undefined) {
RETURN_OBJ(nullptr);
+10
View File
@@ -18,6 +18,7 @@
#define RUNTIME_NATIVES_H
#include "Types.h"
#include "Exceptions.h"
inline void* AddressOfElementAt(ArrayHeader* obj, int32_t index) {
// Instance size is negative.
@@ -109,6 +110,13 @@ KInt Kotlin_IntArray_get(KConstRef thiz, KInt index);
void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value);
KInt Kotlin_IntArray_getArrayLength(KConstRef thiz);
KLong Kotlin_LongArray_get(KConstRef thiz, KInt index);
void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value);
KNativePtr Kotlin_NativePtrArray_get(KConstRef thiz, KInt index);
void Kotlin_NativePtrArray_set(KRef thiz, KInt index, KNativePtr value);
KInt Kotlin_NativePtrArray_getArrayLength(KConstRef thiz);
// io/Console.kt
void Kotlin_io_Console_print(KString message);
void Kotlin_io_Console_println(KString message);
@@ -132,6 +140,8 @@ OBJ_GETTER(Kotlin_String_subSequence, KString thiz, KInt startIndex, KInt endInd
OBJ_GETTER0(Kotlin_getCurrentStackTrace);
OBJ_GETTER(Kotlin_getStackTraceStrings, KConstRef stackTrace);
OBJ_GETTER0(Kotlin_native_internal_undefined);
void Kotlin_native_internal_GC_suspend(KRef);
+1
View File
@@ -91,6 +91,7 @@ extern const TypeInfo* theThrowableTypeInfo;
extern const TypeInfo* theUnitTypeInfo;
extern const TypeInfo* theForeignObjCObjectTypeInfo;
extern const TypeInfo* theObjCObjectWrapperTypeInfo;
extern const TypeInfo* theNativePtrArrayTypeInfo;
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
+14 -3
View File
@@ -6,6 +6,7 @@
package kotlin
import kotlin.native.internal.ExportTypeInfo
import kotlin.native.internal.NativePtrArray
/**
* The base class for all errors and exceptions. Only instances of this class can be thrown or caught.
@@ -22,11 +23,18 @@ public open class Throwable(open val message: String?, open val cause: Throwable
constructor() : this(null, null)
private val stacktrace: Array<String> = getCurrentStackTrace()
private val stackTrace = getCurrentStackTrace()
private val stackTraceStrings: Array<String> by lazy {
getStackTraceStrings(stackTrace)
}
fun getStackTrace(): Array<String> = stackTraceStrings
fun printStackTrace() {
println(this.toString())
for (element in stacktrace) {
for (element in stackTraceStrings) {
println(" at " + element)
}
@@ -48,4 +56,7 @@ public open class Throwable(open val message: String?, open val cause: Throwable
}
@SymbolName("Kotlin_getCurrentStackTrace")
private external fun getCurrentStackTrace(): Array<String>
private external fun getCurrentStackTrace(): NativePtrArray
@SymbolName("Kotlin_getStackTraceStrings")
private external fun getStackTraceStrings(stackTrace: NativePtrArray): Array<String>
@@ -36,3 +36,16 @@ internal inline class NonNullNativePtr(val value: NotNullPointerValue) { // TODO
override fun equals(other: Any?) = false
}
@ExportTypeInfo("theNativePtrArrayTypeInfo")
internal class NativePtrArray {
@SymbolName("Kotlin_NativePtrArray_get")
external public operator fun get(index: Int): NativePtr
@SymbolName("Kotlin_NativePtrArray_set")
external public operator fun set(index: Int, value: NativePtr): Unit
@SymbolName("Kotlin_NativePtrArray_getArrayLength")
external private fun getArrayLength(): Int
}