Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:42:56

0001 //===-- TraceDumper.h -------------------------------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 
0009 #include "lldb/Symbol/SymbolContext.h"
0010 #include "lldb/Target/TraceCursor.h"
0011 #include <optional>
0012 #include <stack>
0013 
0014 #ifndef LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H
0015 #define LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H
0016 
0017 namespace lldb_private {
0018 
0019 /// Class that holds the configuration used by \a TraceDumper for
0020 /// traversing and dumping instructions.
0021 struct TraceDumperOptions {
0022   /// If \b true, the cursor will be iterated forwards starting from the
0023   /// oldest instruction. Otherwise, the iteration starts from the most
0024   /// recent instruction.
0025   bool forwards = false;
0026   /// Dump only instruction addresses without disassembly nor symbol
0027   /// information.
0028   bool raw = false;
0029   /// Dump in json format.
0030   bool json = false;
0031   /// When dumping in JSON format, pretty print the output.
0032   bool pretty_print_json = false;
0033   /// For each trace item, print the corresponding timestamp in nanoseconds if
0034   /// available.
0035   bool show_timestamps = false;
0036   /// Dump the events that happened between instructions.
0037   bool show_events = false;
0038   /// Dump events and none of the instructions.
0039   bool only_events = false;
0040   /// For each instruction, print the instruction kind.
0041   bool show_control_flow_kind = false;
0042   /// Optional custom id to start traversing from.
0043   std::optional<uint64_t> id;
0044   /// Optional number of instructions to skip from the starting position
0045   /// of the cursor.
0046   std::optional<size_t> skip;
0047 };
0048 
0049 /// Class used to dump the instructions of a \a TraceCursor using its current
0050 /// state and granularity.
0051 class TraceDumper {
0052 public:
0053   /// Helper struct that holds symbol, disassembly and address information of an
0054   /// instruction.
0055   struct SymbolInfo {
0056     SymbolContext sc;
0057     Address address;
0058     lldb::DisassemblerSP disassembler;
0059     lldb::InstructionSP instruction;
0060     lldb_private::ExecutionContext exe_ctx;
0061   };
0062 
0063   /// Helper struct that holds all the information we know about a trace item
0064   struct TraceItem {
0065     lldb::user_id_t id;
0066     lldb::addr_t load_address;
0067     std::optional<double> timestamp;
0068     std::optional<uint64_t> hw_clock;
0069     std::optional<std::string> sync_point_metadata;
0070     std::optional<llvm::StringRef> error;
0071     std::optional<lldb::TraceEvent> event;
0072     std::optional<SymbolInfo> symbol_info;
0073     std::optional<SymbolInfo> prev_symbol_info;
0074     std::optional<lldb::cpu_id_t> cpu_id;
0075   };
0076 
0077   /// An object representing a traced function call.
0078   ///
0079   /// A function call is represented using segments and subcalls.
0080   ///
0081   /// TracedSegment:
0082   ///   A traced segment is a maximal list of consecutive traced instructions
0083   ///   that belong to the same function call. A traced segment will end in
0084   ///   three possible ways:
0085   ///     - With a call to a function deeper in the callstack. In this case,
0086   ///     most of the times this nested call will return
0087   ///       and resume with the next segment of this segment's owning function
0088   ///       call. More on this later.
0089   ///     - Abruptly due to end of trace. In this case, we weren't able to trace
0090   ///     the end of this function call.
0091   ///     - Simply a return higher in the callstack.
0092   ///
0093   ///   In terms of implementation details, as segment can be represented with
0094   ///   the beginning and ending instruction IDs from the instruction trace.
0095   ///
0096   ///  UntracedPrefixSegment:
0097   ///   It might happen that we didn't trace the beginning of a function and we
0098   ///   saw it for the first time as part of a return. As a way to signal these
0099   ///   cases, we have a placeholder UntracedPrefixSegment class that completes the
0100   ///   callgraph.
0101   ///
0102   ///  Example:
0103   ///   We might have this piece of execution:
0104   ///
0105   ///     main() [offset 0x00 to 0x20] [traced instruction ids 1 to 4]
0106   ///       foo()  [offset 0x00 to 0x80] [traced instruction ids 5 to 20] # main
0107   ///       invoked foo
0108   ///     main() [offset 0x24 to 0x40] [traced instruction ids 21 to 30]
0109   ///
0110   ///   In this case, our function main invokes foo. We have 3 segments: main
0111   ///   [offset 0x00 to 0x20], foo() [offset 0x00 to 0x80], and main() [offset
0112   ///   0x24 to 0x40]. We also have the instruction ids from the corresponding
0113   ///   linear instruction trace for each segment.
0114   ///
0115   ///   But what if we started tracing since the middle of foo? Then we'd have
0116   ///   an incomplete trace
0117   ///
0118   ///       foo() [offset 0x30 to 0x80] [traced instruction ids 1 to 10]
0119   ///     main() [offset 0x24 to 0x40] [traced instruction ids 11 to 20]
0120   ///
0121   ///   Notice that we changed the instruction ids because this is a new trace.
0122   ///   Here, in order to have a somewhat complete tree with good traversal
0123   ///   capabilities, we can create an UntracedPrefixSegment to signal the portion of
0124   ///   main() that we didn't trace. We don't know if this segment was in fact
0125   ///   multiple segments with many function calls. We'll never know. The
0126   ///   resulting tree looks like the following:
0127   ///
0128   ///     main() [untraced]
0129   ///       foo() [offset 0x30 to 0x80] [traced instruction ids 1 to 10]
0130   ///     main() [offset 0x24 to 0x40] [traced instruction ids 11 to 20]
0131   ///
0132   ///   And in pseudo-code:
0133   ///
0134   ///     FunctionCall [
0135   ///       UntracedPrefixSegment {
0136   ///         symbol: main()
0137   ///         nestedCall: FunctionCall [ # this untraced segment has a nested
0138   ///         call
0139   ///           TracedSegment {
0140   ///             symbol: foo()
0141   ///             fromInstructionId: 1
0142   ///             toInstructionId: 10
0143   ///             nestedCall: none # this doesn't have a nested call
0144   ///           }
0145   ///         }
0146   ///       ],
0147   ///       TracedSegment {
0148   ///         symbol: main()
0149   ///         fromInstructionId: 11
0150   ///         toInstructionId: 20
0151   ///         nestedCall: none # this also doesn't have a nested call
0152   ///       }
0153   ///   ]
0154   ///
0155   ///   We can see the nested structure and how instructions are represented as
0156   ///   segments.
0157   ///
0158   ///
0159   ///   Returns:
0160   ///     Code doesn't always behave intuitively. Some interesting functions
0161   ///     might modify the stack and thus change the behavior of common
0162   ///     instructions like CALL and RET. We try to identify these cases, and
0163   ///     the result is that the return edge from a segment might connect with a
0164   ///     function call very high the stack. For example, you might have
0165   ///
0166   ///     main()
0167   ///       foo()
0168   ///         bar()
0169   ///         # here bar modifies the stack and pops foo() from it. Then it
0170   ///         finished the a RET (return)
0171   ///     main() # we came back directly to main()
0172   ///
0173   ///     I have observed some trampolines doing this, as well as some std
0174   ///     functions (like ostream functions). So consumers should be aware of
0175   ///     this.
0176   ///
0177   ///     There are all sorts of "abnormal" behaviors you can see in code, and
0178   ///     whenever we fail at identifying what's going on, we prefer to create a
0179   ///     new tree.
0180   ///
0181   ///   Function call forest:
0182   ///     A single tree would suffice if a trace didn't contain errors nor
0183   ///     abnormal behaviors that made our algorithms fail. Sadly these
0184   ///     anomalies exist and we prefer not to use too many heuristics and
0185   ///     probably end up lying to the user. So we create a new tree from the
0186   ///     point we can't continue using the previous tree. This results in
0187   ///     having a forest instead of a single tree. This is probably the best we
0188   ///     can do if we consumers want to use this data to perform performance
0189   ///     analysis or reverse debugging.
0190   ///
0191   ///   Non-functions:
0192   ///     Not everything in a program is a function. There are blocks of
0193   ///     instructions that are simply labeled or even regions without symbol
0194   ///     information that we don't what they are. We treat all of them as
0195   ///     functions for simplicity.
0196   ///
0197   ///   Errors:
0198   ///     Whenever an error is found, a new tree with a single segment is
0199   ///     created. All consecutive errors after the original one are then
0200   ///     appended to this segment. As a note, something that GDB does is to use
0201   ///     some heuristics to merge trees that were interrupted by errors. We are
0202   ///     leaving that out of scope until a feature like that one is really
0203   ///     needed.
0204 
0205   /// Forward declaration
0206   class FunctionCall;
0207   using FunctionCallUP = std::unique_ptr<FunctionCall>;
0208 
0209   class FunctionCall {
0210   public:
0211     class TracedSegment {
0212     public:
0213       /// \param[in] cursor_sp
0214       ///   A cursor pointing to the beginning of the segment.
0215       ///
0216       /// \param[in] symbol_info
0217       ///   The symbol information of the first instruction of the segment.
0218       ///
0219       /// \param[in] call
0220       ///   The FunctionCall object that owns this segment.
0221       TracedSegment(const lldb::TraceCursorSP &cursor_sp,
0222                     const SymbolInfo &symbol_info, FunctionCall &owning_call)
0223           : m_first_insn_id(cursor_sp->GetId()),
0224             m_last_insn_id(cursor_sp->GetId()),
0225             m_first_symbol_info(symbol_info), m_last_symbol_info(symbol_info),
0226             m_owning_call(owning_call) {}
0227 
0228       /// \return
0229       ///   The chronologically first instruction ID in this segment.
0230       lldb::user_id_t GetFirstInstructionID() const;
0231       /// \return
0232       ///   The chronologically last instruction ID in this segment.
0233       lldb::user_id_t GetLastInstructionID() const;
0234 
0235       /// \return
0236       ///   The symbol information of the chronologically first instruction ID
0237       ///   in this segment.
0238       const SymbolInfo &GetFirstInstructionSymbolInfo() const;
0239 
0240       /// \return
0241       ///   The symbol information of the chronologically last instruction ID in
0242       ///   this segment.
0243       const SymbolInfo &GetLastInstructionSymbolInfo() const;
0244 
0245       /// \return
0246       ///   Get the call that owns this segment.
0247       const FunctionCall &GetOwningCall() const;
0248 
0249       /// Append a new instruction to this segment.
0250       ///
0251       /// \param[in] cursor_sp
0252       ///   A cursor pointing to the new instruction.
0253       ///
0254       /// \param[in] symbol_info
0255       ///   The symbol information of the new instruction.
0256       void AppendInsn(const lldb::TraceCursorSP &cursor_sp,
0257                       const SymbolInfo &symbol_info);
0258 
0259       /// Create a nested call at the end of this segment.
0260       ///
0261       /// \param[in] cursor_sp
0262       ///   A cursor pointing to the first instruction of the nested call.
0263       ///
0264       /// \param[in] symbol_info
0265       ///   The symbol information of the first instruction of the nested call.
0266       FunctionCall &CreateNestedCall(const lldb::TraceCursorSP &cursor_sp,
0267                                      const SymbolInfo &symbol_info);
0268 
0269       /// Executed the given callback if there's a nested call at the end of
0270       /// this segment.
0271       void IfNestedCall(std::function<void(const FunctionCall &function_call)>
0272                             callback) const;
0273 
0274     private:
0275       TracedSegment(const TracedSegment &) = delete;
0276       TracedSegment &operator=(TracedSegment const &);
0277 
0278       /// Delimiting instruction IDs taken chronologically.
0279       /// \{
0280       lldb::user_id_t m_first_insn_id;
0281       lldb::user_id_t m_last_insn_id;
0282       /// \}
0283       /// An optional nested call starting at the end of this segment.
0284       FunctionCallUP m_nested_call;
0285       /// The symbol information of the delimiting instructions
0286       /// \{
0287       SymbolInfo m_first_symbol_info;
0288       SymbolInfo m_last_symbol_info;
0289       /// \}
0290       FunctionCall &m_owning_call;
0291     };
0292 
0293     class UntracedPrefixSegment {
0294     public:
0295       /// Note: Untraced segments can only exist if have also seen a traced
0296       /// segment of the same function call. Thus, we can use those traced
0297       /// segments if we want symbol information and such.
0298 
0299       UntracedPrefixSegment(FunctionCallUP &&nested_call)
0300           : m_nested_call(std::move(nested_call)) {}
0301 
0302       const FunctionCall &GetNestedCall() const;
0303 
0304     private:
0305       UntracedPrefixSegment(const UntracedPrefixSegment &) = delete;
0306       UntracedPrefixSegment &operator=(UntracedPrefixSegment const &);
0307       FunctionCallUP m_nested_call;
0308     };
0309 
0310     /// Create a new function call given an instruction. This will also create a
0311     /// segment for that instruction.
0312     ///
0313     /// \param[in] cursor_sp
0314     ///   A cursor pointing to the first instruction of that function call.
0315     ///
0316     /// \param[in] symbol_info
0317     ///   The symbol information of that first instruction.
0318     FunctionCall(const lldb::TraceCursorSP &cursor_sp,
0319                  const SymbolInfo &symbol_info);
0320 
0321     /// Append a new traced segment to this function call.
0322     ///
0323     /// \param[in] cursor_sp
0324     ///   A cursor pointing to the first instruction of the new segment.
0325     ///
0326     /// \param[in] symbol_info
0327     ///   The symbol information of that first instruction.
0328     void AppendSegment(const lldb::TraceCursorSP &cursor_sp,
0329                        const SymbolInfo &symbol_info);
0330 
0331     /// \return
0332     ///   The symbol info of some traced instruction of this call.
0333     const SymbolInfo &GetSymbolInfo() const;
0334 
0335     /// \return
0336     ///   \b true if and only if the instructions in this function call are
0337     ///   trace errors, in which case this function call is a fake one.
0338     bool IsError() const;
0339 
0340     /// \return
0341     ///   The list of traced segments of this call.
0342     const std::deque<TracedSegment> &GetTracedSegments() const;
0343 
0344     /// \return
0345     ///   A non-const reference to the most-recent traced segment.
0346     TracedSegment &GetLastTracedSegment();
0347 
0348     /// Create an untraced segment for this call that jumps to the provided
0349     /// nested call.
0350     void SetUntracedPrefixSegment(FunctionCallUP &&nested_call);
0351 
0352     /// \return
0353     ///   A optional to the untraced prefix segment of this call.
0354     const std::optional<UntracedPrefixSegment> &
0355     GetUntracedPrefixSegment() const;
0356 
0357     /// \return
0358     ///   A pointer to the parent call. It may be \b nullptr.
0359     FunctionCall *GetParentCall() const;
0360 
0361     void SetParentCall(FunctionCall &parent_call);
0362 
0363   private:
0364     /// An optional untraced segment that precedes all the traced segments.
0365     std::optional<UntracedPrefixSegment> m_untraced_prefix_segment;
0366     /// The traced segments in order. We used a deque to prevent moving these
0367     /// objects when appending to the list, which would happen with vector.
0368     std::deque<TracedSegment> m_traced_segments;
0369     /// The parent call, which might be null. Useful for reconstructing
0370     /// callstacks.
0371     FunctionCall *m_parent_call = nullptr;
0372     /// Whether this call represents a list of consecutive errors.
0373     bool m_is_error;
0374   };
0375 
0376   /// Interface used to abstract away the format in which the instruction
0377   /// information will be dumped.
0378   class OutputWriter {
0379   public:
0380     virtual ~OutputWriter() = default;
0381 
0382     /// Notify this writer that the cursor ran out of data.
0383     virtual void NoMoreData() {}
0384 
0385     /// Dump a trace item (instruction, error or event).
0386     virtual void TraceItem(const TraceItem &item) = 0;
0387 
0388     /// Dump a function call forest.
0389     virtual void
0390     FunctionCallForest(const std::vector<FunctionCallUP> &forest) = 0;
0391   };
0392 
0393   /// Create a instruction dumper for the cursor.
0394   ///
0395   /// \param[in] cursor
0396   ///     The cursor whose instructions will be dumped.
0397   ///
0398   /// \param[in] s
0399   ///     The stream where to dump the instructions to.
0400   ///
0401   /// \param[in] options
0402   ///     Additional options for configuring the dumping.
0403   TraceDumper(lldb::TraceCursorSP cursor_sp, Stream &s,
0404               const TraceDumperOptions &options);
0405 
0406   /// Dump \a count instructions of the thread trace starting at the current
0407   /// cursor position.
0408   ///
0409   /// This effectively moves the cursor to the next unvisited position, so that
0410   /// a subsequent call to this method continues where it left off.
0411   ///
0412   /// \param[in] count
0413   ///     The number of instructions to print.
0414   ///
0415   /// \return
0416   ///     The instruction id of the last traversed instruction, or \b
0417   ///     std::nullopt if no instructions were visited.
0418   std::optional<lldb::user_id_t> DumpInstructions(size_t count);
0419 
0420   /// Dump all function calls forwards chronologically and hierarchically
0421   void DumpFunctionCalls();
0422 
0423 private:
0424   /// Create a trace item for the current position without symbol information.
0425   TraceItem CreatRawTraceItem();
0426 
0427   lldb::TraceCursorSP m_cursor_sp;
0428   TraceDumperOptions m_options;
0429   std::unique_ptr<OutputWriter> m_writer_up;
0430 };
0431 
0432 } // namespace lldb_private
0433 
0434 #endif // LLDB_TARGET_TRACE_INSTRUCTION_DUMPER_H