22 Commits

Author SHA1 Message Date
Med Ismail Bennani
e1cd55879b
[lldb] Fix circular dependency and deadlock in scripted frame providers (#187411)
When a scripted frame provider calls back into the thread's frame
machinery (e.g. via HandleCommand or EvaluateExpression), two problems
arise:

1. GetStackFrameList() re-enters the SyntheticStackFrameList
 construction, causing infinite recursion.
2. ClearStackFrames() tries to read-lock the StackFrameList's
 shared_mutex that is already write-locked by GetFramesUpTo,
 causing a deadlock.

This patch fixes those issues by tracking when a provider is actively
fetching frames via a per-host-thread map (m_provider_frames_by_thread)
keyed by HostThread. The map is pushed/popped in
SyntheticStackFrameList::FetchFramesUpTo before calling into the
provider. GetStackFrameList() checks it to route re-entrant calls:

- The provider's own host thread gets the parent frame list, preventing
circular dependency when get_frame_at_index calls back into
GetFrameAtIndex.
- The private state thread also gets the parent frame list, preventing
deadlock when a provider calls EvaluateExpression (which needs the
private state thread to process events).
- Other host threads proceed normally and block on the frame list mutex
until the provider finishes, getting the correct synthetic result.

ClearStackFrames() returns early if any provider is active, since the
frame state is shared and tearing it down while a provider is
mid-construction is both unnecessary and unsafe.

rdar://171558394

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2026-03-25 16:22:25 -07:00
Jonas Devlieghere
091296f3e3
[lldb] Revert scripted symbol locator (#181945)
This revert #181334 and its follow-up PRs (including #181488, #181492,
#181493, #181494 and #181498) as well as Ismail's documentation changes
(#181594, #181717). The original commit causes a test failure in CI
(https://github.com/llvm/llvm-project/issues/181938) but the more I look
at the patch, the more I'm convinced it was not ready to land. It will
be easier to iterate on the feedback by re-landing this than by using
post-commit review.
2026-02-17 16:52:21 -08:00
rchamala
1ee03d1e09
[lldb] Add ScriptedSymbolLocator plugin for source file resolution (#181334)
## Summary                                                        
                                                                    
Based on discussion from
[RFC](https://discourse.llvm.org/t/rfc-python-callback-for-source-file-resolution/83545),
this PR adds a new `SymbolLocatorScripted` plugin that allows Python
scripts to implement custom symbol and source file resolution logic.
This enables downstream users to build custom symbol servers, source
file remapping, and build artifact resolution entirely in Python.
                                                                    
  ### Changes

- Adds `LocateSourceFile()` to the SymbolLocator plugin interface,
called during source path resolution with a fully loaded `ModuleSP`, so
the plugin has access to the module's UUID, file paths, and symbols.
- Adds `SymbolLocatorScripted` plugin that delegates all four
SymbolLocator methods (`LocateExecutableObjectFile`,
`LocateExecutableSymbolFile`, `DownloadObjectAndSymbolFile`,
`LocateSourceFile`) to a user-provided Python class.
- Adds `ScriptedSymbolLocatorPythonInterface` to bridge C++ calls to
Python, with proper GIL management and error handling.
- Results for `LocateSourceFile` are cached per (module UUID, source
file) pair.
- The Python class is configured via: `settings set
plugin.symbol-locator.scripted.script-class module.ClassName`

  ### Python class interface

  ```python
  class MyLocator:
      def __init__(self, exe_ctx, args): ...
      def locate_source_file(self, module, original_source_file):
  ...
      def locate_executable_object_file(self, module_spec): ...
      def locate_executable_symbol_file(self, module_spec,
  default_search_paths): ...
      def download_object_and_symbol_file(self, module_spec,
  force_lookup, copy_executable): ...
```

  ### Test plan
```
  Added TestScriptedSymbolLocator.py with 3 test cases:
  - test_locate_source_file — verifies the locator resolves source
  files, receives a valid SBModule with UUID, and remaps paths correctly
  - test_locate_source_file_none_fallthrough — verifies returning
None falls through to default LLDB resolution, and that having no script
  class set works normally
  - test_invalid_script_class — verifies graceful handling of
  invalid class names without crashing
```

Co-authored-by: Rahul Reddy Chamala <rachamal@fb.com>
2026-02-14 07:39:00 -08:00
Adrian Vogelsgesang
9eed43b386
[lldb][doc] Improve documentation for ScriptedFrameProvider (#179996)
* Provide a minimal, working example
* Document the instance variables
* Remove mention of `thread.SetScriptedFrameProvider` (which doesn't exist)
* add missing `@staticmethod` annotation
* fix rendering of bullet-pointed lists
2026-02-12 11:47:01 +01:00
Med Ismail Bennani
d6a73d08d5
[lldb] Add priority support to synthetic frame providers (#172848)
This patch adds `get_priority()` support to synthetic frame providers to
enable priority-based selection when multiple providers match a thread.
This is the first step toward supporting frame provider chaining for
visualizing coroutines, Swift async tasks, and et al.

Priority ordering follows Unix nice convention where lower numbers
indicate higher priority (0 = highest). Providers without explicit
priority return `std::nullopt`, which maps to UINT32_MAX (lowest
priority), ensuring backward compatibility with existing providers.

The implementation adds `GetPriority()` as a virtual method to
`SyntheticFrameProvider` base class, implements it through the scripting
interface hierarchy (`ScriptedFrameProviderInterface` and
`ScriptedFrameProviderPythonInterface`), and updates
`Thread::GetStackFrameList()` to sort applicable providers by priority
before attempting to load them.

Python frame providers can now specify priority:

```python
@staticmethod
def get_priority():
   return 10  # Or return None for default priority.
```

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-12-18 23:20:38 +01:00
Med Ismail Bennani
53972216d1
[lldb] Add arm32/thumb register layout to Scripted{Frame,Thread} (#172005) 2025-12-12 20:06:08 +01:00
Med Ismail Bennani
c50802cbee
Reland "[lldb] Introduce ScriptedFrameProvider for real threads (#161870)" (#170236)
This patch re-lands #161870 with fixes to the previous test failures.

rdar://161834688

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-12-02 18:59:40 +00:00
n2h9
a4d42775b9
[lldb] [scripting bridge] 167388 chore: add api to return arch name for target (#168273)
This pr fixes #167388 .

## Description

This pr adds new method `GetArchName` to `SBTarget` so that no need to
parse triple to get arch name in client code.

## Testing

### All from `TestTargetAPI.py`

run test with

```
./build/bin/lldb-dotest -v -p TestTargetAPI.py
```
<details>
<summary>existing tests (without newly added)</summary>
<img width="1425" height="804" alt="image"
src="https://github.com/user-attachments/assets/617e4c69-5c6b-44c4-9aeb-b751a47e253c"
/>
</details>

<details>
<summary>existing tests (with newly added)</summary>
<img width="1422" height="778" alt="image"
src="https://github.com/user-attachments/assets/746990a1-df88-4348-a090-224963d3c640"
/>

</details>

### Only `test_get_arch_name`

run test with 
```
./build/bin/lldb-dotest -v -p TestTargetAPI.py -f test_get_arch_name_dwarf -f test_get_arch_name_dwo -f test_get_arch_name_dsym lldb/test/API/python_api/target

```
<details>
<summary>only newly added</summary>
<img width="1422" height="778" alt="image"
src="https://github.com/user-attachments/assets/fcaafa5d-2622-4171-acee-e104ecee0652"
/>
</details>

---------

Signed-off-by: Nikita B <n2h9z4@gmail.com>
Co-authored-by: Jonas Devlieghere <jonas@devlieghere.com>
2025-11-26 11:36:19 -08:00
Michael Buch
b7bc4a2103
Revert "[lldb] Introduce ScriptedFrameProvider for real threads" (#167662)
The new test fails on x86 and arm64 public macOS bots:
```
09:27:59  ======================================================================
09:27:59  FAIL: test_append_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase)
09:27:59     Test that we can add frames after real stack.
09:27:59  ----------------------------------------------------------------------
09:27:59  Traceback (most recent call last):
09:27:59    File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 122, in test_append_frames
09:27:59      self.assertEqual(new_frame_count, original_frame_count + 1)
09:27:59  AssertionError: 5 != 6
09:27:59  Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang
09:27:59  ======================================================================
09:27:59  FAIL: test_applies_to_thread (TestScriptedFrameProvider.ScriptedFrameProviderTestCase)
09:27:59     Test that applies_to_thread filters which threads get the provider.
09:27:59  ----------------------------------------------------------------------
09:27:59  Traceback (most recent call last):
09:27:59    File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 218, in test_applies_to_thread
09:27:59      self.assertEqual(
09:27:59  AssertionError: 5 != 1 : Thread with ID 1 should have 1 synthetic frame
09:27:59  Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang
09:27:59  ======================================================================
09:27:59  FAIL: test_prepend_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase)
09:27:59     Test that we can add frames before real stack.
09:27:59  ----------------------------------------------------------------------
09:27:59  Traceback (most recent call last):
09:27:59    File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 84, in test_prepend_frames
09:27:59      self.assertEqual(new_frame_count, original_frame_count + 2)
09:27:59  AssertionError: 5 != 7
09:27:59  Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang
09:27:59  ======================================================================
09:27:59  FAIL: test_remove_frame_provider_by_id (TestScriptedFrameProvider.ScriptedFrameProviderTestCase)
09:27:59     Test that RemoveScriptedFrameProvider removes a specific provider by ID.
09:27:59  ----------------------------------------------------------------------
09:27:59  Traceback (most recent call last):
09:27:59    File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 272, in test_remove_frame_provider_by_id
09:27:59      self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames")
09:27:59  AssertionError: 5 != 3 : Should have 3 synthetic frames
09:27:59  Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang
09:27:59  ======================================================================
09:27:59  FAIL: test_replace_all_frames (TestScriptedFrameProvider.ScriptedFrameProviderTestCase)
09:27:59     Test that we can replace the entire stack.
09:27:59  ----------------------------------------------------------------------
09:27:59  Traceback (most recent call last):
09:27:59    File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 41, in test_replace_all_frames
09:27:59      self.assertEqual(thread.GetNumFrames(), 3, "Should have 3 synthetic frames")
09:27:59  AssertionError: 5 != 3 : Should have 3 synthetic frames
09:27:59  Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang
09:27:59  ======================================================================
09:27:59  FAIL: test_scripted_frame_objects (TestScriptedFrameProvider.ScriptedFrameProviderTestCase)
09:27:59     Test that provider can return ScriptedFrame objects.
09:27:59  ----------------------------------------------------------------------
09:27:59  Traceback (most recent call last):
09:27:59    File "/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/llvm-project/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py", line 159, in test_scripted_frame_objects
09:27:59      self.assertEqual(frame0.GetFunctionName(), "custom_scripted_frame_0")
09:27:59  AssertionError: 'thread_func(int)' != 'custom_scripted_frame_0'
09:27:59  - thread_func(int)
09:27:59  + custom_scripted_frame_0
09:27:59  
09:27:59  Config=arm64-/Users/ec2-user/jenkins/workspace/llvm.org/as-lldb-cmake/lldb-build/bin/clang
09:27:59  ----------------------------------------------------------------------
09:27:59  Ran 6 tests in 14.242s
09:27:59  
09:27:59  FAILED (failures=6)
```

Reverts llvm/llvm-project#161870
2025-11-12 10:13:43 +00:00
Med Ismail Bennani
1e467e4485
[lldb] Introduce ScriptedFrameProvider for real threads (#161870)
This patch extends ScriptedFrame to work with real (non-scripted)
threads,
enabling frame providers to synthesize frames for native processes.

Previously, ScriptedFrame only worked within
ScriptedProcess/ScriptedThread
contexts. This patch decouples ScriptedFrame from ScriptedThread,
allowing
users to augment or replace stack frames in real debugging sessions for
use
cases like custom calling conventions, reconstructing corrupted frames
from
core files, or adding diagnostic frames.

Key changes:

- ScriptedFrame::Create() now accepts ThreadSP instead of requiring
ScriptedThread, extracting architecture from the target triple rather
than ScriptedProcess.arch

- Added SBTarget::RegisterScriptedFrameProvider() and
ClearScriptedFrameProvider() APIs, with Target storing a
SyntheticFrameProviderDescriptor template for new threads

- Added "target frame-provider register/clear" commands for CLI access

- Thread class gains LoadScriptedFrameProvider(),
ClearScriptedFrameProvider(),
and GetFrameProvider() methods for per-thread frame provider management

- New SyntheticStackFrameList overrides FetchFramesUpTo() to lazily
provide
frames from either the frame provider or the real stack

This enables practical use of the SyntheticFrameProvider infrastructure
in
real debugging workflows.

rdar://161834688

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-11-11 20:18:45 +00:00
Med Ismail Bennani
4cd17eeaeb
[lldb/Interpreter] Implement ScriptedFrameProvider{,Python}Interface (#166662)
This patch implements the base and python interface for the
ScriptedFrameProvider class.

This is necessary to call python APIs from the ScriptedFrameProvider
that will come in a follow-up.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-11-06 11:54:17 -08:00
jimingham
e2d9420272
Make flag-only options work in the ParsedCommand mode of adding commands (#157756)
I neglected to add a test when I was writing tests for this, so of
course it broke. This makes it work again and adds a test.

rdar://159459160
2025-09-10 09:27:29 -07:00
Med Ismail Bennani
84b56202fb
[lldb] Introduce ScriptedFrame affordance (#149622)
This patch introduces a new scripting affordance in lldb:
`ScriptedFrame`.

This allows user to produce mock stackframes in scripted threads and
scripted processes from a python script.

With this change, StackFrame can be synthetized from different sources:
- Either from a dictionary containing a load address, and a frame index,
  which is the legacy way.
- Or by creating a ScriptedFrame python object.

One particularity of synthezising stackframes from the ScriptedFrame
python object, is that these frame have an optional PC, meaning that
they don't have a report a valid PC and they can act as shells that just
contain static information, like the frame function name, the list of
variables or registers, etc. It can also provide a symbol context.

rdar://157260006

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-09-04 15:07:11 -07:00
Med Ismail Bennani
58f48011b3
[lldb] Add support for x86_64h to scripted process (#145099)
This patch adds support to the haswell sub-architecture (x86_64h) to
scripted processes.

rdar://147208252

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-06-20 13:28:21 -07:00
Jason Molenda
fda4a324a3 [lldb] Only run scripted process test on x86_64/arm64
The newly added
test/API/functionalities/scripted_process_empty_memory_region/dummy_scripted_process.py
imports
examples/python/templates/scripted_process.py
which only has register definitions for x86_64 and arm64.

Only run this test on those two architectures for now.
2024-11-15 00:56:34 -08:00
jimingham
04b443e778
Add the ability to define custom completers to the parsed_cmd template. (#109062)
If your arguments or option values are of a type that naturally uses one
of our common completion mechanisms, you will get completion for free.
But if you have your own custom values or if you want to do fancy things
like have `break set -s foo.dylib -n ba<TAB>` only complete on symbols
in foo.dylib, you can use this new mechanism to achieve that.
2024-09-24 10:00:00 -07:00
Med Ismail Bennani
622df0ee92
[lldb] Add scripted thread plan python base class to lldb & website (#97481)
Following a feedback request in #97262, I took out the scripted thread
plan python base class from it and make a separate PR for it.

This patch adds the scripted thread plan base python class to the lldb
python module as well as the lldb documentation website.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2024-07-02 15:20:18 -07:00
Med Ismail Bennani
59f4267c8e
[lldb/docs] Add scripting extensions documentation to the website (#97262)
This patch adds the documentation for a subset of scripting extensions
such as scripted process, scripted thread, operating system threads &
scritped thread plans to the lldb website.

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2024-07-02 12:52:50 -07:00
Jim Ingham
22d2f3aa30 Move the parsed_cmd conversion def's to module level functions.
Python3.9 does not allow you to put a reference to a class staticmethod
in a table and call it from there.  Python3.10 and following do allow
this, but we still support 3.9.  staticmethod was slightly cleaner,
but this will do.
2024-02-13 18:08:02 -08:00
jimingham
a69ecb2420
Add the ability to define a Python based command that uses CommandObjectParsed (#70734)
This allows you to specify options and arguments and their definitions
and then have lldb handle the completions, help, etc. in the same way
that lldb does for its parsed commands internally.

This feature has some design considerations as well as the code, so I've
also set up an RFC, but I did this one first and will put the RFC
address in here once I've pushed it...

Note, the lldb "ParsedCommand interface" doesn't actually do all the
work that it should. For instance, saying the type of an option that has
a completer doesn't automatically hook up the completer, and ditto for
argument values. We also do almost no work to verify that the arguments
match their definition, or do auto-completion for them. This patch
allows you to make a command that's bug-for-bug compatible with built-in
ones, but I didn't want to stall it on getting the auto-command checking
to work all the way correctly.

As an overall design note, my primary goal here was to make an interface
that worked well in the script language. For that I needed, for
instance, to have a property-based way to get all the option values that
were specified. It was much more convenient to do that by making a
fairly bare-bones C interface to define the options and arguments of a
command, and set their values, and then wrap that in a Python class
(installed along with the other bits of the lldb python module) which
you can then derive from to make your new command. This approach will
also make it easier to experiment.

See the file test_commands.py in the test case for examples of how this
works.
2024-02-13 11:09:47 -08:00
Med Ismail Bennani
ec456ba9ca [lldb] Add OperatingSystem base class to the lldb python module
This patch introduces an `OperatingSystem` base implementation in the
`lldb` python module to make it easier for lldb users to write their own
implementation.

The `OperatingSystem` base implementation is derived itself from the
`ScriptedThread` base implementation since they share some common grounds.

To achieve that, this patch makes changes to the `ScriptedThread`
initializer since it gets called by the `OperatingSystem` initializer.

I also took the opportunity to document the `OperatingSystem` base
class and methods.

Differential Revision: https://reviews.llvm.org/D159315

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2023-10-26 15:12:22 -07:00
Med Ismail Bennani
6bff2d51dc [lldb] Move template python files to separate directory
This patch moves the template files for the various scripting
affordances to a separate directory.

This is a preparatory work for upcoming improvements and consolidations
to other scripting affordances.

Differential Revision: https://reviews.llvm.org/D159310

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2023-10-23 09:51:25 -07:00