| /* |
| * Copyright 2010-2017 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "KDebug.h" |
| |
| #include <string.h> |
| |
| #include "KAssert.h" |
| #include "KString.h" |
| #include "Memory.h" |
| #include "Natives.h" |
| #include "Porting.h" |
| #include "Types.h" |
| |
| #ifndef KONAN_NO_DEBUG_API |
| |
| extern "C" OBJ_GETTER(KonanObjectToUtf8Array, KRef object); |
| |
| namespace { |
| |
| char debugBuffer[4096]; |
| |
| constexpr int runtimeTypeSize[] = { |
| -1, // INVALID |
| sizeof(ObjHeader*), // OBJECT |
| 1, // INT8 |
| 2, // INT16 |
| 4, // INT32 |
| 8, // INT64 |
| 4, // FLOAT32 |
| 8, // FLOAT64 |
| sizeof(void*), // NATIVE_PTR |
| 1, // BOOLEAN |
| 16 // VECTOR128 |
| }; |
| |
| constexpr int runtimeTypeAlignment[] = { |
| -1, // INVALID |
| alignof(ObjHeader*), // OBJECT |
| alignof(int8_t), // INT8 |
| alignof(int16_t), // INT16 |
| alignof(int32_t), // INT32 |
| alignof(int64_t), // INT64 |
| alignof(float), // FLOAT32 |
| alignof(double), // FLOAT64 |
| alignof(void*), // NATIVE_PTR |
| 1, // BOOLEAN |
| 16 // VECTOR128 |
| }; |
| |
| // 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, |
| }; |
| |
| 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. |
| char* Konan_DebugBufferImpl() { |
| return debugBuffer; |
| } |
| |
| 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. |
| int32_t Konan_DebugObjectToUtf8ArrayImpl(KRef obj, char* buffer, int32_t bufferSize) { |
| // We need the runnable thread state to call the Kotlin function 'KonanObjectToUtf8Array' and operate with it's result. |
| // But the current thread can be in any state when this function is called by the debugger. So we use the reentrant state switch. |
| // Finally, let's be on the safe side and assume that current thread might be uninitialized, |
| // so use CalledFromNativeGuard instead of ThreadStateGuard. |
| kotlin::CalledFromNativeGuard guard(/* reentrant */ true); |
| ObjHolder stringHolder; |
| // Kotlin call. |
| ArrayHeader* data = KonanObjectToUtf8Array(obj, stringHolder.slot())->array(); |
| if (data == nullptr) return 0; |
| if (bufferSize < 1) return 0; |
| KInt toCopy = data->count_ > static_cast<uint32_t>(bufferSize - 1) ? bufferSize - 1 : data->count_; |
| ::memcpy(buffer, ByteArrayAddressOfElementAt(data, 0), toCopy); |
| buffer[toCopy] = '\0'; |
| return toCopy + 1; |
| } |
| |
| 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; |
| } |
| |
| int32_t Konan_DebugIsArrayImpl(KRef obj) { |
| return obj == nullptr || IsArray(obj) ? 1 : 0; |
| } |
| |
| int32_t Konan_DebugGetFieldCountImpl(KRef obj) { |
| if (obj == nullptr) |
| return 0; |
| |
| auto* typeInfo = obj->type_info(); |
| auto* extendedTypeInfo = typeInfo->extendedInfo_; |
| |
| if (extendedTypeInfo == nullptr) |
| return 0; |
| |
| if (IsArray(obj)) |
| return obj->array()->count_; |
| |
| return extendedTypeInfo->fieldsCount_; |
| } |
| |
| int32_t Konan_DebugGetFieldTypeImpl(KRef obj, int32_t index) { |
| if (obj == nullptr || index < 0) |
| return Konan_RuntimeType::RT_INVALID; |
| |
| auto typeInfo = obj->type_info(); |
| auto extendedTypeInfo = typeInfo->extendedInfo_; |
| |
| if (extendedTypeInfo == nullptr) |
| return Konan_RuntimeType::RT_INVALID; |
| |
| if (extendedTypeInfo->fieldsCount_ < 0) |
| return -extendedTypeInfo->fieldsCount_; |
| |
| if (index >= extendedTypeInfo->fieldsCount_) |
| return Konan_RuntimeType::RT_INVALID; |
| |
| return extendedTypeInfo->fieldTypes_[index]; |
| } |
| |
| void* Konan_DebugGetFieldAddressImpl(KRef obj, int32_t index) { |
| if (obj == nullptr || index < 0) |
| return nullptr; |
| |
| auto typeInfo = obj->type_info(); |
| auto extendedTypeInfo = typeInfo->extendedInfo_; |
| |
| if (extendedTypeInfo == nullptr) |
| return nullptr; |
| |
| if (extendedTypeInfo->fieldsCount_ < 0) { |
| if (static_cast<uint32_t>(index) > obj->array()->count_) |
| return nullptr; |
| |
| int32_t typeIndex = -extendedTypeInfo->fieldsCount_; |
| return reinterpret_cast<uint8_t*>(obj->array()) |
| + alignUp(sizeof(struct ArrayHeader), runtimeTypeAlignment[typeIndex]) |
| + index * runtimeTypeSize[typeIndex]; |
| } |
| |
| if (index >= extendedTypeInfo->fieldsCount_) |
| return nullptr; |
| |
| return reinterpret_cast<uint8_t*>(obj) + extendedTypeInfo->fieldOffsets_[index]; |
| } |
| |
| // Compute address of field or an array element at the index, or null, if incorrect. |
| const char* Konan_DebugGetFieldNameImpl(KRef obj, int32_t index) { |
| if (obj == nullptr || index < 0) |
| return nullptr; |
| |
| auto typeInfo = obj->type_info(); |
| auto extendedTypeInfo = typeInfo->extendedInfo_; |
| |
| if (extendedTypeInfo == nullptr) |
| return nullptr; |
| |
| // For arrays, field name makes not much sense. |
| if (extendedTypeInfo->fieldsCount_ < 0) |
| return ""; |
| |
| if (index >= extendedTypeInfo->fieldsCount_) |
| return nullptr; |
| |
| return extendedTypeInfo->fieldNames_[index]; |
| } |
| |
| const char* Konan_DebugGetTypeNameImpl(KRef obj) { |
| if (obj == nullptr) |
| return nullptr; |
| |
| auto type_info = obj->type_info(); |
| if (type_info == nullptr) |
| return "<unknown>"; |
| |
| return CreateCStringFromString(type_info->relativeName_); |
| } |
| |
| } // namespace |
| |
| extern "C" { |
| |
| RUNTIME_EXPORT RUNTIME_WEAK char* Konan_DebugBuffer() { |
| return Konan_DebugBufferImpl(); |
| } |
| |
| RUNTIME_EXPORT RUNTIME_WEAK int32_t Konan_DebugBufferSize() { |
| return Konan_DebugBufferSizeImpl(); |
| } |
| |
| RUNTIME_EXPORT RUNTIME_WEAK char* Konan_DebugBufferWithObject(KRef obj) { |
| auto* impl = getImpl<char* (*)(KRef)>(obj, DO_DebugBufferWithObject); |
| if (impl == nullptr) return nullptr; |
| return impl(obj); |
| } |
| |
| RUNTIME_EXPORT 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_EXPORT 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_EXPORT 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_EXPORT 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_EXPORT RUNTIME_WEAK int32_t Konan_DebugIsInstance(KRef obj, const TypeInfo* typeInfo) { |
| return IsInstanceInternal(obj, typeInfo); |
| } |
| |
| RUNTIME_EXPORT 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_EXPORT 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_EXPORT 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_EXPORT 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_EXPORT 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 |