from abc import ABCMeta, abstractmethod import lldb class ScriptedFrameProvider(metaclass=ABCMeta): """ The base class for a scripted frame provider. A scripted frame provider allows you to provide custom stack frames for a thread, which can be used to augment or replace the standard unwinding mechanism. This is useful for: - Providing frames for custom calling conventions or languages - Reconstructing missing frames from crash dumps or core files - Adding diagnostic or synthetic frames for debugging - Visualizing state machines or async execution contexts Most of the base class methods are `@abstractmethod` that need to be overwritten by the inheriting class. Example usage: .. code-block:: python # Attach a frame provider to a thread thread = process.GetSelectedThread() error = thread.SetScriptedFrameProvider( "my_module.MyFrameProvider", lldb.SBStructuredData() ) """ @staticmethod def applies_to_thread(thread): """Determine if this frame provider should be used for a given thread. This static method is called before creating an instance of the frame provider to determine if it should be applied to a specific thread. Override this method to provide custom filtering logic. Args: thread (lldb.SBThread): The thread to check. Returns: bool: True if this frame provider should be used for the thread, False otherwise. The default implementation returns True for all threads. Example: .. code-block:: python @staticmethod def applies_to_thread(thread): # Only apply to thread 1 return thread.GetIndexID() == 1 """ return True @staticmethod @abstractmethod def get_description(): """Get a description of this frame provider. This method should return a human-readable string describing what this frame provider does. The description is used for debugging and display purposes. Returns: str: A description of the frame provider. Example: .. code-block:: python def get_description(self): return "Crash log frame provider for thread 1" """ pass def __init__(self, input_frames, args): """Construct a scripted frame provider. Args: input_frames (lldb.SBFrameList): The frame list to use as input. This allows you to access frames by index. The frames are materialized lazily as you access them. args (lldb.SBStructuredData): A Dictionary holding arbitrary key/value pairs used by the scripted frame provider. """ self.input_frames = None self.args = None self.thread = None self.target = None self.process = None if isinstance(input_frames, lldb.SBFrameList) and input_frames.IsValid(): self.input_frames = input_frames self.thread = input_frames.GetThread() if self.thread and self.thread.IsValid(): self.process = self.thread.GetProcess() if self.process and self.process.IsValid(): self.target = self.process.GetTarget() if isinstance(args, lldb.SBStructuredData) and args.IsValid(): self.args = args @abstractmethod def get_frame_at_index(self, index): """Get a single stack frame at the given index. This method is called lazily when a specific frame is needed in the thread's backtrace (e.g., via the 'bt' command). Each frame is requested individually as needed. Args: index (int): The frame index to retrieve (0 for youngest/top frame). Returns: Dict or None: A frame dictionary describing the stack frame, or None if no frame exists at this index. The dictionary should contain: Required fields: - idx (int): The synthetic frame index (0 for youngest/top frame) - pc (int): The program counter address for the synthetic frame Alternatively, you can return: - A ScriptedFrame object for full control over frame behavior - An integer representing an input frame index to reuse - None to indicate no more frames exist Example: .. code-block:: python def get_frame_at_index(self, index): # Return None when there are no more frames if index >= self.total_frames: return None # Re-use an input frame by returning its index if self.should_use_input_frame(index): return index # Returns input frame at this index # Or create a custom frame dictionary if index == 0: return { "idx": 0, "pc": 0x100001234, } return None Note: The frames are indexed from 0 (youngest/top) to N (oldest/bottom). This method will be called repeatedly with increasing indices until None is returned. """ pass