From 7d72beb27c45084d0422ecc6e9ac6e8e93c3c4dd Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Tue, 29 Aug 2017 10:21:47 +0300 Subject: [PATCH] Improve memory tracing, fix 32-bit platforms (#815) --- .../kotlin/kotlinx/cinterop/JvmNativeMem.kt | 1 + .../backend/konan/llvm/CodeGenerator.kt | 10 +- .../kotlin/backend/konan/llvm/Runtime.kt | 9 ++ runtime/src/main/cpp/Memory.cpp | 126 +++++------------- runtime/src/main/cpp/Porting.cpp | 57 ++++---- runtime/src/main/cpp/Porting.h | 1 + runtime/src/main/cpp/Types.h | 2 +- .../kotlin/konan/target/ClangArgs.kt | 4 +- 8 files changed, 86 insertions(+), 124 deletions(-) diff --git a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmNativeMem.kt b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmNativeMem.kt index 2bffc0b99c3..d7b71f7352a 100644 --- a/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmNativeMem.kt +++ b/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmNativeMem.kt @@ -33,6 +33,7 @@ private val dataModel: DataModel = when (System.getProperty("sun.arch.data.model else -> throw IllegalStateException() } +// Must be only used in interop, contains host pointer size, not target! val pointerSize: Int = dataModel.pointerSize.toInt() object nativeMemUtils { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 37cc9dabd8f..678a3a534eb 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -110,7 +110,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, private var returnSlot: LLVMValueRef? = null private var slotsPhi: LLVMValueRef? = null private val frameOverlaySlotCount = - (LLVMStoreSizeOfType(llvmTargetData, runtime.frameOverlayType) / pointerSize).toInt() + (LLVMStoreSizeOfType(llvmTargetData, runtime.frameOverlayType) / runtime.pointerSize).toInt() private var slotCount = frameOverlaySlotCount private var localAllocs = 0 private var arenaSlot: LLVMValueRef? = null @@ -496,11 +496,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, if (needSlots) { // Zero-init slots. val slotsMem = bitcast(kInt8Ptr, slots) - val pointerSize = LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt() - val alignment = LLVMABIAlignmentOfType(llvmTargetData, kObjHeaderPtr) call(context.llvm.memsetFunction, listOf(slotsMem, Int8(0).llvm, - Int32(slotCount * pointerSize).llvm, Int32(alignment).llvm, + Int32(slotCount * codegen.runtime.pointerSize).llvm, + Int32(codegen.runtime.pointerAlignment).llvm, Int1(0).llvm)) call(context.llvm.enterFrameFunction, listOf(slots, Int32(slotCount).llvm)) } @@ -508,7 +507,8 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, val slotOffset = pointerSize * slotCount memScoped { slotToVariableLocation.forEach { slot, variable -> - val expr = longArrayOf(DwarfOp.DW_OP_minus.value, slotOffset + pointerSize * slot.toLong()).toCValues() + val expr = longArrayOf(DwarfOp.DW_OP_minus.value, + slotOffset + runtime.pointerSize * slot.toLong()).toCValues() DIInsertDeclaration( builder = codegen.context.debugInfo.builder, value = slotsPhi, diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt index 63b8389aa61..25a81641e7e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/Runtime.kt @@ -58,4 +58,13 @@ class Runtime(bitcodeFile: String) { val kotlinObjCClassInfo by lazy { getStructType("KotlinObjCClassInfo") } val objCMethodDescription by lazy { getStructType("ObjCMethodDescription") } + + + val pointerSize: Int by lazy { + LLVMABISizeOfType(targetData, objHeaderPtrType).toInt() + } + + val pointerAlignment: Int by lazy { + LLVMABIAlignmentOfType(targetData, objHeaderPtrType) + } } diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 34d20670928..f9d40656d26 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -30,8 +30,6 @@ #define USE_GC 1 // Define to 1 to print all memory operations. #define TRACE_MEMORY 0 -// Trace garbage collection phases. -#define TRACE_GC_PHASES 0 ContainerHeader ObjHeader::theStaticObjectsContainer = { CONTAINER_TAG_PERMANENT | CONTAINER_TAG_INCREMENT @@ -44,6 +42,12 @@ constexpr container_size_t kContainerAlignment = 1024; // Single object alignment. constexpr container_size_t kObjectAlignment = 8; +#if TRACE_MEMORY +#define MEMORY_LOG(...) konan::consolePrintf(__VA_ARGS__) +#else +#define MEMORY_LOG(...) +#endif + #if USE_GC // Collection threshold default (collect after having so many elements in the // release candidates set). Better be a prime number. @@ -181,9 +185,7 @@ static inline void DeinitInstanceBodyImpl(const TypeInfo* typeInfo, void* body) for (int index = 0; index < typeInfo->objOffsetsCount_; index++) { ObjHeader** location = reinterpret_cast( reinterpret_cast(body) + typeInfo->objOffsets_[index]); -#if TRACE_MEMORY - fprintf(stderr, "Calling UpdateRef from DeinitInstanceBodyImpl\n"); -#endif + MEMORY_LOG("Calling UpdateRef from DeinitInstanceBodyImpl\n"); UpdateRef(location, nullptr); } } @@ -306,9 +308,8 @@ void traverseContainerReferredObjects(ContainerHeader* container, func process) #if TRACE_MEMORY || USE_GC void dumpWorker(const char* prefix, ContainerHeader* header, ContainerHeaderSet* seen) { - fprintf(stderr, "%s: %p (%08x): %d refs\n", - prefix, - header, header->refCount_, header->refCount_ >> CONTAINER_TAG_SHIFT); + MEMORY_LOG("%s: %p (%08x): %d refs\n", prefix, header, header->refCount_, + header->refCount_ >> CONTAINER_TAG_SHIFT); seen->insert(header); traverseContainerReferredObjects(header, [prefix, seen](ObjHeader* ref) { auto child = ref->container(); @@ -322,7 +323,7 @@ void dumpWorker(const char* prefix, ContainerHeader* header, ContainerHeaderSet* void dumpReachable(const char* prefix, const ContainerHeaderSet* roots) { ContainerHeaderSet seen; for (auto container : *roots) { - fprintf(stderr, "%p is root\n", container); + MEMORY_LOG("%p is root\n", container); dumpWorker(prefix, container, &seen); } } @@ -471,12 +472,10 @@ inline void Release(ContainerHeader* header) { // do two allocations per frame (ArenaContainer + actual container). inline ArenaContainer* initedArena(ObjHeader** auxSlot) { auto frame = asFrameOverlay(auxSlot); -#if TRACE_MEMORY - fprintf(stderr, "Initializing arena at %p\n", frame); -#endif auto arena = frame->arena; if (!arena) { arena = konanConstructInstance(); + MEMORY_LOG("Initializing arena in %p\n", frame); arena->Init(); frame->arena = arena; } @@ -492,8 +491,8 @@ ContainerHeader* AllocContainer(size_t size) { // is how to get actual size of container. #endif ContainerHeader* result = konanConstructSizedInstance(size); + MEMORY_LOG(">>> alloc %d -> %p\n", static_cast(size), result); #if TRACE_MEMORY - fprintf(stderr, ">>> alloc %d -> %p\n", static_cast(size), result); state->containers->insert(result); #endif state->allocCount++; @@ -505,16 +504,12 @@ void FreeContainer(ContainerHeader* header) { auto state = memoryState; #if TRACE_MEMORY if (isFreeable(header)) { - fprintf(stderr, "<<< free %p\n", header); + MEMORY_LOG("<<< free %p\n", header); } #endif // Now let's clean all object's fields in this container. traverseContainerObjectFields(header, [](ObjHeader** location) { -#if TRACE_MEMORY - fprintf(stderr, "Calling UpdateRef from FreeContainer\n"); -#endif - UpdateRef(location, nullptr); }); @@ -546,9 +541,7 @@ void ObjectContainer::Init(const TypeInfo* type_info) { header_->setObjectCount(1); // header->refCount_ is zero initialized by AllocContainer(). SetMeta(GetPlace(), type_info); -#if TRACE_MEMORY - fprintf(stderr, "object at %p\n", GetPlace()); -#endif + MEMORY_LOG("object at %p\n", GetPlace()); } } @@ -565,9 +558,7 @@ void ArrayContainer::Init(const TypeInfo* type_info, uint32_t elements) { // header->refCount_ is zero initialized by AllocContainer(). GetPlace()->count_ = elements; SetMeta(GetPlace()->obj(), type_info); -#if TRACE_MEMORY - fprintf(stderr, "array at %p\n", GetPlace()); -#endif + MEMORY_LOG("array at %p\n", GetPlace()); } } @@ -578,15 +569,11 @@ void ArenaContainer::Init() { } void ArenaContainer::Deinit() { -#if TRACE_MEMORY - fprintf(stderr, "Arena::Deinit start\n"); -#endif + MEMORY_LOG("Arena::Deinit start: %p\n", this); auto chunk = currentChunk_; while (chunk != nullptr) { // FreeContainer() doesn't release memory when CONTAINER_TAG_STACK is set. -#if TRACE_MEMORY - fprintf(stderr, "Arena::Deinit free chunk\n"); -#endif + MEMORY_LOG("Arena::Deinit free chunk %p\n", chunk); FreeContainer(chunk->asHeader()); chunk = chunk->next; } @@ -596,9 +583,6 @@ void ArenaContainer::Deinit() { chunk = chunk->next; konanFreeMemory(toRemove); } -#if TRACE_MEMORY - fprintf(stderr, "Arena::Deinit end\n"); -#endif } bool ArenaContainer::allocContainer(container_size_t minSize) { @@ -670,16 +654,12 @@ ArrayHeader* ArenaContainer::PlaceArray(const TypeInfo* type_info, uint32_t coun } inline void AddRef(const ObjHeader* object) { -#if TRACE_MEMORY - fprintf(stderr, "AddRef on %p in %p\n", object, object->container()); -#endif + MEMORY_LOG("AddRef on %p in %p\n", object, object->container()); AddRef(object->container()); } inline void ReleaseRef(const ObjHeader* object) { -#if TRACE_MEMORY - fprintf(stderr, "ReleaseRef on %p in %p\n", object, object->container()); -#endif + MEMORY_LOG("ReleaseRef on %p in %p\n", object, object->container()); Release(object->container()); } @@ -718,10 +698,7 @@ void DeinitMemory(MemoryState* memoryState) { #if TRACE_MEMORY // Free all global objects, to ensure no memory leaks happens. for (auto location: *memoryState->globalObjects) { - fprintf(stderr, "Release global in *%p: %p\n", location, *location); -#if TRACE_MEMORY - fprintf(stderr, "Calling UpdateRef from DeinitMemory\n"); -#endif + MEMORY_LOG("Release global in *%p: %p\n", location, *location); UpdateRef(location, nullptr); } konanDestructInstance(memoryState->globalObjects); @@ -741,8 +718,8 @@ void DeinitMemory(MemoryState* memoryState) { #if TRACE_MEMORY if (memoryState->allocCount > 0) { - fprintf(stderr, "*** Memory leaks, leaked %d containers ***\n", - memoryState->allocCount); + MEMORY_LOG("*** Memory leaks, leaked %d containers ***\n", + memoryState->allocCount); dumpReachable("", memoryState->containers); } konanDestructInstance(memoryState->containers); @@ -760,9 +737,7 @@ OBJ_GETTER(AllocInstance, const TypeInfo* type_info) { if (isArenaSlot(OBJ_RESULT)) { auto arena = initedArena(asArenaSlot(OBJ_RESULT)); auto result = arena->PlaceObject(type_info); -#if TRACE_MEMORY - fprintf(stderr, "instance %p in arena: %p\n", result, arena); -#endif + MEMORY_LOG("instance %p in arena: %p\n", result, arena); return result; } RETURN_OBJ(ObjectContainer(type_info).GetPlace()); @@ -773,9 +748,7 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, uint32_t elements) { if (isArenaSlot(OBJ_RESULT)) { auto arena = initedArena(asArenaSlot(OBJ_RESULT)); auto result = arena->PlaceArray(type_info, elements)->obj(); -#if TRACE_MEMORY - fprintf(stderr, "array[%d] %p in arena: %p\n", elements, result, arena); -#endif + MEMORY_LOG("array[%d] %p in arena: %p\n", elements, result, arena); return result; } RETURN_OBJ(ArrayContainer(type_info, elements).GetPlace()->obj()); @@ -791,9 +764,7 @@ OBJ_GETTER(InitInstance, } ObjHeader* object = AllocInstance(type_info, OBJ_RESULT); -#if TRACE_MEMORY - fprintf(stderr, "Calling UpdateRef from InitInstance\n"); -#endif + MEMORY_LOG("Calling UpdateRef from InitInstance\n"); UpdateRef(location, object); #if KONAN_NO_EXCEPTIONS ctor(object); @@ -809,13 +780,7 @@ OBJ_GETTER(InitInstance, #endif return object; } catch (...) { -#if TRACE_MEMORY - fprintf(stderr, "Calling UpdateRef from InitInstance #2\n"); -#endif UpdateRef(OBJ_RESULT, nullptr); -#if TRACE_MEMORY - fprintf(stderr, "Calling UpdateRef from InitInstance #3\n"); -#endif UpdateRef(location, nullptr); throw; } @@ -823,9 +788,7 @@ OBJ_GETTER(InitInstance, } void SetRef(ObjHeader** location, const ObjHeader* object) { -#if TRACE_MEMORY - fprintf(stderr, "SetRef *%p: %p\n", location, object); -#endif + MEMORY_LOG("SetRef *%p: %p\n", location, object); *const_cast(location) = object; AddRef(object); } @@ -850,9 +813,7 @@ void UpdateReturnRef(ObjHeader** returnSlot, const ObjHeader* object) { auto arena = initedArena(asArenaSlot(returnSlot)); returnSlot = arena->getSlot(); } -#if TRACE_MEMORY - fprintf(stderr, "Calling UpdateRef from UpdateReturnRef\n"); -#endif + MEMORY_LOG("Calling UpdateRef from UpdateReturnRef\n"); UpdateRef(returnSlot, object); } @@ -860,10 +821,7 @@ void UpdateRef(ObjHeader** location, const ObjHeader* object) { RuntimeAssert(!isArenaSlot(location), "must not be a slot"); ObjHeader* old = *location; if (old != object) { -#if TRACE_MEMORY - fprintf(stderr, "UpdateRef *%p: %p -> %p\n", location, old, object); - fprintf(stderr, " *%p: %p -> %p\n", location, old == nullptr ? nullptr : old->container(), object == nullptr ? nullptr : object->container()); -#endif + MEMORY_LOG("UpdateRef *%p: %p -> %p\n", location, old, object); if (object != nullptr) { AddRef(object); } @@ -875,33 +833,23 @@ void UpdateRef(ObjHeader** location, const ObjHeader* object) { } void EnterFrame(ObjHeader** start, int count) { -#if TRACE_MEMORY - fprintf(stderr, "EnterFrame %p .. %p\n", start, start + count); -#endif + MEMORY_LOG("EnterFrame %p .. %p\n", start, start + count); } void LeaveFrame(ObjHeader** start, int count) { -#if TRACE_MEMORY - fprintf(stderr, "LeaveFrame %p .. %p\n", start, start + count); -#endif + MEMORY_LOG("LeaveFrame %p .. %p\n", start, start + count); ReleaseRefs(start + kFrameOverlaySlots, count - kFrameOverlaySlots); if (*start != nullptr) { auto arena = initedArena(start); -#if TRACE_MEMORY - fprintf(stderr, "LeaveFrame: free arena %p\n", arena); -#endif + MEMORY_LOG("LeaveFrame: free arena %p\n", arena); arena->Deinit(); konanFreeMemory(arena); -#if TRACE_MEMORY - fprintf(stderr, "LeaveFrame: free arena done %p\n", arena); -#endif + MEMORY_LOG("LeaveFrame: free arena done %p\n", arena); } } void ReleaseRefs(ObjHeader** start, int count) { -#if TRACE_MEMORY - fprintf(stderr, "ReleaseRefs %p .. %p\n", start, start + count); -#endif + MEMORY_LOG("ReleaseRefs %p .. %p\n", start, start + count); ObjHeader** current = start; auto state = memoryState; while (count-- > 0) { @@ -921,9 +869,7 @@ void GarbageCollect() { MemoryState* state = memoryState; RuntimeAssert(!state->gcInProgress, "Recursive GC is disallowed"); -#if TRACE_MEMORY - fprintf(stderr, "Garbage collect\n"); -#endif + MEMORY_LOG("Garbage collect\n"); state->gcInProgress = true; @@ -1041,9 +987,7 @@ bool ClearSubgraphReferences(ObjHeader* root, bool checked) { if (subgraph.count(header) != 0) continue; subgraph.insert(header); -#if TRACE_MEMORY - fprintf(stderr, "Calling removeFreeable from ClearSubgraphReferences\n"); -#endif + MEMORY_LOG("Calling removeFreeable from ClearSubgraphReferences\n"); traverseContainerReferredObjects(header, [&todo](ObjHeader* ref) { auto child = ref->container(); RuntimeAssert(!isArena(child), "A reference to local object is encountered"); diff --git a/runtime/src/main/cpp/Porting.cpp b/runtime/src/main/cpp/Porting.cpp index 6017013205b..0920e0c22d0 100644 --- a/runtime/src/main/cpp/Porting.cpp +++ b/runtime/src/main/cpp/Porting.cpp @@ -67,6 +67,24 @@ uint32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes) { return ::strlen(result); } +#if KONAN_INTERNAL_SNPRINTF +extern "C" int rpl_vsnprintf(char *, size_t, const char *, va_list); +#define vsnprintf_impl rpl_vsnprintf +#else +#define vsnprintf_impl ::vsnprintf +#endif + + +void consolePrintf(const char* format, ...) { + char buffer[1024]; + va_list args; + va_start(args, format); + int rv = vsnprintf_impl(buffer, sizeof(buffer) - 1, format, args); + va_end(args); + consoleWriteUtf8(buffer, rv); +} + + // Process execution. void abort() { ::abort(); @@ -89,18 +107,10 @@ void* memmem(const void *big, size_t bigLen, const void *little, size_t littleLe } // The sprintf family. -#if KONAN_INTERNAL_SNPRINTF -extern "C" int rpl_vsnprintf(char *, size_t, const char *, va_list); -#endif - int snprintf(char* buffer, size_t size, const char* format, ...) { va_list args; va_start(args, format); -#if KONAN_INTERNAL_SNPRINTF - int rv = rpl_vsnprintf(buffer, size, format, args); -#else - int rv = ::vsnprintf(buffer, size, format, args); -#endif + int rv = vsnprintf_impl(buffer, size, format, args); va_end(args); return rv; } @@ -113,22 +123,19 @@ size_t strnlen(const char* buffer, size_t maxSize) { #if KONAN_INTERNAL_DLMALLOC extern "C" void* dlcalloc(size_t, size_t); extern "C" void dlfree(void*); +#define calloc_impl dlcalloc +#define free_impl dlfree +#else +#define calloc_impl ::calloc +#define free_impl ::free #endif void* calloc(size_t count, size_t size) { -#if KONAN_INTERNAL_DLMALLOC - return dlcalloc(count, size); -#else - return ::calloc(count, size); -#endif + return calloc_impl(count, size); } void free(void* pointer) { -#if KONAN_INTERNAL_DLMALLOC - dlfree(pointer); -#else - ::free(pointer); -#endif + free_impl(pointer); } #if KONAN_INTERNAL_NOW @@ -240,11 +247,11 @@ extern "C" { int _ZNSt3__212__next_primeEm(unsigned long n) { static unsigned long primes[] = { 11UL, - 101UL, - 1009UL, - 10007UL, - 100003UL, - 1000003UL, + 101UL, + 1009UL, + 10007UL, + 100003UL, + 1000003UL, 10000019UL, 100000007UL, 1000000007UL @@ -288,7 +295,7 @@ extern "C" { if (src < dst) { for (long i = len; i != 0; --i) { *((char*)dst + i - 1) = *((char*)src + i - 1); - } + } } else { memcpy(dst, src, len); } diff --git a/runtime/src/main/cpp/Porting.h b/runtime/src/main/cpp/Porting.h index f0d38d6044e..568e6af69a9 100644 --- a/runtime/src/main/cpp/Porting.h +++ b/runtime/src/main/cpp/Porting.h @@ -24,6 +24,7 @@ namespace konan { // Console operations. void consoleInit(); +void consolePrintf(const char* format, ...); void consoleWriteUtf8(const void* utf8, uint32_t sizeBytes); void consoleErrorUtf8(const void* utf8, uint32_t sizeBytes); uint32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes); diff --git a/runtime/src/main/cpp/Types.h b/runtime/src/main/cpp/Types.h index 3292edb5263..f2842db5f49 100644 --- a/runtime/src/main/cpp/Types.h +++ b/runtime/src/main/cpp/Types.h @@ -19,7 +19,7 @@ #include -#if KONAN_WASM +#if KONAN_WASM && !defined(assert) // assert() is needed by WASM STL. #define assert(cond) if (!(cond)) abort() #endif diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt index 087238830b8..c9a0d6384a0 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/target/ClangArgs.kt @@ -47,11 +47,11 @@ class ClangTarget(val target: KonanTarget, konanProperties: KonanProperties) { "-DKONAN_OBJC_INTEROP=1") KonanTarget.IPHONE -> - listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0", + listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", sysRoot, "-miphoneos-version-min=8.0.0", "-DKONAN_OBJC_INTEROP=1") KonanTarget.IPHONE_SIM -> - listOf("-stdlib=libc++", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0", + listOf("-stdlib=libc++", "-isysroot", sysRoot, "-miphoneos-version-min=8.0.0", "-DKONAN_OBJC_INTEROP=1") KonanTarget.ANDROID_ARM32 ->