Skip to content

Commit bd434d4

Browse files
authored
Merge pull request #41160 from mikeash/swift-inspect-concurrency
[RemoteMirror][swift-inspect] Add a command to inspect the state of the concurrency runtime.
2 parents fda0b80 + a82ea12 commit bd434d4

File tree

15 files changed

+962
-42
lines changed

15 files changed

+962
-42
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 219 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ class ReflectionContext
101101
std::vector<MemoryReader::ReadBytesResult> savedBuffers;
102102
std::vector<std::tuple<RemoteAddress, RemoteAddress>> imageRanges;
103103

104+
bool setupTargetPointers = false;
105+
typename super::StoredPointer target_non_future_adapter = 0;
106+
typename super::StoredPointer target_future_adapter = 0;
107+
typename super::StoredPointer target_task_wait_throwing_resume_adapter = 0;
108+
typename super::StoredPointer target_task_future_wait_resume_adapter = 0;
109+
104110
public:
105111
using super::getBuilder;
106112
using super::readDemanglingForContextDescriptor;
@@ -137,6 +143,21 @@ class ReflectionContext
137143
std::vector<AsyncTaskAllocationChunk> Chunks;
138144
};
139145

146+
struct AsyncTaskInfo {
147+
uint32_t JobFlags;
148+
uint64_t TaskStatusFlags;
149+
uint64_t Id;
150+
StoredPointer RunJob;
151+
StoredPointer AllocatorSlabPtr;
152+
std::vector<StoredPointer> ChildTasks;
153+
std::vector<StoredPointer> AsyncBacktraceFrames;
154+
};
155+
156+
struct ActorInfo {
157+
StoredSize Flags;
158+
StoredPointer FirstJob;
159+
};
160+
140161
explicit ReflectionContext(std::shared_ptr<MemoryReader> reader)
141162
: super(std::move(reader), *this)
142163
{}
@@ -1083,6 +1104,31 @@ class ReflectionContext
10831104
return dyn_cast_or_null<const RecordTypeInfo>(TypeInfo);
10841105
}
10851106

1107+
bool metadataIsActor(StoredPointer MetadataAddress) {
1108+
auto Metadata = readMetadata(MetadataAddress);
1109+
if (!Metadata)
1110+
return false;
1111+
1112+
// Only classes can be actors.
1113+
if (Metadata->getKind() != MetadataKind::Class)
1114+
return false;
1115+
1116+
auto DescriptorAddress =
1117+
super::readAddressOfNominalTypeDescriptor(Metadata);
1118+
if (!DescriptorAddress)
1119+
return false;
1120+
1121+
auto DescriptorBytes =
1122+
getReader().readBytes(RemoteAddress(DescriptorAddress),
1123+
sizeof(TargetTypeContextDescriptor<Runtime>));
1124+
if (!DescriptorBytes)
1125+
return false;
1126+
auto Descriptor =
1127+
reinterpret_cast<const TargetTypeContextDescriptor<Runtime> *>(
1128+
DescriptorBytes.get());
1129+
return Descriptor->getTypeContextDescriptorFlags().class_isActor();
1130+
}
1131+
10861132
/// Iterate the protocol conformance cache tree rooted at NodePtr, calling
10871133
/// Call with the type and protocol in each node.
10881134
void iterateConformanceTree(StoredPointer NodePtr,
@@ -1394,22 +1440,179 @@ class ReflectionContext
13941440
return {llvm::None, {Slab->Next, SlabSize, {Chunk}}};
13951441
}
13961442

