[Lldb-commits] [lldb] r343900 - Add support for artificial tail call frames

Vedant Kumar via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 5 16:23:15 PDT 2018


Author: vedantk
Date: Fri Oct  5 16:23:15 2018
New Revision: 343900

URL: http://llvm.org/viewvc/llvm-project?rev=343900&view=rev
Log:
Add support for artificial tail call frames

This patch teaches lldb to detect when there are missing frames in a
backtrace due to a sequence of tail calls, and to fill in the backtrace
with artificial tail call frames when this happens. This is only done
when the execution history can be determined from the call graph and
from the return PC addresses of calls on the stack. Ambiguous sequences
of tail calls (e.g anything involving tail calls and recursion) are
detected and ignored.

Depends on D49887.

Differential Revision: https://reviews.llvm.org/D50478

Added:
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/TestAmbiguousTailCallSeq1.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/TestAmbiguousTailCallSeq2.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/TestDisambiguateCallSite.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/TestDisambiguatePathsToCommonSink.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/TestDisambiguateTailCallSeq.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/TestInliningAndTailCalls.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/TestTailCallFrameSBAPI.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/TestArtificialFrameStepOutMessage.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/TestSteppingOutWithArtificialFrames.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/TestUnambiguousTailCalls.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/main.cpp
Modified:
    lldb/trunk/include/lldb/API/SBFrame.h
    lldb/trunk/include/lldb/Core/FormatEntity.h
    lldb/trunk/include/lldb/Symbol/Block.h
    lldb/trunk/include/lldb/Symbol/Function.h
    lldb/trunk/include/lldb/Symbol/SymbolFile.h
    lldb/trunk/include/lldb/Target/StackFrame.h
    lldb/trunk/include/lldb/Target/StackFrameList.h
    lldb/trunk/include/lldb/Target/ThreadPlanStepOut.h
    lldb/trunk/packages/Python/lldbsuite/test/decorators.py
    lldb/trunk/scripts/interface/SBFrame.i
    lldb/trunk/source/API/SBFrame.cpp
    lldb/trunk/source/Core/Debugger.cpp
    lldb/trunk/source/Core/FormatEntity.cpp
    lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
    lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
    lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
    lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
    lldb/trunk/source/Symbol/Block.cpp
    lldb/trunk/source/Symbol/Function.cpp
    lldb/trunk/source/Target/StackFrame.cpp
    lldb/trunk/source/Target/StackFrameList.cpp
    lldb/trunk/source/Target/ThreadPlanStepOut.cpp

Modified: lldb/trunk/include/lldb/API/SBFrame.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBFrame.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/API/SBFrame.h (original)
+++ lldb/trunk/include/lldb/API/SBFrame.h Fri Oct  5 16:23:15 2018
@@ -90,6 +90,10 @@ public:
 
   bool IsInlined() const;
 
+  bool IsArtificial();
+
+  bool IsArtificial() const;
+
   /// The version that doesn't supply a 'use_dynamic' value will use the
   /// target's default.
   lldb::SBValue EvaluateExpression(const char *expr);

Modified: lldb/trunk/include/lldb/Core/FormatEntity.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/FormatEntity.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/FormatEntity.h (original)
+++ lldb/trunk/include/lldb/Core/FormatEntity.h Fri Oct  5 16:23:15 2018
@@ -88,6 +88,7 @@ public:
       FrameRegisterFP,
       FrameRegisterFlags,
       FrameRegisterByName,
+      FrameIsArtificial,
       ScriptFrame,
       FunctionID,
       FunctionDidChange,

Modified: lldb/trunk/include/lldb/Symbol/Block.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/Block.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/Block.h (original)
+++ lldb/trunk/include/lldb/Symbol/Block.h Fri Oct  5 16:23:15 2018
@@ -327,6 +327,14 @@ public:
     return m_inlineInfoSP.get();
   }
 
+  //------------------------------------------------------------------
+  /// Get the symbol file which contains debug info for this block's
+  /// symbol context module.
+  ///
+  /// @return A pointer to the symbol file or nullptr.
+  //------------------------------------------------------------------
+  SymbolFile *GetSymbolFile();
+
   CompilerDeclContext GetDeclContext();
 
   //------------------------------------------------------------------

Modified: lldb/trunk/include/lldb/Symbol/Function.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/Function.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/Function.h (original)
+++ lldb/trunk/include/lldb/Symbol/Function.h Fri Oct  5 16:23:15 2018
@@ -16,6 +16,7 @@
 #include "lldb/Symbol/Block.h"
 #include "lldb/Symbol/Declaration.h"
 #include "lldb/Utility/UserID.h"
+#include "llvm/ADT/ArrayRef.h"
 
 namespace lldb_private {
 
@@ -290,6 +291,62 @@ private:
   Declaration m_call_decl;
 };
 
+class Function;
+
+//----------------------------------------------------------------------
+/// @class CallEdge Function.h "lldb/Symbol/Function.h"
+///
+/// Represent a call made within a Function. This can be used to find a path
+/// in the call graph between two functions.
+//----------------------------------------------------------------------
+class CallEdge {
+public:
+  /// Construct a call edge using a symbol name to identify the calling
+  /// function, and a return PC within the calling function to identify a
+  /// specific call site.
+  ///
+  /// TODO: A symbol name may not be globally unique. To disambiguate ODR
+  /// conflicts, it's necessary to determine the \c Target a call edge is
+  /// associated with before resolving it.
+  CallEdge(const char *symbol_name, lldb::addr_t return_pc);
+
+  CallEdge(CallEdge &&) = default;
+  CallEdge &operator=(CallEdge &&) = default;
+
+  /// Get the callee's definition.
+  ///
+  /// Note that this might lazily invoke the DWARF parser.
+  Function *GetCallee(ModuleList &images);
+
+  /// Get the load PC address of the instruction which executes after the call
+  /// returns. Returns LLDB_INVALID_ADDRESS iff this is a tail call. \p caller
+  /// is the Function containing this call, and \p target is the Target which
+  /// made the call.
+  lldb::addr_t GetReturnPCAddress(Function &caller, Target &target) const;
+
+  /// Like \ref GetReturnPCAddress, but returns an unresolved file address.
+  lldb::addr_t GetUnresolvedReturnPCAddress() const { return return_pc; }
+
+private:
+  void ParseSymbolFileAndResolve(ModuleList &images);
+
+  /// Either the callee's mangled name or its definition, discriminated by
+  /// \ref resolved.
+  union {
+    const char *symbol_name;
+    Function *def;
+  } lazy_callee;
+
+  /// An invalid address if this is a tail call. Otherwise, the return PC for
+  /// the call. Note that this is a file address which must be resolved.
+  lldb::addr_t return_pc;
+
+  /// Whether or not an attempt was made to find the callee's definition.
+  bool resolved;
+
+  DISALLOW_COPY_AND_ASSIGN(CallEdge);
+};
+
 //----------------------------------------------------------------------
 /// @class Function Function.h "lldb/Symbol/Function.h"
 /// A class that describes a function.
@@ -397,6 +454,18 @@ public:
   void GetEndLineSourceInfo(FileSpec &source_file, uint32_t &line_no);
 
   //------------------------------------------------------------------
+  /// Get the outgoing call edges from this function, sorted by their return
+  /// PC addresses (in increasing order).
+  //------------------------------------------------------------------
+  llvm::MutableArrayRef<CallEdge> GetCallEdges();
+
+  //------------------------------------------------------------------
+  /// Get the outgoing tail-calling edges from this function. If none exist,
+  /// return None.
+  //------------------------------------------------------------------
+  llvm::MutableArrayRef<CallEdge> GetTailCallingEdges();
+
+  //------------------------------------------------------------------
   /// Get accessor for the block list.
   ///
   /// @return
@@ -587,6 +656,10 @@ protected:
   Flags m_flags;
   uint32_t
       m_prologue_byte_size; ///< Compute the prologue size once and cache it
