Skip to content

[swift-inspect] Add a command for dumping metadata cache nodes. #32795

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

Merged
merged 2 commits into from
Jul 23, 2020
Merged
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
30 changes: 30 additions & 0 deletions include/swift/Reflection/ReflectionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,36 @@ class ReflectionContext
}
}

llvm::Optional<MetadataCacheNode<Runtime>>
metadataAllocationCacheNode(MetadataAllocation<Runtime> Allocation) {
switch (Allocation.Tag) {
case BoxesTag:
case ObjCClassWrappersTag:
case FunctionTypesTag:
case MetatypeTypesTag:
case ExistentialMetatypeValueWitnessTablesTag:
case ExistentialMetatypesTag:
case ExistentialTypesTag:
case OpaqueExistentialValueWitnessTablesTag:
case ClassExistentialValueWitnessTablesTag:
case ForeignWitnessTablesTag:
case TupleCacheTag:
case GenericMetadataCacheTag:
case ForeignMetadataCacheTag:
case GenericWitnessTableCacheTag: {
auto NodeBytes = getReader().readBytes(
RemoteAddress(Allocation.Ptr), sizeof(MetadataCacheNode<Runtime>));
auto Node =
reinterpret_cast<const MetadataCacheNode<Runtime> *>(NodeBytes.get());
if (!Node)
return llvm::None;
return *Node;
}
default:
return llvm::None;
}
}

/// Iterate the metadata allocations in the target process, calling Call with
/// each allocation found. Returns None on success, and a string describing
/// the error on failure.
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Reflection/RuntimeInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ struct MetadataAllocation {
unsigned Size;
};

template <typename Runtime> struct MetadataCacheNode {
typename Runtime::StoredPointer Left;
typename Runtime::StoredPointer Right;
};

} // end namespace reflection
} // end namespace swift

Expand Down
6 changes: 6 additions & 0 deletions include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,12 @@ const char *
swift_reflection_metadataAllocationTagName(SwiftReflectionContextRef ContextRef,
swift_metadata_allocation_tag_t Tag);

SWIFT_REMOTE_MIRROR_LINKAGE
int swift_reflection_metadataAllocationCacheNode(
SwiftReflectionContextRef ContextRef,
swift_metadata_allocation_t Allocation,
swift_metadata_cache_node_t *OutNode);

