From 9d746b804df1caaf77e74b7295e2e4f46a3c3d1b Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 19 Jun 2020 14:46:39 +0200 Subject: [PATCH] stdlib: add a utility to print what and where dynamic metadata is created. The logging can be enabled at runtime with _setMetaDataDebugging(logLevel: Int). Depending on the log level it prints what metadata is created and also where it is created (the backtrace). --- include/swift/Runtime/Metadata.h | 3 ++ stdlib/public/core/Misc.swift | 9 ++++++ stdlib/public/runtime/Metadata.cpp | 27 ++++++++++++++++ test/stdlib/metadata_debugging.swift | 48 ++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 test/stdlib/metadata_debugging.swift diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 51c547e9bfe70..0c8c6213215c6 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -829,6 +829,9 @@ const TypeContextDescriptor *swift_getTypeContextDescriptor(const Metadata *type SWIFT_RUNTIME_EXPORT const HeapObject *swift_getKeyPath(const void *pattern, const void *arguments); +SWIFT_RUNTIME_EXPORT +void swift_setMetadataDebugLogLevel(int level); + #if defined(swiftCore_EXPORTS) /// Given a pointer to a borrowed value of type `Root` and a /// `KeyPath`, project a pointer to a borrowed value of type diff --git a/stdlib/public/core/Misc.swift b/stdlib/public/core/Misc.swift index 83ec946ea64eb..ff23a2d8d7707 100644 --- a/stdlib/public/core/Misc.swift +++ b/stdlib/public/core/Misc.swift @@ -132,3 +132,12 @@ public func _getTypeByMangledNameInContext( genericContext: UnsafeRawPointer?, genericArguments: UnsafeRawPointer?) -> Any.Type? + +/// Sets the log level for printing type metadata creation. +/// +/// 0 ... No debug logging (default) +/// 1 ... Print the name of types for which dynamic metadata is created. +/// 2 ... Like '1' but also prints the backtrace. +@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) +@_silgen_name("swift_setMetadataDebugLogLevel") +public func _setMetaDataDebugging(logLevel: Int) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index e4572d8529479..eb1bdbca84a92 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -79,6 +79,27 @@ extern "C" void _objc_setClassCopyFixupHandler(void (* _Nonnull newFixupHandler) using namespace swift; using namespace metadataimpl; +/// 0 ... no debug logging +/// 1 ... print type names +/// 2 ... print type names and backtrace +static int metadataDebugLogLevel = 0; + +void swift::swift_setMetadataDebugLogLevel(int level) { + metadataDebugLogLevel = level; +} + +static __attribute__((noinline)) void debugLogMetadataImpl(Metadata *metadata) { + fprintf(stderr, "### %s\n", nameForMetadata(metadata).c_str()); + if (metadataDebugLogLevel > 1) { + printCurrentBacktrace(/*framesToSkip*/ 3); + } +} + +static inline void debugLogMetadata(Metadata *metadata) { + if (metadataDebugLogLevel > 0) + debugLogMetadataImpl(metadata); +} + static ClassMetadata * _swift_relocateClassMetadata(const ClassDescriptor *description, const ResilientClassMetadataPattern *pattern); @@ -670,6 +691,8 @@ swift::swift_allocateGenericValueMetadata(const ValueTypeDescriptor *description // Copy the generic arguments into place. installGenericArguments(metadata, description, arguments); + debugLogMetadata(metadata); + return metadata; } @@ -1801,6 +1824,8 @@ TupleCacheEntry::tryInitialize(Metadata *metadata, // Okay, we're all done with layout and setting up the elements. // Check transitive completeness. + debugLogMetadata(getValue()); + // We don't need to check the element statuses again in a couple of cases: // - If all the elements are transitively complete, we are, too. @@ -2909,6 +2934,8 @@ _swift_initClassMetadataImpl(ClassMetadata *self, assert(!self->getDescription()->hasObjCResilientClassStub()); #endif + debugLogMetadata(self); + return MetadataDependency(); } diff --git a/test/stdlib/metadata_debugging.swift b/test/stdlib/metadata_debugging.swift new file mode 100644 index 0000000000000..62b9eb6ebc062 --- /dev/null +++ b/test/stdlib/metadata_debugging.swift @@ -0,0 +1,48 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -module-name=test %s -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | grep 'check-prefix' > %t/prefix-option +// RUN: %target-run %t/a.out 2>&1 >/dev/null | %FileCheck `cat %t/prefix-option` %s + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +// This test doesn't use StdlibUnittest because it's primarily concerned with +// checking the presence and absence of output. + +// A tricky way to make the FileCheck tests conditional on the OS version. +if #available(macOS 9999, *) { + print("-check-prefix=CHECK") +} else { + print("-check-prefix=DONT-CHECK") + // Need at least one check, otherwise FileCheck will complain. + // DONT-CHECK: {{.}} +} + +if #available(macOS 9999, *) { + _setMetaDataDebugging(logLevel: 2) +} + +struct S { +} + +class K { +} + +@_optimize(none) +func test_tuple(_ t: T) -> Any { + return (t, t) +} + +// CHECK: ### test.S +// CHECK: {{^[0-9]+ +a.out +}} +public let s: Any = S() + +// CHECK: ### (Swift.Int, Swift.Int) +// CHECK: {{^[0-9]+ +a.out +}} +public let t: Any = test_tuple(27) + +// CHECK: ### test.K +// CHECK: {{^[0-9]+ +a.out +}} +public let k: Any = K() +