Skip to content

Commit 41f0ae8

Browse files
committed
[lldb] Add support for PC-less scripted frames
Scripted frames that materialize Python functions or other non-native code are PC-less by design, meaning they don't have valid program counter values. Previously, these frames would display invalid addresses (0xffffffffffffffff) in backtrace output. This patch updates FormatEntity to detect and suppress invalid address display for PC-less frames, adds fallback to frame methods when symbol context is unavailable, and modifies StackFrame::GetSymbolContext to skip PC-based symbol resolution for invalid addresses. The changes enable PC-less frames to display cleanly with proper function names, file paths, and line numbers, and allow for source display of foreign sources (like Python). Includes comprehensive test coverage demonstrating frames pointing to Python source files. Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 0cb237d commit 41f0ae8

File tree

10 files changed

+437
-121
lines changed

10 files changed

+437
-121
lines changed

lldb/include/lldb/Target/StackID.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class StackID {
5252

5353
protected:
5454
friend class StackFrame;
55+
friend class SyntheticStackFrameList;
5556

5657
void SetPC(lldb::addr_t pc, Process *process);
5758
void SetCFA(lldb::addr_t cfa, Process *process);

lldb/source/Core/CoreProperties.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ let Definition = "debugger" in {
5959
Desc<"The default disassembly format string to use when disassembling instruction sequences.">;
6060
def FrameFormat: Property<"frame-format", "FormatEntity">,
6161
Global,
62-
DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
62+
DefaultStringValue<"frame #${frame.index}: {${ansi.fg.cyan}${frame.pc}${ansi.normal}}{ ${module.file.basename}{`}}{${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
6363
Desc<"The default frame format string to use when displaying stack frame information for threads.">;
6464
def NotiftVoid: Property<"notify-void", "Boolean">,
6565
Global,
@@ -235,7 +235,7 @@ let Definition = "debugger" in {
235235
Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">;
236236
def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">,
237237
Global,
238-
DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
238+
DefaultStringValue<"frame #${frame.index}: {${ansi.fg.cyan}${frame.pc}${ansi.normal}}{ ${module.file.basename}{`}}{${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
239239
Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">;
240240
def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">,
241241
Global,

lldb/source/Core/FormatEntity.cpp

Lines changed: 95 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,10 +1684,9 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16841684
StackFrame *frame = exe_ctx->GetFramePtr();
16851685
if (frame) {
16861686
const Address &pc_addr = frame->GetFrameCodeAddress();
1687-
if (pc_addr.IsValid() || frame->IsSynthetic()) {
1687+
if (pc_addr.IsValid())
16881688
if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
16891689
return true;
1690-
}
16911690
}
16921691
}
16931692
return false;
@@ -1808,70 +1807,91 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
18081807
return initial_function;
18091808

18101809
case Entry::Type::FunctionName: {
1811-
if (!sc)
1812-
return false;
1810+
if (sc) {
1811+
Language *language_plugin = nullptr;
1812+
bool language_plugin_handled = false;
1813+
StreamString ss;
18131814

1814-
Language *language_plugin = nullptr;
1815-
bool language_plugin_handled = false;
1816-
StreamString ss;
1815+
if (sc->function)
1816+
language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1817+
else if (sc->symbol)
1818+
language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
18171819

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());
1820+
if (language_plugin)
1821+
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1822+
*sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
18221823

1823-
if (language_plugin)
1824-
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1825-
*sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss);
1824+
if (language_plugin_handled) {
1825+
s << ss.GetString();
1826+
return true;
1827+
}
18261828

1827-
if (language_plugin_handled) {
1828-
s << ss.GetString();
1829-
return true;
1829+
const char *name = sc->GetPossiblyInlinedFunctionName()
1830+
.GetName(Mangled::NamePreference::ePreferDemangled)
1831+
.AsCString();
1832+
if (name) {
1833+
s.PutCString(name);
1834+
return true;
1835+
}
18301836
}
18311837

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;
1838+
// Fallback to frame methods if available.
1839+
if (exe_ctx) {
1840+
StackFrame *frame = exe_ctx->GetFramePtr();
1841+
if (frame) {
1842+
const char *name = frame->GetFunctionName();
1843+
if (name) {
1844+
s.PutCString(name);
1845+
return true;
1846+
}
1847+
}
1848+
}
1849+
return false;
18411850
}
18421851

18431852
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);
1853+
if (sc) {
1854+
Language *language_plugin = nullptr;
1855+
bool language_plugin_handled = false;
1856+
StreamString ss;
1857+
if (sc->function)
1858+
language_plugin = Language::FindPlugin(sc->function->GetLanguage());
1859+
else if (sc->symbol)
1860+
language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
1861+
1862+
if (language_plugin)
1863+
language_plugin_handled = language_plugin->GetFunctionDisplayName(
1864+
*sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs,
1865+
ss);
1866+
1867+
if (language_plugin_handled) {
1868+
s << ss.GetString();
1869+
return true;
1870+
}
18591871

1860-
if (language_plugin_handled) {
1861-
s << ss.GetString();
1862-
return true;
1872+
const char *name =
1873+
sc->GetPossiblyInlinedFunctionName()
1874+
.GetName(
1875+
Mangled::NamePreference::ePreferDemangledWithoutArguments)
1876+
.AsCString();
1877+
if (name) {
1878+
s.PutCString(name);
1879+
return true;
1880+
}
18631881
}
18641882

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;
1883+
// Fallback to frame methods if available.
1884+
if (exe_ctx) {
1885+
StackFrame *frame = exe_ctx->GetFramePtr();
1886+
if (frame) {
1887+
const char *name = frame->GetFunctionName();
1888+
if (name) {
1889+
s.PutCString(name);
1890+
return true;
1891+
}
1892+
}
1893+
}
1894+
return false;
18751895
}
18761896