/// Backtrace iterator callback passed to
/// swift_reflection_iterateMetadataAllocationBacktraces
typedef void (*swift_metadataAllocationBacktraceIterator)(
Expand Down
5 changes: 5 additions & 0 deletions include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ typedef struct swift_metadata_allocation {
unsigned Size;
} swift_metadata_allocation_t;

typedef struct swift_metadata_cache_node {
swift_reflection_ptr_t Left;
swift_reflection_ptr_t Right;
} swift_metadata_cache_node_t;

/// An opaque pointer to a context which maintains state and
/// caching of reflection structure for heap instances.
typedef struct SwiftReflectionContext *SwiftReflectionContextRef;
Expand Down
19 changes: 19 additions & 0 deletions stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,25 @@ const char *swift_reflection_metadataAllocationTagName(
return returnableCString(ContextRef, Result);
}

int swift_reflection_metadataAllocationCacheNode(
SwiftReflectionContextRef ContextRef,
swift_metadata_allocation_t Allocation,
swift_metadata_cache_node_t *OutNode) {
auto Context = ContextRef->nativeContext;
MetadataAllocation<Runtime> ConvertedAllocation;
ConvertedAllocation.Tag = Allocation.Tag;
ConvertedAllocation.Ptr = Allocation.Ptr;
ConvertedAllocation.Size = Allocation.Size;

auto Result = Context->metadataAllocationCacheNode(ConvertedAllocation);
if (!Result)
return 0;

OutNode->Left = Result->Left;
OutNode->Right = Result->Right;
return 1;
}

const char *swift_reflection_iterateMetadataAllocationBacktraces(
SwiftReflectionContextRef ContextRef,
swift_metadataAllocationBacktraceIterator Call, void *ContextPtr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,23 @@ extension SwiftReflectionContextRef {
swift_reflection_allocationMetadataPointer(self, allocation)
}

func metadataTagName(_ tag: swift_metadata_allocation_tag_t) -> String? {
swift_reflection_metadataAllocationTagName(self, tag)
.map(String.init)
}

func metadataAllocationCacheNode(
_ allocation: swift_metadata_allocation_t
) -> swift_metadata_cache_node_t? {
var node = swift_metadata_cache_node_t();
let success = swift_reflection_metadataAllocationCacheNode(
self, allocation, &node)
if success == 0 {
return nil
}
return node
}

private func throwError(str: UnsafePointer<CChar>?) throws {
if let str = str {
throw Error(cString: str)
Expand Down
98 changes: 69 additions & 29 deletions tools/swift-inspect/Sources/swift-inspect/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ func dumpRawMetadata(
) throws {
let backtraces = backtraceStyle != nil ? context.allocationBacktraces : [:]
for allocation in context.allocations {
let tagNameC = swift_reflection_metadataAllocationTagName(context, allocation.tag)
let tagName = tagNameC.map(String.init) ?? "<unknown>"
let tagName = context.metadataTagName(allocation.tag) ?? "<unknown>"
print("Metadata allocation at: \(hex: allocation.ptr) " +
"size: \(allocation.size) tag: \(allocation.tag) (\(tagName))")
printBacktrace(style: backtraceStyle, for: allocation.ptr, in: backtraces, inspector: inspector)
Expand Down Expand Up @@ -74,6 +73,22 @@ func dumpGenericMetadata(
}
}

func dumpMetadataCacheNodes(
context: SwiftReflectionContextRef,
inspector: Inspector
) throws {
print("Address","Tag","Tag Name","Size","Left","Right", separator: "\t")
for allocation in context.allocations {
guard let node = context.metadataAllocationCacheNode(allocation.allocation_t) else {
continue
}

let tagName = context.metadataTagName(allocation.tag) ?? "<unknown>"
print("\(hex: allocation.ptr)\t\(allocation.tag)\t\(tagName)\t" +
"\(allocation.size)\t\(hex: node.Left)\t\(hex: node.Right)")
}
}

func printBacktrace(
style: Backtrace.Style?,
for ptr: swift_reflection_ptr_t,
Expand Down Expand Up @@ -133,18 +148,38 @@ struct SwiftInspect: ParsableCommand {
DumpConformanceCache.self,
DumpRawMetadata.self,
DumpGenericMetadata.self,
DumpCacheNodes.self,
])
}

struct UniversalOptions: ParsableArguments {
@Argument(help: "The pid or partial name of the target process")
var nameOrPid: String
}

struct BacktraceOptions: ParsableArguments {
@Flag(help: "Show the backtrace for each allocation")
var backtrace: Bool

@Flag(help: "Show a long-form backtrace for each allocation")
var backtraceLong: Bool

var style: Backtrace.Style? {
backtrace ? .oneLine :
backtraceLong ? .long :
nil
}
}

struct DumpConformanceCache: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Print the contents of the target's protocol conformance cache.")

@Argument(help: "The pid or partial name of the target process")
var nameOrPid: String
@OptionGroup()
var options: UniversalOptions

func run() throws {
try withReflectionContext(nameOrPid: nameOrPid) { context, _ in
try withReflectionContext(nameOrPid: options.nameOrPid) { context, _ in
try dumpConformanceCache(context: context)
}
}
Expand All @@ -153,22 +188,18 @@ struct DumpConformanceCache: ParsableCommand {
struct DumpRawMetadata: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Print the target's metadata allocations.")
@Argument(help: "The pid or partial name of the target process")

var nameOrPid: String

@Flag(help: "Show the backtrace for each allocation")
var backtrace: Bool
@OptionGroup()
var universalOptions: UniversalOptions

@Flag(help: "Show a long-form backtrace for each allocation")
var backtraceLong: Bool
@OptionGroup()
var backtraceOptions: BacktraceOptions

func run() throws {
let style = backtrace ? Backtrace.Style.oneLine :
backtraceLong ? Backtrace.Style.long :
nil
try withReflectionContext(nameOrPid: nameOrPid) {
try dumpRawMetadata(context: $0, inspector: $1, backtraceStyle: style)
try withReflectionContext(nameOrPid: universalOptions.nameOrPid) {
try dumpRawMetadata(context: $0,
inspector: $1,
backtraceStyle: backtraceOptions.style)
}
}
}
Expand All @@ -177,23 +208,32 @@ struct DumpGenericMetadata: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Print the target's generic metadata allocations.")

@Argument(help: "The pid or partial name of the target process")
var nameOrPid: String
@OptionGroup()
var universalOptions: UniversalOptions

@Flag(help: "Show the backtrace for each allocation")
var backtrace: Bool

@Flag(help: "Show a long-form backtrace for each allocation")
var backtraceLong: Bool
@OptionGroup()
var backtraceOptions: BacktraceOptions

func run() throws {
let style = backtrace ? Backtrace.Style.oneLine :
backtraceLong ? Backtrace.Style.long :
nil
try withReflectionContext(nameOrPid: nameOrPid) {
try withReflectionContext(nameOrPid: universalOptions.nameOrPid) {
try dumpGenericMetadata(context: $0,
inspector: $1,
backtraceStyle: style)
backtraceStyle: backtraceOptions.style)
}
}
}

struct DumpCacheNodes: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Print the target's metadata cache nodes.")

@OptionGroup()
var options: UniversalOptions

func run() throws {
try withReflectionContext(nameOrPid: options.nameOrPid) {
try dumpMetadataCacheNodes(context: $0,
inspector: $1)
}
}
}
Expand Down