Files
kotlin-fork/kotlin-native/runtime/src/debug/cpp/KDebug.cpp
T
2023-05-22 12:49:58 +00:00

330 lines
10 KiB
C++

/*
* 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_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_DebugIsInstance(KRef obj, const TypeInfo* typeInfo) {
return IsInstanceInternal(obj, typeInfo);
}
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