1397-
std::pair<llvm::Optional<std::string>, StoredPointer>
1398-
asyncTaskSlabPtr(StoredPointer AsyncTaskPtr) {
1399-
using AsyncTask = AsyncTask<Runtime>;
1400-
1401-
auto AsyncTaskBytes =
1402-
getReader().readBytes(RemoteAddress(AsyncTaskPtr), sizeof(AsyncTask));
1403-
auto *AsyncTaskObj =
1404-
reinterpret_cast<const AsyncTask *>(AsyncTaskBytes.get());
1443+
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
1444+
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
1445+
auto AsyncTaskObj = readObj<AsyncTask<Runtime>>(AsyncTaskPtr);
14051446
if (!AsyncTaskObj)
1406-
return {std::string("failure reading async task"), 0};
1447+
return {std::string("failure reading async task"), {}};
1448+
1449+
AsyncTaskInfo Info{};
1450+
Info.JobFlags = AsyncTaskObj->Flags;
1451+
Info.TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags;
1452+
Info.Id =
1453+
AsyncTaskObj->Id | ((uint64_t)AsyncTaskObj->PrivateStorage.Id << 32);
1454+
Info.AllocatorSlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab;
1455+
Info.RunJob = getRunJob(AsyncTaskObj.get());
1456+
1457+
// Find all child tasks.
1458+
auto RecordPtr = AsyncTaskObj->PrivateStorage.Status.Record;
1459+
while (RecordPtr) {
1460+
auto RecordObj = readObj<TaskStatusRecord<Runtime>>(RecordPtr);
1461+
if (!RecordObj)
1462+
break;
1463+
1464+
// This cuts off high bits if our size_t doesn't match the target's. We
1465+
// only read the Kind bits which are at the bottom, so that's OK here.
1466+
// Beware of this when reading anything else.
1467+
TaskStatusRecordFlags Flags{RecordObj->Flags};
1468+
auto Kind = Flags.getKind();
1469+
1470+
StoredPointer ChildTask = 0;
1471+
if (Kind == TaskStatusRecordKind::ChildTask) {
1472+
auto RecordObj = readObj<ChildTaskStatusRecord<Runtime>>(RecordPtr);
1473+
if (RecordObj)
1474+
ChildTask = RecordObj->FirstChild;
1475+
} else if (Kind == TaskStatusRecordKind::TaskGroup) {
1476+
auto RecordObj = readObj<TaskGroupTaskStatusRecord<Runtime>>(RecordPtr);
1477+
if (RecordObj)
1478+
ChildTask = RecordObj->FirstChild;
1479+
}
1480+
1481+
while (ChildTask) {
1482+
Info.ChildTasks.push_back(ChildTask);
1483+
1484+
StoredPointer ChildFragmentAddr =
1485+
ChildTask + sizeof(AsyncTask<Runtime>);
1486+
auto ChildFragmentObj =
1487+
readObj<ChildFragment<Runtime>>(ChildFragmentAddr);
1488+
if (ChildFragmentObj)
1489+
ChildTask = ChildFragmentObj->NextChild;
1490+
else
1491+
ChildTask = 0;
1492+
}
1493+
1494+
RecordPtr = RecordObj->Parent;
1495+
}
1496+
1497+
// Walk the async backtrace if the task isn't running or cancelled.
1498+
// TODO: Use isEnqueued from https://github.com/apple/swift/pull/41088/ once
1499+
// that's available.
1500+
int IsCancelledFlag = 0x100;
1501+
int IsRunningFlag = 0x800;
1502+
if (!(AsyncTaskObj->PrivateStorage.Status.Flags & IsCancelledFlag) &&
1503+
!(AsyncTaskObj->PrivateStorage.Status.Flags & IsRunningFlag)) {
1504+
auto ResumeContext = AsyncTaskObj->ResumeContextAndReserved[0];
1505+
while (ResumeContext) {
1506+
auto ResumeContextObj = readObj<AsyncContext<Runtime>>(ResumeContext);
1507+
if (!ResumeContextObj)
1508+
break;
1509+
Info.AsyncBacktraceFrames.push_back(
1510+
stripSignedPointer(ResumeContextObj->ResumeParent));
1511+
ResumeContext = stripSignedPointer(ResumeContextObj->Parent);
1512+
}
1513+
}
1514+
1515+
return {llvm::None, Info};
1516+
}
1517+
1518+
std::pair<llvm::Optional<std::string>, ActorInfo>
1519+
actorInfo(StoredPointer ActorPtr) {
1520+
using DefaultActorImpl = DefaultActorImpl<Runtime>;
1521+
1522+
auto ActorObj = readObj<DefaultActorImpl>(ActorPtr);
1523+
if (!ActorObj)
1524+
return {std::string("failure reading actor"), {}};
1525+
1526+
ActorInfo Info{};
1527+
Info.Flags = ActorObj->Flags;
1528+
1529+
// Status is the low 3 bits of Flags. Status of 0 is Idle. Don't read
1530+
// FirstJob when idle.
1531+
auto Status = Info.Flags & 0x7;
1532+
if (Status != 0) {
1533+
// This is a JobRef which stores flags in the low bits.
1534+
Info.FirstJob = ActorObj->FirstJob & ~StoredPointer(0x3);
1535+
}
1536+
return {llvm::None, Info};
1537+
}
14071538

