Skip to content

Commit cb00990

Browse files
committed
[lldb] Add support for PC-less scripted frames
This adds support for scripted frames without valid PC addresses, allowing them to display properly without showing 0xffffffffffffffff. Changes include: - Make StackFrame::GetSymbolContext() resilient to PC-less frames by adding an early return when the lookup address is invalid, preserving any already-populated SymbolContext fields. - Populate module and compile unit for scripted frames by searching existing modules for matching LineEntry files, falling back to the scripted module - Create synthetic CompileUnits with language type deduced from the script - Fix frame formatting to avoid extra spaces for PC-less frames by removing the space from the frame format string and adding it conditionally in FormatEntity::Format only when the frame has a valid PC. - Add fallbacks in FormatEntity for FunctionName, FunctionNameNoArgs, and FunctionNameWithArgs to use StackFrame methods when SymbolContext lookup fails, enabling proper function name display for scripted frames. - Update test to include a scripted frame pointing to Python source with a LineEntry referencing the Python file containing my_python_function(). Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 0e0c0b7 commit cb00990

File tree

4 files changed

+239
-61
lines changed

4 files changed

+239
-61
lines changed

lldb/source/Core/FormatEntity.cpp

Lines changed: 102 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,6 +1636,14 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16361636
if (sc) {
16371637
Module *module = sc->module_sp.get();
16381638
if (module) {
1639+
// Add a space before module name if frame has a valid PC
1640+
// (so "PC module" but ": module" for PC-less frames)
1641+
if (exe_ctx) {
1642+
StackFrame *frame = exe_ctx->GetFramePtr();
1643+
if (frame && frame->GetFrameCodeAddress().IsValid()) {
1644+
s.PutChar(' ');
1645+
}
1646+
}
16391647
if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number))
16401648
return true;
16411649
}
@@ -1684,10 +1692,11 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16841692
StackFrame *frame = exe_ctx->GetFramePtr();
16851693
if (frame) {
16861694
const Address &pc_addr = frame->GetFrameCodeAddress();
1687-
if (pc_addr.IsValid() || frame->IsSynthetic()) {
1695+
if (pc_addr.IsValid()) {
16881696
if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
16891697
return true;
1690-
}
1698+
} else if (frame->IsSynthetic())
1699+
return true;
16911700
}
16921701
}
16931702
return false;
@@ -1808,70 +1817,91 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
18081817
return initial_function;
18091818

