Skip to content

Commit 52ad202

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 <ismail@bennani.ma>
1 parent 5911754 commit 52ad202

File tree

10 files changed

+466
-125
lines changed

10 files changed

+466
-125
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: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,20 @@ let Definition = "debugger" in {
5757
Global,
5858
DefaultStringValue<"{${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\\\\n}{${function.changed}\\\\n{${module.file.basename}`}{${function.name-without-args}}:\\\\n}{${ansi.fg.yellow}${current-pc-arrow}${ansi.normal} }${addr-file-or-load}{ <${function.concrete-only-addr-offset-no-padding}>}: ">,
5959
Desc<"The default disassembly format string to use when disassembling instruction sequences.">;
60-
def FrameFormat: Property<"frame-format", "FormatEntity">,
61-
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">,
63-
Desc<"The default frame format string to use when displaying stack frame information for threads.">;
60+
def FrameFormat
61+
: Property<"frame-format", "FormatEntity">,
62+
Global,
63+
DefaultStringValue<
64+
"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ "
65+
"${module.file.basename}{`}}{${function.name-with-args}{${frame.no-"
66+
"debug}${function.pc-offset}}}{ at "
67+
"${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg."
68+
"yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line."
69+
"column}${ansi.normal}}}${frame.kind}{${function.is-optimized} "
70+
"[opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} "
71+
"[artificial]}\\\\n">,
72+
Desc<"The default frame format string to use when displaying stack "
73+
"frame information for threads.">;
6474
def NotiftVoid: Property<"notify-void", "Boolean">,
6575
Global,
6676
DefaultFalse,
@@ -233,10 +243,20 @@ let Definition = "debugger" in {
233243
Global,
234244
DefaultTrue,
235245
Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">;
236-
def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">,
237-
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">,
239-
Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">;
246+
def FrameFormatUnique
247+
: Property<"frame-format-unique", "FormatEntity">,
248+
Global,
249+
DefaultStringValue<
250+
"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ "
251+
"${module.file.basename}{`}}{${function.name-without-args}{${frame."
252+
"no-debug}${function.pc-offset}}}{ at "
253+
"${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg."
254+
"yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line."
255+
"column}${ansi.normal}}}${frame.kind}{${function.is-optimized} "
256+
"[opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} "
257+
"[artificial]}\\\\n">,
258+
Desc<"The default frame format string to use when displaying stack "
259+
"frame information for threads from thread backtrace unique.">;
240260
def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">,
241261
Global,
242262
DefaultFalse,

lldb/source/Core/FormatEntity.cpp

Lines changed: 98 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,10 +1684,11 @@ 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-
}
1690+
} else if (frame->IsSynthetic())
1691+
return true;
16911692
}
16921693
}
16931694
return false;
@@ -1808,70 +1809,91 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
18081809
return initial_function;
18091810

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

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

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

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

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

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

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

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

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

18771899
case Entry::Type::FunctionPrefix:
@@ -1898,13 +1920,26 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
18981920
}
18991921

19001922
case Entry::Type::FunctionNameWithArgs: {
1901-
if (!sc)
1902-
return false;
1923+
if (sc) {
1924+
if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
1925+
return true;
19031926

1904-
if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
1905-
return true;
1927+
if (HandleFunctionNameWithArgs(s, exe_ctx, *sc))
1928+
return true;
1929+
}
19061930

1907-
return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
1931+
// Fallback to frame methods if available.
1932+
if (exe_ctx) {
1933+
StackFrame *frame = exe_ctx->GetFramePtr();
1934+
if (frame) {
1935+
const char *name = frame->GetDisplayFunctionName();
1936+
if (name) {
1937+
s.PutCString(name);
1938+
return true;
1939+
}
1940+
}
1941+
}
1942+
return false;
19081943
}
19091944
case Entry::Type::FunctionMangledName: {
19101945
if (!sc)
@@ -1951,6 +1986,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
19511986
frame->GetFrameCodeAddress(), false,
19521987
false, false))
19531988
return true;
1989+
else if (frame->IsSynthetic())
1990+
return true;
19541991
}
19551992
}
19561993
return false;
@@ -1975,11 +2012,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
19752012

19762013
case Entry::Type::LineEntryFile:
19772014
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-
}
2015+
if (DumpFile(s, sc->line_entry.GetFile(), (FileKind)entry.number))
2016+
return true;
19832017
}
19842018
return false;
19852019

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)