Skip to content

Commit cd624bf

Browse files
committed
[Runtime][Reflection][swift-inspect] Add facilities for tracking and examining backtraces for metadata allocations.
rdar://problem/63674755
1 parent f2fb539 commit cd624bf

File tree

13 files changed

+350
-36
lines changed

13 files changed

+350
-36
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@
3434
#include "swift/Runtime/Unreachable.h"
3535

3636
#include <set>
37-
#include <vector>
37+
#include <sstream>
3838
#include <unordered_map>
3939
#include <utility>
40+
#include <vector>
4041

4142
namespace {
4243

@@ -794,7 +795,7 @@ class ReflectionContext
794795
llvm::Optional<std::string> iterateConformances(
795796
std::function<void(StoredPointer Type, StoredPointer Proto)> Call) {
796797
std::string ConformancesPointerName =
797-
"__swift_debug_protocolConformanceStatePointer";
798+
"_swift_debug_protocolConformanceStatePointer";
798799
auto ConformancesAddrAddr =
799800
getReader().getSymbolAddress(ConformancesPointerName);
800801
if (!ConformancesAddrAddr)
@@ -844,9 +845,9 @@ class ReflectionContext
844845
llvm::Optional<std::string> iterateMetadataAllocations(
845846
std::function<void (MetadataAllocation<Runtime>)> Call) {
846847
std::string IterationEnabledName =
847-
"__swift_debug_metadataAllocationIterationEnabled";
848+
"_swift_debug_metadataAllocationIterationEnabled";
848849
std::string AllocationPoolPointerName =
849-
"__swift_debug_allocationPoolPointer";
850+
"_swift_debug_allocationPoolPointer";
850851

851852
auto IterationEnabledAddr =
852853
getReader().getSymbolAddress(IterationEnabledName);
@@ -922,6 +923,50 @@ class ReflectionContext
922923
return llvm::None;
923924
}
924925

926+
llvm::Optional<std::string> iterateMetadataAllocationBacktraces(
927+
std::function<void(StoredPointer, uint32_t, const StoredPointer *)>
928+
Call) {
929+
std::string BacktraceListName =
930+
"_swift_debug_metadataAllocationBacktraceList";
931+
932+
auto BacktraceListAddr = getReader().getSymbolAddress(BacktraceListName);
933+
if (!BacktraceListAddr)
934+
return "unable to look up debug variable " + BacktraceListName;
935+
auto BacktraceListNextPtr =
936+
getReader().readPointer(BacktraceListAddr, sizeof(StoredPointer));
937+
if (!BacktraceListNextPtr)
938+
return llvm::None;
939+
940+
auto BacktraceListNext = BacktraceListNextPtr->getResolvedAddress();
941+
while (BacktraceListNext) {
942+
auto HeaderBytes = getReader().readBytes(
943+
RemoteAddress(BacktraceListNext),
944+
sizeof(MetadataAllocationBacktraceHeader<Runtime>));
945+
auto HeaderPtr =
946+
reinterpret_cast<const MetadataAllocationBacktraceHeader<Runtime> *>(
947+
HeaderBytes.get());
948+
if (HeaderPtr == nullptr) {
949+
std::stringstream stream;
950+
stream << "unable to read Next pointer 0x" << std::hex
951+
<< BacktraceListNext.getAddressData();
952+
return stream.str();
953+
}
954+
auto BacktraceAddrPtr =
955+
BacktraceListNext +
956+
sizeof(MetadataAllocationBacktraceHeader<Runtime>);
957+
auto BacktraceBytes =
958+
getReader().readBytes(RemoteAddress(BacktraceAddrPtr),
959+
HeaderPtr->Count * sizeof(StoredPointer));
960+
auto BacktracePtr =
961+
reinterpret_cast<const StoredPointer *>(BacktraceBytes.get());
962+
963+
Call(HeaderPtr->Allocation, HeaderPtr->Count, BacktracePtr);
964+
965+
BacktraceListNext = RemoteAddress(HeaderPtr->Next);
966+
}
967+
return llvm::None;
968+
}
969+
925970
private:
926971
const TypeInfo *getClosureContextInfo(StoredPointer Context,
927972
const ClosureContextInfo &Info) {

include/swift/Runtime/Debug.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
#ifndef SWIFT_RUNTIME_DEBUG_HELPERS_H
1818
#define SWIFT_RUNTIME_DEBUG_HELPERS_H
1919

20+
#include "swift/Runtime/Config.h"
21+
#include "swift/Runtime/Unreachable.h"
22+
#include <atomic>
2023
#include <cstdarg>
2124
#include <cstdio>
25+
#include <functional>
2226
#include <stdint.h>
23-
#include "swift/Runtime/Config.h"
24-
#include "swift/Runtime/Unreachable.h"
2527

2628
#ifdef SWIFT_HAVE_CRASHREPORTERCLIENT
2729

@@ -145,6 +147,9 @@ void swift_abortDynamicReplacementDisabling();
145147
void dumpStackTraceEntry(unsigned index, void *framePC,
146148
bool shortOutput = false);
147149

150+
SWIFT_RUNTIME_ATTRIBUTE_NOINLINE
151+
bool withCurrentBacktrace(std::function<void(void **, int)> call);
152+
148153
SWIFT_RUNTIME_ATTRIBUTE_NOINLINE
149154
void printCurrentBacktrace(unsigned framesToSkip = 1);
150155

@@ -237,6 +242,9 @@ bool _swift_debug_metadataAllocationIterationEnabled;
237242
SWIFT_RUNTIME_STDLIB_SPI
238243
const void * const _swift_debug_allocationPoolPointer;
239244

245+
SWIFT_RUNTIME_STDLIB_SPI
246+
std::atomic<const void *> _swift_debug_metadataAllocationBacktraceList;
247+
240248
SWIFT_RUNTIME_STDLIB_SPI
241249
const void * const _swift_debug_protocolConformanceStatePointer;
242250

include/swift/Runtime/Metadata.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ enum MetadataAllocatorTags : uint16_t {
5454
GenericValueMetadataTag,
5555
};
5656

57+
template <typename Runtime> struct MetadataAllocationBacktraceHeader {
58+
TargetPointer<Runtime, const void> Next;
59+
TargetPointer<Runtime, void> Allocation;
60+
uint32_t Count;
61+
// Count backtrace pointers immediately follow.
62+
};
63+
5764
/// The buffer used by a yield-once coroutine (such as the generalized
5865
/// accessors `read` and `modify`).
5966
struct YieldOnceBuffer {

include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,28 @@ swift_reflection_ptr_t swift_reflection_allocationMetadataPointer(
331331
SwiftReflectionContextRef ContextRef,
332332
swift_metadata_allocation_t Allocation);
333333

334+
/// Backtrace iterator callback passed to
335+
/// swift_reflection_iterateMetadataAllocationBacktraces
336+
typedef void (*swift_metadataAllocationIterator)(
337+
swift_reflection_ptr_t AllocationPtr, size_t Count,
338+
const swift_reflection_ptr_t Ptrs[], void *ContextPtr);
339+
340+
/// Iterate over all recorded metadata allocation backtraces in the process.
341+
///
342+
/// Calls the passed in Call function for each recorded backtrace. The function
343+
/// is passed the number of backtrace entries and an array of those entries, as
344+
/// pointers. The array is stored from deepest to shallowest, so main() will be
345+
/// somewhere near the end. This array is valid only for the duration of the
346+
/// call.
347+
///
348+
/// Returns NULL on success. On error, returns a pointer to a C string
349+
/// describing the error. This pointer remains valid until the next
350+
/// swift_reflection call on the given context.
351+
SWIFT_REMOTE_MIRROR_LINKAGE
352+
const char *swift_reflection_iterateMetadataAllocationBacktraces(
353+
SwiftReflectionContextRef ContextRef, swift_metadataAllocationIterator Call,
354+
void *ContextPtr);
355+
334356
#ifdef __cplusplus
335357
} // extern "C"
336358
#endif

stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,3 +635,21 @@ swift_reflection_ptr_t swift_reflection_allocationMetadataPointer(
635635
NativeAllocation.Size = Allocation.Size;
636636
return Context->allocationMetadataPointer(NativeAllocation);
637637
}
638+
639+
const char *swift_reflection_iterateMetadataAllocationBacktraces(
640+
SwiftReflectionContextRef ContextRef, swift_metadataAllocationIterator Call,
641+
void *ContextPtr) {
642+
auto Context = ContextRef->nativeContext;
643+
auto Error = Context->iterateMetadataAllocationBacktraces(
644+
[&](auto AllocationPtr, auto Count, auto Ptrs) {
645+
// Ptrs is an array of StoredPointer, but the callback expects an array
646+
// of swift_reflection_ptr_t. Those may are not always the same type.
647+
// (For example, swift_reflection_ptr_t can be 64-bit on 32-bit systems,
648+
// while StoredPointer is always the pointer size of the target system.)
649+
// Convert the array to an array of swift_reflection_ptr_t.
650+
std::vector<swift_reflection_ptr_t> ConvertedPtrs{&Ptrs[0],
651+
&Ptrs[Count]};
652+
Call(AllocationPtr, Count, ConvertedPtrs.data(), ContextPtr);
653+
});
654+
return convertError(ContextRef, Error);
655+
}

stdlib/public/runtime/EnvironmentVariables.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ VARIABLE(SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION, bool, false,
2626
"Enable additional metadata allocation tracking for swift-inspect to "
2727
"use.")
2828

29+
VARIABLE(SWIFT_DEBUG_ENABLE_METADATA_BACKTRACE_LOGGING, bool, false,
30+
"Enable logging of backtraces for each metadata allocation. Requires "
31+
"SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION to be enabled.")
32+
2933
VARIABLE(SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT, uint8_t, 2,
3034
"Print warnings when using implicit @objc entrypoints. Set to "
3135
"desired reporting level, 0-3.")

stdlib/public/runtime/Errors.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ static _Unwind_Reason_Code SwiftUnwindFrame(struct _Unwind_Context *context, voi
223223
}
224224
#endif
225225

226-
SWIFT_NOINLINE
227-
void swift::printCurrentBacktrace(unsigned framesToSkip) {
226+
SWIFT_ALWAYS_INLINE
227+
static bool withCurrentBacktraceImpl(std::function<void(void **, int)> call) {
228228
#if SWIFT_SUPPORTS_BACKTRACE_REPORTING
229229
constexpr unsigned maxSupportedStackDepth = 128;
230230
void *addrs[maxSupportedStackDepth];
@@ -237,14 +237,29 @@ void swift::printCurrentBacktrace(unsigned framesToSkip) {
237237
#else
238238
int symbolCount = backtrace(addrs, maxSupportedStackDepth);
239239
#endif
240-
for (int i = framesToSkip; i < symbolCount; ++i) {
241-
dumpStackTraceEntry(i - framesToSkip, addrs[i]);
242-
}
240+
call(addrs, symbolCount);
241+
return true;
243242
#else
244-
fprintf(stderr, "<backtrace unavailable>\n");
243+
return false;
245244
#endif
246245
}
247246

247+
SWIFT_NOINLINE
248+
bool swift::withCurrentBacktrace(std::function<void(void **, int)> call) {
249+
return withCurrentBacktraceImpl(call);
250+
}
251+
252+
SWIFT_NOINLINE
253+
void swift::printCurrentBacktrace(unsigned framesToSkip) {
254+
bool success = withCurrentBacktraceImpl([&](void **addrs, int symbolCount) {
255+
for (int i = framesToSkip; i < symbolCount; ++i) {
256+
dumpStackTraceEntry(i - framesToSkip, addrs[i]);
257+
}
258+
});
259+
if (!success)
260+
fprintf(stderr, "<backtrace unavailable>\n");
261+
}
262+
248263
#ifdef SWIFT_HAVE_CRASHREPORTERCLIENT
249264
#include <malloc/malloc.h>
250265

stdlib/public/runtime/Metadata.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5504,12 +5504,19 @@ AllocationPool{PoolRange{InitialAllocationPool.Pool,
55045504

55055505
bool swift::_swift_debug_metadataAllocationIterationEnabled = false;
55065506
const void * const swift::_swift_debug_allocationPoolPointer = &AllocationPool;
5507+
std::atomic<const void *> swift::_swift_debug_metadataAllocationBacktraceList;
55075508

55085509
static void checkAllocatorDebugEnvironmentVariable(void *context) {
55095510
_swift_debug_metadataAllocationIterationEnabled
55105511
= runtime::environment::SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION();
5511-
if (!_swift_debug_metadataAllocationIterationEnabled)
5512+
if (!_swift_debug_metadataAllocationIterationEnabled) {
5513+
if (runtime::environment::SWIFT_DEBUG_ENABLE_METADATA_BACKTRACE_LOGGING())
5514+
swift::warning(RuntimeErrorFlagNone,
5515+
"Warning: SWIFT_DEBUG_ENABLE_METADATA_BACKTRACE_LOGGING "
5516+
"without SWIFT_DEBUG_ENABLE_METADATA_ALLOCATION_ITERATION "
5517+
"has no effect.\n");
55125518
return;
5519+
}
55135520

55145521
// Write a PoolTrailer to the end of InitialAllocationPool and shrink
55155522
// the pool accordingly.
@@ -5523,6 +5530,24 @@ static void checkAllocatorDebugEnvironmentVariable(void *context) {
55235530
AllocationPool.store(poolCopy, std::memory_order_relaxed);
55245531
}
55255532

5533+
static void recordBacktrace(void *allocation) {
5534+
withCurrentBacktrace([&](void **addrs, int count) {
5535+
MetadataAllocationBacktraceHeader<InProcess> *record =
5536+
(MetadataAllocationBacktraceHeader<InProcess> *)malloc(
5537+
sizeof(*record) + count * sizeof(void *));
5538+
record->Allocation = allocation;
5539+
record->Count = count;
5540+
memcpy(record + 1, addrs, count * sizeof(void *));
5541+
5542+
record->Next = _swift_debug_metadataAllocationBacktraceList.load(
5543+
std::memory_order_relaxed);
5544+
while (!_swift_debug_metadataAllocationBacktraceList.compare_exchange_weak(
5545+
record->Next, record, std::memory_order_release,
5546+
std::memory_order_relaxed))
5547+
; // empty
5548+
});
5549+
}
5550+
55265551
void *MetadataAllocator::Allocate(size_t size, size_t alignment) {
55275552
assert(Tag != 0);
55285553
assert(alignment <= alignof(void*));
@@ -5582,8 +5607,14 @@ void *MetadataAllocator::Allocate(size_t size, size_t alignment) {
55825607
AllocationHeader *header = (AllocationHeader *)allocation;
55835608
header->Size = size;
55845609
header->Tag = Tag;
5585-
5586-
return allocation + sizeof(AllocationHeader);
5610+
5611+
auto *returnedAllocation = allocation + sizeof(AllocationHeader);
5612+
5613+
if (runtime::environment ::
5614+
SWIFT_DEBUG_ENABLE_METADATA_BACKTRACE_LOGGING())
5615+
recordBacktrace(returnedAllocation);
5616+
5617+
return returnedAllocation;
55875618
} else {
55885619
return allocation;
55895620
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftRemoteMirror
14+
15+
struct Backtrace {
16+
enum Style {
17+
case oneLine
18+
case long
19+
}
20+
21+
/// The pointers to the locations in the backtrace. These are stored from
22+
/// deepest to shallowest, so main() will be somewhere near the end.
23+
var ptrs: [swift_reflection_ptr_t]
24+
25+
func symbolString(
26+
ptr: swift_reflection_ptr_t,
27+
inspector: Inspector
28+
) -> String {
29+
let symbol = inspector.getSymbol(address: ptr)
30+
let name = symbol.name ?? "<unknown>"
31+
let library = symbol.library ?? "<unknown>"
32+
return "\(hex: ptr) (\(library)) \(name)"
33+
}
34+
35+
func symbolicatedOneLine(inspector: Inspector) -> String {
36+
return ptrs.reversed().map {
37+
symbolString(ptr: $0, inspector: inspector)
38+
}.joined(separator: " | ")
39+
}
40+
41+
func symbolicatedLong(inspector: Inspector) -> String {
42+
return ptrs.reversed().enumerated().map {
43+
let indent = String(repeating: " ", count: $0 + 1)
44+
return indent + symbolString(ptr: $1, inspector: inspector)
45+
}.joined(separator: "\n")
46+
}
47+
48+
func symbolicated(style: Style, inspector: Inspector) -> String {
49+
switch style {
50+
case .oneLine:
51+
return symbolicatedOneLine(inspector: inspector)
52+
case .long:
53+
return symbolicatedLong(inspector: inspector)
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)