Skip to content

stdlib: add a utility to print what and where dynamic metadata is created. #32467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Root, Value>`, project a pointer to a borrowed value of type
Expand Down
9 changes: 9 additions & 0 deletions stdlib/public/core/Misc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
27 changes: 27 additions & 0 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -670,6 +691,8 @@ swift::swift_allocateGenericValueMetadata(const ValueTypeDescriptor *description
// Copy the generic arguments into place.
installGenericArguments(metadata, description, arguments);

debugLogMetadata(metadata);

return metadata;
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -2909,6 +2934,8 @@ _swift_initClassMetadataImpl(ClassMetadata *self,
assert(!self->getDescription()->hasObjCResilientClassStub());
#endif

debugLogMetadata(self);

return MetadataDependency();
}

Expand Down
48 changes: 48 additions & 0 deletions test/stdlib/metadata_debugging.swift
Original file line number Diff line number Diff line change
@@ -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<T> {
}

class K<T> {
}

@_optimize(none)
func test_tuple<T>(_ t: T) -> Any {
return (t, t)
}

// CHECK: ### test.S<Swift.Float>
// CHECK: {{^[0-9]+ +a.out +}}
public let s: Any = S<Float>()

// CHECK: ### (Swift.Int, Swift.Int)
// CHECK: {{^[0-9]+ +a.out +}}
public let t: Any = test_tuple(27)

// CHECK: ### test.K<Swift.Int>
// CHECK: {{^[0-9]+ +a.out +}}
public let k: Any = K<Int>()