1408-
StoredPointer SlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab;
1409-
return {llvm::None, SlabPtr};
1539+
StoredPointer nextJob(StoredPointer JobPtr) {
1540+
using Job = Job<Runtime>;
1541+
1542+
auto JobBytes = getReader().readBytes(RemoteAddress(JobPtr), sizeof(Job));
1543+
auto *JobObj = reinterpret_cast<const Job *>(JobBytes.get());
1544+
if (!JobObj)
1545+
return 0;
1546+
1547+
// This is a JobRef which stores flags in the low bits.
1548+
return JobObj->SchedulerPrivate[0] & ~StoredPointer(0x3);
14101549
}
14111550

14121551
private:
1552+
// Get the most human meaningful "run job" function pointer from the task,
1553+
// like AsyncTask::getResumeFunctionForLogging does.
1554+
StoredPointer getRunJob(const AsyncTask<Runtime> *AsyncTaskObj) {
1555+
auto Fptr = stripSignedPointer(AsyncTaskObj->RunJob);
1556+
1557+
loadTargetPointers();
1558+
auto ResumeContextPtr = AsyncTaskObj->ResumeContextAndReserved[0];
1559+
if (target_non_future_adapter && Fptr == target_non_future_adapter) {
1560+
using Prefix = AsyncContextPrefix<Runtime>;
1561+
auto PrefixAddr = ResumeContextPtr - sizeof(Prefix);
1562+
auto PrefixBytes =
1563+
getReader().readBytes(RemoteAddress(PrefixAddr), sizeof(Prefix));
1564+
if (PrefixBytes) {
1565+
auto PrefixPtr = reinterpret_cast<const Prefix *>(PrefixBytes.get());
1566+
return stripSignedPointer(PrefixPtr->AsyncEntryPoint);
1567+
}
1568+
} else if (target_future_adapter && Fptr == target_future_adapter) {
1569+
using Prefix = FutureAsyncContextPrefix<Runtime>;
1570+
auto PrefixAddr = ResumeContextPtr - sizeof(Prefix);
1571+
auto PrefixBytes =
1572+
getReader().readBytes(RemoteAddress(PrefixAddr), sizeof(Prefix));
1573+
if (PrefixBytes) {
1574+
auto PrefixPtr = reinterpret_cast<const Prefix *>(PrefixBytes.get());
1575+
return stripSignedPointer(PrefixPtr->AsyncEntryPoint);
1576+
}
1577+
} else if ((target_task_wait_throwing_resume_adapter &&
1578+
Fptr == target_task_wait_throwing_resume_adapter) ||
1579+
(target_task_future_wait_resume_adapter &&
1580+
Fptr == target_task_future_wait_resume_adapter)) {
1581+
auto ContextBytes = getReader().readBytes(RemoteAddress(ResumeContextPtr),
1582+
sizeof(AsyncContext<Runtime>));
1583+
if (ContextBytes) {
1584+
auto ContextPtr =
1585+
reinterpret_cast<const AsyncContext<Runtime> *>(ContextBytes.get());
1586+
return stripSignedPointer(ContextPtr->ResumeParent);
1587+
}
1588+
}
1589+
1590+
return Fptr;
1591+
}
1592+
1593+
void loadTargetPointers() {
1594+
if (setupTargetPointers)
1595+
return;
1596+
1597+
auto getFunc = [&](const std::string &name) -> StoredPointer {
1598+
auto Symbol = getReader().getSymbolAddress(name);
1599+
if (!Symbol)
1600+
return 0;
1601+
auto Pointer = getReader().readPointer(Symbol, sizeof(StoredPointer));
1602+
if (!Pointer)
1603+
return 0;
1604+
return Pointer->getResolvedAddress().getAddressData();
1605+
};
1606+
target_non_future_adapter =
1607+
getFunc("_swift_concurrency_debug_non_future_adapter");
1608+
target_future_adapter = getFunc("_swift_concurrency_debug_future_adapter");
1609+
target_task_wait_throwing_resume_adapter =
1610+
getFunc("_swift_concurrency_debug_task_wait_throwing_resume_adapter");
1611+
target_task_future_wait_resume_adapter =
1612+
getFunc("_swift_concurrency_debug_task_future_wait_resume_adapter");
1613+
setupTargetPointers = true;
1614+
}
1615+
14131616
const TypeInfo *
14141617
getClosureContextInfo(StoredPointer Context, const ClosureContextInfo &Info,
14151618
remote::TypeInfoProvider *ExternalTypeInfo) {
@@ -1631,6 +1834,11 @@ class ReflectionContext
16311834

16321835
return llvm::None;
16331836
}
1837+
1838+
template <typename T>
1839+
MemoryReader::ReadObjResult<T> readObj(StoredPointer Ptr) {
1840+
return getReader().template readObj<T>(RemoteAddress(Ptr));
1841+
}
16341842
};
16351843