18101819
case Entry::Type::FunctionName: {
1811-
if (!sc)
1812-
return false;
1820+
if (sc) {
1821+
Language *language_plugin = nullptr;
1822+
bool language_plugin_handled = false;
1823+
StreamString ss;
18131824

1814-
Language *language_plugin = nullptr;
1815-
bool language_plugin_handled = false;
1816-
StreamString ss;
1825+
if (sc->function)
1826+
language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1827+
else if (sc->symbol)
1828+
language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
18171829

1818-
if (sc->function)
1819-
language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1820-
else if (sc->symbol)
1821-
language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1830+
if (language_plugin)
1831+
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1832+
*sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
18221833

1823-
if (language_plugin)
1824-
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1825-
*sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
1834+
if (language_plugin_handled) {
1835+
s << ss.GetString();
1836+
return true;
1837+
}
18261838

1827-
if (language_plugin_handled) {
1828-
s << ss.GetString();
1829-
return true;
1839+
const char *name = sc->GetPossiblyInlinedFunctionName()
1840+
.GetName(Mangled::NamePreference::ePreferDemangled)
1841+
.AsCString();
1842+
if (name) {
1843+
s.PutCString(name);
1844+
return true;
1845+
}
18301846
}
18311847

1832-
const char *name = sc->GetPossiblyInlinedFunctionName()
1833-
.GetName(Mangled::NamePreference::ePreferDemangled)
1834-
.AsCString();
1835-
if (!name)
1836-
return false;
1837-
1838-
s.PutCString(name);
1839-
1840-
return true;
1848+
// Fallback to frame methods if available
1849+
if (exe_ctx) {
1850+
StackFrame *frame = exe_ctx->GetFramePtr();
1851+
if (frame) {
1852+
const char *name = frame->GetFunctionName();
1853+
if (name) {
1854+
s.PutCString(name);
1855+
return true;
1856+
}
1857+
}
1858+
}
1859+
return false;
18411860
}
18421861

18431862
case Entry::Type::FunctionNameNoArgs: {
1844-
if (!sc)
1845-
return false;
1846-
1847-
Language *language_plugin = nullptr;
1848-
bool language_plugin_handled = false;
1849-
StreamString ss;
1850-
if (sc->function)
1851-
language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1852-
else if (sc->symbol)
1853-
language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1854-
1855-
if (language_plugin)
1856-
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1857-
*sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
1858-
ss);
1863+
if (sc) {
1864+
Language *language_plugin = nullptr;
1865+
bool language_plugin_handled = false;
1866+
StreamString ss;
1867+
if (sc->function)
1868+
language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1869+
else if (sc->symbol)
1870+
language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1871+
1872+
if (language_plugin)
1873+
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1874+
*sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
1875+
ss);
1876+
1877+
if (language_plugin_handled) {
1878+
s << ss.GetString();
1879+
return true;
1880+
}
18591881

1860-
if (language_plugin_handled) {
1861-
s << ss.GetString();
1862-
return true;
1882+
const char *name =
1883+
sc->GetPossiblyInlinedFunctionName()
1884+
.GetName(
1885+
Mangled::NamePreference::ePreferDemangledWithoutArguments)
1886+
.AsCString();
1887+
if (name) {
1888+
s.PutCString(name);
1889+
return true;
1890+
}
18631891
}
18641892

1865-
const char *name =
1866-
sc->GetPossiblyInlinedFunctionName()
1867-
.GetName(Mangled::NamePreference::ePreferDemangledWithoutArguments)
1868-
.AsCString();
1869-
if (!name)
1870-
return false;
1871-
1872-
s.PutCString(name);
1873-
1874-
return true;
1893+
// Fallback to frame methods if available
1894+
if (exe_ctx) {
1895+
StackFrame *frame = exe_ctx->GetFramePtr();
1896+
if (frame) {
1897+
const char *name = frame->GetFunctionName();
1898+
if (name) {
1899+
s.PutCString(name);
1900+
return true;
1901+
}
1902+
}
1903+
}
1904+
return false;
18751905
}
18761906

18771907
case Entry::Type::FunctionPrefix:
@@ -1898,13 +1928,26 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
18981928
}
18991929

