https://github.com/medismailben updated 
https://github.com/llvm/llvm-project/pull/149622

>From 583a83e1001a824fcfbc9a0811af3fbaf446e5ca Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ism...@bennani.ma>
Date: Tue, 2 Sep 2025 17:36:27 -0700
Subject: [PATCH 1/2] [lldb] Mark scripted frames as synthetic instead of
 artificial

This patch changes the way frames created from scripted affordances like
Scripted Threads are displayed. Currently, they're marked artificial
which is used usually for compiler generated frames.

This patch changes that behaviour by introducing a new synthetic
StackFrame kind and moves 'artificial' to be a distinct StackFrame attribut.

On top of making these frames less confusing, this allows us to know
when a frame was created from a scripted affordance.

rdar://155949703

Signed-off-by: Med Ismail Bennani <ism...@bennani.ma>
---
 lldb/include/lldb/API/SBFrame.h               |  2 ++
 lldb/include/lldb/Core/FormatEntity.h         |  1 +
 lldb/include/lldb/Target/StackFrame.h         | 17 +++++++---
 lldb/source/API/SBFrame.cpp                   | 16 ++++++++++
 lldb/source/Core/CoreProperties.td            |  4 +--
 lldb/source/Core/FormatEntity.cpp             | 14 ++++++++
 .../Process/scripted/ScriptedThread.cpp       |  4 ++-
 lldb/source/Target/StackFrame.cpp             | 14 ++++----
 lldb/source/Target/StackFrameList.cpp         |  6 ++--
 .../scripted_process/TestScriptedProcess.py   |  2 +-
 .../app_specific_backtrace_crashlog.test      | 32 +++++++++----------
 .../interactive_crashlog_arm64_register.test  | 30 ++++++++---------
 .../Crashlog/interactive_crashlog_json.test   | 30 ++++++++---------
 .../Crashlog/interactive_crashlog_legacy.test | 30 ++++++++---------
 .../last_exception_backtrace_crashlog.test    | 32 +++++++++----------
 .../skipped_status_interactive_crashlog.test  | 30 ++++++++---------
 lldb/unittests/Core/FormatEntityTest.cpp      |  1 +
 17 files changed, 156 insertions(+), 109 deletions(-)

diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 08de0605b3240..e4bbcd5ddcd9c 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -104,6 +104,8 @@ class LLDB_API SBFrame {
 
   bool IsArtificial() const;
 
+  bool IsSynthetic() const;
+
   /// Return whether a frame recognizer decided this frame should not
   /// be displayes in backtraces etc.
   bool IsHidden() const;
diff --git a/lldb/include/lldb/Core/FormatEntity.h 
b/lldb/include/lldb/Core/FormatEntity.h
index d602edffb4c88..40916dc48a70b 100644
--- a/lldb/include/lldb/Core/FormatEntity.h
+++ b/lldb/include/lldb/Core/FormatEntity.h
@@ -80,6 +80,7 @@ struct Entry {
     FrameRegisterFlags,
     FrameRegisterByName,
     FrameIsArtificial,
+    FrameKind,
     ScriptFrame,
     FunctionID,
     FunctionDidChange,
diff --git a/lldb/include/lldb/Target/StackFrame.h 
b/lldb/include/lldb/Target/StackFrame.h
index d4104bfe49d20..4ffbf97ce6e90 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -60,10 +60,9 @@ class StackFrame : public ExecutionContextScope,
     /// local variables.
     History,
 
-    /// An artificial stack frame (e.g. a synthesized result of inferring
-    /// missing tail call frames from a backtrace) with limited support for
-    /// local variables.
-    Artificial
+    /// An synthetic stack frame (e.g. a synthesized result from script
+    /// resource) possibly without support for local variables or register.
+    Synthetic
   };
 
   /// Construct a StackFrame object without supplying a RegisterContextSP.
@@ -109,7 +108,8 @@ class StackFrame : public ExecutionContextScope,
   StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx,
              lldb::user_id_t concrete_frame_idx, lldb::addr_t cfa,
              bool cfa_is_valid, lldb::addr_t pc, Kind frame_kind,
-             bool behaves_like_zeroth_frame, const SymbolContext *sc_ptr);
+             bool artificial, bool behaves_like_zeroth_frame,
+             const SymbolContext *sc_ptr);
 
   StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx,
              lldb::user_id_t concrete_frame_idx,
@@ -400,6 +400,9 @@ class StackFrame : public ExecutionContextScope,
   ///   true if this is an inlined frame.
   bool IsInlined();
 
+  /// Query whether this frame is synthetic.
+  bool IsSynthetic() const;
+
   /// Query whether this frame is part of a historical backtrace.
   bool IsHistorical() const;
 
@@ -571,6 +574,10 @@ class StackFrame : public ExecutionContextScope,
   /// Does this frame have a CFA?  Different from CFA == LLDB_INVALID_ADDRESS.
   bool m_cfa_is_valid;
   Kind m_stack_frame_kind;
+  /// Is this an artificial stack frame (e.g. a synthesized result of inferring
+  /// missing tail call frames from a backtrace) with limited support for
+  /// local variables. Orthogonal to `StackFrame::Kind`.
+  bool m_artificial;
 
   /// Whether this frame behaves like the zeroth frame, in the sense
   /// that its pc value might not immediately follow a call (and thus might
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index b12cfceacd75d..b6724bb0c4119 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -1118,6 +1118,22 @@ bool SBFrame::IsArtificial() const {
   return false;
 }
 
+bool SBFrame::IsSynthetic() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  llvm::Expected<StoppedExecutionContext> exe_ctx =
+      GetStoppedExecutionContext(m_opaque_sp);
+  if (!exe_ctx) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+    return false;
+  }
+
+  if (StackFrame *frame = exe_ctx->GetFramePtr())
+    return frame->IsSynthetic();
+
+  return false;
+}
+
 bool SBFrame::IsHidden() const {
   LLDB_INSTRUMENT_VA(this);
 
diff --git a/lldb/source/Core/CoreProperties.td 
b/lldb/source/Core/CoreProperties.td
index 53dd333f045c9..fda34a8ad2630 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -59,7 +59,7 @@ let Definition = "debugger" in {
     Desc<"The default disassembly format string to use when disassembling 
instruction sequences.">;
   def FrameFormat: Property<"frame-format", "FormatEntity">,
     Global,
-    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}}}{${function.is-optimized}
 [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} 
[artificial]}\\\\n">,
+    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">,
     Desc<"The default frame format string to use when displaying stack frame 
information for threads.">;
   def NotiftVoid: Property<"notify-void", "Boolean">,
     Global,
@@ -233,7 +233,7 @@ let Definition = "debugger" in {
     Desc<"If true, LLDB will automatically escape non-printable and escape 
characters when formatting strings.">;
   def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">,
     Global,
-    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}}}{${function.is-optimized}
 [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} 
[artificial]}\\\\n">,
+    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">,
     Desc<"The default frame format string to use when displaying stack frame 
information for threads from thread backtrace unique.">;
   def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">,
     Global,
diff --git a/lldb/source/Core/FormatEntity.cpp 
b/lldb/source/Core/FormatEntity.cpp
index 5d3c8b421d5d1..2ff73979e4976 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -108,6 +108,7 @@ constexpr Definition g_frame_child_entries[] = {
     Entry::DefinitionWithChildren("reg", EntryType::FrameRegisterByName,
                                   g_string_entry),
     Definition("is-artificial", EntryType::FrameIsArtificial),
+    Definition("kind", EntryType::FrameKind),
 };
 
 constexpr Definition g_function_child_entries[] = {
@@ -380,6 +381,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
     ENUM_TO_CSTR(FrameRegisterFlags);
     ENUM_TO_CSTR(FrameRegisterByName);
     ENUM_TO_CSTR(FrameIsArtificial);
+    ENUM_TO_CSTR(FrameKind);
     ENUM_TO_CSTR(ScriptFrame);
     ENUM_TO_CSTR(FunctionID);
     ENUM_TO_CSTR(FunctionDidChange);
@@ -1747,6 +1749,18 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
     return false;
   }
 
