)]}'
{
  "commit": "a8d2d169c7add4b0106ae76e186cf815c0b84825",
  "tree": "8a49933fa8d33583e275d222c131943da878febc",
  "parents": [
    "6afe5e5d1a6dcbf7ba83acf4c57b964f58c364a1"
  ],
  "author": {
    "name": "Tom Yang",
    "email": "zhenyutyang@gmail.com",
    "time": "Mon Mar 31 13:29:31 2025 -0700"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Mon Mar 31 13:29:31 2025 -0700"
  },
  "message": "Parallelize module loading in POSIX dyld code (#130912)\n\nThis patch improves LLDB launch time on Linux machines for **preload\nscenarios**, particularly for executables with a lot of shared library\ndependencies (or modules). Specifically:\n* Launching a binary with `target.preload-symbols \u003d true` \n* Attaching to a process with `target.preload-symbols \u003d true`.\nIt\u0027s completely controlled by a new flag added in the first commit\n`plugin.dynamic-loader.posix-dyld.parallel-module-load`, which *defaults\nto false*. This was inspired by similar work on Darwin #110646.\n\nSome rough numbers to showcase perf improvement, run on a very beefy\nmachine:\n* Executable with ~5600 modules: baseline 45s, improvement 15s\n* Executable with ~3800 modules: baseline 25s,  improvement 10s\n* Executable with ~6650 modules: baseline 67s, improvement 20s\n* Executable with ~12500 modules: baseline 185s, improvement 85s\n* Executable with ~14700 modules: baseline 235s, improvement 120s\nA lot of targets we deal with have a *ton* of modules, and unfortunately\nwe\u0027re unable to convince other folks to reduce the number of modules, so\nperformance improvements like this can be very impactful for user\nexperience.\n\nThis patch achieves the performance improvement by parallelizing\n`DynamicLoaderPOSIXDYLD::RefreshModules` for the launch scenario, and\n`DynamicLoaderPOSIXDYLD::LoadAllCurrentModules` for the attach scenario.\nThe commits have some context on their specific changes as well --\nhopefully this helps the review.\n\n# More context on implementation\n\nWe discovered the bottlenecks by via `perf record -g -p \u003clldb\u0027s pid\u003e` on\na Linux machine. With an executable known to have 1000s of shared\nlibrary dependencies, I ran\n```\n(lldb) b main\n(lldb) r\n# taking a while\n```\nand showed the resulting perf trace (snippet shown)\n```\nSamples: 85K of event \u0027cycles:P\u0027, Event count (approx.): 54615855812\n  Children      Self  Command          Shared Object              Symbol\n-   93.54%     0.00%  intern-state     libc.so.6                  [.] clone3\n     clone3\n     start_thread\n     lldb_private::HostNativeThreadBase::ThreadCreateTrampoline(void*)                                                                           r\n     std::_Function_handler\u003cvoid* (), lldb_private::Process::StartPrivateStateThread(bool)::$_0\u003e::_M_invoke(std::_Any_data const\u0026)\n     lldb_private::Process::RunPrivateStateThread(bool)                                                                                          n\n   - lldb_private::Process::HandlePrivateEvent(std::shared_ptr\u003clldb_private::Event\u003e\u0026)\n      - 93.54% lldb_private::Process::ShouldBroadcastEvent(lldb_private::Event*)\n         - 93.54% lldb_private::ThreadList::ShouldStop(lldb_private::Event*)\n            - lldb_private::Thread::ShouldStop(lldb_private::Event*)                                                                             *\n               - 93.53% lldb_private::StopInfoBreakpoint::ShouldStopSynchronous(lldb_private::Event*)                                            t\n                  - 93.52% lldb_private::BreakpointSite::ShouldStop(lldb_private::StoppointCallbackContext*)                                     i\n                       lldb_private::BreakpointLocationCollection::ShouldStop(lldb_private::StoppointCallbackContext*)                           k\n                       lldb_private::BreakpointLocation::ShouldStop(lldb_private::StoppointCallbackContext*)                                     b\n                       lldb_private::BreakpointOptions::InvokeCallback(lldb_private::StoppointCallbackContext*, unsigned long, unsigned long)    i\n                       DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void*, lldb_private::StoppointCallbackContext*, unsigned long, unsigned lo\n                     - DynamicLoaderPOSIXDYLD::RefreshModules()                                                                                  O\n                        - 93.42% DynamicLoaderPOSIXDYLD::RefreshModules()::$_0::operator()(DYLDRendezvous::SOEntry const\u0026) const                 u\n                           - 93.40% DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(lldb_private::FileSpec const\u0026, unsigned long, unsigned long, bools\n                              - lldb_private::DynamicLoader::LoadModuleAtAddress(lldb_private::FileSpec const\u0026, unsigned long, unsigned long, boos\n                                 - 83.90% lldb_private::DynamicLoader::FindModuleViaTarget(lldb_private::FileSpec const\u0026)                        o\n                                    - 83.01% lldb_private::Target::GetOrCreateModule(lldb_private::ModuleSpec const\u0026, bool, lldb_private::Status*\n                                       - 77.89% lldb_private::Module::PreloadSymbols()\n                                          - 44.06% lldb_private::Symtab::PreloadSymbols()\n                                             - 43.66% lldb_private::Symtab::InitNameIndexes()\n...\n```\nWe saw that majority of time was spent in `RefreshModules`, with the\nmain culprit within it `LoadModuleAtAddress` which eventually calls\n`PreloadSymbols`.\n\nAt first, `DynamicLoaderPOSIXDYLD::LoadModuleAtAddress` appears fairly\nindependent -- most of it deals with different files and then getting or\ncreating Modules from these files. The portions that aren\u0027t independent\nseem to deal with ModuleLists, which appear concurrency safe. There were\nmembers of `DynamicLoaderPOSIXDYLD` I had to synchronize though: namely\n`m_loaded_modules` which `DynamicLoaderPOSIXDYLD` maintains to map its\nloaded modules to their link addresses. Without synchronizing this, I\nran into SEGFAULTS and other issues when running `check-lldb`. I also\nlocked the assignment and comparison of `m_interpreter_module`, which\nmay be unnecessary.\n\n# Alternate implementations\n\nWhen creating this patch, another implementation I considered was\ndirectly background-ing the call to `Module::PreloadSymbol` in\n`Target::GetOrCreateModule`. It would have the added benefit of working\nacross platforms generically, and appeared to be concurrency safe. It\nwas done via `Debugger::GetThreadPool().async` directly. However, there\nwere a ton of concurrency issues, so I abandoned that approach for now.\n\n# Testing\n\nWith the feature active, I tested via `ninja check-lldb` on both Debug\nand Release builds several times (~5 or 6 altogether?), and didn\u0027t spot\nadditional failing or flaky tests.\n\nI also tested manually on several different binaries, some with around\n14000 modules, but just basic operations: launching, reaching main,\nsetting breakpoint, stepping, showing some backtraces.\n\nI\u0027ve also tested with the flag off just to make sure things behave\nproperly synchronously.\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "3cdbe9221a0bcf1d92d943beb5a05f6014117c8d",
      "old_mode": 33188,
      "old_path": "lldb/include/lldb/Target/Target.h",
      "new_id": "29183cc267721a127dedc291025e29da6babf7fa",
      "new_mode": 33188,
      "new_path": "lldb/include/lldb/Target/Target.h"
    },
    {
      "type": "modify",
      "old_id": "53ba11ac21bd3d7b150dd9292b2011b23c9d402c",
      "old_mode": 33188,
      "old_path": "lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp",
      "new_id": "326b6910b526780cbe22e5a883cded03dbd2e16d",
      "new_mode": 33188,
      "new_path": "lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp"
    },
    {
      "type": "modify",
      "old_id": "bde334aaca40b44d54f5b07b7c97f2d71953e376",
      "old_mode": 33188,
      "old_path": "lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h",
      "new_id": "6efb92673a13cc6abde9ccde5bd8b14606a907a6",
      "new_mode": 33188,
      "new_path": "lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"
    },
    {
      "type": "modify",
      "old_id": "c26bca546891edd62ff2a1acd9e4c53b43ccf8a6",
      "old_mode": 33188,
      "old_path": "lldb/source/Target/Target.cpp",
      "new_id": "09c0c0b8a5db0d6179f26fcf97d300e331736464",
      "new_mode": 33188,
      "new_path": "lldb/source/Target/Target.cpp"
    },
    {
      "type": "modify",
      "old_id": "38a345dfd884950452d75b8b628fa6823e9b8135",
      "old_mode": 33188,
      "old_path": "lldb/source/Target/TargetProperties.td",
      "new_id": "3940ac00a2bd9c77659efc28355f7ea4c546b39d",
      "new_mode": 33188,
      "new_path": "lldb/source/Target/TargetProperties.td"
    }
  ]
}