16361844
} // end namespace reflection

include/swift/Reflection/RuntimeInternals.h

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ struct Job {
7575
uint32_t Flags;
7676
uint32_t Id;
7777
typename Runtime::StoredPointer Reserved[2];
78-
typename Runtime::StoredPointer RunJob;
78+
typename Runtime::StoredSignedPointer RunJob;
7979
};
8080

8181
template <typename Runtime>
@@ -104,6 +104,8 @@ struct AsyncTaskPrivateStorage {
104104
ActiveTaskStatus<Runtime> Status;
105105
StackAllocator<Runtime> Allocator;
106106
typename Runtime::StoredPointer Local;
107+
typename Runtime::StoredPointer ExclusivityAccessSet[2];
108+
uint32_t Id;
107109
};
108110

109111
template <typename Runtime>
@@ -112,7 +114,61 @@ struct AsyncTask: Job<Runtime> {
112114
typename Runtime::StoredPointer ResumeContextAndReserved[
113115
sizeof(typename Runtime::StoredPointer) == 8 ? 2 : 1];
114116

115-
AsyncTaskPrivateStorage<Runtime> PrivateStorage;
117+
union {
118+
AsyncTaskPrivateStorage<Runtime> PrivateStorage;
119+
typename Runtime::StoredPointer PrivateStorageRaw[14];
120+
};
121+
};
122+
123+
template <typename Runtime>
124+
struct AsyncContext {
125+
typename Runtime::StoredSignedPointer Parent;
126+
typename Runtime::StoredSignedPointer ResumeParent;
127+
uint32_t Flags;
128+
};
129+
130+
template <typename Runtime>
131+
struct AsyncContextPrefix {
132+
typename Runtime::StoredSignedPointer AsyncEntryPoint;
133+
typename Runtime::StoredPointer ClosureContext;
134+
typename Runtime::StoredPointer ErrorResult;
135+
};
136+
137+
template <typename Runtime>
138+
struct FutureAsyncContextPrefix {
139+
typename Runtime::StoredPointer IndirectResult;
140+
typename Runtime::StoredSignedPointer AsyncEntryPoint;
141+
typename Runtime::StoredPointer ClosureContext;
142+
typename Runtime::StoredPointer ErrorResult;
143+
};
144+
145+
template <typename Runtime>
146+
struct DefaultActorImpl {
147+
HeapObject<Runtime> HeapObject;
148+
typename Runtime::StoredPointer FirstJob;
149+
typename Runtime::StoredSize Flags;
150+
};
151+
152+
template <typename Runtime>
153+
struct TaskStatusRecord {
154+
typename Runtime::StoredSize Flags;
155+
typename Runtime::StoredPointer Parent;
156+
};
157+
158+
template <typename Runtime>
159+
struct ChildTaskStatusRecord : TaskStatusRecord<Runtime> {
160+
typename Runtime::StoredPointer FirstChild;
161+
};
162+
163+
template <typename Runtime>
164+
struct TaskGroupTaskStatusRecord : TaskStatusRecord<Runtime> {
165+
typename Runtime::StoredPointer FirstChild;
166+
};
167+
168+
template <typename Runtime>
169+
struct ChildFragment {
170+
typename Runtime::StoredPointer Parent;
171+
typename Runtime::StoredPointer NextChild;
116172
};
117173

118174
} // end namespace reflection

include/swift/Remote/MemoryReader.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class MemoryReader {
4040
using ReadBytesResult =
4141
std::unique_ptr<const void, std::function<void(const void *)>>;
4242

43+
template <typename T>
44+
using ReadObjResult =
45+
std::unique_ptr<const T, std::function<void(const void *)>>;
46+
4347
virtual bool queryDataLayout(DataLayoutQueryType type, void *inBuffer,
4448
void *outBuffer) = 0;
4549

@@ -90,6 +94,15 @@ class MemoryReader {
9094
return true;
9195
}
9296

97+
template <typename T>
98+
ReadObjResult<T> readObj(RemoteAddress address) {
99+
auto bytes = readBytes(address, sizeof(T));
100+
auto deleter = bytes.get_deleter();
101+
auto ptr = bytes.get();
102+
bytes.release();
103+
return ReadObjResult<T>(reinterpret_cast<const T *>(ptr), deleter);
104+
}
105+
93106
/// Attempts to read 'size' bytes from the given address in the remote process.
94107
///
95108
/// Returns a pointer to the requested data and a function that must be called to

0 commit comments

Comments
 (0)