+  case Entry::Type::FrameKind: {
+    if (exe_ctx)
+      if (StackFrame *frame = exe_ctx->GetFramePtr()) {
+        if (frame->IsSynthetic())
+          s.PutCString(" [synthetic]");
+        else if (frame->IsHistorical())
+          s.PutCString(" [history]");
+        return true;
+      }
+    return false;
+  }
+
   case Entry::Type::ScriptFrame:
     if (exe_ctx) {
       StackFrame *frame = exe_ctx->GetFramePtr();
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp 
b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index d0d1508e85172..45ad83e4ed671 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -200,13 +200,15 @@ bool ScriptedThread::LoadArtificialStackFrames() {
 
     lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
     bool cfa_is_valid = false;
+    const bool artificial = false;
     const bool behaves_like_zeroth_frame = false;
     SymbolContext sc;
     symbol_addr.CalculateSymbolContext(&sc);
 
     StackFrameSP synth_frame_sp = std::make_shared<StackFrame>(
         this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
-        StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc);
+        StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame,
+        &sc);
 
     if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp))
       return ScriptedInterface::ErrorWithMessage<bool>(
diff --git a/lldb/source/Target/StackFrame.cpp 
b/lldb/source/Target/StackFrame.cpp
index f27004c2d134c..2ed58c5331df4 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -57,14 +57,14 @@ using namespace lldb_private;
 StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
                        user_id_t unwind_frame_index, addr_t cfa,
                        bool cfa_is_valid, addr_t pc, StackFrame::Kind kind,
-                       bool behaves_like_zeroth_frame,
+                       bool artificial, bool behaves_like_zeroth_frame,
                        const SymbolContext *sc_ptr)
     : m_thread_wp(thread_sp), m_frame_index(frame_idx),
       m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(),
       m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()),
       m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
       m_frame_base_error(), m_cfa_is_valid(cfa_is_valid),
-      m_stack_frame_kind(kind),
+      m_stack_frame_kind(kind), m_artificial(artificial),
       m_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
       m_variable_list_sp(), m_variable_list_value_objects(),
       m_recognized_frame_sp(), m_disassembly(), m_mutex() {
@@ -92,7 +92,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t 
frame_idx,
       m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()),
       m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
       m_frame_base_error(), m_cfa_is_valid(true),
-      m_stack_frame_kind(StackFrame::Kind::Regular),
+      m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false),
       m_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
       m_variable_list_sp(), m_variable_list_value_objects(),
       m_recognized_frame_sp(), m_disassembly(), m_mutex() {
@@ -120,7 +120,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t 
frame_idx,
            nullptr, thread_sp->GetProcess().get()),
       m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(),
       m_frame_base_error(), m_cfa_is_valid(true),