18771897
case Entry::Type::FunctionPrefix:
@@ -1898,13 +1918,26 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
18981918
}
18991919

19001920
case Entry::Type::FunctionNameWithArgs: {
1901-
if (!sc)
1902-
return false;
1921+
if (sc) {
1922+
if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
1923+
return true;
19031924

1904-
if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
1905-
return true;
1925+
if (HandleFunctionNameWithArgs(s, exe_ctx, *sc))
1926+
return true;
1927+
}
19061928

1907-
return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
1929+
// Fallback to frame methods if available.
1930+
if (exe_ctx) {
1931+
StackFrame *frame = exe_ctx->GetFramePtr();
1932+
if (frame) {
1933+
const char *name = frame->GetDisplayFunctionName();
1934+
if (name) {
1935+
s.PutCString(name);
1936+
return true;
1937+
}
1938+
}
1939+
}
1940+
return false;
19081941
}
19091942
case Entry::Type::FunctionMangledName: {
19101943
if (!sc)
@@ -1946,12 +1979,11 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
19461979
case Entry::Type::FunctionPCOffset:
19471980
if (exe_ctx) {
19481981
StackFrame *frame = exe_ctx->GetFramePtr();
1949-
if (frame) {
1982+
if (frame)
19501983
if (DumpAddressOffsetFromFunction(s, sc, exe_ctx,
19511984
frame->GetFrameCodeAddress(), false,
19521985
false, false))
19531986
return true;
1954-
}
19551987
}
19561988
return false;
19571989

@@ -1975,11 +2007,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
19752007

19762008
case Entry::Type::LineEntryFile:
19772009
if (sc && sc->line_entry.IsValid()) {
1978-
Module *module = sc->module_sp.get();
1979-
if (module) {
1980-
if (DumpFile(s, sc->line_entry.GetFile(), (FileKind)entry.number))
1981-
return true;
1982-
}
2010+
if (DumpFile(s, sc->line_entry.GetFile(), (FileKind)entry.number))
2011+
return true;
19832012
}
19842013
return false;
19852014

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

Lines changed: 8 additions & 4 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"
@@ -98,9 +103,8 @@ ScriptedFrame::Create(ThreadSP thread_sp,
98103

99104
std::optional<SymbolContext> maybe_sym_ctx =
100105
scripted_frame_interface->GetSymbolContext();
101-
if (maybe_sym_ctx) {
106+
if (maybe_sym_ctx)
102107
sc = *maybe_sym_ctx;
103-
}
104108

105109
StructuredData::DictionarySP reg_info =
106110
scripted_frame_interface->GetRegisterInfo();
@@ -162,7 +166,7 @@ const char *ScriptedFrame::GetFunctionName() {
162166
CheckInterpreterAndScriptObject();
163167
std::optional<std::string> function_name = GetInterface()->GetFunctionName();
164168
if (!function_name)
165-
return nullptr;
169+
return StackFrame::GetFunctionName();
166170
return ConstString(function_name->c_str()).AsCString();
167171
}
168172

@@ -171,7 +175,7 @@ const char *ScriptedFrame::GetDisplayFunctionName() {
171175
std::optional<std::string> function_name =
172176
GetInterface()->GetDisplayFunctionName();
173177
if (!function_name)
174-
return nullptr;
178+
return StackFrame::GetDisplayFunctionName();
175179
return ConstString(function_name->c_str()).AsCString();
176180
}
177181

lldb/source/Target/StackFrame.cpp

Lines changed: 8 additions & 1 deletion
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
@@ -2057,7 +2064,7 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
20572064
disasm_display = debugger.GetStopDisassemblyDisplay();
20582065

20592066
GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry);
2060-
if (m_sc.comp_unit && m_sc.line_entry.IsValid()) {
2067+
if (m_sc.comp_unit || m_sc.line_entry.IsValid()) {
20612068
have_debuginfo = true;
20622069
if (source_lines_before > 0 || source_lines_after > 0) {
20632070
SupportFileNSP source_file_sp = m_sc.line_entry.file_sp;

lldb/source/Target/StackFrameList.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ SyntheticStackFrameList::SyntheticStackFrameList(
6464

6565
bool SyntheticStackFrameList::FetchFramesUpTo(
6666
uint32_t end_idx, InterruptionControl allow_interrupt) {
67+
68+
size_t num_synthetic_frames = 0;
6769
// Check if the thread has a synthetic frame provider.
6870
if (auto provider_sp = m_thread.GetFrameProvider()) {
6971
// Use the synthetic frame provider to generate frames lazily.
@@ -81,6 +83,9 @@ bool SyntheticStackFrameList::FetchFramesUpTo(
8183
break;
8284
}
8385
StackFrameSP frame_sp = *frame_or_err;
86+
if (frame_sp->IsSynthetic())
87+
frame_sp->GetStackID().SetCFA(num_synthetic_frames++,
88+
GetThread().GetProcess().get());
8489
// Set the frame list weak pointer so ExecutionContextRef can resolve
8590
// the frame without calling Thread::GetStackFrameList().
8691
frame_sp->m_frame_list_wp = shared_from_this();

0 commit comments

Comments
 (0)