|
19 | 19 | #include "Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.h"
|
20 | 20 | #include "lldb/DataFormatters/FormattersHelpers.h"
|
21 | 21 | #include "lldb/DataFormatters/StringPrinter.h"
|
| 22 | +#include "lldb/DataFormatters/TypeSynthetic.h" |
22 | 23 | #include "lldb/Symbol/CompilerType.h"
|
23 | 24 | #include "lldb/Target/ExecutionContext.h"
|
24 | 25 | #include "lldb/Target/Process.h"
|
@@ -755,6 +756,34 @@ class EnumSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
755 | 756 | size_t m_child_index;
|
756 | 757 | };
|
757 | 758 |
|
| 759 | +static std::string mangledTypenameForTasksTuple(size_t count) { |
| 760 | + /* |
| 761 | + Global > TypeMangling > Type > Tuple |
| 762 | + TupleElement > Type > Structure |
| 763 | + Module, text="Swift" |
| 764 | + Identifier, text="UnsafeCurrentTask" |
| 765 | + */ |
| 766 | + using namespace ::swift::Demangle; |
| 767 | + using Kind = Node::Kind; |
| 768 | + NodeFactory factory; |
| 769 | + auto [root, tuple] = swift_demangle::MakeNodeChain( |
| 770 | + {Kind::TypeMangling, Kind::Type, Kind::Tuple}, factory); |
| 771 | + |
| 772 | + // Make a TupleElement subtree N times, where N is the number of subtasks. |
| 773 | + for (size_t i = 0; i < count; ++i) { |
| 774 | + auto *structure = swift_demangle::MakeNodeChain( |
| 775 | + tuple, {Kind::TupleElement, Kind::Type, Kind::Structure}, factory); |
| 776 | + if (structure) { |
| 777 | + structure->addChild( |
| 778 | + factory.createNode(Kind::Module, ::swift::STDLIB_NAME), factory); |
| 779 | + structure->addChild( |
| 780 | + factory.createNode(Kind::Identifier, "UnsafeCurrentTask"), factory); |
| 781 | + } |
| 782 | + } |
| 783 | + |
| 784 | + return mangleNode(root).result(); |
| 785 | +} |
| 786 | + |
758 | 787 | /// Synthetic provider for `Swift.Task`.
|
759 | 788 | ///
|
760 | 789 | /// As seen by lldb, a `Task` instance is an opaque pointer, with neither type
|
@@ -937,35 +966,6 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
937 | 966 | return std::distance(children.begin(), it);
|
938 | 967 | }
|
939 | 968 |
|
940 |
| -private: |
941 |
| - std::string mangledTypenameForTasksTuple(size_t count) { |
942 |
| - /* |
943 |
| - Global > TypeMangling > Type > Tuple |
944 |
| - TupleElement > Type > Structure |
945 |
| - Module, text="Swift" |
946 |
| - Identifier, text="UnsafeCurrentTask" |
947 |
| - */ |
948 |
| - using namespace ::swift::Demangle; |
949 |
| - using Kind = Node::Kind; |
950 |
| - NodeFactory factory; |
951 |
| - auto [root, tuple] = swift_demangle::MakeNodeChain( |
952 |
| - {Kind::TypeMangling, Kind::Type, Kind::Tuple}, factory); |
953 |
| - |
954 |
| - // Make a TupleElement subtree N times, where N is the number of subtasks. |
955 |
| - for (size_t i = 0; i < count; ++i) { |
956 |
| - auto *structure = swift_demangle::MakeNodeChain( |
957 |
| - tuple, {Kind::TupleElement, Kind::Type, Kind::Structure}, factory); |
958 |
| - if (structure) { |
959 |
| - structure->addChild( |
960 |
| - factory.createNode(Kind::Module, ::swift::STDLIB_NAME), factory); |
961 |
| - structure->addChild( |
962 |
| - factory.createNode(Kind::Identifier, "UnsafeCurrentTask"), factory); |
963 |
| - } |
964 |
| - } |
965 |
| - |
966 |
| - return mangleNode(root).result(); |
967 |
| - } |
968 |
| - |
969 | 969 | private:
|
970 | 970 | TypeSystemSwiftTypeRef *m_ts = nullptr;
|
971 | 971 | addr_t m_task_ptr = LLDB_INVALID_ADDRESS;
|
@@ -1320,6 +1320,177 @@ class TaskGroupSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
|
1320 | 1320 | // Cache and storage of constructed child values.
|
1321 | 1321 | std::vector<ValueObjectSP> m_children;
|
1322 | 1322 | };
|
| 1323 | + |
| 1324 | +class ActorSyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
| 1325 | +public: |
| 1326 | + ActorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
| 1327 | + : SyntheticChildrenFrontEnd(*valobj_sp.get()) { |
| 1328 | + bool is_64bit = false; |
| 1329 | + if (auto target_sp = m_backend.GetTargetSP()) |
| 1330 | + is_64bit = target_sp->GetArchitecture().GetTriple().isArch64Bit(); |
| 1331 | + |
| 1332 | + std::optional<uint32_t> concurrency_version; |
| 1333 | + if (auto process_sp = m_backend.GetProcessSP()) |
| 1334 | + concurrency_version = |
| 1335 | + SwiftLanguageRuntime::FindConcurrencyDebugVersion(*process_sp); |
| 1336 | + |
| 1337 | + m_is_supported_target = is_64bit && concurrency_version.value_or(0) == 1; |
| 1338 | + if (!m_is_supported_target) |
| 1339 | + return; |
| 1340 | + |
| 1341 | + auto target_sp = m_backend.GetTargetSP(); |
| 1342 | + auto ts_or_err = |
| 1343 | + target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeSwift); |
| 1344 | + if (auto err = ts_or_err.takeError()) { |
| 1345 | + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters | LLDBLog::Types), |
| 1346 | + std::move(err), |
| 1347 | + "could not get Swift type system for Task synthetic " |
| 1348 | + "provider: {0}"); |
| 1349 | + return; |
| 1350 | + } |
| 1351 | + m_ts = llvm::dyn_cast_or_null<TypeSystemSwiftTypeRef>(ts_or_err->get()); |
| 1352 | + } |
| 1353 | + |
| 1354 | + llvm::Expected<uint32_t> CalculateNumChildren() override { |
| 1355 | + return m_is_supported_target ? 1 : 0; |
| 1356 | + } |
| 1357 | + |
| 1358 | + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { |
| 1359 | + if (!m_is_supported_target || idx != 0) |
| 1360 | + return {}; |
| 1361 | + |
| 1362 | + if (!m_unprioritised_jobs_sp) { |
| 1363 | + std::string mangled_typename = |
| 1364 | + mangledTypenameForTasksTuple(m_job_addrs.size()); |
| 1365 | + CompilerType tasks_tuple_type = |
| 1366 | + m_ts->GetTypeFromMangledTypename(ConstString(mangled_typename)); |
| 1367 | + DataExtractor data{m_job_addrs.data(), |
| 1368 | + m_job_addrs.size() * sizeof(addr_t), |
| 1369 | + endian::InlHostByteOrder(), sizeof(void *)}; |
| 1370 | + m_unprioritised_jobs_sp = ValueObject::CreateValueObjectFromData( |
| 1371 | + "unprioritised_jobs", data, m_backend.GetExecutionContextRef(), |
| 1372 | + tasks_tuple_type); |
| 1373 | + } |
| 1374 | + return m_unprioritised_jobs_sp; |
| 1375 | + } |
| 1376 | + |
| 1377 | + size_t GetIndexOfChildWithName(ConstString name) override { |
| 1378 | + if (m_is_supported_target && name == "unprioritised_jobs") |
| 1379 | + return 0; |
| 1380 | + return UINT32_MAX; |
| 1381 | + } |
| 1382 | + |
| 1383 | + lldb::ChildCacheState Update() override { |
| 1384 | + if (!m_is_supported_target) |
| 1385 | + return ::eReuse; |
| 1386 | + |
| 1387 | + m_job_addrs.clear(); |
| 1388 | + |
| 1389 | + if (!m_task_type) |
| 1390 | + if (auto target_sp = m_backend.GetTargetSP()) { |
| 1391 | + if (auto ts_or_err = target_sp->GetScratchTypeSystemForLanguage( |
| 1392 | + eLanguageTypeSwift)) { |
| 1393 | + if (auto *ts = llvm::dyn_cast_or_null<TypeSystemSwiftTypeRef>( |
| 1394 | + ts_or_err->get())) |
| 1395 | + // TypeMangling for "Swift.UnsafeCurrentTask" |
| 1396 | + m_task_type = ts->GetTypeFromMangledTypename(ConstString("$sSctD")); |
| 1397 | + } else { |
| 1398 | + LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters | LLDBLog::Types), |
| 1399 | + ts_or_err.takeError(), |
| 1400 | + "could not get Swift type system for Task synthetic " |
| 1401 | + "provider: {0}"); |
| 1402 | + return ChildCacheState::eReuse; |
| 1403 | + } |
| 1404 | + } |
| 1405 | + |
| 1406 | + if (!m_task_type) |
| 1407 | + return ChildCacheState::eReuse; |
| 1408 | + |
| 1409 | + // Get the actor's queue of unprioritized jobs (tasks) by following the |
| 1410 | + // "linked list" embedded in storage provided by SchedulerPrivate. |
| 1411 | + DefaultActorImpl actor{m_backend.GetProcessSP(), |
| 1412 | + m_backend.GetLoadAddress()}; |
| 1413 | + Status status; |
| 1414 | + Job first_job = actor.getFirstJob(status); |
| 1415 | + Job current_job = first_job; |
| 1416 | + while (current_job) { |
| 1417 | + m_job_addrs.push_back(current_job.addr); |
| 1418 | + current_job = current_job.getNextScheduledJob(status); |
| 1419 | + } |
| 1420 | + |
| 1421 | + if (status.Fail()) { |
| 1422 | + LLDB_LOG(GetLog(LLDBLog::DataFormatters | LLDBLog::Types), |
| 1423 | + "could not read actor's job pointers: {0}", status.AsCString()); |
| 1424 | + return ChildCacheState::eReuse; |
| 1425 | + } |
| 1426 | + |
| 1427 | + return ChildCacheState::eRefetch; |
| 1428 | + } |
| 1429 | + |
| 1430 | + bool MightHaveChildren() override { return m_is_supported_target; } |
| 1431 | + |
| 1432 | +private: |
| 1433 | + /// Lightweight wrapper around Job pointers, for the purpose of traversing to |
| 1434 | + /// the next scheduled Job. |
| 1435 | + struct Job { |
| 1436 | + ProcessSP process_sp; |
| 1437 | + addr_t addr; |
| 1438 | + |
| 1439 | + operator bool() const { return addr && addr != LLDB_INVALID_ADDRESS; } |
| 1440 | + |
| 1441 | + // void *SchedulerPrivate[2] is the first Job specific field, its layout |
| 1442 | + // follows the HeapObject base class (size 16). |
| 1443 | + static constexpr offset_t SchedulerPrivateOffset = 16; |
| 1444 | + static constexpr offset_t NextJobOffset = SchedulerPrivateOffset; |
| 1445 | + |
| 1446 | + Job getNextScheduledJob(Status &status) { |
| 1447 | + addr_t next_job = LLDB_INVALID_ADDRESS; |
| 1448 | + if (status.Success()) |
| 1449 | + next_job = |
| 1450 | + process_sp->ReadPointerFromMemory(addr + NextJobOffset, status); |
| 1451 | + return {process_sp, next_job}; |
| 1452 | + } |
| 1453 | + }; |
| 1454 | + |
| 1455 | + /// Lightweight wrapper around DefaultActorImpl/$defaultActor, for the purpose |
| 1456 | + /// of accessing contents of ActiveActorStatus. |
| 1457 | + struct DefaultActorImpl { |
| 1458 | + ProcessSP process_sp; |
| 1459 | + addr_t addr; |
| 1460 | + |
| 1461 | + // `$defaultActor`'s offset within the actor object. |
| 1462 | + // |
| 1463 | + // The $defaultActor field does not point to the start of DefaultActorImpl, |
| 1464 | + // it has as an address that points past the HeapObject layout. |
| 1465 | + static constexpr offset_t DefaultActorFieldOffset = 16; |
| 1466 | + // ActiveActorStatus's offset within DefaultActorImpl. |
| 1467 | + // |
| 1468 | + // ActiveActorStatus is declared alignas(2*sizeof(void*)). The layout of |
| 1469 | + // DefaultActorImpl puts the status record after HeapObject (size 16), and |
| 1470 | + // its first field (bool size 1), an offset of +32 from the start of the |
| 1471 | + // actor. This offset is relative to DefaultActorImpl, but this code needs |
| 1472 | + // an offset relative to the $defaultActor field, and is adjusted as such. |
| 1473 | + static constexpr offset_t ActiveActorStatusOffset = |
| 1474 | + 32 - DefaultActorFieldOffset; |
| 1475 | + // FirstJob's offset within ActiveActorStatus. |
| 1476 | + static constexpr offset_t FirstJobOffset = ActiveActorStatusOffset + 8; |
| 1477 | + |
| 1478 | + Job getFirstJob(Status &status) { |
| 1479 | + addr_t first_job = LLDB_INVALID_ADDRESS; |
| 1480 | + if (status.Success()) |
| 1481 | + first_job = |
| 1482 | + process_sp->ReadPointerFromMemory(addr + FirstJobOffset, status); |
| 1483 | + return {process_sp, first_job}; |
| 1484 | + } |
| 1485 | + }; |
| 1486 | + |
| 1487 | +private: |
| 1488 | + bool m_is_supported_target = false; |
| 1489 | + TypeSystemSwiftTypeRef *m_ts = nullptr; |
| 1490 | + std::vector<addr_t> m_job_addrs; |
| 1491 | + CompilerType m_task_type; |
| 1492 | + ValueObjectSP m_unprioritised_jobs_sp; |
| 1493 | +}; |
1323 | 1494 | }
|
1324 | 1495 | }
|
1325 | 1496 | }
|
@@ -1410,6 +1581,14 @@ lldb_private::formatters::swift::TaskGroupSyntheticFrontEndCreator(
|
1410 | 1581 | return new TaskGroupSyntheticFrontEnd(valobj_sp);
|
1411 | 1582 | }
|
1412 | 1583 |
|
| 1584 | +SyntheticChildrenFrontEnd * |
| 1585 | +lldb_private::formatters::swift::ActorSyntheticFrontEndCreator( |
| 1586 | + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
| 1587 | + if (!valobj_sp) |
| 1588 | + return nullptr; |
| 1589 | + return new ActorSyntheticFrontEnd(valobj_sp); |
| 1590 | +} |
| 1591 | + |
1413 | 1592 | bool lldb_private::formatters::swift::ObjC_Selector_SummaryProvider(
|
1414 | 1593 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
|
1415 | 1594 | LLDB_SCOPED_TIMER();
|
|
0 commit comments