-      m_stack_frame_kind(StackFrame::Kind::Regular),
+      m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false),
       m_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
       m_variable_list_sp(), m_variable_list_value_objects(),
       m_recognized_frame_sp(), m_disassembly(), m_mutex() {
@@ -1264,10 +1264,12 @@ bool StackFrame::IsHistorical() const {
   return m_stack_frame_kind == StackFrame::Kind::History;
 }
 
-bool StackFrame::IsArtificial() const {
-  return m_stack_frame_kind == StackFrame::Kind::Artificial;
+bool StackFrame::IsSynthetic() const {
+  return m_stack_frame_kind == StackFrame::Kind::Synthetic;
 }
 
+bool StackFrame::IsArtificial() const { return m_artificial; }
+
 bool StackFrame::IsHidden() {
   if (auto recognized_frame_sp = GetRecognizedFrame())
     return recognized_frame_sp->ShouldHide();
diff --git a/lldb/source/Target/StackFrameList.cpp 
b/lldb/source/Target/StackFrameList.cpp
index aedfc52cfb4cb..d83c90582ba9c 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -321,13 +321,14 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame 
&next_frame) {
     addr_t pc = calleeInfo.address;
     // If the callee address refers to the call instruction, we do not want to
     // subtract 1 from this value.
+    const bool artificial = true;
     const bool behaves_like_zeroth_frame =
         calleeInfo.address_type == CallEdge::AddrType::Call;
     SymbolContext sc;
     callee->CalculateSymbolContext(&sc);
     auto synth_frame = std::make_shared<StackFrame>(
         m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa,
-        cfa_is_valid, pc, StackFrame::Kind::Artificial,
+        cfa_is_valid, pc, StackFrame::Kind::Regular, artificial,
         behaves_like_zeroth_frame, &sc);
     m_frames.push_back(synth_frame);
     LLDB_LOG(log, "Pushed frame {0} at {1:x}", callee->GetDisplayName(), pc);
@@ -470,7 +471,8 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx,
       const bool cfa_is_valid = true;
       unwind_frame_sp = std::make_shared<StackFrame>(
           m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid,
-          pc, StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr);
+          pc, StackFrame::Kind::Regular, false, behaves_like_zeroth_frame,
+          nullptr);
 
       // Create synthetic tail call frames between the previous frame and the
       // newly-found frame. The new frame's index may change after this call,
diff --git 
a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py 
b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
index 9519c576689d0..5916e62c44f2e 100644
--- a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
+++ b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
@@ -284,6 +284,6 @@ def cleanup():
                 break
             self.assertEqual(idx, int(reg.value, 16))
 
-        self.assertTrue(frame.IsArtificial(), "Frame is not artificial")
+        self.assertTrue(frame.IsSynthetic(), "Frame is not synthetic")
         pc = frame.GetPCAddress().GetLoadAddress(target_0)
         self.assertEqual(pc, 0x0100001B00)
diff --git 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
index 9c0510c34ccae..f680158fdf735 100644
--- 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
+++ 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
@@ -11,7 +11,7 @@
 # CHECK: (lldb) process status --verbose
 # CHECK-NEXT: Process 96535 stopped
 # CHECK-NEXT: * thread #1, queue = 'com.apple.main-thread', stop reason = 
EXC_CRASH (code=0, subcode=0x0)
-# CHECK-NEXT:     frame #0: 0x00000001a08c7224{{.*}}[artificial]
+# CHECK-NEXT:     frame #0: 0x00000001a08c7224{{.*}}[synthetic]
 # CHECK: Extended Crash Information:
 # CHECK:   Application Specific Information:
 # CHECK-NEXT:     CoreFoundation: *** Terminating app due to uncaught 
exception 'NSRangeException', reason: '*** __boundsFail: index 10 beyond bounds 
[0 .. 3]'
@@ -21,21 +21,21 @@
 
 # CHECK: (lldb) thread backtrace --extended true
 # CHECK: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH 
(code=0, subcode=0x0)
-# CHECK-NEXT:   * frame #0: 0x00000001a08c7224{{.*}}[artificial]
-# CHECK-NEXT:     frame #1: 0x00000001a08fdceb{{.*}}[artificial]
-# CHECK-NEXT:     frame #2: 0x00000001a08372c7{{.*}}[artificial]
-# CHECK-NEXT:     frame #3: 0x00000001a08b7b17{{.*}}[artificial]
-# CHECK-NEXT:     frame #4: 0x00000001a08a7a0b{{.*}}[artificial]
-# CHECK-NEXT:     frame #5: 0x00000001a05ab763{{.*}}[artificial]
-# CHECK-NEXT:     frame #6: 0x00000001a08b6eb3{{.*}}[artificial]
-# CHECK-NEXT:     frame #7: 0x00000001a08b9c2b{{.*}}[artificial]
-# CHECK-NEXT:     frame #8: 0x00000001a08b9bd7{{.*}}[artificial]
-# CHECK-NEXT:     frame #9: 0x00000001a05a3007{{.*}}[artificial]
-# CHECK-NEXT:     frame #10: 0x00000001a0b3dcc3{{.*}}[artificial]
-# CHECK-NEXT:     frame #11: 0x00000001a0b46af3{{.*}}[artificial]
-# CHECK-NEXT:     frame #12: 0x00000001a09a12a3{{.*}}[artificial]
-# CHECK-NEXT:     frame #13: 0x00000001047e3ecf asi`main{{.*}}[artificial]
-# CHECK-NEXT:     frame #14: 0x00000001a05d3e4f{{.*}}[artificial]
+# CHECK-NEXT:   * frame #0: 0x00000001a08c7224{{.*}}[synthetic]
+# CHECK-NEXT:     frame #1: 0x00000001a08fdceb{{.*}}[synthetic]
+# CHECK-NEXT:     frame #2: 0x00000001a08372c7{{.*}}[synthetic]
+# CHECK-NEXT:     frame #3: 0x00000001a08b7b17{{.*}}[synthetic]
+# CHECK-NEXT:     frame #4: 0x00000001a08a7a0b{{.*}}[synthetic]
+# CHECK-NEXT:     frame #5: 0x00000001a05ab763{{.*}}[synthetic]
+# CHECK-NEXT:     frame #6: 0x00000001a08b6eb3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #7: 0x00000001a08b9c2b{{.*}}[synthetic]
+# CHECK-NEXT:     frame #8: 0x00000001a08b9bd7{{.*}}[synthetic]
+# CHECK-NEXT:     frame #9: 0x00000001a05a3007{{.*}}[synthetic]
+# CHECK-NEXT:     frame #10: 0x00000001a0b3dcc3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #11: 0x00000001a0b46af3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #12: 0x00000001a09a12a3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #13: 0x00000001047e3ecf asi`main{{.*}}[synthetic]
+# CHECK-NEXT:     frame #14: 0x00000001a05d3e4f{{.*}}[synthetic]
 
 # CHECK:   thread #4294967295: tid = 0x0001, 0x00000001a0a58418{{.*}}, queue = 
'Application Specific Backtrace'
 # CHECK-NEXT:     frame #0: 0x00000001a0a58418{{.*}}
diff --git 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test
 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test
index 3f572c3300c0f..9da21d9aab78d 100644
--- 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test
+++ 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test
@@ -15,9 +15,9 @@
 
 # CHECK: (lldb) thread backtrace
 # CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
 
 # CHECK: (lldb) thread list
 # CHECK-NEXT: Process 22511 stopped
@@ -27,20 +27,20 @@
 
 # CHECK: (lldb) bt all
 # CHECK:  thread #1, queue = 'com.apple.main-thread'
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
 # CHECK-NEXT:  thread #2
-# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
 # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
 
 # CHECK: (lldb) register read
 # CHECK: General Purpose Registers:
diff --git 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test
 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test
index 684be2846f78d..2509f2e88d9b2 100644
--- 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test
+++ 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test
@@ -15,9 +15,9 @@
 
 # CHECK: (lldb) thread backtrace
 # CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
 
 # CHECK: (lldb) thread list
 # CHECK-NEXT: Process 22511 stopped
@@ -27,17 +27,17 @@
 
 # CHECK: (lldb) bt all
 # CHECK:  thread #1, queue = 'com.apple.main-thread'
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
 # CHECK-NEXT:  thread #2
-# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
 # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
diff --git 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test
 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test
index 271a4c2aa90f4..74ced35fcfae0 100644
--- 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test
+++ 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test
@@ -15,9 +15,9 @@
 
 # CHECK: (lldb) thread backtrace
 # CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
 
 # CHECK: (lldb) thread list
 # CHECK-NEXT: Process 22511 stopped
@@ -27,17 +27,17 @@
 
 # CHECK: (lldb) bt all
 # CHECK:  thread #1
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
 # CHECK-NEXT:  thread #2
-# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
 # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
diff --git 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test
 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test
index a17b7ac18a620..53c0732deb549 100644
--- 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test
+++ 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test
@@ -11,7 +11,7 @@
 # CHECK: (lldb) process status --verbose
 # CHECK-NEXT: Process 96535 stopped
 # CHECK-NEXT: * thread #1, queue = 'com.apple.main-thread', stop reason = 
EXC_CRASH (code=0, subcode=0x0)
-# CHECK-NEXT:     frame #0: 0x00000001a08c7224{{.*}}[artificial]
+# CHECK-NEXT:     frame #0: 0x00000001a08c7224{{.*}}[synthetic]
 # CHECK: Extended Crash Information:
 # CHECK:   Application Specific Information:
 # CHECK-NEXT:     CoreFoundation: *** Terminating app due to uncaught 
exception 'NSRangeException', reason: '*** __boundsFail: index 10 beyond bounds 
[0 .. 3]'
@@ -21,21 +21,21 @@
 
 # CHECK: (lldb) thread backtrace --extended true
 # CHECK: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH 
(code=0, subcode=0x0)
-# CHECK-NEXT:   * frame #0: 0x00000001a08c7224{{.*}}[artificial]
-# CHECK-NEXT:     frame #1: 0x00000001a08fdceb{{.*}}[artificial]
-# CHECK-NEXT:     frame #2: 0x00000001a08372c7{{.*}}[artificial]
-# CHECK-NEXT:     frame #3: 0x00000001a08b7b17{{.*}}[artificial]
-# CHECK-NEXT:     frame #4: 0x00000001a08a7a0b{{.*}}[artificial]
-# CHECK-NEXT:     frame #5: 0x00000001a05ab763{{.*}}[artificial]
-# CHECK-NEXT:     frame #6: 0x00000001a08b6eb3{{.*}}[artificial]
-# CHECK-NEXT:     frame #7: 0x00000001a08b9c2b{{.*}}[artificial]
-# CHECK-NEXT:     frame #8: 0x00000001a08b9bd7{{.*}}[artificial]
-# CHECK-NEXT:     frame #9: 0x00000001a05a3007{{.*}}[artificial]
-# CHECK-NEXT:     frame #10: 0x00000001a0b3dcc3{{.*}}[artificial]
-# CHECK-NEXT:     frame #11: 0x00000001a0b46af3{{.*}}[artificial]
-# CHECK-NEXT:     frame #12: 0x00000001a09a12a3{{.*}}[artificial]
-# CHECK-NEXT:     frame #13: 0x00000001047e3ecf asi`main{{.*}}[artificial]
-# CHECK-NEXT:     frame #14: 0x00000001a05d3e4f{{.*}}[artificial]
+# CHECK-NEXT:   * frame #0: 0x00000001a08c7224{{.*}}[synthetic]
+# CHECK-NEXT:     frame #1: 0x00000001a08fdceb{{.*}}[synthetic]
+# CHECK-NEXT:     frame #2: 0x00000001a08372c7{{.*}}[synthetic]
+# CHECK-NEXT:     frame #3: 0x00000001a08b7b17{{.*}}[synthetic]
+# CHECK-NEXT:     frame #4: 0x00000001a08a7a0b{{.*}}[synthetic]
+# CHECK-NEXT:     frame #5: 0x00000001a05ab763{{.*}}[synthetic]
+# CHECK-NEXT:     frame #6: 0x00000001a08b6eb3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #7: 0x00000001a08b9c2b{{.*}}[synthetic]
+# CHECK-NEXT:     frame #8: 0x00000001a08b9bd7{{.*}}[synthetic]
+# CHECK-NEXT:     frame #9: 0x00000001a05a3007{{.*}}[synthetic]
+# CHECK-NEXT:     frame #10: 0x00000001a0b3dcc3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #11: 0x00000001a0b46af3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #12: 0x00000001a09a12a3{{.*}}[synthetic]
+# CHECK-NEXT:     frame #13: 0x00000001047e3ecf asi`main{{.*}}[synthetic]
+# CHECK-NEXT:     frame #14: 0x00000001a05d3e4f{{.*}}[synthetic]
 
 # CHECK:   thread #4294967295: tid = 0x0001, 0x00000001a0a5840c{{.*}}, queue = 
'Application Specific Backtrace'
 # CHECK-NEXT:     frame #0: 0x00000001a0a5840c{{.*}}
diff --git 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test
 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test
index 52a185b8cf760..138cd2bdffc44 100644
--- 
a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test
+++ 
b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test
@@ -15,9 +15,9 @@ process status
 
 thread backtrace
 # CHECK: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT:   * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:     frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
 
 thread list
 # CHECK: Process 22511 stopped
@@ -27,20 +27,20 @@ thread list
 
 bt all
 # CHECK:  thread #1, queue = 'com.apple.main-thread'
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} 
[synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
 # CHECK-NEXT:  thread #2
-# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:    frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x0000000100ec5957 
multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
 # CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[artificial]
-# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT:  * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} 
[synthetic]
+# CHECK-NEXT:    frame #2: 0x0000000100ec5a87 
multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK:    frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
 
 image list
 # CHECK: 11111111-2222-3333-4444-555555555555 {{.*}}bogus.dylib
diff --git a/lldb/unittests/Core/FormatEntityTest.cpp 
b/lldb/unittests/Core/FormatEntityTest.cpp
index e056b6fe7de52..309294a501883 100644
--- a/lldb/unittests/Core/FormatEntityTest.cpp
+++ b/lldb/unittests/Core/FormatEntityTest.cpp
@@ -117,6 +117,7 @@ constexpr llvm::StringRef lookupStrings[] = {
     "${frame.no-debug}",
     "${frame.reg.*}",
     "${frame.is-artificial}",
+    "${frame.kind}",
     "${function.id}",
     "${function.name}",
     "${function.name-without-args}",

>From 5b4e9ce93e78d18488e775b2a112c97fa55f4016 Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ism...@bennani.ma>
Date: Tue, 2 Sep 2025 17:56:26 -0700
Subject: [PATCH 2/2] [lldb] Introduce ScriptedFrame affordance

This patch introduces a new scripting affordance: `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 <ism...@bennani.ma>
---
 lldb/bindings/python/python-wrapper.swig      |   1 +
 .../python/templates/scripted_process.py      | 136 +++++++++++++
 lldb/include/lldb/API/SBSymbolContext.h       |   1 +
 .../Interfaces/ScriptedFrameInterface.h       |  57 ++++++
 .../Interfaces/ScriptedThreadInterface.h      |  10 +
 .../lldb/Interpreter/ScriptInterpreter.h      |   5 +
 lldb/include/lldb/Target/StackFrame.h         |  34 ++--
 lldb/include/lldb/lldb-forward.h              |   3 +
 lldb/source/Core/FormatEntity.cpp             |   2 +-
 .../Plugins/Process/scripted/CMakeLists.txt   |   1 +
 .../Process/scripted/ScriptedFrame.cpp        | 192 ++++++++++++++++++
 .../Plugins/Process/scripted/ScriptedFrame.h  |  66 ++++++
 .../Process/scripted/ScriptedThread.cpp       |  82 +++++++-
 .../Plugins/Process/scripted/ScriptedThread.h |   5 +-
 .../Python/Interfaces/CMakeLists.txt          |   1 +
 .../ScriptInterpreterPythonInterfaces.h       |   1 +
 .../ScriptedFramePythonInterface.cpp          | 157 ++++++++++++++
 .../Interfaces/ScriptedFramePythonInterface.h |  59 ++++++
 .../Interfaces/ScriptedPythonInterface.cpp    |   3 +-
 .../ScriptedThreadPythonInterface.cpp         |  17 ++
 .../ScriptedThreadPythonInterface.h           |   5 +
 .../Python/ScriptInterpreterPython.cpp        |   5 +
 .../Python/ScriptInterpreterPythonImpl.h      |   2 +
 .../dummy_scripted_process.py                 |  61 +++++-
 24 files changed, 874 insertions(+), 32 deletions(-)
 create mode 100644 
lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
 create mode 100644 lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
 create mode 100644 lldb/source/Plugins/Process/scripted/ScriptedFrame.h
 create mode 100644 
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
 create mode 100644 
lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h

diff --git a/lldb/bindings/python/python-wrapper.swig 
b/lldb/bindings/python/python-wrapper.swig
index 2c30d536a753d..c0ad456bc12e8 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -519,6 +519,7 @@ void 
*lldb_private::python::LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyOb
   return sb_ptr;
 }
 
+
 void 
*lldb_private::python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyObject 
*
                                                                     data) {
   lldb::SBExecutionContext *sb_ptr = NULL;
diff --git a/lldb/examples/python/templates/scripted_process.py 
b/lldb/examples/python/templates/scripted_process.py
index b6360b8519077..49059d533f38a 100644
--- a/lldb/examples/python/templates/scripted_process.py
+++ b/lldb/examples/python/templates/scripted_process.py
@@ -383,6 +383,142 @@ def get_extended_info(self):
         """
         return self.extended_info
 
+    def get_scripted_frame_plugin(self):
+        """Get scripted frame plugin name.
+
+        Returns:
+            str: Name of the scripted frame plugin.
+        """
+        return None
+
+
+class ScriptedFrame(metaclass=ABCMeta):
+    """
+    The base class for a scripted frame.
+
+    Most of the base class methods are `@abstractmethod` that need to be
+    overwritten by the inheriting class.
+    """
+
+    @abstractmethod
+    def __init__(self, thread, args):
+        """Construct a scripted frame.
+
+        Args:
+            thread (ScriptedThread): The thread owning this frame.
+            args (lldb.SBStructuredData): A Dictionary holding arbitrary
+                key/value pairs used by the scripted frame.
+        """
+        self.target = None
+        self.originating_thread = None
+        self.thread = None
+        self.args = None
+        self.id = None
+        self.name = None
+        self.register_info = None
+        self.register_ctx = {}
+        self.variables = []
+
+        if (
+            isinstance(thread, ScriptedThread)
+            or isinstance(thread, lldb.SBThread)
+            and thread.IsValid()
+        ):
+            self.target = thread.target
+            self.process = thread.process
+            self.originating_thread = thread
+            self.thread = self.process.GetThreadByIndexID(thread.tid)
+            self.get_register_info()
+
+    @abstractmethod
+    def get_id(self):
+        """Get the scripted frame identifier.
+
+        Returns:
+            int: The identifier of the scripted frame in the scripted thread.
+        """
+        pass
+
+    def get_pc(self):
+        """Get the scripted frame address.
+
+        Returns:
+            int: The optional address of the scripted frame in the scripted 
thread.
+        """
+        return None
+
+    def get_symbol_context(self):
+        """Get the scripted frame symbol context.
+
+        Returns:
+            lldb.SBSymbolContext: The symbol context of the scripted frame in 
the scripted thread.
+        """
+        return None
+
+    def is_inlined(self):
+        """Check if the scripted frame is inlined.
+
+        Returns:
+            bool: True if scripted frame is inlined. False otherwise.
+        """
+        return False
+
+    def is_artificial(self):
+        """Check if the scripted frame is artificial.
+
+        Returns:
+            bool: True if scripted frame is artificial. False otherwise.
+        """
+        return True
+
+    def is_hidden(self):
+        """Check if the scripted frame is hidden.
+
+        Returns:
+            bool: True if scripted frame is hidden. False otherwise.
+        """
+        return False
+
+    def get_function_name(self):
+        """Get the scripted frame function name.
+
+        Returns:
+            str: The function name of the scripted frame.
+        """
+        return self.name
+
+    def get_display_function_name(self):
+        """Get the scripted frame display function name.
+
+        Returns:
+            str: The display function name of the scripted frame.
+        """
+        return self.get_function_name()
+
+    def get_variables(self, filters):
+        """Get the scripted thread state type.
+
+        Args:
+            filter (lldb.SBVariablesOptions): The filter used to resolve the 
variables
+        Returns:
+            lldb.SBValueList: The SBValueList containing the SBValue for each 
resolved variable.
+                              Returns None by default.
+        """
+        return None
+
+    def get_register_info(self):
+        if self.register_info is None:
+            self.register_info = self.originating_thread.get_register_info()
+        return self.register_info
+
+    @abstractmethod
+    def get_register_context(self):
+        """Get the scripted thread register context
+
+        Returns:
+            str: A byte representing all register's value.
+        """
+        pass
 
 class PassthroughScriptedProcess(ScriptedProcess):
     driving_target = None
diff --git a/lldb/include/lldb/API/SBSymbolContext.h 
b/lldb/include/lldb/API/SBSymbolContext.h
index 128b0b65b7860..19f29c629d094 100644
--- a/lldb/include/lldb/API/SBSymbolContext.h
+++ b/lldb/include/lldb/API/SBSymbolContext.h
@@ -66,6 +66,7 @@ class LLDB_API SBSymbolContext {
   friend class SBTarget;
   friend class SBSymbolContextList;
 
+  friend class lldb_private::ScriptInterpreter;
   friend class lldb_private::python::SWIGBridge;
 
   SBSymbolContext(const lldb_private::SymbolContext &sc_ptr);
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h 
b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
new file mode 100644
index 0000000000000..c70009abcfc3e
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
@@ -0,0 +1,57 @@
+//===-- ScriptedFrameInterface.h --------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
+
+#include "ScriptedInterface.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+#include "lldb/lldb-private.h"
+
+#include <optional>
+#include <string>
+
+namespace lldb_private {
+class ScriptedFrameInterface : virtual public ScriptedInterface {
+public:
+  virtual llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
+                     StructuredData::DictionarySP args_sp,
+                     StructuredData::Generic *script_obj = nullptr) = 0;
+
+  virtual lldb::user_id_t GetID() { return LLDB_INVALID_FRAME_ID; }
+
+  virtual lldb::addr_t GetPC() { return LLDB_INVALID_ADDRESS; }
+
+  virtual std::optional<SymbolContext> GetSymbolContext() {
+    return std::nullopt;
+  }
+
+  virtual std::optional<std::string> GetFunctionName() { return std::nullopt; }
+
+  virtual std::optional<std::string> GetDisplayFunctionName() {
+    return std::nullopt;
+  }
+
+  virtual bool IsInlined() { return false; }
+
+  virtual bool IsArtificial() { return true; }
+
+  virtual bool IsHidden() { return false; }
+
+  virtual StructuredData::DictionarySP GetRegisterInfo() { return {}; }
+
+  virtual std::optional<std::string> GetRegisterContext() {
+    return std::nullopt;
+  }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h 
b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
index a7cfc690b67dc..bc58f344d36f8 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
@@ -44,6 +44,16 @@ class ScriptedThreadInterface : virtual public 
ScriptedInterface {
   }
 
   virtual StructuredData::ArraySP GetExtendedInfo() { return {}; }
+
+  virtual std::optional<std::string> GetScriptedFramePluginName() {
+    return std::nullopt;
+  }
+
+protected:
+  friend class ScriptedFrame;
+  virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
+    return {};
+  }
 };
 } // namespace lldb_private
 
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h 
b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index dffb9b82abf3d..024bbc90a9a39 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -26,6 +26,7 @@
 #include "lldb/Host/PseudoTerminal.h"
 #include "lldb/Host/StreamFile.h"
 #include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -531,6 +532,10 @@ class ScriptInterpreter : public PluginInterface {
     return {};
   }
 
+  virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
+    return {};
+  }
+
   virtual lldb::ScriptedThreadPlanInterfaceSP
   CreateScriptedThreadPlanInterface() {
     return {};
diff --git a/lldb/include/lldb/Target/StackFrame.h 
b/lldb/include/lldb/Target/StackFrame.h
index 4ffbf97ce6e90..cdbe8ae3c6779 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -398,7 +398,7 @@ class StackFrame : public ExecutionContextScope,
   ///
   /// \return
   ///   true if this is an inlined frame.
-  bool IsInlined();
+  virtual bool IsInlined();
 
   /// Query whether this frame is synthetic.
   bool IsSynthetic() const;
@@ -409,12 +409,12 @@ class StackFrame : public ExecutionContextScope,
   /// Query whether this frame is artificial (e.g a synthesized result of
   /// inferring missing tail call frames from a backtrace). Artificial frames
   /// may have limited support for inspecting variables.
-  bool IsArtificial() const;
+  virtual bool IsArtificial() const;
 
   /// Query whether this frame should be hidden from backtraces. Frame
   /// recognizers can customize this behavior and hide distracting
   /// system implementation details this way.
-  bool IsHidden();
+  virtual bool IsHidden();
 
   /// Language plugins can use this API to report language-specific
   /// runtime information about this compile unit, such as additional
@@ -425,13 +425,13 @@ class StackFrame : public ExecutionContextScope,
   ///
   ///  /// \return
   ///   A C-String containing the function demangled name. Can be null.
-  const char *GetFunctionName();
+  virtual const char *GetFunctionName();
 
   /// Get the frame's demangled display name.
   ///
   ///  /// \return
   ///   A C-String containing the function demangled display name. Can be null.
-  const char *GetDisplayFunctionName();
+  virtual const char *GetDisplayFunctionName();
 
   /// Query this frame to find what frame it is in this Thread's
   /// StackFrameList.
@@ -543,18 +543,7 @@ class StackFrame : public ExecutionContextScope,
 
   bool HasCachedData() const;
 
-private:
-  /// Private methods, called from GetValueForVariableExpressionPath.
-  /// See that method for documentation of parameters and return value.
-  lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
-      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error);
-
-  lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
-      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
-      uint32_t options, lldb::VariableSP &var_sp, Status &error);
-
-  /// For StackFrame only.
+  /// For StackFrame and derived classes only.
   /// \{
   lldb::ThreadWP m_thread_wp;
   uint32_t m_frame_index;
@@ -591,6 +580,17 @@ class StackFrame : public ExecutionContextScope,
   StreamString m_disassembly;
   std::recursive_mutex m_mutex;
 
+private:
+  /// Private methods, called from GetValueForVariableExpressionPath.
+  /// See that method for documentation of parameters and return value.
+  lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
+      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+      uint32_t options, lldb::VariableSP &var_sp, Status &error);
+
+  lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
+      llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+      uint32_t options, lldb::VariableSP &var_sp, Status &error);
+
   StackFrame(const StackFrame &) = delete;
   const StackFrame &operator=(const StackFrame &) = delete;
 };
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index 483dce98ea427..af5656b3dcad1 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -187,6 +187,7 @@ class SaveCoreOptions;
 class Scalar;
 class ScriptInterpreter;
 class ScriptInterpreterLocker;
+class ScriptedFrameInterface;
 class ScriptedMetadata;
 class ScriptedBreakpointInterface;
 class ScriptedPlatformInterface;
@@ -408,6 +409,8 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame>
 typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
     ScriptSummaryFormatSP;
 typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
+typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
+    ScriptedFrameInterfaceSP;
 typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
 typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
     ScriptedPlatformInterfaceUP;
diff --git a/lldb/source/Core/FormatEntity.cpp 
b/lldb/source/Core/FormatEntity.cpp
index 2ff73979e4976..491f5c6320d97 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -1681,7 +1681,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
       StackFrame *frame = exe_ctx->GetFramePtr();
       if (frame) {
         const Address &pc_addr = frame->GetFrameCodeAddress();
-        if (pc_addr.IsValid()) {
+        if (pc_addr.IsValid() || frame->IsSynthetic()) {
           if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
             return true;
         }
diff --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt 
b/lldb/source/Plugins/Process/scripted/CMakeLists.txt
index 590166591a41e..1516ad3132e3b 100644
--- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_library(lldbPluginScriptedProcess PLUGIN
   ScriptedProcess.cpp
   ScriptedThread.cpp
+  ScriptedFrame.cpp
 
   LINK_COMPONENTS
     BinaryFormat
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp 
b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
new file mode 100644
index 0000000000000..11dd50e1f9124
--- /dev/null
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
@@ -0,0 +1,192 @@
+//===-- ScriptedFrame.cpp 
-------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ScriptedFrame.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void ScriptedFrame::CheckInterpreterAndScriptObject() const {
+  lldbassert(m_script_object_sp && "Invalid Script Object.");
+  lldbassert(GetInterface() && "Invalid Scripted Frame Interface.");
+}
+
+llvm::Expected<std::shared_ptr<ScriptedFrame>>
+ScriptedFrame::Create(ScriptedThread &thread,
+                      StructuredData::DictionarySP args_sp,
+                      StructuredData::Generic *script_object) {
+  if (!thread.IsValid())
+    return llvm::createStringError("Invalid scripted thread.");
+
+  thread.CheckInterpreterAndScriptObject();
+
+  auto scripted_frame_interface =
+      thread.GetInterface()->CreateScriptedFrameInterface();
+  if (!scripted_frame_interface)
+    return llvm::createStringError(
+        "Failed to create scripted frame interface.");
+
+  llvm::StringRef frame_class_name;
+  if (!script_object) {
+    std::optional<std::string> class_name =
+        thread.GetInterface()->GetScriptedFramePluginName();
+    if (!class_name || class_name->empty())
+      return llvm::createStringError(
+          "Failed to get scripted thread class name.");
+    frame_class_name = *class_name;
+  }
+
+  ExecutionContext exe_ctx(thread);
+  auto obj_or_err = scripted_frame_interface->CreatePluginObject(
+      frame_class_name, exe_ctx, args_sp, script_object);
+
+  if (!obj_or_err) {
+    llvm::consumeError(obj_or_err.takeError());
+    return llvm::createStringError("Failed to create script object.");
+  }
+
+  StructuredData::GenericSP owned_script_object_sp = *obj_or_err;
+
+  if (!owned_script_object_sp->IsValid())
+    return llvm::createStringError("Created script object is invalid.");
+
+  lldb::user_id_t frame_id = scripted_frame_interface->GetID();
+
+  lldb::addr_t pc = scripted_frame_interface->GetPC();
+  SymbolContext sc;
+  Address symbol_addr;
+  if (pc != LLDB_INVALID_ADDRESS) {
+    symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget());
+    symbol_addr.CalculateSymbolContext(&sc);
+  }
+
+  std::optional<SymbolContext> maybe_sym_ctx =
+      scripted_frame_interface->GetSymbolContext();
+  if (maybe_sym_ctx) {
+    sc = *maybe_sym_ctx;
+  }
+
+  StructuredData::DictionarySP reg_info =
+      scripted_frame_interface->GetRegisterInfo();
+
+  if (!reg_info)
+    return llvm::createStringError(
+        "Failed to get scripted thread registers info.");
+
+  std::shared_ptr<DynamicRegisterInfo> register_info_sp =
+      DynamicRegisterInfo::Create(
+          *reg_info, thread.GetProcess()->GetTarget().GetArchitecture());
+
+  lldb::RegisterContextSP reg_ctx_sp;
+
+  std::optional<std::string> reg_data =
+      scripted_frame_interface->GetRegisterContext();
+  if (reg_data) {
+    DataBufferSP data_sp(
+        std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size()));
+
+    if (!data_sp->GetByteSize())
+      return llvm::createStringError("Failed to copy raw registers data");
+
+    std::shared_ptr<RegisterContextMemory> reg_ctx_memory =
+        std::make_shared<RegisterContextMemory>(
+            thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS);
+    if (!reg_ctx_memory)
+      return llvm::createStringError("Failed to create a register context.");
+
+    reg_ctx_memory->SetAllRegisterData(data_sp);
+    reg_ctx_sp = reg_ctx_memory;
+  }
+
+  return std::make_shared<ScriptedFrame>(
+      thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp,
+      register_info_sp, owned_script_object_sp);
+}
+
+ScriptedFrame::ScriptedFrame(ScriptedThread &thread,
+                             ScriptedFrameInterfaceSP interface_sp,
+                             lldb::user_id_t id, lldb::addr_t pc,
+                             SymbolContext &sym_ctx,
+                             lldb::RegisterContextSP reg_ctx_sp,
+                             std::shared_ptr<DynamicRegisterInfo> reg_info_sp,
+                             StructuredData::GenericSP script_object_sp)
+    : StackFrame(thread.shared_from_this(), /*frame_idx=*/id,
+                 /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp,
+                 /*cfa=*/0, /*pc=*/pc,
+                 /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx),
+      m_scripted_thread(thread), m_scripted_frame_interface_sp(interface_sp),
+      m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {}
+
+ScriptedFrame::~ScriptedFrame() {}
+
+const char *ScriptedFrame::GetFunctionName() {
+  CheckInterpreterAndScriptObject();
+  std::optional<std::string> function_name = GetInterface()->GetFunctionName();
+  if (!function_name)
+    return nullptr;
+  return ConstString(function_name->c_str()).AsCString();
+}
+
+const char *ScriptedFrame::GetDisplayFunctionName() {
+  CheckInterpreterAndScriptObject();
+  std::optional<std::string> function_name =
+      GetInterface()->GetDisplayFunctionName();
+  if (!function_name)
+    return nullptr;
+  return ConstString(function_name->c_str()).AsCString();
+}
+
+bool ScriptedFrame::IsInlined() { return GetInterface()->IsInlined(); }
+
+bool ScriptedFrame::IsArtificial() const {
+  return GetInterface()->IsArtificial();
+}
+
+bool ScriptedFrame::IsHidden() { return GetInterface()->IsHidden(); }
+
+lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const {
+  return m_scripted_frame_interface_sp;
+}
+
+std::shared_ptr<DynamicRegisterInfo> ScriptedFrame::GetDynamicRegisterInfo() {
+  CheckInterpreterAndScriptObject();
+
+  if (!m_register_info_sp) {
+    StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo();
+
+    Status error;
+    if (!reg_info)
+      return ScriptedInterface::ErrorWithMessage<
+          std::shared_ptr<DynamicRegisterInfo>>(
+          LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.",
+          error, LLDBLog::Thread);
+
+    ThreadSP thread_sp = m_thread_wp.lock();
+    if (!thread_sp || !thread_sp->IsValid())
+      return ScriptedInterface::ErrorWithMessage<
+          std::shared_ptr<DynamicRegisterInfo>>(
+          LLVM_PRETTY_FUNCTION,
+          "Failed to get scripted frame registers info: invalid thread.", 
error,
+          LLDBLog::Thread);
+
+    ProcessSP process_sp = thread_sp->GetProcess();
+    if (!process_sp || !process_sp->IsValid())
+      return ScriptedInterface::ErrorWithMessage<
+          std::shared_ptr<DynamicRegisterInfo>>(
+          LLVM_PRETTY_FUNCTION,
+          "Failed to get scripted frame registers info: invalid process.",
+          error, LLDBLog::Thread);
+
+    m_register_info_sp = DynamicRegisterInfo::Create(
+        *reg_info, process_sp->GetTarget().GetArchitecture());
+  }
+
+  return m_register_info_sp;
+}
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h 
b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
new file mode 100644
index 0000000000000..639bf6964cb6c
--- /dev/null
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
@@ -0,0 +1,66 @@
+//===-- ScriptedFrame.h -----------------------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
+#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
+
+#include <string>
+
+#include "ScriptedThread.h"
+
+#include "Plugins/Process/Utility/RegisterContextMemory.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
+#include "lldb/Target/StackFrame.h"
+
+namespace lldb_private {
+class ScriptedThread;
+}
+
+namespace lldb_private {
+
+class ScriptedFrame : public lldb_private::StackFrame {
+
+public:
+  ScriptedFrame(ScriptedThread &thread,
+                lldb::ScriptedFrameInterfaceSP interface_sp,
+                lldb::user_id_t frame_idx, lldb::addr_t pc,
+                SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp,
+                std::shared_ptr<DynamicRegisterInfo> reg_info_sp,
+                StructuredData::GenericSP script_object_sp = nullptr);
+
+  ~ScriptedFrame() override;
+
+  static llvm::Expected<std::shared_ptr<ScriptedFrame>>
+  Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp,
+         StructuredData::Generic *script_object = nullptr);
+
+  bool IsInlined() override;
+  bool IsArtificial() const override;
+  bool IsHidden() override;
+  const char *GetFunctionName() override;
+  const char *GetDisplayFunctionName() override;
+
+private:
+  void CheckInterpreterAndScriptObject() const;
+  lldb::ScriptedFrameInterfaceSP GetInterface() const;
+
+  ScriptedFrame(const ScriptedFrame &) = delete;
+  const ScriptedFrame &operator=(const ScriptedFrame &) = delete;
+
+  std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo();
+
+  const ScriptedThread &m_scripted_thread;
+  lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp = nullptr;
+  lldb_private::StructuredData::GenericSP m_script_object_sp = nullptr;
+  std::shared_ptr<DynamicRegisterInfo> m_register_info_sp = nullptr;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp 
b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index 45ad83e4ed671..491efac5aadef 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "ScriptedThread.h"
+#include "ScriptedFrame.h"
 
 #include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
 #include "Plugins/Process/Utility/StopInfoMachException.h"
@@ -173,27 +174,31 @@ bool ScriptedThread::LoadArtificialStackFrames() {
             .str(),
         error, LLDBLog::Thread);
 
-  StackFrameListSP frames = GetStackFrameList();
-
-  for (size_t idx = 0; idx < arr_size; idx++) {
+  auto create_frame_from_dict =
+      [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> {
+    Status error;
     std::optional<StructuredData::Dictionary *> maybe_dict =
         arr_sp->GetItemAtIndexAsDictionary(idx);
-    if (!maybe_dict)
-      return ScriptedInterface::ErrorWithMessage<bool>(
+    if (!maybe_dict) {
+      ScriptedInterface::ErrorWithMessage<bool>(
           LLVM_PRETTY_FUNCTION,
           llvm::Twine(
               "Couldn't get artificial stackframe dictionary at index (" +
               llvm::Twine(idx) + llvm::Twine(") from stackframe array."))
               .str(),
           error, LLDBLog::Thread);
+      return error.ToError();
+    }
     StructuredData::Dictionary *dict = *maybe_dict;
 
     lldb::addr_t pc;
-    if (!dict->GetValueForKeyAsInteger("pc", pc))
-      return ScriptedInterface::ErrorWithMessage<bool>(
+    if (!dict->GetValueForKeyAsInteger("pc", pc)) {
+      ScriptedInterface::ErrorWithMessage<bool>(
           LLVM_PRETTY_FUNCTION,
           "Couldn't find value for key 'pc' in stackframe dictionary.", error,
           LLDBLog::Thread);
+      return error.ToError();
+    }
 
     Address symbol_addr;
     symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget());
@@ -205,10 +210,65 @@ bool ScriptedThread::LoadArtificialStackFrames() {
     SymbolContext sc;
     symbol_addr.CalculateSymbolContext(&sc);
 
-    StackFrameSP synth_frame_sp = std::make_shared<StackFrame>(
-        this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
-        StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame,
-        &sc);
+    return std::make_shared<StackFrame>(this->shared_from_this(), idx, idx, 
cfa,
+                                        cfa_is_valid, pc,
+                                        StackFrame::Kind::Synthetic, 
artificial,
+                                        behaves_like_zeroth_frame, &sc);
+  };
+
+  auto create_frame_from_script_object =
+      [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> {
+    Status error;
+    StructuredData::ObjectSP object_sp = arr_sp->GetItemAtIndex(idx);
+    if (!object_sp || !object_sp->GetAsGeneric()) {
+      ScriptedInterface::ErrorWithMessage<bool>(
+          LLVM_PRETTY_FUNCTION,
+          llvm::Twine("Couldn't get artificial stackframe object at index (" +
+                      llvm::Twine(idx) +
+                      llvm::Twine(") from stackframe array."))
+              .str(),
+          error, LLDBLog::Thread);
+      return error.ToError();
+    }
+
+    auto frame_or_error =
+        ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric());
+
+    if (!frame_or_error) {
+      ScriptedInterface::ErrorWithMessage<bool>(
+          LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error);
+      return error.ToError();
+    }
+
+    StackFrameSP frame_sp = frame_or_error.get();
+    lldbassert(frame_sp && "Couldn't initialize scripted frame.");
+
+    return frame_sp;
+  };
+
+  StackFrameListSP frames = GetStackFrameList();
+
+  for (size_t idx = 0; idx < arr_size; idx++) {
+    StackFrameSP synth_frame_sp = nullptr;
+
+    auto frame_from_dict_or_err = create_frame_from_dict(idx);
+    if (!frame_from_dict_or_err) {
+      auto frame_from_script_obj_or_err = create_frame_from_script_object(idx);
+
+      if (!frame_from_script_obj_or_err) {
+        return ScriptedInterface::ErrorWithMessage<bool>(
+            LLVM_PRETTY_FUNCTION,
+            llvm::Twine("Couldn't add artificial frame (" + llvm::Twine(idx) +
+                        llvm::Twine(") to ScriptedThread StackFrameList."))
+                .str(),
+            error, LLDBLog::Thread);
+      } else {
+        llvm::consumeError(frame_from_dict_or_err.takeError());
+        synth_frame_sp = *frame_from_script_obj_or_err;
+      }
+    } else {
+      synth_frame_sp = *frame_from_dict_or_err;
+    }
 
     if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp))
       return ScriptedInterface::ErrorWithMessage<bool>(
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h 
b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
index cd224d60ceef8..ee5ace4059673 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
@@ -15,11 +15,12 @@
 
 #include "Plugins/Process/Utility/RegisterContextMemory.h"
 #include "lldb/Interpreter/ScriptInterpreter.h"
-#include "lldb/Target//DynamicRegisterInfo.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
 #include "lldb/Target/Thread.h"
 
 namespace lldb_private {
 class ScriptedProcess;
+class ScriptedFrame;
 }
 
 namespace lldb_private {
@@ -61,6 +62,8 @@ class ScriptedThread : public lldb_private::Thread {
   StructuredData::ObjectSP FetchThreadExtendedInfo() override;
 
 private:
+  friend class ScriptedFrame;
+
   void CheckInterpreterAndScriptObject() const;
   lldb::ScriptedThreadInterfaceSP GetInterface() const;
 
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
index 04370940423ae..09103573b89c5 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
@@ -22,6 +22,7 @@ endif()
 add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
   OperatingSystemPythonInterface.cpp
   ScriptInterpreterPythonInterfaces.cpp
+  ScriptedFramePythonInterface.cpp
   ScriptedPlatformPythonInterface.cpp
   ScriptedProcessPythonInterface.cpp
   ScriptedPythonInterface.cpp
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
index 02dc06507caf2..3814f46615078 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -17,6 +17,7 @@
 
 #include "OperatingSystemPythonInterface.h"
 #include "ScriptedBreakpointPythonInterface.h"
+#include "ScriptedFramePythonInterface.h"
 #include "ScriptedPlatformPythonInterface.h"
 #include "ScriptedProcessPythonInterface.h"
 #include "ScriptedStopHookPythonInterface.h"
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
new file mode 100644
index 0000000000000..a6ae2d3206465
--- /dev/null
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
@@ -0,0 +1,157 @@
+//===-- ScriptedFramePythonInterface.cpp 
----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/lldb-enumerations.h"
+
+#if LLDB_ENABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "../lldb-python.h"
+
+#include "../SWIGPythonBridge.h"
+#include "../ScriptInterpreterPythonImpl.h"
+#include "ScriptedFramePythonInterface.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+ScriptedFramePythonInterface::ScriptedFramePythonInterface(
+    ScriptInterpreterPythonImpl &interpreter)
+    : ScriptedFrameInterface(), ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+ScriptedFramePythonInterface::CreatePluginObject(
+    const llvm::StringRef class_name, ExecutionContext &exe_ctx,
+    StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) 
{
+  ExecutionContextRefSP exe_ctx_ref_sp =
+      std::make_shared<ExecutionContextRef>(exe_ctx);
+  StructuredDataImpl sd_impl(args_sp);
+  return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj,
+                                                     exe_ctx_ref_sp, sd_impl);
+}
+
+lldb::user_id_t ScriptedFramePythonInterface::GetID() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_id", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return LLDB_INVALID_FRAME_ID;
+
+  return obj->GetUnsignedIntegerValue(LLDB_INVALID_FRAME_ID);
+}
+
+lldb::addr_t ScriptedFramePythonInterface::GetPC() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_pc", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return LLDB_INVALID_ADDRESS;
+
+  return obj->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS);
+}
+
+std::optional<SymbolContext> ScriptedFramePythonInterface::GetSymbolContext() {
+  Status error;
+  auto sym_ctx = Dispatch<SymbolContext>("get_symbol_context", error);
+
+  if (error.Fail()) {
+    return ErrorWithMessage<SymbolContext>(LLVM_PRETTY_FUNCTION,
+                                           error.AsCString(), error);
+  }
+
+  return sym_ctx;
+}
+
+std::optional<std::string> ScriptedFramePythonInterface::GetFunctionName() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_function_name", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetStringValue().str();
+}
+
+std::optional<std::string>
+ScriptedFramePythonInterface::GetDisplayFunctionName() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_display_function_name", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetStringValue().str();
+}
+
+bool ScriptedFramePythonInterface::IsInlined() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("is_inlined", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return false;
+
+  return obj->GetBooleanValue();
+}
+
+bool ScriptedFramePythonInterface::IsArtificial() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("is_artificial", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return false;
+
+  return obj->GetBooleanValue();
+}
+
+bool ScriptedFramePythonInterface::IsHidden() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("is_hidden", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return false;
+
+  return obj->GetBooleanValue();
+}
+
+StructuredData::DictionarySP ScriptedFramePythonInterface::GetRegisterInfo() {
+  Status error;
+  StructuredData::DictionarySP dict =
+      Dispatch<StructuredData::DictionarySP>("get_register_info", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
+                                                    error))
+    return {};
+
+  return dict;
+}
+
+std::optional<std::string> ScriptedFramePythonInterface::GetRegisterContext() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_register_context", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetAsString()->GetValue().str();
+}
+
+#endif
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
new file mode 100644
index 0000000000000..6c15b97307989
--- /dev/null
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
@@ -0,0 +1,59 @@
+//===-- ScriptedFramePythonInterface.h --------------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef 
LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
+#define 
LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
+
+#include "lldb/Host/Config.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "ScriptedPythonInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
+#include <optional>
+
+namespace lldb_private {
+class ScriptedFramePythonInterface : public ScriptedFrameInterface,
+                                     public ScriptedPythonInterface {
+public:
+  ScriptedFramePythonInterface(ScriptInterpreterPythonImpl &interpreter);
+
+  llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
+                     StructuredData::DictionarySP args_sp,
+                     StructuredData::Generic *script_obj = nullptr) override;
+
+  llvm::SmallVector<AbstractMethodRequirement>
+  GetAbstractMethodRequirements() const override {
+    return llvm::SmallVector<AbstractMethodRequirement>({{"get_id"}});
+  }
+
+  lldb::user_id_t GetID() override;
+
+  lldb::addr_t GetPC() override;
+
+  std::optional<SymbolContext> GetSymbolContext() override;
+
+  std::optional<std::string> GetFunctionName() override;
+
+  std::optional<std::string> GetDisplayFunctionName() override;
+
+  bool IsInlined() override;
+
+  bool IsArtificial() override;
+
+  bool IsHidden() override;
+
+  StructuredData::DictionarySP GetRegisterInfo() override;
+
+  std::optional<std::string> GetRegisterContext() override;
+};
+} // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
+#endif // 
LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index b49d1d82fe535..8083ccae04026 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -167,7 +167,8 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
 
   if (!sb_mem_reg_info) {
     error = Status::FromErrorStringWithFormat(
-        "Couldn't cast lldb::SBMemoryRegionInfo to lldb::MemoryRegionInfoSP.");
+        "Couldn't cast lldb::SBMemoryRegionInfo to "
+        "lldb_private::MemoryRegionInfo.");
     return {};
   }
 
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
index 8af89d761764b..fd4d231a747fe 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
@@ -144,4 +144,21 @@ StructuredData::ArraySP 
ScriptedThreadPythonInterface::GetExtendedInfo() {
   return arr;
 }
 
+std::optional<std::string>
+ScriptedThreadPythonInterface::GetScriptedFramePluginName() {
+  Status error;
+  StructuredData::ObjectSP obj = Dispatch("get_scripted_frame_plugin", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+                                                    error))
+    return {};
+
+  return obj->GetStringValue().str();
+}
+
+lldb::ScriptedFrameInterfaceSP
+ScriptedThreadPythonInterface::CreateScriptedFrameInterface() {
+  return m_interpreter.CreateScriptedFrameInterface();
+}
+
 #endif
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
index 1fb23b39c7076..043557a827461 100644
--- 
a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
+++ 
b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
@@ -51,6 +51,11 @@ class ScriptedThreadPythonInterface : public 
ScriptedThreadInterface,
   std::optional<std::string> GetRegisterContext() override;
 
   StructuredData::ArraySP GetExtendedInfo() override;
+
+  std::optional<std::string> GetScriptedFramePluginName() override;
+
+protected:
+  lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
 };
 } // namespace lldb_private
 
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp 
b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 9330a634489a2..73c5c72932ff1 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1521,6 +1521,11 @@ 
ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() {
   return std::make_shared<ScriptedThreadPythonInterface>(*this);
 }
 
+ScriptedFrameInterfaceSP
+ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() {
+  return std::make_shared<ScriptedFramePythonInterface>(*this);
+}
+
 ScriptedThreadPlanInterfaceSP
 ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() {
   return std::make_shared<ScriptedThreadPlanPythonInterface>(*this);
diff --git 
a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h 
b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 83b64b85faebd..dedac280788f4 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -99,6 +99,8 @@ class ScriptInterpreterPythonImpl : public 
ScriptInterpreterPython {
 
   lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
 
+  lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
+
   lldb::ScriptedThreadPlanInterfaceSP
   CreateScriptedThreadPlanInterface() override;
 
diff --git 
a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py 
b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
index 2721d961bcb9d..2f23179c20893 100644
--- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
+++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
@@ -5,6 +5,7 @@
 import lldb
 from lldb.plugins.scripted_process import ScriptedProcess
 from lldb.plugins.scripted_process import ScriptedThread
+from lldb.plugins.scripted_process import ScriptedFrame
 
 
 class DummyStopHook:
@@ -22,7 +23,7 @@ class DummyScriptedProcess(ScriptedProcess):
 
     def __init__(self, exe_ctx: lldb.SBExecutionContext, args: 
lldb.SBStructuredData):
         super().__init__(exe_ctx, args)
-        self.threads[0] = DummyScriptedThread(self, None)
+        self.threads[0] = DummyScriptedThread(self, args)
         self.memory = {}
         addr = 0x500000000
         debugger = self.target.GetDebugger()
@@ -69,6 +70,9 @@ class DummyScriptedThread(ScriptedThread):
     def __init__(self, process, args):
         super().__init__(process, args)
         self.frames.append({"pc": 0x0100001B00})
+        self.frames.append(DummyScriptedFrame(self, args, len(self.frames), 
"baz123"))
+        self.frames.append(DummyScriptedFrame(self, args, len(self.frames), 
"bar"))
+        self.frames.append(DummyScriptedFrame(self, args, len(self.frames), 
"foo"))
 
     def get_thread_id(self) -> int:
         return 0x19
@@ -109,6 +113,61 @@ def get_register_context(self) -> str:
         )
 
 
+class DummyScriptedFrame(ScriptedFrame):
+    def __init__(self, thread, args, id, name):
+        super().__init__(thread, args)
+        self.id = id
+        self.name = name
+
+    def get_id(self):
+        return self.id
+
+    def get_function_name(self):
+        return self.name
+
+    def get_register_context(self) -> str:
+        return struct.pack(
+            "21Q",
+            0x10001,
+            0x10002,
+            0x10003,
+            0x10004,
+            0x10005,
+            0x10006,
+            0x10007,
+            0x10008,
+            0x10009,
+            0x100010,
+            0x100011,
+            0x100012,
+            0x100013,
+            0x100014,
+            0x100015,
+            0x100016,
+            0x100017,
+            0x100018,
+            0x100019,
+            0x100020,
+            0x100021,
+        )
+
+    def get_symbol_context(self):
+        def get_symbol_context_for_function(func_name):
+            module = self.target.FindModule(self.target.GetExecutable())
+            if not module.IsValid():
+                return None
+
+            sym_ctx_list = module.FindFunctions(func_name)
+            if not sym_ctx_list.IsValid() or sym_ctx_list.GetSize() == 0:
+                return None
+
+            return sym_ctx_list.GetContextAtIndex(0)
+
+        return get_symbol_context_for_function(self.name)
+
+    def get_scripted_frame_plugin(self):
+        return DummyScriptedFrame.__module__ + "." + 
DummyScriptedFrame.__name__
+
 def __lldb_init_module(debugger, dict):
     # This is used when loading the script in an interactive debug session to
     # automatically, register the stop-hook and launch the scripted process.

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to