Rework debugging interface to allow coexisting for multiple frameworks. (#3818)

This commit is contained in:
Nikolay Igotti
2020-02-06 07:39:10 +03:00
committed by GitHub
parent d7cee0f863
commit c0c8ed4cb3
11 changed files with 240 additions and 39 deletions
@@ -61,7 +61,11 @@ internal val disposeLLVMPhase = namedUnitPhase(
internal val RTTIPhase = makeKonanModuleOpPhase(
name = "RTTI",
description = "RTTI generation",
op = { context, irModule -> irModule.acceptVoid(RTTIGeneratorVisitor(context)) }
op = { context, irModule ->
val visitor = RTTIGeneratorVisitor(context)
irModule.acceptVoid(visitor)
visitor.dispose()
}
)
internal val generateDebugInfoHeaderPhase = makeKonanModuleOpPhase(
@@ -105,6 +105,9 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
}
}
fun dispose() {
generator.dispose()
}
}
//-------------------------------------------------------------------------//
@@ -395,6 +395,30 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
private fun mapRuntimeType(type: LLVMTypeRef): Int =
runtimeTypeMap[type] ?: throw Error("Unmapped type: ${llvmtype2string(type)}")
private val debugRuntimeOrNull: LLVMModuleRef? by lazy {
context.config.runtimeNativeLibraries.singleOrNull { it.endsWith("debug.bc")}?.let {
parseBitcodeFile(it)
}
}
private val debugOperations: ConstValue by lazy {
if (debugRuntimeOrNull != null) {
val external = LLVMGetNamedGlobal(debugRuntimeOrNull, "Konan_debugOperationsList")!!
val local = LLVMAddGlobal(context.llvmModule, LLVMGetElementType(LLVMTypeOf(external)),"Konan_debugOperationsList")!!
constPointer(LLVMConstBitCast(local, kInt8PtrPtr)!!)
} else {
Zero(kInt8PtrPtr)
}
}
val debugOperationsSize: ConstValue by lazy {
if (debugRuntimeOrNull != null) {
val external = LLVMGetNamedGlobal(debugRuntimeOrNull, "Konan_debugOperationsList")!!
Int32(LLVMGetArrayLength(LLVMGetElementType(LLVMTypeOf(external))))
} else
Int32(0)
}
private fun makeExtendedInfo(irClass: IrClass): ConstPointer {
// TODO: shall we actually do that?
if (context.shouldOptimize())
@@ -404,12 +428,14 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val llvmDeclarations = context.llvmDeclarations.forClass(irClass)
val bodyType = llvmDeclarations.bodyType
val elementType = arrayClasses[className]
val value = if (elementType != null) {
// An array type.
val runtimeElementType = mapRuntimeType(elementType)
Struct(runtime.extendedTypeInfoType,
Int32(-runtimeElementType),
NullPointer(int32Type), NullPointer(int8Type), NullPointer(kInt8Ptr))
NullPointer(int32Type), NullPointer(int8Type), NullPointer(kInt8Ptr),
debugOperationsSize, debugOperations)
} else {
data class FieldRecord(val offset: Int, val type: Int, val name: String)
val fields = getStructElements(bodyType).drop(1).mapIndexed { index, type ->
@@ -424,7 +450,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
fields.map { Int8(it.type.toByte()) })
val namesPtr = staticData.placeGlobalConstArray("kextname:$className", kInt8Ptr,
fields.map { staticData.placeCStringLiteral(it.name) })
Struct(runtime.extendedTypeInfoType, Int32(fields.size), offsetsPtr, typesPtr, namesPtr)
Struct(runtime.extendedTypeInfoType, Int32(fields.size), offsetsPtr, typesPtr, namesPtr,
debugOperationsSize, debugOperations)
}
val result = staticData.placeGlobal("", value)
result.setConstant(true)
@@ -566,6 +595,10 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
.joinToString(".") { it.name.asString() }
)
}
fun dispose() {
debugRuntimeOrNull?.let { LLVMDisposeModule(it) }
}
}
// Keep in sync with Konan_TypeFlags in TypeInfo.h.
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
/**
* Creates const array-typed global with given name and value.
* Returns pointer to the first element of the array.
@@ -51,6 +51,10 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
}
}
fun dispose() {
rttiGenerator.dispose()
}
// TODO: currently bridges don't have any custom `landingpad`s,
// so it is correct to use [callAtFunctionScope] here.
// However, exception handling probably should be refactored
@@ -103,6 +107,7 @@ internal class ObjCExportBlockCodeGenerator(codegen: CodeGenerator) : ObjCExport
fun generate() {
emitFunctionConverters()
emitBlockToKotlinFunctionConverters()
dispose()
}
}
@@ -102,6 +102,7 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) {
}
objCCodeGenerator.emitRtti()
objCCodeGenerator.dispose()
}
private fun produceFrameworkSpecific(headerLines: List<String>) {
+143 -18
View File
@@ -43,7 +43,8 @@ constexpr int runtimeTypeSize[] = {
4, // FLOAT32
8, // FLOAT64
sizeof(void*), // NATIVE_PTR
1 // BOOLEAN
1, // BOOLEAN
16 // VECTOR128
};
constexpr int runtimeTypeAlignment[] = {
@@ -56,24 +57,59 @@ constexpr int runtimeTypeAlignment[] = {
alignof(float), // FLOAT32
alignof(double), // FLOAT64
alignof(void*), // NATIVE_PTR
1 // BOOLEAN
1, // BOOLEAN
16 // VECTOR128
};
} // namespace
// Never ever change numbering in this enum, as it will break debugging of older binaries.
enum Konan_DebugOperation {
DO_DebugBuffer = 1,
DO_DebugBufferSize = 2,
DO_DebugBufferWithObject = 3,
DO_DebugBufferSizeWithObject = 4,
DO_DebugObjectToUtf8Array = 5,
DO_DebugPrint = 6,
DO_DebugIsArray = 7,
DO_DebugGetFieldCount = 8,
DO_DebugGetFieldType = 9,
DO_DebugGetFieldAddress = 10,
DO_DebugGetFieldName = 11,
DO_DebugGetTypeName = 12,
};
extern "C" {
template <typename F>
F getImpl(KRef obj, Konan_DebugOperation operation) {
if (obj == nullptr)
return nullptr;
auto* typeInfo = obj->type_info();
auto* extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return nullptr;
if (static_cast<int32_t>(operation) >= extendedTypeInfo->debugOperationsCount_)
return nullptr;
return reinterpret_cast<F>(extendedTypeInfo->debugOperations_[operation]);
}
// Buffer that can be used by debugger for inspections.
RUNTIME_USED char* Konan_DebugBuffer() {
char* Konan_DebugBufferImpl() {
return debugBuffer;
}
RUNTIME_USED int Konan_DebugBufferSize() {
int Konan_DebugBufferSizeImpl() {
return sizeof(debugBuffer);
}
char* Konan_DebugBufferWithObjectImpl(KRef obj) {
return debugBuffer;
}
int Konan_DebugBufferSizeWithObjectImpl(KRef obj) {
return sizeof(debugBuffer);
}
// Auxilary function which can be called by developer/debugger to inspect an object.
RUNTIME_USED KInt Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, KInt bufferSize) {
int32_t Konan_DebugObjectToUtf8ArrayImpl(KRef obj, char* buffer, int32_t bufferSize) {
ObjHolder stringHolder;
auto data = KonanObjectToUtf8Array(obj, stringHolder.slot())->array();
if (data == nullptr) return 0;
@@ -83,23 +119,23 @@ RUNTIME_USED KInt Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, KInt buff
return toCopy + 1;
}
RUNTIME_USED KInt Konan_DebugPrint(KRef obj) {
KInt size = Konan_DebugObjectToUtf8Array(obj, Konan_DebugBuffer(), Konan_DebugBufferSize());
int32_t Konan_DebugPrintImpl(KRef obj) {
int32_t size = Konan_DebugObjectToUtf8Array(obj, Konan_DebugBuffer(), Konan_DebugBufferSize());
if (size > 1)
konan::consoleWriteUtf8(Konan_DebugBuffer(), size - 1);
return 0;
}
RUNTIME_USED int Konan_DebugIsArray(KRef obj) {
int32_t Konan_DebugIsArrayImpl(KRef obj) {
return obj == nullptr || IsArray(obj) ? 1 : 0;
}
RUNTIME_USED int Konan_DebugGetFieldCount(KRef obj) {
int32_t Konan_DebugGetFieldCountImpl(KRef obj) {
if (obj == nullptr)
return 0;
auto typeInfo = obj->type_info();
auto extendedTypeInfo = typeInfo->extendedInfo_;
auto* typeInfo = obj->type_info();
auto* extendedTypeInfo = typeInfo->extendedInfo_;
if (extendedTypeInfo == nullptr)
return 0;
@@ -110,8 +146,7 @@ RUNTIME_USED int Konan_DebugGetFieldCount(KRef obj) {
return extendedTypeInfo->fieldsCount_;
}
RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index) {
int32_t Konan_DebugGetFieldTypeImpl(KRef obj, int32_t index) {
if (obj == nullptr || index < 0)
return Konan_RuntimeType::RT_INVALID;
@@ -130,7 +165,7 @@ RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index) {
return extendedTypeInfo->fieldTypes_[index];
}
RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index) {
void* Konan_DebugGetFieldAddressImpl(KRef obj, int32_t index) {
if (obj == nullptr || index < 0)
return nullptr;
@@ -157,7 +192,7 @@ RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index) {
}
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED const char* Konan_DebugGetFieldName(KRef obj, int index) {
const char* Konan_DebugGetFieldNameImpl(KRef obj, int32_t index) {
if (obj == nullptr || index < 0)
return nullptr;
@@ -177,7 +212,7 @@ RUNTIME_USED const char* Konan_DebugGetFieldName(KRef obj, int index) {
return extendedTypeInfo->fieldNames_[index];
}
RUNTIME_USED const char* Konan_DebugGetTypeName(KRef obj) {
const char* Konan_DebugGetTypeNameImpl(KRef obj) {
if (obj == nullptr)
return nullptr;
@@ -188,6 +223,96 @@ RUNTIME_USED const char* Konan_DebugGetTypeName(KRef obj) {
return CreateCStringFromString(type_info->relativeName_);
}
} // namespace
extern "C" {
RUNTIME_USED RUNTIME_WEAK char* Konan_DebugBuffer() {
return Konan_DebugBufferImpl();
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugBufferSize() {
return Konan_DebugBufferSizeImpl();
}
RUNTIME_USED RUNTIME_WEAK char* Konan_DebugBufferWithObject(KRef obj) {
auto* impl = getImpl<char* (*)(KRef)>(obj, DO_DebugBufferWithObject);
if (impl == nullptr) return nullptr;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugBufferSizeWithObject(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugBufferSizeWithObject);
if (impl == nullptr) return 0;
return impl(obj);
}
// Auxilary function which can be called by developer/debugger to inspect an object.
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int32_t bufferSize) {
auto* impl = getImpl<int32_t (*)(KRef, char*, int32_t)>(obj, DO_DebugObjectToUtf8Array);
if (impl == nullptr) return 0;
return impl(obj, buffer, bufferSize);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugPrint(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugPrint);
if (impl == nullptr) return 0;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugIsArray(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugIsArray);
if (impl == nullptr) return 0;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugGetFieldCount(KRef obj) {
auto* impl = getImpl<int32_t (*)(KRef)>(obj, DO_DebugGetFieldCount);
if (impl == nullptr) return 0;
return impl(obj);
}
RUNTIME_USED RUNTIME_WEAK int32_t Konan_DebugGetFieldType(KRef obj, int32_t index) {
auto* impl = getImpl<int32_t (*)(KRef, int32_t)>(obj, DO_DebugGetFieldType);
if (impl == nullptr) return 0;
return impl(obj, index);
}
RUNTIME_USED RUNTIME_WEAK void* Konan_DebugGetFieldAddress(KRef obj, int32_t index) {
auto* impl = getImpl<void* (*)(KRef, int32_t)>(obj, DO_DebugGetFieldAddress);
if (impl == nullptr) return nullptr;
return impl(obj, index);
}
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED RUNTIME_WEAK const char* Konan_DebugGetFieldName(KRef obj, int32_t index) {
auto* impl = getImpl<const char* (*)(KRef, int32_t)>(obj, DO_DebugGetFieldName);
if (impl == nullptr) return nullptr;
return impl(obj, index);
}
RUNTIME_USED RUNTIME_WEAK const char* Konan_DebugGetTypeName(KRef obj) {
auto* impl = getImpl<const char* (*)(KRef)>(obj, DO_DebugGetTypeName);
if (impl == nullptr) return nullptr;
return impl(obj);
}
const void* Konan_debugOperationsList[] = {
nullptr,
reinterpret_cast<const void*>(&Konan_DebugBufferImpl),
reinterpret_cast<const void*>(&Konan_DebugBufferSizeImpl),
reinterpret_cast<const void*>(&Konan_DebugBufferWithObjectImpl),
reinterpret_cast<const void*>(&Konan_DebugBufferSizeWithObjectImpl),
reinterpret_cast<const void*>(&Konan_DebugObjectToUtf8ArrayImpl),
reinterpret_cast<const void*>(&Konan_DebugPrintImpl),
reinterpret_cast<const void*>(&Konan_DebugIsArrayImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldCountImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldTypeImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldAddressImpl),
reinterpret_cast<const void*>(&Konan_DebugGetFieldNameImpl),
reinterpret_cast<const void*>(&Konan_DebugGetTypeNameImpl)
};
} // extern "C"
#endif // !KONAN_NO_DEBUG_API
+1
View File
@@ -22,6 +22,7 @@
#define RUNTIME_CONST __attribute__((const))
#define RUNTIME_PURE __attribute__((pure))
#define RUNTIME_USED __attribute__((used))
#define RUNTIME_WEAK __attribute__((weak))
#define ALWAYS_INLINE __attribute__((always_inline))
#define NO_INLINE __attribute__((noinline))
+36 -11
View File
@@ -28,40 +28,65 @@
extern "C" {
#endif
// PLEASE READ: please do not alter signatures of the existing functions, and when adding
// the new function please do not forget to add new functions into the debug operations list.
// Get memory buffer where debugger can put data in Konan app process.
RUNTIME_USED
RUNTIME_USED RUNTIME_WEAK
char* Konan_DebugBuffer();
// Same, but runtime-specific.
RUNTIME_USED RUNTIME_WEAK
char* Konan_DebugBufferWithObject(KRef obj);
// Get size of memory buffer where debugger can put data in Konan app process.
RUNTIME_USED
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugBufferSize();
// Same, but runtime-specific.
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugBufferSizeWithObject(KRef obj);
// Put string representation of an object to the provided buffer.
RUNTIME_USED
int32_t Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int bufferSize);
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int32_t bufferSize);
// Print to console string representation of an object.
RUNTIME_USED
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugPrint(KRef obj);
// Returns 1 if obj refers to an array, string or binary blob and 0 otherwise.
RUNTIME_USED int Konan_DebugIsArray(KRef obj);
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugIsArray(KRef obj);
// Returns number of fields in an objects, or elements in an array.
RUNTIME_USED int Konan_DebugGetFieldCount(KRef obj);
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugGetFieldCount(KRef obj);
// Compute type of field or an array element at the index, or 0, if incorrect,
// see Konan_RuntimeType.
RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index);
RUNTIME_USED RUNTIME_WEAK
int32_t Konan_DebugGetFieldType(KRef obj, int32_t index);
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index);
RUNTIME_USED RUNTIME_WEAK
void* Konan_DebugGetFieldAddress(KRef obj, int32_t index);
// Compute address of field or an array element at the index, or null, if incorrect.
RUNTIME_USED const char* Konan_DebugGetFieldName(KRef obj, int index);
RUNTIME_USED RUNTIME_WEAK
const char* Konan_DebugGetFieldName(KRef obj, int32_t index);
// Returns name of type.
RUNTIME_USED const char* Konan_DebugGetTypeName(KRef obj);
RUNTIME_USED RUNTIME_WEAK
const char* Konan_DebugGetTypeName(KRef obj);
/**
* Given an object finds debugger interface operation suitable for manipulation with this object.
* Important for cases where multiple K/N runtimes coexist in the same address space and debugger
* doesn't know which debug operation to use on particular instance.
*/
RUNTIME_USED RUNTIME_WEAK
void* Konan_DebugGetOperation(KRef obj, /* Konan_DebugOperation */ int32_t operation);
#ifdef __cplusplus
}
+5 -4
View File
@@ -22,7 +22,7 @@
// This function replaces `__cxa_demangle` defined in GNU libstdc++
// by adding `--defsym` flag in `konan.properties`.
// This allows to avoid linking `__cxa_demangle` and its dependencies, thus reducing binary size.
RUNTIME_USED extern "C" char* Konan_cxa_demangle(
RUNTIME_USED RUNTIME_WEAK extern "C" char* Konan_cxa_demangle(
const char* __mangled_name, char* __output_buffer,
size_t* __length, int* __status
) {
@@ -31,9 +31,10 @@ RUNTIME_USED extern "C" char* Konan_cxa_demangle(
}
namespace std {
void __throw_length_error(const char* __s __attribute__((unused))) {
RuntimeAssert(false, __s);
}
void __throw_length_error(const char* __s __attribute__((unused))) {
RuntimeAssert(false, __s);
}
} // namespace std
#endif // KONAN_LINUX || KONAN_WINDOWS
+6 -2
View File
@@ -49,7 +49,8 @@ enum Konan_RuntimeType {
RT_FLOAT32 = 6,
RT_FLOAT64 = 7,
RT_NATIVE_PTR = 8,
RT_BOOLEAN = 9
RT_BOOLEAN = 9,
RT_VECTOR128 = 10
};
// Flags per type.
@@ -77,7 +78,10 @@ struct ExtendedTypeInfo {
const uint8_t* fieldTypes_;
// Names of all fields.
const char** fieldNames_;
// TODO: do we want any other info here?
// Number of supported debug operations.
int32_t debugOperationsCount_;
// Table of supported debug operations functions.
void** debugOperations_;
};
typedef void const* VTableElement;