+
+  bool m_call_edges_resolved = false; ///< Whether call site info has been
+                                      ///  parsed.
+  std::vector<CallEdge> m_call_edges; ///< Outgoing call edges.
 private:
   DISALLOW_COPY_AND_ASSIGN(Function);
 };

Modified: lldb/trunk/include/lldb/Symbol/SymbolFile.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/SymbolFile.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/SymbolFile.h (original)
+++ lldb/trunk/include/lldb/Symbol/SymbolFile.h Fri Oct  5 16:23:15 2018
@@ -14,6 +14,7 @@
 #include "lldb/Symbol/CompilerDecl.h"
 #include "lldb/Symbol/CompilerDeclContext.h"
 #include "lldb/Symbol/CompilerType.h"
+#include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/Type.h"
 #include "lldb/lldb-private.h"
 
@@ -194,6 +195,10 @@ public:
   ObjectFile *GetObjectFile() { return m_obj_file; }
   const ObjectFile *GetObjectFile() const { return m_obj_file; }
 
+  virtual std::vector<CallEdge> ParseCallEdgesInFunction(UserID func_id) {
+    return {};
+  }
+
   //------------------------------------------------------------------
   /// Notify the SymbolFile that the file addresses in the Sections
   /// for this module have been changed.

Modified: lldb/trunk/include/lldb/Target/StackFrame.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/StackFrame.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/StackFrame.h (original)
+++ lldb/trunk/include/lldb/Target/StackFrame.h Fri Oct  5 16:23:15 2018
@@ -35,9 +35,9 @@ namespace lldb_private {
 /// This base class provides an interface to stack frames.
 ///
 /// StackFrames may have a Canonical Frame Address (CFA) or not.
-/// A frame may have a plain pc value or it may have a pc value + stop_id
-/// to indicate a specific point in the debug session so the correct section
-/// load list is used for symbolication.
+/// A frame may have a plain pc value or it may  indicate a specific point in
+/// the debug session so the correct section load list is used for
+/// symbolication.
 ///
 /// Local variables may be available, or not.  A register context may be
 /// available, or not.
@@ -54,14 +54,27 @@ public:
     eExpressionPathOptionsInspectAnonymousUnions = (1u << 5)
   };
 
+  enum class Kind {
+    /// A regular stack frame with access to registers and local variables.
+    Regular,
+
+    /// A historical stack frame -- possibly without CFA or registers or
+    /// 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
+  };
+
   //------------------------------------------------------------------
   /// Construct a StackFrame object without supplying a RegisterContextSP.
   ///
   /// This is the one constructor that doesn't take a RegisterContext
   /// parameter.  This ctor may be called when creating a history StackFrame;
   /// these are used if we've collected a stack trace of pc addresses at some
-  /// point in the past.  We may only have pc values.  We may have pc values
-  /// and the stop_id when the stack trace was recorded.  We may have a CFA,
+  /// point in the past.  We may only have pc values. We may have a CFA,
   /// or more likely, we won't.
   ///
   /// @param [in] thread_sp
@@ -92,23 +105,7 @@ public:
   /// @param [in] pc
   ///   The current pc value of this stack frame.
   ///
-  /// @param [in] stop_id
-  ///   The stop_id which should be used when looking up symbols for the pc
-  ///   value,
-  ///   if appropriate.  This argument is ignored if stop_id_is_valid is false.
-  ///
-  /// @param [in] stop_id_is_valid
-  ///   If the stop_id argument provided is not needed for this StackFrame, this
-  ///   should be false.  If this is a history stack frame and we know the
-  ///   stop_id
-  ///   when the pc value was collected, that stop_id should be provided and
-  ///   this
-  ///   will be true.
-  ///
-  /// @param [in] is_history_frame
-  ///   If this is a historical stack frame -- possibly without CFA or registers
-  ///   or
-  ///   local variables -- then this should be set to true.
+  /// @param [in] frame_kind
   ///
   /// @param [in] sc_ptr
   ///   Optionally seed the StackFrame with the SymbolContext information that
@@ -117,8 +114,7 @@ public:
   //------------------------------------------------------------------
   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, uint32_t stop_id,
-             bool stop_id_is_valid, bool is_history_frame,
+             bool cfa_is_valid, lldb::addr_t pc, Kind frame_kind,
              const SymbolContext *sc_ptr);
 
   StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx,
@@ -403,6 +399,18 @@ public:
   bool IsInlined();
 
   //------------------------------------------------------------------
+  /// Query whether this frame is part of a historical backtrace.
+  //------------------------------------------------------------------
+  bool IsHistorical() const;
+
+  //------------------------------------------------------------------
+  /// 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;
+
+  //------------------------------------------------------------------
   /// Query this frame to find what frame it is in this Thread's
   /// StackFrameList.
   ///
@@ -413,6 +421,11 @@ public:
   uint32_t GetFrameIndex() const;
 
   //------------------------------------------------------------------
+  /// Set this frame's synthetic frame index.
+  //------------------------------------------------------------------
+  void SetFrameIndex(uint32_t index) { m_frame_index = index; }
+
+  //------------------------------------------------------------------
   /// Query this frame to find what frame it is in this Thread's
   /// StackFrameList, not counting inlined frames.
   ///
@@ -560,10 +573,7 @@ private:
   Status m_frame_base_error;
   bool m_cfa_is_valid; // Does this frame have a CFA?  Different from CFA ==
                        // LLDB_INVALID_ADDRESS
-  uint32_t m_stop_id;
-  bool m_stop_id_is_valid; // Does this frame have a stop_id?  Use it when
-                           // referring to the m_frame_code_addr.
-  bool m_is_history_frame;
+  Kind m_stack_frame_kind;
   lldb::VariableListSP m_variable_list_sp;
   ValueObjectList m_variable_list_value_objects; // Value objects for each
                                                  // variable in

Modified: lldb/trunk/include/lldb/Target/StackFrameList.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/StackFrameList.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/StackFrameList.h (original)
+++ lldb/trunk/include/lldb/Target/StackFrameList.h Fri Oct  5 16:23:15 2018
@@ -99,6 +99,8 @@ protected:
 
   void GetOnlyConcreteFramesUpTo(uint32_t end_idx, Unwind *unwinder);
 
+  void SynthesizeTailCallFrames(StackFrame &next_frame);
+
   bool GetAllFramesFetched() { return m_concrete_frames_fetched == UINT32_MAX; }
 
   void SetAllFramesFetched() { m_concrete_frames_fetched = UINT32_MAX; }

Modified: lldb/trunk/include/lldb/Target/ThreadPlanStepOut.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/ThreadPlanStepOut.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/ThreadPlanStepOut.h (original)
+++ lldb/trunk/include/lldb/Target/ThreadPlanStepOut.h Fri Oct  5 16:23:15 2018
@@ -74,6 +74,7 @@ private:
                                                  // if ShouldStopHere told us
                                                  // to.
   Function *m_immediate_step_from_function;
+  std::vector<lldb::StackFrameSP> m_stepped_past_frames;
   lldb::ValueObjectSP m_return_valobj_sp;
   bool m_calculate_return_value;
 