19001930
case Entry::Type::FunctionNameWithArgs: {
1901-
if (!sc)
1902-
return false;
1931+
if (sc) {
1932+
if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
1933+
return true;
19031934

1904-
if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
1905-
return true;
1935+
if (HandleFunctionNameWithArgs(s, exe_ctx, *sc))
1936+
return true;
1937+
}
19061938

1907-
return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
1939+
// Fallback to frame methods if available
1940+
if (exe_ctx) {
1941+
StackFrame *frame = exe_ctx->GetFramePtr();
1942+
if (frame) {
1943+
const char *name = frame->GetDisplayFunctionName();
1944+
if (name) {
1945+
s.PutCString(name);
1946+
return true;
1947+
}
1948+
}
1949+
}
1950+
return false;
19081951
}
19091952
case Entry::Type::FunctionMangledName: {
19101953
if (!sc)

lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,15 @@
1111

1212
#include "lldb/Core/Address.h"
1313
#include "lldb/Core/Debugger.h"
14+
#include "lldb/Core/Module.h"
15+
#include "lldb/Core/ModuleList.h"
16+
#include "lldb/Host/FileSystem.h"
1417
#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
1518
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
1619
#include "lldb/Interpreter/ScriptInterpreter.h"
20+
#include "lldb/Symbol/CompileUnit.h"
1721
#include "lldb/Symbol/SymbolContext.h"
22+
#include "lldb/Symbol/SymbolFile.h"
1823
#include "lldb/Target/ExecutionContext.h"
1924
#include "lldb/Target/Process.h"
2025
#include "lldb/Target/RegisterContext.h"
@@ -102,6 +107,104 @@ ScriptedFrame::Create(ThreadSP thread_sp,
102107
sc = *maybe_sym_ctx;
103108
}
104109

110+
// Ensure the symbol context has at least the scripted frame implementation
111+
// for scripted frames without a valid PC or custom symbol context
112+
if (!sc.module_sp) {
113+
// First, check if we have a valid line entry with a file, and try to find
114+
// an existing module that contains that file
115+
if (sc.line_entry.IsValid() && sc.line_entry.file_sp) {
116+
const FileSpec &line_file = sc.line_entry.GetFile();
117+
118+
// Search through all modules to find one that contains this file
119+
ModuleList &module_list = process_sp->GetTarget().GetImages();
120+
const size_t num_modules = module_list.GetSize();
121+
for (size_t i = 0; i < num_modules; ++i) {
122+
ModuleSP module_sp = module_list.GetModuleAtIndex(i);
123+
if (module_sp) {
124+
SymbolFile *sym_file = module_sp->GetSymbolFile();
125+
if (sym_file) {
126+
// Check if this module has compile units with our file
127+
const uint32_t num_comp_units = sym_file->GetNumCompileUnits();
128+
for (uint32_t cu_idx = 0; cu_idx < num_comp_units; ++cu_idx) {
129+
CompUnitSP cu_sp = sym_file->GetCompileUnitAtIndex(cu_idx);
130+
if (cu_sp &&
131+
FileSpec::Equal(cu_sp->GetPrimarySupportFile()->GetSpecOnly(),
132+
line_file, false)) {
133+
sc.module_sp = module_sp;
134+
sc.comp_unit = cu_sp.get();
135+
break;
136+
}
137+
}
138+
if (sc.module_sp)
139+
break;
140+
}
141+
}
142+
}
143+
}
144+
145+
// If no existing module found, try to create one from the scripted module
146+
// path
147+
if (!sc.module_sp) {
148+
auto module_path_or_err =
149+
scripted_frame_interface->GetScriptedModulePath();
150+
if (!module_path_or_err) {
151+
LLDB_LOG_ERROR(GetLog(LLDBLog::Script), module_path_or_err.takeError(),
152+
"Failed to get scripted module path: {0}.");
153+
} else {
154+
FileSpec module_path = *module_path_or_err;
155+
if (FileSystem::Instance().Exists(module_path)) {
156+
FileSpec module_file_spec = FileSpec(module_path);
157+
const ArchSpec &arch_spec = process_sp->GetTarget().GetArchitecture();
158+
ModuleSP script_module_sp =
159+
std::make_shared<Module>(module_file_spec, arch_spec);
160+
sc.module_sp = script_module_sp;
161+
}
162+
}
163+
164+
if (!sc.module_sp) {
165+
// Fall back to using the executable module
166+
sc.module_sp = process_sp->GetTarget()
167+
.GetExecutableModulePointer()
168+
->shared_from_this();
169+
}
170+
}
171+
}
172+
173+
// Create a CompileUnit for the scripted frame if we don't have one
174+
if (!sc.comp_unit && sc.module_sp) {
175+
const FileSpec &module_file_spec = sc.module_sp->GetFileSpec();
176+
if (module_file_spec) {
177+
// Get the script language and convert to LanguageType
178+
ScriptInterpreter *script_interp =
179+
process_sp->GetTarget().GetDebugger().GetScriptInterpreter();
180+
lldb::LanguageType language = lldb::eLanguageTypeUnknown;
181+
if (script_interp) {
182+
switch (script_interp->GetLanguage()) {
183+
case lldb::eScriptLanguagePython:
184+
language = lldb::eLanguageTypePython;
185+
break;
186+
case lldb::eScriptLanguageLua:
187+
// NOTE: Lua isn't part of the DWARF Source Languages so we cannot use
188+
// it to create a compile unit.
189+
language = lldb::eLanguageTypeUnknown;
190+
break;
191+
default:
192+
language = lldb::eLanguageTypeUnknown;
193+
break;
194+
}
195+
}
196+
197+
sc.comp_unit = new CompileUnit(
198+
sc.module_sp,
199+
nullptr, // user_data
200+
std::make_shared<SupportFile>(module_file_spec), // support_file
201+
frame_id, // unique ID
202+
language,
203+
eLazyBoolCalculate // not optimized
204+
);
205+
}
206+
}
207+
105208
StructuredData::DictionarySP reg_info =
106209
scripted_frame_interface->GetRegisterInfo();
107210

@@ -162,7 +265,7 @@ const char *ScriptedFrame::GetFunctionName() {
162265
CheckInterpreterAndScriptObject();
163266
std::optional<std::string> function_name = GetInterface()->GetFunctionName();
164267
if (!function_name)
165-
return nullptr;
268+
return StackFrame::GetFunctionName();
166269
return ConstString(function_name->c_str()).AsCString();
167270
}
168271

@@ -171,7 +274,7 @@ const char *ScriptedFrame::GetDisplayFunctionName() {
171274
std::optional<std::string> function_name =
172275
GetInterface()->GetDisplayFunctionName();
173276
if (!function_name)
174-
return nullptr;
277+
return StackFrame::GetDisplayFunctionName();
175278
return ConstString(function_name->c_str()).AsCString();
176279
}
177280

lldb/source/Target/StackFrame.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,13 @@ StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) {
331331
// following the function call instruction...
332332
Address lookup_addr(GetFrameCodeAddressForSymbolication());
333333

334+
// For PC-less frames (e.g., scripted frames), skip PC-based symbol
335+
// resolution and preserve any already-populated SymbolContext fields.
336+
if (!lookup_addr.IsValid()) {
337+
m_flags.Set(resolve_scope | resolved);
338+
return m_sc;
339+
}
340+
334341
if (m_sc.module_sp) {
335342
// We have something in our stack frame symbol context, lets check if we
336343
// haven't already tried to lookup one of those things. If we haven't

lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
from lldb.plugins.scripted_process import ScriptedFrame
99

1010

11+
def my_python_function(x, y):
12+
"""A sample Python function to demonstrate Python source display in scripted frames."""
13+
result = x + y
14+
if result > 100:
15+
return result * 2
16+
else:
17+
return result
18+
19+
1120
class DummyStopHook:
1221
def __init__(self, target, args):
1322
self.target = target
@@ -88,6 +97,22 @@ def __init__(self, process, args):
8897
DummyScriptedFrame(self, args, len(self.frames), "baz", sym_ctx)
8998
)
9099

100+
# Add a frame with Python source
101+
code = my_python_function.__code__
102+
lineno = code.co_firstlineno
103+
col_offset = getattr(code, "co_firstcol_offset", 0) # Python ≥3.11 has column info
104+
py_le = lldb.SBLineEntry()
105+
py_le.SetFileSpec(lldb.SBFileSpec(__file__, True))
106+
py_le.SetLine(lineno) # Line where my_python_function is defined
107+
py_le.SetColumn(col_offset)
108+
109+
py_sym_ctx = lldb.SBSymbolContext()
110+
py_sym_ctx.SetLineEntry(py_le)
111+
112+
self.frames.append(
113+
DummyScriptedFrame(self, args, len(self.frames), "my_python_function", py_sym_ctx)
114+
)
115+
91116
def get_thread_id(self) -> int:
92117
return 0x19
93118

0 commit comments

Comments
 (0)