Add Kotlin object inspection to simplify debugging (#763)

This commit is contained in:
Nikolay Igotti
2017-08-02 18:55:52 +03:00
committed by GitHub
parent eabe2435bf
commit beb10306dd
10 changed files with 283 additions and 77 deletions
@@ -211,6 +211,10 @@ internal class LinkStage(val context: Context) {
private val emitted = context.bitcodeFileName
private val libraries = context.config.libraries
private fun MutableList<String>.addNonEmpty(elements: List<String>) {
addAll(elements.filter { !it.isEmpty() })
}
private fun llvmLto(files: List<BitcodeFile>): ObjectFile {
val tmpCombined = createTempFile("combined", ".o")
tmpCombined.deleteOnExit()
@@ -218,13 +222,13 @@ internal class LinkStage(val context: Context) {
val tool = distribution.llvmLto
val command = mutableListOf(tool, "-o", combined)
command.addAll(platform.llvmLtoFlags)
command.addNonEmpty(platform.llvmLtoFlags)
when {
optimize -> command.addAll(platform.llvmLtoOptFlags)
debug -> command.addAll(platform.llvmDebugOptFlags)
else -> command.addAll(platform.llvmLtoNooptFlags)
optimize -> command.addNonEmpty(platform.llvmLtoOptFlags)
debug -> command.addNonEmpty(platform.llvmDebugOptFlags)
else -> command.addNonEmpty(platform.llvmLtoNooptFlags)
}
command.addAll(files)
command.addNonEmpty(files)
runTool(*command.toTypedArray())
return combined
+10 -9
View File
@@ -30,7 +30,7 @@ targetToolchain.osx = clang-llvm-3.9.0-darwin-macos
arch.osx = x86_64
targetSysRoot.osx = target-sysroot-1-darwin-macos
libffiDir.osx = libffi-3.2.1-2-darwin-macos
llvmLtoFlags.osx = -exported-symbol=_Konan_main
llvmLtoFlags.osx =
llvmLtoOptFlags.osx = -O3 -function-sections
llvmLtoNooptFlags.osx = -O1
linkerKonanFlags.osx = -lc++
@@ -55,7 +55,7 @@ arch.ios = arm64
entrySelector.ios = -alias _Konan_main _main
targetSysRoot.ios = target-sysroot-2-darwin-ios
libffiDir.ios = libffi-3.2.1-2-darwin-ios
llvmLtoFlags.ios = -exported-symbol=_Konan_main
llvmLtoFlags.ios =
llvmLtoOptFlags.ios = -O3 -function-sections
linkerDebugFlags.ios = -S
llvmLtoNooptFlags.ios = -O1
@@ -76,7 +76,7 @@ arch.ios_sim = x86_64
entrySelector.ios_sim = -alias _Konan_main _main
targetSysRoot.ios_sim = target-sysroot-1-darwin-ios-sim
libffiDir.ios_sim = libffi-3.2.1-2-darwin-ios-sim
llvmLtoFlags.ios_sim = -exported-symbol=_Konan_main
llvmLtoFlags.ios_sim =
llvmLtoOptFlags.ios_sim = -O3 -function-sections
llvmLtoNooptFlags.ios_sim = -O1
linkerKonanFlags.ios_sim = -lc++
@@ -97,7 +97,7 @@ targetSysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-g
libffiDir.linux = libffi-3.2.1-2-linux-x86-64
# targetSysroot-relative.
libGcc.linux = ../../lib/gcc/x86_64-unknown-linux-gnu/4.8.5
llvmLtoFlags.linux = -exported-symbol=Konan_main
llvmLtoFlags.linux =
llvmLtoOptFlags.linux = -O3 -function-sections
llvmLtoNooptFlags.linux = -O1
llvmLlcFlags.linux = -march=x86-64
@@ -132,7 +132,7 @@ libffiDir.raspberrypi = libffi-3.2.1-2-raspberrypi
linkerKonanFlags.raspberrypi = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread
# targetSysroot-relative.
libGcc.raspberrypi = lib/gcc/arm-linux-gnueabihf/4.8.3
llvmLtoFlags.raspberrypi = -exported-symbol=Konan_main
llvmLtoFlags.raspberrypi =
llvmLtoOptFlags.raspberrypi = -O3 -function-sections
llvmLtoNooptFlags.raspberrypi = -O1
dynamicLinker.raspberrypi = /lib/ld-linux-armhf.so.3
@@ -159,7 +159,7 @@ dependencies.linux-android_arm32 = \
quadruple.android_arm32 = arm-linux-androideabi
entrySelector.android_arm32 = -Wl,--defsym -Wl,main=Konan_main
llvmLtoFlags.android_arm32 = -exported-symbol=Konan_main -emulated-tls -relocation-model=pic
llvmLtoFlags.android_arm32 = -emulated-tls -relocation-model=pic
llvmLtoOptFlags.android_arm32 = -O3 -function-sections
linkerDebugFlags.android_arm32 = -Wl,-S
llvmLtoNooptFlags.android_arm32 = -O1
@@ -184,7 +184,7 @@ dependencies.linux-android_arm64 = \
quadruple.android_arm64 = aarch64-linux-android
entrySelector.android_arm64 = -Wl,--defsym -Wl,main=Konan_main
llvmLtoFlags.android_arm64 = -exported-symbol=Konan_main -emulated-tls -relocation-model=pic
llvmLtoFlags.android_arm64 = -emulated-tls -relocation-model=pic
targetSysRoot.android_arm64 = target-sysroot-21-android_arm64
libffiDir.android_arm64 = libffi-3.2.1-2-android_arm64
linkerKonanFlags.android_arm64 = -lm -latomic -lstdc++ -landroid
@@ -198,7 +198,7 @@ targetToolchain.mingw = msys2-mingw-w64-x86_64-gcc-6.3.0-clang-llvm-3.9.1-window
quadruple.mingw = x86_64-w64-mingw32
targetSysRoot.mingw = msys2-mingw-w64-x86_64-gcc-6.3.0-clang-llvm-3.9.1-windows-x86-64
libffiDir.mingw = libffi-3.2.1-mingw-w64-x86-64
llvmLtoFlags.mingw = -exported-symbol=Konan_main
llvmLtoFlags.mingw =
llvmLtoOptFlags.mingw = -O3 -function-sections
llvmLtoNooptFlags.mingw = -O1
linkerDebugFlags.mingw = -Wl,-S
@@ -219,10 +219,11 @@ dependencies.osx-wasm32 = \
quadruple.wasm32 = wasm32
entrySelector.wasm32 = -Wl,--defsym -Wl,main=Konan_main
llvmLtoFlags.wasm32 = -exported-symbol=Konan_main
llvmLtoFlags.wasm32 =
targetSysRoot.wasm32 = target-sysroot-1-wasm
clangFlags.wasm32 = -O1 -fno-rtti -fno-exceptions \
-D_LIBCPP_ABI_VERSION=2 -DKONAN_NO_FFI=1 -DKONAN_NO_THREADS=1 -DKONAN_NO_EXCEPTIONS=1 \
-DKONAN_NO_DEBUG_API=1 \
-nostdinc -Xclang -nobuiltininc -Xclang -nostdsysteminc \
-Xclang -isystem<sysrootDir>/include/libcxx -Xclang -isystem<sysrootDir>/lib/libcxxabi/include \
-Xclang -isystem<sysrootDir>/include/compat -Xclang -isystem<sysrootDir>/include/libc
+1 -1
View File
@@ -38,7 +38,7 @@ OBJ_GETTER(setupArgs, int argc, const char** argv) {
//--- main --------------------------------------------------------------------//
extern "C" KInt Konan_start(const ObjHeader*);
extern "C" int Konan_main(int argc, const char** argv) {
extern "C" RUNTIME_USED int Konan_main(int argc, const char** argv) {
RuntimeState* state = InitRuntime();
if (state == nullptr) {
+2
View File
@@ -20,6 +20,8 @@
#define RUNTIME_NOTHROW __attribute__((nothrow))
#define RUNTIME_CONST __attribute__((const))
#define RUNTIME_PURE __attribute__((pure))
#define RUNTIME_USED __attribute__((used))
#if KONAN_NO_THREADS
#define THREAD_LOCAL_VARIABLE
#else
+103
View File
@@ -0,0 +1,103 @@
/*
* 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 "Assert.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];
} // namespace
extern "C" {
// Buffer that can be used by debugger for inspections.
RUNTIME_USED char* Konan_DebugBuffer() {
return debugBuffer;
}
RUNTIME_USED int Konan_DebugBufferSize() {
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) {
ObjHolder stringHolder;
auto data = KonanObjectToUtf8Array(obj, stringHolder.slot())->array();
if (data == nullptr) return 0;
KInt toCopy = data->count_ > bufferSize - 1 ? bufferSize - 1 : data->count_;
::memcpy(buffer, ByteArrayAddressOfElementAt(data, 0), toCopy);
buffer[toCopy + 1] = '\0';
return toCopy + 1;
}
RUNTIME_USED KInt Konan_DebugPrint(KRef obj) {
KInt size = Konan_DebugObjectToUtf8Array(obj, Konan_DebugBuffer(), Konan_DebugBufferSize());
if (size > 1)
konan::consoleWriteUtf8(Konan_DebugBuffer(), size - 1);
return 0;
}
RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index) {
if (obj == nullptr || index < 0) return Konan_RuntimeType::INVALID;
auto typeInfo = obj->type_info();
if (typeInfo->instanceSize_ < 0) {
// Arrays.
if (index >= obj->array()->count_) return Konan_RuntimeType::INVALID;
if (typeInfo == theArrayTypeInfo) {
return Konan_RuntimeType::OBJECT;
}
// TODO: support other array types.
return Konan_RuntimeType::INVALID;
}
// TODO: support primitive type fields as well!
if (index >= typeInfo->objOffsetsCount_) return Konan_RuntimeType::INVALID;
return Konan_RuntimeType::OBJECT;
}
RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index) {
if (obj == nullptr || index < 0) return nullptr;
auto typeInfo = obj->type_info();
if (typeInfo->instanceSize_ < 0) {
// Arrays.
if (index >= obj->array()->count_) return nullptr;
if (typeInfo == theArrayTypeInfo) {
return ArrayAddressOfElementAt(obj->array(), index);
}
// TODO: support other array types.
return nullptr;
}
// TODO: support primitive type fields as well!
if (index >= typeInfo->objOffsetsCount_) return nullptr;
return reinterpret_cast<uint8_t*>(obj + 1) + typeInfo->objOffsets_[index];
}
} // extern "C"
#endif // !KONAN_NO_DEBUG_API
+74
View File
@@ -0,0 +1,74 @@
/*
* 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.
*/
#ifndef RUNTIME_KDEBUG_H
#define RUNTIME_KDEBUG_H
#include "Common.h"
#include "Memory.h"
#include "Types.h"
#include "TypeInfo.h"
#ifndef KONAN_NO_DEBUG_API
// Type for runtime representation of Konan object.
enum Konan_RuntimeType {
INVALID = 0,
OBJECT = 1,
INT8 = 2,
INT16 = 3,
INT32 = 4,
INT64 = 5,
FLOAT32 = 6,
FLOAT64 = 7
};
#ifdef __cplusplus
extern "C" {
#endif
// Get memory buffer where debugger can put data in Konan app process.
RUNTIME_USED
char* Konan_DebugBuffer();
// Get size of memory buffer where debugger can put data in Konan app process.
RUNTIME_USED
int Konan_DebugBufferSize();
// Put string representation of an object to the provided buffer.
RUNTIME_USED
int Konan_DebugObjectToUtf8Array(KRef obj, char* buffer, int bufferSize);
// Print to console string representation of an object.
RUNTIME_USED
int Konan_DebugPrint(KRef obj);
// Compute type of field or an array element at the index, or 0, if incorrect,
// see Konan_RuntimeType.
// TODO: currently, only object fields are supported, will be fixed soon.
RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index);
// Compute address of field or an array element at the index, or null, if incorrect.
// TODO: currently, only object fields are supported, will be fixed soon.
RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index);
#ifdef __cplusplus
}
#endif
#endif // !KONAN_NO_DEBUG_API
#endif // RUNTIME_KDEBUG_H
+3 -3
View File
@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef RUNTIME_STRING_H
#define RUNTIME_STRING_H
#ifndef RUNTIME_KSTRING_H
#define RUNTIME_KSTRING_H
#include "Common.h"
#include "Memory.h"
@@ -52,4 +52,4 @@ int binarySearchRange(const T* array, int arrayLength, T needle) {
return middle - (needle < value ? 1 : 0);
}
#endif // RUNTIME_STRING_H
#endif // RUNTIME_KSTRING_H
+43 -54
View File
@@ -23,54 +23,60 @@
#include "Memory.h"
#include "Natives.h"
#include "KString.h"
#include "Porting.h"
#include "Types.h"
namespace {
char int_to_digit(uint32_t value) {
if (value < 10) {
return '0' + value;
} else {
return 'a' + (value - 10);
}
}
// Radix is checked on the Kotlin side.
template <typename T> OBJ_GETTER(Kotlin_toStringRadix, T value, KInt radix) {
if (value == 0) {
RETURN_RESULT_OF(CreateStringFromCString, "0");
}
char cstring[sizeof(T) * CHAR_BIT + 1];
bool negative = (value < 0);
if (!negative) {
value = -value;
}
int32_t length = 0;
while (value < 0) {
cstring[length++] = int_to_digit(-(value % radix));
value /= radix;
}
if (negative) {
cstring[length++] = '-';
}
for (int i = 0, j = length - 1; i < j; i++, j--) {
char tmp = cstring[i];
cstring[i] = cstring[j];
cstring[j] = tmp;
}
cstring[length] = '\0';
RETURN_RESULT_OF(CreateStringFromCString, cstring);
char int_to_digit(uint32_t value) {
if (value < 10) {
return '0' + value;
} else {
return 'a' + (value - 10);
}
}
// Radix is checked on the Kotlin side.
template <typename T> OBJ_GETTER(Kotlin_toStringRadix, T value, KInt radix) {
if (value == 0) {
RETURN_RESULT_OF(CreateStringFromCString, "0");
}
char cstring[sizeof(T) * CHAR_BIT + 1];
bool negative = (value < 0);
if (!negative) {
value = -value;
}
int32_t length = 0;
while (value < 0) {
cstring[length++] = int_to_digit(-(value % radix));
value /= radix;
}
if (negative) {
cstring[length++] = '-';
}
for (int i = 0, j = length - 1; i < j; i++, j--) {
char tmp = cstring[i];
cstring[i] = cstring[j];
cstring[j] = tmp;
}
cstring[length] = '\0';
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
} // namespace
extern "C" {
OBJ_GETTER(Kotlin_Any_toString, KConstRef thiz) {
char cstring[80];
snprintf(cstring, sizeof(cstring), "%s %p type %p",
IsArray(thiz) ? "array" : "object",
thiz, thiz->type_info_);
if (IsArray(thiz)) {
konan::snprintf(cstring, sizeof(cstring), "%d@%p: array of %d",
Kotlin_Any_hashCode(thiz), thiz->type_info_, thiz->array()->count_);
} else {
konan::snprintf(cstring, sizeof(cstring), "%d@%p: object",
Kotlin_Any_hashCode(thiz), thiz->type_info_);
}
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
@@ -113,21 +119,4 @@ OBJ_GETTER(Kotlin_Long_toStringRadix, KLong value, KInt radix) {
RETURN_RESULT_OF(Kotlin_toStringRadix<KLong>, value, radix)
}
// TODO: use David Gay's dtoa() here instead. It's *very* big and ugly.
OBJ_GETTER(Kotlin_Float_toString, KFloat value) {
char cstring[32];
snprintf(cstring, sizeof(cstring), "%G", value);
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
OBJ_GETTER(Kotlin_Double_toString, KDouble value) {
char cstring[32];
snprintf(cstring, sizeof(cstring), "%G", value);
RETURN_RESULT_OF(CreateStringFromCString, cstring);
}
OBJ_GETTER(Kotlin_Boolean_toString, KBoolean value) {
RETURN_RESULT_OF(CreateStringFromCString, value ? "true" : "false");
}
} // extern "C"
@@ -0,0 +1,36 @@
/*
* 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.
*/
package konan.internal
import kotlin.text.toUtf8Array
// Called by debugger.
@ExportForCppRuntime
fun KonanObjectToUtf8Array(value: Any?): ByteArray {
val string = when (value) {
is Array<*> -> value.contentToString()
is CharArray -> value.contentToString()
is BooleanArray -> value.contentToString()
is ByteArray -> value.contentToString()
is ShortArray -> value.contentToString()
is IntArray -> value.contentToString()
is LongArray -> value.contentToString()
is FloatArray -> value.contentToString()
is DoubleArray -> value.contentToString()
else -> value.toString()
}
return toUtf8Array(string, 0, string.length)
}
+2 -5
View File
@@ -54,10 +54,7 @@ public final class Boolean : Comparable<Boolean> {
public override fun equals(other: Any?): Boolean =
other is Boolean && konan.internal.areEqualByValue(this, other)
@SymbolName("Kotlin_Boolean_toString")
external public override fun toString(): String
public override fun toString() = if (this) "true" else "false"
public override fun hashCode(): Int {
return if (this) 1 else 0;
}
public override fun hashCode() = if (this) 1 else 0
}