Modified: lldb/trunk/packages/Python/lldbsuite/test/decorators.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/decorators.py?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/decorators.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/decorators.py Fri Oct  5 16:23:15 2018
@@ -687,6 +687,30 @@ def skipUnlessSupportedTypeAttribute(att
         return None
     return skipTestIfFn(compiler_doesnt_support_struct_attribute)
 
+def skipUnlessHasCallSiteInfo(func):
+    """Decorate the function to skip testing unless call site info from clang is available."""
+
+    def is_compiler_clang_with_call_site_info(self):
+        compiler_path = self.getCompiler()
+        compiler = os.path.basename(compiler_path)
+        if not compiler.startswith("clang"):
+            return "Test requires clang as compiler"
+
+        f = tempfile.NamedTemporaryFile()
+        cmd = "echo 'int main() {}' | " \
+              "%s -g -glldb -O1 -S -emit-llvm -x c -o %s -" % (compiler_path, f.name)
+        if os.popen(cmd).close() is not None:
+            return "Compiler can't compile with call site info enabled"
+
+        with open(f.name, 'r') as ir_output_file:
+            buf = ir_output_file.read()
+
+        if 'DIFlagAllCallsDescribed' not in buf:
+            return "Compiler did not introduce DIFlagAllCallsDescribed IR flag"
+
+        return None
+    return skipTestIfFn(is_compiler_clang_with_call_site_info)(func)
+
 def skipUnlessThreadSanitizer(func):
     """Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
 

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/TestAmbiguousTailCallSeq1.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/TestAmbiguousTailCallSeq1.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/TestAmbiguousTailCallSeq1.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/TestAmbiguousTailCallSeq1.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq1/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,33 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; //% self.filecheck("bt", "main.cpp")
+  // CHECK-NOT: func{{[23]}}_amb
+}
+
+void __attribute__((noinline)) func3_amb() { sink(); /* tail */ }
+
+void __attribute__((noinline)) func2_amb() { sink(); /* tail */ }
+
+void __attribute__((noinline)) func1() {
+  if (x > 0)
+    func2_amb(); /* tail */
+  else
+    func3_amb(); /* tail */
+}
+
+int __attribute__((disable_tail_calls)) main(int argc, char **) {
+  // The sequences `main -> func1 -> f{2,3}_amb -> sink` are both plausible. Test
+  // that lldb doesn't attempt to guess which one occurred.
+  func1();
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/TestAmbiguousTailCallSeq2.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/TestAmbiguousTailCallSeq2.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/TestAmbiguousTailCallSeq2.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/TestAmbiguousTailCallSeq2.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/ambiguous_tail_call_seq2/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,38 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; //% self.filecheck("bt", "main.cpp")
+  // CHECK-NOT: func{{[23]}}
+}
+
+void func2();
+
+void __attribute__((noinline)) func1() {
+  if (x < 1)
+    func2();
+  else
+    sink();
+}
+
+void __attribute__((noinline)) func2() {
+  if (x < 1)
+    sink();
+  else
+    func1();
+}
+
+int main() {
+  // Tail recursion creates ambiguous execution histories.
+  x = 0;
+  func1();
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/TestDisambiguateCallSite.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/TestDisambiguateCallSite.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/TestDisambiguateCallSite.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/TestDisambiguateCallSite.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_call_site/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,32 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; //% self.filecheck("bt", "main.cpp", "-implicit-check-not=artificial")
+  // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`sink() at main.cpp:[[@LINE-1]]:4 [opt]
+  // CHECK-NEXT: func2{{.*}} [opt] [artificial]
+  // CHECK-NEXT: main{{.*}} [opt]
+}
+
+void __attribute__((noinline)) func2() {
+  sink(); /* tail */
+}
+
+void __attribute__((noinline)) func1() { sink(); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main(int argc, char **) {
+  // The sequences `main -> f{1,2} -> sink` are both plausible. Test that
+  // return-pc call site info allows lldb to pick the correct sequence.
+  func2();
+  if (argc == 100)
+    func1();
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/TestDisambiguatePathsToCommonSink.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/TestDisambiguatePathsToCommonSink.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/TestDisambiguatePathsToCommonSink.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/TestDisambiguatePathsToCommonSink.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_paths_to_common_sink/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,38 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink2() {
+  x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=FROM-FUNC1")
+  // FROM-FUNC1: frame #0: 0x{{[0-9a-f]+}} a.out`sink{{.*}} at main.cpp:[[@LINE-1]]:{{.*}} [opt]
+  // FROM-FUNC1-NEXT: sink({{.*}} [opt]
+  // FROM-FUNC1-NEXT: func1{{.*}} [opt] [artificial]
+  // FROM-FUNC1-NEXT: main{{.*}} [opt]
+}
+
+void __attribute__((noinline)) sink(bool called_from_main) {
+  if (called_from_main) {
+    x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=FROM-MAIN")
+    // FROM-MAIN: frame #0: 0x{{[0-9a-f]+}} a.out`sink{{.*}} at main.cpp:[[@LINE-1]]:{{.*}} [opt]
+    // FROM-MAIN-NEXT: main{{.*}} [opt]
+  } else {
+    sink2();
+  }
+}
+
+void __attribute__((noinline)) func1() { sink(false); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main(int argc, char **) {
+  // When func1 tail-calls sink, make sure that the former appears in the
+  // backtrace.
+  sink(true);
+  func1();
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/TestDisambiguateTailCallSeq.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/TestDisambiguateTailCallSeq.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/TestDisambiguateTailCallSeq.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/TestDisambiguateTailCallSeq.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/disambiguate_tail_call_seq/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,31 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; //% self.filecheck("bt", "main.cpp", "-implicit-check-not=artificial")
+  // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`sink() at main.cpp:[[@LINE-1]]:4 [opt]
+  // CHECK-NEXT: func3{{.*}} [opt] [artificial]
+  // CHECK-NEXT: func1{{.*}} [opt] [artificial]
+  // CHECK-NEXT: main{{.*}} [opt]
+}
+
+void __attribute__((noinline)) func3() { sink(); /* tail */ }
+
+void __attribute__((noinline)) func2() { sink(); /* tail */ }
+
+void __attribute__((noinline)) func1() { func3(); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main(int argc, char **) {
+  // The sequences `main -> func1 -> f{2,3} -> sink` are both plausible. Test
+  // that lldb picks the latter sequence.
+  func1();
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/TestInliningAndTailCalls.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/TestInliningAndTailCalls.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/TestInliningAndTailCalls.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/TestInliningAndTailCalls.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,50 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) tail_call_sink() {
+  x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=TAIL-CALL-SINK")
+  // TAIL-CALL-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`tail_call_sink() at main.cpp:[[@LINE-1]]:4 [opt]
+  // TAIL-CALL-SINK-NEXT: func3{{.*}} [opt] [artificial]
+  // TAIL-CALL-SINK-NEXT: main{{.*}} [opt]
+
+  // TODO: The backtrace should include inlinable_function_which_tail_calls.
+}
+
+void __attribute__((always_inline)) inlinable_function_which_tail_calls() {
+  tail_call_sink();
+}
+
+void __attribute__((noinline)) func3() {
+  inlinable_function_which_tail_calls();
+}
+
+void __attribute__((always_inline)) inline_sink() {
+  x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=INLINE-SINK")
+  // INLINE-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`func2() [inlined] inline_sink() at main.cpp:[[@LINE-1]]:4 [opt]
+  // INLINE-SINK-NEXT: func2{{.*}} [opt]
+  // INLINE-SINK-NEXT: func1{{.*}} [opt] [artificial]
+  // INLINE-SINK-NEXT: main{{.*}} [opt]
+}
+
+void __attribute__((noinline)) func2() { inline_sink(); /* inlined */ }
+
+void __attribute__((noinline)) func1() { func2(); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main() {
+  // First, call a function that tail-calls a function, which itself inlines
+  // a third function.
+  func1();
+
+  // Next, call a function which contains an inlined tail-call.
+  func3();
+
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/TestTailCallFrameSBAPI.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/TestTailCallFrameSBAPI.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/TestTailCallFrameSBAPI.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/TestTailCallFrameSBAPI.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,65 @@
+"""
+Test SB API support for identifying artificial (tail call) frames.
+"""
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+class TestTailCallFrameSBAPI(TestBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    # If your test case doesn't stress debug info, the
+    # set this to true.  That way it won't be run once for
+    # each debug info format.
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_tail_call_frame_sbapi(self):
+        self.build()
+        self.do_test()
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+
+    def do_test(self):
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        breakpoint = target.BreakpointCreateBySourceRegex("break here",
+                lldb.SBFileSpec("main.cpp"))
+        self.assertTrue(breakpoint and
+                        breakpoint.GetNumLocations() == 1,
+                        VALID_BREAKPOINT)
+
+        error = lldb.SBError()
+        launch_info = lldb.SBLaunchInfo(None)
+        process = target.Launch(launch_info, error)
+        self.assertTrue(process, PROCESS_IS_VALID)
+
+        # Did we hit our breakpoint?
+        threads = lldbutil.get_threads_stopped_at_breakpoint(process,
+                breakpoint)
+        self.assertTrue(
+            len(threads) == 1,
+            "There should be a thread stopped at our breakpoint")
+
+        self.assertTrue(breakpoint.GetHitCount() == 1)
+
+        thread = threads[0]
+
+        # Here's what we expect to see in the backtrace:
+        #   frame #0: ... a.out`sink() at main.cpp:13:4 [opt]
+        #   frame #1: ... a.out`func3() at main.cpp:14:1 [opt] [artificial]
+        #   frame #2: ... a.out`func2() at main.cpp:18:62 [opt]
+        #   frame #3: ... a.out`func1() at main.cpp:18:85 [opt] [artificial]
+        #   frame #4: ... a.out`main at main.cpp:23:3 [opt]
+        names = ["sink()", "func3()", "func2()", "func1()", "main"]
+        artificiality = [False, True, False, True, False]
+        for idx, (name, is_artificial) in enumerate(zip(names, artificiality)):
+            frame = thread.GetFrameAtIndex(idx)
+            self.assertTrue(frame.GetDisplayFunctionName() == name)
+            self.assertTrue(frame.IsArtificial() == is_artificial)

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/sbapi_support/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,25 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; /* break here */
+}
+
+void __attribute__((noinline)) func3() { sink(); /* tail */ }
+
+void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ }
+
+void __attribute__((noinline)) func1() { func2(); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main() {
+  func1(); /* regular */
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/TestArtificialFrameStepOutMessage.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/TestArtificialFrameStepOutMessage.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/TestArtificialFrameStepOutMessage.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/TestArtificialFrameStepOutMessage.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_message/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,28 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; //% self.filecheck("finish", "main.cpp", "-implicit-check-not=artificial")
+  // CHECK: stop reason = step out
+  // CHECK-NEXT: Stepped out past: frame #1: 0x{{[0-9a-f]+}} a.out`func3{{.*}} [opt] [artificial]
+  // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`func2{{.*}} [opt]
+}
+
+void __attribute__((noinline)) func3() { sink(); /* tail */ }
+
+void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ }
+
+void __attribute__((noinline)) func1() { func2(); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main() {
+  func1(); /* regular */
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/TestSteppingOutWithArtificialFrames.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/TestSteppingOutWithArtificialFrames.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/TestSteppingOutWithArtificialFrames.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/TestSteppingOutWithArtificialFrames.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,92 @@
+"""
+Test SB API support for identifying artificial (tail call) frames.
+"""
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+class TestArtificialFrameThreadStepOut1(TestBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    # If your test case doesn't stress debug info, the
+    # set this to true.  That way it won't be run once for
+    # each debug info format.
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def prepare_thread(self):
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        breakpoint = target.BreakpointCreateBySourceRegex("break here",
+                lldb.SBFileSpec("main.cpp"))
+        self.assertTrue(breakpoint and
+                        breakpoint.GetNumLocations() == 1,
+                        VALID_BREAKPOINT)
+
+        error = lldb.SBError()
+        launch_info = lldb.SBLaunchInfo(None)
+        process = target.Launch(launch_info, error)
+        self.assertTrue(process, PROCESS_IS_VALID)
+
+        # Did we hit our breakpoint?
+        threads = lldbutil.get_threads_stopped_at_breakpoint(process,
+                breakpoint)
+        self.assertTrue(
+            len(threads) == 1,
+            "There should be a thread stopped at our breakpoint")
+
+        self.assertTrue(breakpoint.GetHitCount() == 1)
+
+        thread = threads[0]
+
+        # Here's what we expect to see in the backtrace:
+        #   frame #0: ... a.out`sink() at main.cpp:13:4 [opt]
+        #   frame #1: ... a.out`func3() at main.cpp:14:1 [opt] [artificial]
+        #   frame #2: ... a.out`func2() at main.cpp:18:62 [opt]
+        #   frame #3: ... a.out`func1() at main.cpp:18:85 [opt] [artificial]
+        #   frame #4: ... a.out`main at main.cpp:23:3 [opt]
+        return thread
+
+    def test_stepping_out_past_artificial_frame(self):
+        self.build()
+        thread = self.prepare_thread()
+
+        # Frame #0's ancestor is artificial. Stepping out should move to
+        # frame #2, because we behave as-if artificial frames were not present.
+        thread.StepOut()
+        frame2 = thread.GetSelectedFrame()
+        self.assertTrue(frame2.GetDisplayFunctionName() == "func2()")
+        self.assertFalse(frame2.IsArtificial())
+
+        # Ditto: stepping out of frame #2 should move to frame #4.
+        thread.StepOut()
+        frame4 = thread.GetSelectedFrame()
+        self.assertTrue(frame4.GetDisplayFunctionName() == "main")
+        self.assertFalse(frame2.IsArtificial())
+
+    def test_return_past_artificial_frame(self):
+        self.build()
+        thread = self.prepare_thread()
+
+        value = lldb.SBValue()
+
+        # Frame #0's ancestor is artificial. Returning from frame #0 should move
+        # to frame #2.
+        thread.ReturnFromFrame(thread.GetSelectedFrame(), value)
+        frame2 = thread.GetSelectedFrame()
+        self.assertTrue(frame2.GetDisplayFunctionName() == "func2()")
+        self.assertFalse(frame2.IsArtificial())
+
+        # Ditto: stepping out of frame #2 should move to frame #4.
+        thread.ReturnFromFrame(thread.GetSelectedFrame(), value)
+        frame4 = thread.GetSelectedFrame()
+        self.assertTrue(frame4.GetDisplayFunctionName() == "main")
+        self.assertFalse(frame2.IsArtificial())
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/thread_step_out_or_return/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,25 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; // break here
+}
+
+void __attribute__((noinline)) func3() { sink(); /* tail */ }
+
+void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ }
+
+void __attribute__((noinline)) func1() { func2(); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main() {
+  func1(); /* regular */
+  return 0;
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/Makefile?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/Makefile Fri Oct  5 16:23:15 2018
@@ -0,0 +1,4 @@
+LEVEL = ../../../make
+CXX_SOURCES := main.cpp
+include $(LEVEL)/Makefile.rules
+CXXFLAGS += -g -O1 -glldb

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/TestUnambiguousTailCalls.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/TestUnambiguousTailCalls.py?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/TestUnambiguousTailCalls.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/TestUnambiguousTailCalls.py Fri Oct  5 16:23:15 2018
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals(),
+        [decorators.skipUnlessHasCallSiteInfo])

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/main.cpp?rev=343900&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/tail_call_frames/unambiguous_sequence/main.cpp Fri Oct  5 16:23:15 2018
@@ -0,0 +1,30 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+volatile int x;
+
+void __attribute__((noinline)) sink() {
+  x++; //% self.filecheck("bt", "main.cpp", "-implicit-check-not=artificial")
+  // CHECK: frame #0: 0x{{[0-9a-f]+}} a.out`sink() at main.cpp:[[@LINE-1]]:4 [opt]
+  // CHECK-NEXT: frame #1: 0x{{[0-9a-f]+}} a.out`func3{{.*}} [opt] [artificial]
+  // CHECK-NEXT: frame #2: 0x{{[0-9a-f]+}} a.out`func2{{.*}} [opt]
+  // CHECK-NEXT: frame #3: 0x{{[0-9a-f]+}} a.out`func1{{.*}} [opt] [artificial]
+  // CHECK-NEXT: frame #4: 0x{{[0-9a-f]+}} a.out`main{{.*}} [opt]
+}
+
+void __attribute__((noinline)) func3() { sink(); /* tail */ }
+
+void __attribute__((disable_tail_calls, noinline)) func2() { func3(); /* regular */ }
+
+void __attribute__((noinline)) func1() { func2(); /* tail */ }
+
+int __attribute__((disable_tail_calls)) main() {
+  func1(); /* regular */
+  return 0;
+}

Modified: lldb/trunk/scripts/interface/SBFrame.i
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/interface/SBFrame.i?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/scripts/interface/SBFrame.i (original)
+++ lldb/trunk/scripts/interface/SBFrame.i Fri Oct  5 16:23:15 2018
@@ -154,6 +154,17 @@ public:
     IsInlined() const;
 
     %feature("docstring", "
+    /// Return true if this frame is artificial (e.g a frame synthesized to
+    /// capture a tail call). Local variables may not be available in an artificial
+    /// frame.
+    ") IsArtificial;
+    bool
+    IsArtificial();
+
+    bool
+    IsArtificial() const;
+
+    %feature("docstring", "
     /// The version that doesn't supply a 'use_dynamic' value will use the
     /// target's default.
     ") EvaluateExpression;

Modified: lldb/trunk/source/API/SBFrame.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBFrame.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/API/SBFrame.cpp (original)
+++ lldb/trunk/source/API/SBFrame.cpp Fri Oct  5 16:23:15 2018
@@ -1348,6 +1348,21 @@ bool SBFrame::IsInlined() const {
   return false;
 }
 
+bool SBFrame::IsArtificial() {
+  return static_cast<const SBFrame *>(this)->IsArtificial();
+}
+
+bool SBFrame::IsArtificial() const {
+  std::unique_lock<std::recursive_mutex> lock;
+  ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+  StackFrame *frame = exe_ctx.GetFramePtr();
+  if (frame)
+    return frame->IsArtificial();
+
+  return false;
+}
+
 const char *SBFrame::GetFunctionName() {
   return static_cast<const SBFrame *>(this)->GetFunctionName();
 }

Modified: lldb/trunk/source/Core/Debugger.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/Debugger.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Core/Debugger.cpp (original)
+++ lldb/trunk/source/Core/Debugger.cpp Fri Oct  5 16:23:15 2018
@@ -123,6 +123,8 @@ static constexpr OptionEnumValueElement
   "{ at ${line.file.basename}:${line.number}{:${line.column}}}"
 #define IS_OPTIMIZED "{${function.is-optimized} [opt]}"
 
+#define IS_ARTIFICIAL "{${frame.is-artificial} [artificial]}"
+
 #define DEFAULT_THREAD_FORMAT                                                  \
   "thread #${thread.index}: tid = ${thread.id%tid}"                            \
   "{, ${frame.pc}}" MODULE_WITH_FUNC FILE_AND_LINE                             \
@@ -147,11 +149,11 @@ static constexpr OptionEnumValueElement
 
 #define DEFAULT_FRAME_FORMAT                                                   \
   "frame #${frame.index}: ${frame.pc}" MODULE_WITH_FUNC FILE_AND_LINE          \
-      IS_OPTIMIZED "\\n"
+      IS_OPTIMIZED IS_ARTIFICIAL "\\n"
 
 #define DEFAULT_FRAME_FORMAT_NO_ARGS                                           \
   "frame #${frame.index}: ${frame.pc}" MODULE_WITH_FUNC_NO_ARGS FILE_AND_LINE  \
-      IS_OPTIMIZED "\\n"
+      IS_OPTIMIZED IS_ARTIFICIAL "\\n"
 
 // Three parts to this disassembly format specification:
 //   1. If this is a new function/symbol (no previous symbol/function), print

Modified: lldb/trunk/source/Core/FormatEntity.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/FormatEntity.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Core/FormatEntity.cpp (original)
+++ lldb/trunk/source/Core/FormatEntity.cpp Fri Oct  5 16:23:15 2018
@@ -128,6 +128,7 @@ static FormatEntity::Entry::Definition g
     ENTRY("flags", FrameRegisterFlags, UInt64),
     ENTRY("no-debug", FrameNoDebug, None),
     ENTRY_CHILDREN("reg", FrameRegisterByName, UInt64, g_string_entry),
+    ENTRY("is-artificial", FrameIsArtificial, UInt32),
 };
 
 static FormatEntity::Entry::Definition g_function_child_entries[] = {
@@ -357,6 +358,7 @@ const char *FormatEntity::Entry::TypeToC
     ENUM_TO_CSTR(FrameRegisterFP);
     ENUM_TO_CSTR(FrameRegisterFlags);
     ENUM_TO_CSTR(FrameRegisterByName);
+    ENUM_TO_CSTR(FrameIsArtificial);
     ENUM_TO_CSTR(ScriptFrame);
     ENUM_TO_CSTR(FunctionID);
     ENUM_TO_CSTR(FunctionDidChange);
@@ -1489,6 +1491,13 @@ bool FormatEntity::Format(const Entry &e
     }
     return false;
 
+  case Entry::Type::FrameIsArtificial: {
+    if (exe_ctx)
+      if (StackFrame *frame = exe_ctx->GetFramePtr())
+        return frame->IsArtificial();
+    return false;
+  }
+
   case Entry::Type::ScriptFrame:
     if (exe_ctx) {
       StackFrame *frame = exe_ctx->GetFramePtr();

Modified: lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (original)
+++ lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp Fri Oct  5 16:23:15 2018
@@ -3742,6 +3742,60 @@ size_t SymbolFileDWARF::ParseVariables(c
   return vars_added;
 }
 
+/// Collect call graph edges present in a function DIE.
+static std::vector<lldb_private::CallEdge>
+CollectCallEdges(DWARFDIE function_die) {
+  // Check if the function has a supported call site-related attribute.
+  // TODO: In the future it may be worthwhile to support call_all_source_calls.
+  uint64_t has_call_edges =
+      function_die.GetAttributeValueAsUnsigned(DW_AT_call_all_calls, 0);
+  if (!has_call_edges)
+    return {};
+
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+  LLDB_LOG(log, "CollectCallEdges: Found call site info in {0}",
+           function_die.GetPubname());
+
+  // Scan the DIE for TAG_call_site entries.
+  // TODO: A recursive scan of all blocks in the subprogram is needed in order
+  // to be DWARF5-compliant. This may need to be done lazily to be performant.
+  // For now, assume that all entries are nested directly under the subprogram
+  // (this is the kind of DWARF LLVM produces) and parse them eagerly.
+  std::vector<CallEdge> call_edges;
+  for (DWARFDIE child = function_die.GetFirstChild(); child.IsValid();
+       child = child.GetSibling()) {
+    if (child.Tag() != DW_TAG_call_site)
+      continue;
+
+    // Extract DW_AT_call_origin (the call target's DIE).
+    DWARFDIE call_origin = child.GetReferencedDIE(DW_AT_call_origin);
+    if (!call_origin.IsValid()) {
+      LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}",
+               function_die.GetPubname());
+      continue;
+    }
+
+    // Extract DW_AT_call_return_pc (the PC the call returns to) if it's
+    // available. It should only ever be unavailable for tail call edges, in
+    // which case use LLDB_INVALID_ADDRESS.
+    addr_t return_pc = child.GetAttributeValueAsAddress(DW_AT_call_return_pc,
+                                                        LLDB_INVALID_ADDRESS);
+
+    LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})",
+             call_origin.GetPubname(), return_pc);
+    call_edges.emplace_back(call_origin.GetMangledName(), return_pc);
+  }
+  return call_edges;
+}
+
+std::vector<lldb_private::CallEdge>
+SymbolFileDWARF::ParseCallEdgesInFunction(UserID func_id) {
+  DWARFDIE func_die = GetDIEFromUID(func_id.GetID());
+  if (func_die.IsValid())
+    return CollectCallEdges(func_die);
+  return {};
+}
+
 //------------------------------------------------------------------
 // PluginInterface protocol
 //------------------------------------------------------------------

Modified: lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h (original)
+++ lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h Fri Oct  5 16:23:15 2018
@@ -317,6 +317,9 @@ public:
   DIEInDeclContext(const lldb_private::CompilerDeclContext *parent_decl_ctx,
                    const DWARFDIE &die);
 
+  std::vector<lldb_private::CallEdge>
+  ParseCallEdgesInFunction(UserID func_id) override;
+
   void Dump(lldb_private::Stream &s) override;
 
 protected:

Modified: lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp (original)
+++ lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp Fri Oct  5 16:23:15 2018
@@ -1060,6 +1060,15 @@ size_t SymbolFileDWARFDebugMap::GetTypes
   return type_list.GetSize() - initial_size;
 }
 
+std::vector<lldb_private::CallEdge>
+SymbolFileDWARFDebugMap::ParseCallEdgesInFunction(UserID func_id) {
+  uint32_t oso_idx = GetOSOIndexFromUserID(func_id.GetID());
+  SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
+  if (oso_dwarf)
+    return oso_dwarf->ParseCallEdgesInFunction(func_id);
+  return {};
+}
+
 TypeSP SymbolFileDWARFDebugMap::FindDefinitionTypeForDWARFDeclContext(
     const DWARFDeclContext &die_decl_ctx) {
   TypeSP type_sp;

Modified: lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h (original)
+++ lldb/trunk/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h Fri Oct  5 16:23:15 2018
@@ -122,6 +122,8 @@ public:
   size_t GetTypes(lldb_private::SymbolContextScope *sc_scope,
                   uint32_t type_mask,
                   lldb_private::TypeList &type_list) override;
+  std::vector<lldb_private::CallEdge>
+  ParseCallEdgesInFunction(lldb_private::UserID func_id) override;
 
   //------------------------------------------------------------------
   // PluginInterface protocol

Modified: lldb/trunk/source/Symbol/Block.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/Block.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/Block.cpp (original)
+++ lldb/trunk/source/Symbol/Block.cpp Fri Oct  5 16:23:15 2018
@@ -444,19 +444,16 @@ uint32_t Block::AppendVariables(bool can
   return num_variables_added;
 }
 
-CompilerDeclContext Block::GetDeclContext() {
-  ModuleSP module_sp = CalculateSymbolContextModule();
-
-  if (module_sp) {
-    SymbolVendor *sym_vendor = module_sp->GetSymbolVendor();
-
-    if (sym_vendor) {
-      SymbolFile *sym_file = sym_vendor->GetSymbolFile();
+SymbolFile *Block::GetSymbolFile() {
+  if (ModuleSP module_sp = CalculateSymbolContextModule())
+    if (SymbolVendor *sym_vendor = module_sp->GetSymbolVendor())
+      return sym_vendor->GetSymbolFile();
+  return nullptr;
+}
 
-      if (sym_file)
-        return sym_file->GetDeclContextForUID(GetID());
-    }
-  }
+CompilerDeclContext Block::GetDeclContext() {
+  if (SymbolFile *sym_file = GetSymbolFile())
+    return sym_file->GetDeclContextForUID(GetID());
   return CompilerDeclContext();
 }
 

Modified: lldb/trunk/source/Symbol/Function.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/Function.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/Function.cpp (original)
+++ lldb/trunk/source/Symbol/Function.cpp Fri Oct  5 16:23:15 2018
@@ -10,6 +10,7 @@
 #include "lldb/Symbol/Function.h"
 #include "lldb/Core/Disassembler.h"
 #include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Symbol/CompileUnit.h"
@@ -18,6 +19,7 @@
 #include "lldb/Symbol/SymbolFile.h"
 #include "lldb/Symbol/SymbolVendor.h"
 #include "lldb/Target/Language.h"
+#include "lldb/Utility/Log.h"
 #include "llvm/Support/Casting.h"
 
 using namespace lldb;
@@ -131,6 +133,60 @@ size_t InlineFunctionInfo::MemorySize()
 //----------------------------------------------------------------------
 //
 //----------------------------------------------------------------------
+CallEdge::CallEdge(const char *symbol_name, lldb::addr_t return_pc)
+    : return_pc(return_pc), resolved(false) {
+  lazy_callee.symbol_name = symbol_name;
+}
+
+void CallEdge::ParseSymbolFileAndResolve(ModuleList &images) {
+  if (resolved)
+    return;
+
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+  LLDB_LOG(log, "CallEdge: Lazily parsing the call graph for {0}",
+           lazy_callee.symbol_name);
+
+  auto resolve_lazy_callee = [&]() -> Function * {
+    ConstString callee_name{lazy_callee.symbol_name};
+    SymbolContextList sc_list;
+    size_t num_matches =
+        images.FindFunctionSymbols(callee_name, eFunctionNameTypeAuto, sc_list);
+    if (num_matches == 0 || !sc_list[0].symbol) {
+      LLDB_LOG(log, "CallEdge: Found no symbols for {0}, cannot resolve it",
+               callee_name);
+      return nullptr;
+    }
+    Address callee_addr = sc_list[0].symbol->GetAddress();
+    if (!callee_addr.IsValid()) {
+      LLDB_LOG(log, "CallEdge: Invalid symbol address");
+      return nullptr;
+    }
+    Function *f = callee_addr.CalculateSymbolContextFunction();
+    if (!f) {
+      LLDB_LOG(log, "CallEdge: Could not find complete function");
+      return nullptr;
+    }
+    return f;
+  };
+  lazy_callee.def = resolve_lazy_callee();
+  resolved = true;
+}
+
+Function *CallEdge::GetCallee(ModuleList &images) {
+  ParseSymbolFileAndResolve(images);
+  return lazy_callee.def;
+}
+
+lldb::addr_t CallEdge::GetReturnPCAddress(Function &caller,
+                                          Target &target) const {
+  const Address &base = caller.GetAddressRange().GetBaseAddress();
+  Address return_pc_addr{base.GetSection(), return_pc};
+  return return_pc_addr.GetLoadAddress(&target);
+}
+
+//----------------------------------------------------------------------
+//
+//----------------------------------------------------------------------
 Function::Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
                    lldb::user_id_t type_uid, const Mangled &mangled, Type *type,
                    const AddressRange &range)
@@ -192,6 +248,43 @@ void Function::GetEndLineSourceInfo(File
   }
 }
 
+llvm::MutableArrayRef<CallEdge> Function::GetCallEdges() {
+  if (m_call_edges_resolved)
+    return m_call_edges;
+
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+  LLDB_LOG(log, "GetCallEdges: Attempting to parse call site info for {0}",
+           GetDisplayName());
+
+  m_call_edges_resolved = true;
+
+  // Find the SymbolFile which provided this function's definition.
+  Block &block = GetBlock(/*can_create*/true);
+  SymbolFile *sym_file = block.GetSymbolFile();
+  if (!sym_file)
+    return llvm::None;
+
+  // Lazily read call site information from the SymbolFile.
+  m_call_edges = sym_file->ParseCallEdgesInFunction(GetID());
+
+  // Sort the call edges to speed up return_pc lookups.
+  std::sort(m_call_edges.begin(), m_call_edges.end(),
+            [](const CallEdge &LHS, const CallEdge &RHS) {
+              return LHS.GetUnresolvedReturnPCAddress() <
+                     RHS.GetUnresolvedReturnPCAddress();
+            });
+
+  return m_call_edges;
+}
+
+llvm::MutableArrayRef<CallEdge> Function::GetTailCallingEdges() {
+  // Call edges are sorted by return PC, and tail calling edges have invalid
+  // return PCs. Find them at the end of the list.
+  return GetCallEdges().drop_until([](const CallEdge &edge) {
+    return edge.GetUnresolvedReturnPCAddress() == LLDB_INVALID_ADDRESS;
+  });
+}
+
 Block &Function::GetBlock(bool can_create) {
   if (!m_block.BlockInfoHasBeenParsed() && can_create) {
     SymbolContext sc;

Modified: lldb/trunk/source/Target/StackFrame.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/StackFrame.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Target/StackFrame.cpp (original)
+++ lldb/trunk/source/Target/StackFrame.cpp Fri Oct  5 16:23:15 2018
@@ -49,20 +49,18 @@ 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, uint32_t stop_id,
-                       bool stop_id_is_valid, bool is_history_frame,
+                       bool cfa_is_valid, addr_t pc, StackFrame::Kind kind,
                        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), m_frame_code_addr(pc), m_sc(), m_flags(),
       m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid),
-      m_stop_id(stop_id), m_stop_id_is_valid(stop_id_is_valid),
-      m_is_history_frame(is_history_frame), m_variable_list_sp(),
+      m_stack_frame_kind(kind), m_variable_list_sp(),
       m_variable_list_value_objects(), m_disassembly(), m_mutex() {
   // If we don't have a CFA value, use the frame index for our StackID so that
   // recursive functions properly aren't confused with one another on a history
   // stack.
-  if (m_is_history_frame && !m_cfa_is_valid) {
+  if (IsHistorical() && !m_cfa_is_valid) {
     m_id.SetCFA(m_frame_index);
   }
 
@@ -80,10 +78,9 @@ StackFrame::StackFrame(const ThreadSP &t
       m_concrete_frame_index(unwind_frame_index),
       m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr),
       m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
-      m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0),
-      m_stop_id_is_valid(false), m_is_history_frame(false),
-      m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(),
-      m_mutex() {
+      m_frame_base_error(), m_cfa_is_valid(true),
+      m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(),
+      m_variable_list_value_objects(), m_disassembly(), m_mutex() {
   if (sc_ptr != nullptr) {
     m_sc = *sc_ptr;
     m_flags.Set(m_sc.GetResolvedMask());
@@ -106,10 +103,9 @@ StackFrame::StackFrame(const ThreadSP &t
       m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa,
            nullptr),
       m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(),
-      m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0),
-      m_stop_id_is_valid(false), m_is_history_frame(false),
-      m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(),
-      m_mutex() {
+      m_frame_base_error(), m_cfa_is_valid(true),
+      m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(),
+      m_variable_list_value_objects(), m_disassembly(), m_mutex() {
   if (sc_ptr != nullptr) {
     m_sc = *sc_ptr;
     m_flags.Set(m_sc.GetResolvedMask());
@@ -210,7 +206,7 @@ const Address &StackFrame::GetFrameCodeA
 bool StackFrame::ChangePC(addr_t pc) {
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
   // We can't change the pc value of a history stack frame - it is immutable.
-  if (m_is_history_frame)
+  if (IsHistorical())
     return false;
   m_frame_code_addr.SetRawAddress(pc);
   m_sc.Clear(false);
@@ -456,7 +452,7 @@ StackFrame::GetInScopeVariableList(bool
                                    bool must_have_valid_location) {
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
   // We can't fetch variable information for a history stack frame.
-  if (m_is_history_frame)
+  if (IsHistorical())
     return VariableListSP();
 
   VariableListSP var_list_sp(new VariableList);
@@ -490,7 +486,7 @@ ValueObjectSP StackFrame::GetValueForVar
     VariableSP &var_sp, Status &error) {
   llvm::StringRef original_var_expr = var_expr;
   // We can't fetch variable information for a history stack frame.
-  if (m_is_history_frame)
+  if (IsHistorical())
     return ValueObjectSP();
 
   if (var_expr.empty()) {
@@ -1135,7 +1131,7 @@ StackFrame::GetValueObjectForFrameVariab
                                            DynamicValueType use_dynamic) {
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
   ValueObjectSP valobj_sp;
-  if (m_is_history_frame) {
+  if (IsHistorical()) {
     return valobj_sp;
   }
   VariableList *var_list = GetVariableList(true);
@@ -1164,7 +1160,7 @@ StackFrame::GetValueObjectForFrameVariab
 ValueObjectSP StackFrame::TrackGlobalVariable(const VariableSP &variable_sp,
                                               DynamicValueType use_dynamic) {
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
-  if (m_is_history_frame)
+  if (IsHistorical())
     return ValueObjectSP();
 
   // Check to make sure we aren't already tracking this variable?
@@ -1194,6 +1190,14 @@ bool StackFrame::IsInlined() {
   return false;
 }
 
+bool StackFrame::IsHistorical() const {
+  return m_stack_frame_kind == StackFrame::Kind::History;
+}
+
+bool StackFrame::IsArtificial() const {
+  return m_stack_frame_kind == StackFrame::Kind::Artificial;
+}
+
 lldb::LanguageType StackFrame::GetLanguage() {
   CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit;
   if (cu)

Modified: lldb/trunk/source/Target/StackFrameList.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/StackFrameList.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Target/StackFrameList.cpp (original)
+++ lldb/trunk/source/Target/StackFrameList.cpp Fri Oct  5 16:23:15 2018
@@ -27,6 +27,7 @@
 #include "lldb/Target/Thread.h"
 #include "lldb/Target/Unwind.h"
 #include "lldb/Utility/Log.h"
+#include "llvm/ADT/SmallPtrSet.h"
 
 //#define DEBUG_STACK_FRAMES 1
 
@@ -240,6 +241,178 @@ void StackFrameList::GetOnlyConcreteFram
   m_frames.resize(num_frames);
 }
 
+/// Find the unique path through the call graph from \p begin (with return PC
+/// \p return_pc) to \p end. On success this path is stored into \p path, and 
+/// on failure \p path is unchanged.
+static void FindInterveningFrames(Function &begin, Function &end,
+                                  Target &target, addr_t return_pc,
+                                  std::vector<Function *> &path,
+                                  ModuleList &images, Log *log) {
+  LLDB_LOG(log, "Finding frames between {0} and {1}, retn-pc={2:x}",
+           begin.GetDisplayName(), end.GetDisplayName(), return_pc);
+
+  // Find a non-tail calling edge with the correct return PC.
+  auto first_level_edges = begin.GetCallEdges();
+  if (log)
+    for (const CallEdge &edge : first_level_edges)
+      LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}",
+               edge.GetReturnPCAddress(begin, target));
+  auto first_edge_it = std::lower_bound(
+      first_level_edges.begin(), first_level_edges.end(), return_pc,
+      [&](const CallEdge &edge, addr_t target_pc) {
+        return edge.GetReturnPCAddress(begin, target) < target_pc;
+      });
+  if (first_edge_it == first_level_edges.end() ||
+      first_edge_it->GetReturnPCAddress(begin, target) != return_pc) {
+    LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}",
+             begin.GetDisplayName(), return_pc);
+    return;
+  }
+  CallEdge &first_edge = const_cast<CallEdge &>(*first_edge_it);
+
+  // The first callee may not be resolved, or there may be nothing to fill in.
+  Function *first_callee = first_edge.GetCallee(images);
+  if (!first_callee) {
+    LLDB_LOG(log, "Could not resolve callee");
+    return;
+  }
+  if (first_callee == &end) {
+    LLDB_LOG(log, "Not searching further, first callee is {0} (retn-PC: {1:x})",
+             end.GetDisplayName(), return_pc);
+    return;
+  }
+
+  // Run DFS on the tail-calling edges out of the first callee to find \p end.
+  // Fully explore the set of functions reachable from the first edge via tail
+  // calls in order to detect ambiguous executions.
+  struct DFS {
+    std::vector<Function *> active_path = {};
+    std::vector<Function *> solution_path = {};
+    llvm::SmallPtrSet<Function *, 2> visited_nodes = {};
+    bool ambiguous = false;
+    Function *end;
+    ModuleList &images;
+
+    DFS(Function *end, ModuleList &images) : end(end), images(images) {}
+
+    void search(Function *first_callee, std::vector<Function *> &path) {
+      dfs(first_callee);
+      if (!ambiguous)
+        path = std::move(solution_path);
+    }
+
+    void dfs(Function *callee) {
+      // Found a path to the target function.
+      if (callee == end) {
+        if (solution_path.empty())
+          solution_path = active_path;
+        else
+          ambiguous = true;
+        return;
+      }
+
+      // Terminate the search if tail recursion is found, or more generally if
+      // there's more than one way to reach a target. This errs on the side of
+      // caution: it conservatively stops searching when some solutions are
+      // still possible to save time in the average case.
+      if (!visited_nodes.insert(callee).second) {
+        ambiguous = true;
+        return;
+      }
+
+      // Search the calls made from this callee.
+      active_path.push_back(callee);
+      for (CallEdge &edge : callee->GetTailCallingEdges()) {
+        Function *next_callee = edge.GetCallee(images);
+        if (!next_callee)
+          continue;
+
+        dfs(next_callee);
+        if (ambiguous)
+          return;
+      }
+      active_path.pop_back();
+    }
+  };
+
+  DFS(&end, images).search(first_callee, path);
+}
+
+/// Given that \p next_frame will be appended to the frame list, synthesize
+/// tail call frames between the current end of the list and \p next_frame.
+/// If any frames are added, adjust the frame index of \p next_frame.
+///
+///   --------------
+///   |    ...     | <- Completed frames.
+///   --------------
+///   | prev_frame |
+///   --------------
+///   |    ...     | <- Artificial frames inserted here.
+///   --------------
+///   | next_frame |
+///   --------------
+///   |    ...     | <- Not-yet-visited frames.
+///   --------------
+void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
+  TargetSP target_sp = next_frame.CalculateTarget();
+  if (!target_sp)
+    return;
+
+  lldb::RegisterContextSP next_reg_ctx_sp = next_frame.GetRegisterContext();
+  if (!next_reg_ctx_sp)
+    return;
+
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
+
+  assert(!m_frames.empty() && "Cannot synthesize frames in an empty stack");
+  StackFrame &prev_frame = *m_frames.back().get();
+
+  // Find the functions prev_frame and next_frame are stopped in. The function
+  // objects are needed to search the lazy call graph for intervening frames.
+  Function *prev_func =
+      prev_frame.GetSymbolContext(eSymbolContextFunction).function;
+  if (!prev_func) {
+    LLDB_LOG(log, "SynthesizeTailCallFrames: can't find previous function");
+    return;
+  }
+  Function *next_func =
+      next_frame.GetSymbolContext(eSymbolContextFunction).function;
+  if (!next_func) {
+    LLDB_LOG(log, "SynthesizeTailCallFrames: can't find next function");
+    return;
+  }
+
+  // Try to find the unique sequence of (tail) calls which led from next_frame
+  // to prev_frame.
+  std::vector<Function *> path;
+  addr_t return_pc = next_reg_ctx_sp->GetPC();
+  Target &target = *target_sp.get();
+  ModuleList &images = next_frame.CalculateTarget()->GetImages();
+  FindInterveningFrames(*next_func, *prev_func, target, return_pc, path, images,
+                        log);
+
+  // Push synthetic tail call frames.
+  for (Function *callee : llvm::reverse(path)) {
+    uint32_t frame_idx = m_frames.size();
+    uint32_t concrete_frame_idx = next_frame.GetConcreteFrameIndex();
+    addr_t cfa = LLDB_INVALID_ADDRESS;
+    bool cfa_is_valid = false;
+    addr_t pc =
+        callee->GetAddressRange().GetBaseAddress().GetLoadAddress(&target);
+    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, &sc);
+    m_frames.push_back(synth_frame);
+    LLDB_LOG(log, "Pushed frame {0}", callee->GetDisplayName());
+  }
+
+  // If any frames were created, adjust next_frame's index.
+  if (!path.empty())
+    next_frame.SetFrameIndex(m_frames.size());
+}
+
 void StackFrameList::GetFramesUpTo(uint32_t end_idx) {
   // Do not fetch frames for an invalid thread.
   if (!m_thread.IsValid())
@@ -315,11 +488,15 @@ void StackFrameList::GetFramesUpTo(uint3
         break;
       }
       const bool cfa_is_valid = true;
-      const bool stop_id_is_valid = false;
-      const bool is_history_frame = false;
-      unwind_frame_sp.reset(new StackFrame(
-          m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid,
-          pc, 0, stop_id_is_valid, is_history_frame, nullptr));
+      unwind_frame_sp.reset(
+          new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, cfa,
+                         cfa_is_valid, pc, StackFrame::Kind::Regular, 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,
+      // although its concrete index will stay the same.
+      SynthesizeTailCallFrames(*unwind_frame_sp.get());
+
       m_frames.push_back(unwind_frame_sp);
     }
 
@@ -491,11 +668,9 @@ StackFrameSP StackFrameList::GetFrameAtI
         addr_t pc, cfa;
         if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc)) {
           const bool cfa_is_valid = true;
-          const bool stop_id_is_valid = false;
-          const bool is_history_frame = false;
-          frame_sp.reset(new StackFrame(
-              m_thread.shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, 0,
-              stop_id_is_valid, is_history_frame, nullptr));
+          frame_sp.reset(new StackFrame(m_thread.shared_from_this(), idx, idx,
+                                        cfa, cfa_is_valid, pc,
+                                        StackFrame::Kind::Regular, nullptr));
 
           Function *function =
               frame_sp->GetSymbolContext(eSymbolContextFunction).function;

Modified: lldb/trunk/source/Target/ThreadPlanStepOut.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/ThreadPlanStepOut.cpp?rev=343900&r1=343899&r2=343900&view=diff
==============================================================================
--- lldb/trunk/source/Target/ThreadPlanStepOut.cpp (original)
+++ lldb/trunk/source/Target/ThreadPlanStepOut.cpp Fri Oct  5 16:23:15 2018
@@ -48,18 +48,36 @@ ThreadPlanStepOut::ThreadPlanStepOut(
       m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others),
       m_immediate_step_from_function(nullptr),
       m_calculate_return_value(gather_return_value) {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
   SetFlagsToDefault();
   SetupAvoidNoDebug(step_out_avoids_code_without_debug_info);
 
   m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0);
 
-  StackFrameSP return_frame_sp(m_thread.GetStackFrameAtIndex(frame_idx + 1));
+  uint32_t return_frame_index = frame_idx + 1;
+  StackFrameSP return_frame_sp(
+      m_thread.GetStackFrameAtIndex(return_frame_index));
   StackFrameSP immediate_return_from_sp(
       m_thread.GetStackFrameAtIndex(frame_idx));
 
   if (!return_frame_sp || !immediate_return_from_sp)
     return; // we can't do anything here.  ValidatePlan() will return false.
 
+  // While stepping out, behave as-if artificial frames are not present.
+  while (return_frame_sp->IsArtificial()) {
+    m_stepped_past_frames.push_back(return_frame_sp);
+
+    ++return_frame_index;
+    return_frame_sp = m_thread.GetStackFrameAtIndex(return_frame_index);
+
+    // We never expect to see an artificial frame without a regular ancestor.
+    // If this happens, log the issue and defensively refuse to step out.
+    if (!return_frame_sp) {
+      LLDB_LOG(log, "Can't step out of frame with artificial ancestors");
+      return;
+    }
+  }
+
   m_step_out_to_id = return_frame_sp->GetStackID();
   m_immediate_step_from_id = immediate_return_from_sp->GetStackID();
 
@@ -67,7 +85,7 @@ ThreadPlanStepOut::ThreadPlanStepOut(
   // have to be a little more careful.  It is non-trivial to determine the real
   // "return code address" for an inlined frame, so we have to work our way to
   // that frame and then step out.
-  if (immediate_return_from_sp && immediate_return_from_sp->IsInlined()) {
+  if (immediate_return_from_sp->IsInlined()) {
     if (frame_idx > 0) {
       // First queue a plan that gets us to this inlined frame, and when we get
       // there we'll queue a second plan that walks us out of this frame.
@@ -82,7 +100,7 @@ ThreadPlanStepOut::ThreadPlanStepOut(
       // just do that now.
       QueueInlinedStepPlan(false);
     }
-  } else if (return_frame_sp) {
+  } else {
     // Find the return address and set a breakpoint there:
     // FIXME - can we do this more securely if we know first_insn?
 
@@ -198,6 +216,12 @@ void ThreadPlanStepOut::GetDescription(S
         s->Printf(" using breakpoint site %d", m_return_bp_id);
     }
   }
+
+  s->Printf("\n");
+  for (StackFrameSP frame_sp : m_stepped_past_frames) {
+    s->Printf("Stepped out past: ");
+    frame_sp->DumpUsingSettingsFormat(s);
+  }
 }
 
 bool ThreadPlanStepOut::ValidatePlan(Stream *error) {




More information about the lldb-commits mailing list