diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h +++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h @@ -51,61 +51,6 @@ lldb::addr_t m_address; }; -/// \class IntelPTInstruction -/// An instruction obtained from decoding a trace. It is either an actual -/// instruction or an error indicating a gap in the trace. -/// -/// Gaps in the trace can come in a few flavors: -/// - tracing gaps (e.g. tracing was paused and then resumed) -/// - tracing errors (e.g. buffer overflow) -/// - decoding errors (e.g. some memory region couldn't be decoded) -/// As mentioned, any gap is represented as an error in this class. -class IntelPTInstruction { -public: - IntelPTInstruction(const pt_insn &pt_insn) - : m_pt_insn(pt_insn), m_is_error(false) {} - - /// Error constructor - IntelPTInstruction(); - - /// Check if this object represents an error (i.e. a gap). - /// - /// \return - /// Whether this object represents an error. - bool IsError() const; - - /// \return - /// The instruction pointer address, or \a LLDB_INVALID_ADDRESS if it is - /// an error. - lldb::addr_t GetLoadAddress() const; - - /// Get the size in bytes of an instance of this class - static size_t GetMemoryUsage(); - - /// Get the \a lldb::TraceInstructionControlFlowType categories of the - /// instruction. - /// - /// \param[in] next_load_address - /// The address of the next instruction in the trace or \b - /// LLDB_INVALID_ADDRESS if not available. - /// - /// \return - /// The control flow categories, or \b 0 if the instruction is an error. - lldb::TraceInstructionControlFlowType - GetControlFlowType(lldb::addr_t next_load_address) const; - - IntelPTInstruction(IntelPTInstruction &&other) = default; - -private: - IntelPTInstruction(const IntelPTInstruction &other) = delete; - const IntelPTInstruction &operator=(const IntelPTInstruction &other) = delete; - - // When adding new members to this class, make sure to update - // IntelPTInstruction::GetMemoryUsage() if needed. - pt_insn m_pt_insn; - bool m_is_error; -}; - /// \class DecodedThread /// Class holding the instructions and function call hierarchy obtained from /// decoding a trace, as well as a position cursor used when reverse debugging @@ -178,14 +123,27 @@ /// Append a decoding error (i.e. an instruction that failed to be decoded). void AppendError(llvm::Error &&error); - /// Get the instructions from the decoded trace. Some of them might indicate - /// errors (i.e. gaps) in the trace. For an instruction error, you can access - /// its underlying error message with the \a GetErrorByInstructionIndex() - /// method. + /// Append a decoding error with a corresponding TSC. + void AppendError(llvm::Error &&error, uint64_t tsc); + + /// Get the total number of instruction pointers from the decoded trace. + /// This will include instructions that indicate errors (or gaps) in the + /// trace. For an instruction error, you can access its underlying error + /// message with the \a GetErrorByInstructionIndex() method. + size_t GetInstructionsCount() const; + + /// \return + /// The load address of the instruction at the given index, or \a + /// LLDB_INVALID_ADDRESS if it is an error. + lldb::addr_t GetInstructionLoadAddress(size_t insn_index) const; + + /// Get the \a lldb::TraceInstructionControlFlowType categories of the + /// instruction. /// /// \return - /// The instructions of the trace. - llvm::ArrayRef GetInstructions() const; + /// The control flow categories, or \b 0 if the instruction is an error. + lldb::TraceInstructionControlFlowType + GetInstructionControlFlowType(size_t insn_index) const; /// Construct the TSC range that covers the given instruction index. /// This operation is O(logn) and should be used sparingly. @@ -204,17 +162,6 @@ /// points to a valid instruction. const char *GetErrorByInstructionIndex(size_t ins_idx); - /// Append a decoding error with a corresponding TSC. - void AppendError(llvm::Error &&error, uint64_t TSC); - - /// Record an error decoding a TSC timestamp. - /// - /// See \a GetTscErrors() for more documentation. - /// - /// \param[in] libipt_error_code - /// An error returned by the libipt library. - void RecordTscError(int libipt_error_code); - /// Get a new cursor for the decoded thread. lldb::TraceCursorUP GetCursor(); @@ -235,6 +182,14 @@ /// The number of TSC decoding errors. const LibiptErrors &GetTscErrors() const; + /// Record an error decoding a TSC timestamp. + /// + /// See \a GetTscErrors() for more documentation. + /// + /// \param[in] libipt_error_code + /// An error returned by the libipt library. + void RecordTscError(int libipt_error_code); + /// The approximate size in bytes used by this instance, /// including all the already decoded instructions. size_t CalculateApproximateMemoryUsage() const; @@ -251,7 +206,12 @@ lldb::ThreadSP m_thread_sp; /// The low level storage of all instruction addresses. Each instruction has /// an index in this vector and it will be used in other parts of the code. - std::vector m_instructions; + std::vector m_instruction_ips; + /// The size in bytes of each instruction. + std::vector m_instruction_sizes; + /// The libipt instruction class for each instruction. + std::vector m_instruction_classes; + /// This map contains the TSCs of the decoded instructions. It maps /// `instruction index -> TSC`, where `instruction index` is the first index /// at which the mapped TSC appears. We use this representation because TSCs diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp @@ -35,38 +35,37 @@ OS << "error: " << libipt_error_message; } -IntelPTInstruction::IntelPTInstruction() { - m_pt_insn.ip = LLDB_INVALID_ADDRESS; - m_pt_insn.iclass = ptic_error; - m_is_error = true; +Optional DecodedThread::GetRawTraceSize() const { + return m_raw_trace_size; } -bool IntelPTInstruction::IsError() const { return m_is_error; } - -lldb::addr_t IntelPTInstruction::GetLoadAddress() const { return m_pt_insn.ip; } - -size_t IntelPTInstruction::GetMemoryUsage() { - return sizeof(IntelPTInstruction); +size_t DecodedThread::GetInstructionsCount() const { + return m_instruction_ips.size(); } -Optional DecodedThread::GetRawTraceSize() const { - return m_raw_trace_size; +lldb::addr_t DecodedThread::GetInstructionLoadAddress(size_t insn_index) const { + return m_instruction_ips[insn_index]; } TraceInstructionControlFlowType -IntelPTInstruction::GetControlFlowType(lldb::addr_t next_load_address) const { - if (IsError()) +DecodedThread::GetInstructionControlFlowType(size_t insn_index) const { + if (IsInstructionAnError(insn_index)) return (TraceInstructionControlFlowType)0; TraceInstructionControlFlowType mask = eTraceInstructionControlFlowTypeInstruction; - switch (m_pt_insn.iclass) { + lldb::addr_t load_address = m_instruction_ips[insn_index]; + uint8_t insn_byte_size = m_instruction_sizes[insn_index]; + pt_insn_class iclass = m_instruction_classes[insn_index]; + + switch (iclass) { case ptic_cond_jump: case ptic_jump: case ptic_far_jump: mask |= eTraceInstructionControlFlowTypeBranch; - if (m_pt_insn.ip + m_pt_insn.size != next_load_address) + if (insn_index + 1 < m_instruction_ips.size() && + load_address + insn_byte_size != m_instruction_ips[insn_index + 1]) mask |= eTraceInstructionControlFlowTypeTakenBranch; break; case ptic_return: @@ -92,14 +91,16 @@ // get a first valid TSC not in position 0. We can safely force these error // instructions to use the first valid TSC, so that all the trace has TSCs. size_t start_index = - m_instruction_timestamps.empty() ? 0 : m_instructions.size() - 1; + m_instruction_timestamps.empty() ? 0 : m_instruction_ips.size() - 1; m_instruction_timestamps.emplace(start_index, tsc); m_last_tsc = tsc; } } void DecodedThread::AppendInstruction(const pt_insn &insn) { - m_instructions.emplace_back(insn); + m_instruction_ips.emplace_back(insn.ip); + m_instruction_sizes.emplace_back(insn.size); + m_instruction_classes.emplace_back(insn.iclass); } void DecodedThread::AppendInstruction(const pt_insn &insn, uint64_t tsc) { @@ -108,8 +109,10 @@ } void DecodedThread::AppendError(llvm::Error &&error) { - m_errors.try_emplace(m_instructions.size(), toString(std::move(error))); - m_instructions.emplace_back(); + m_errors.try_emplace(m_instruction_ips.size(), toString(std::move(error))); + m_instruction_ips.emplace_back(LLDB_INVALID_ADDRESS); + m_instruction_sizes.emplace_back(0); + m_instruction_classes.emplace_back(pt_insn_class::ptic_unknown); } void DecodedThread::AppendError(llvm::Error &&error, uint64_t tsc) { @@ -130,10 +133,6 @@ return m_tsc_errors; } -ArrayRef DecodedThread::GetInstructions() const { - return makeArrayRef(m_instructions); -} - Optional DecodedThread::CalculateTscRange(size_t insn_index) const { auto it = m_instruction_timestamps.upper_bound(insn_index); @@ -144,7 +143,7 @@ } bool DecodedThread::IsInstructionAnError(size_t insn_idx) const { - return m_instructions[insn_idx].IsError(); + return m_instruction_ips[insn_idx] == LLDB_INVALID_ADDRESS; } const char *DecodedThread::GetErrorByInstructionIndex(size_t insn_idx) { @@ -167,13 +166,16 @@ lldb::TraceCursorUP DecodedThread::GetCursor() { // We insert a fake error signaling an empty trace if needed becasue the // TraceCursor requires non-empty traces. - if (m_instructions.empty()) + if (m_instruction_ips.empty()) AppendError(createStringError(inconvertibleErrorCode(), "empty trace")); return std::make_unique(m_thread_sp, shared_from_this()); } size_t DecodedThread::CalculateApproximateMemoryUsage() const { - return IntelPTInstruction::GetMemoryUsage() * m_instructions.size() + + return sizeof(pt_insn::ip) * m_instruction_ips.size() + + sizeof(pt_insn::size) * m_instruction_sizes.size() + + sizeof(pt_insn::iclass) * m_instruction_classes.size() + + (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size() + m_errors.getMemorySize(); } @@ -183,7 +185,7 @@ auto next_it = m_it; ++next_it; m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end()) - ? m_decoded_thread->GetInstructions().size() - 1 + ? m_decoded_thread->GetInstructionsCount() - 1 : next_it->first - 1; } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp @@ -20,14 +20,14 @@ TraceCursorIntelPT::TraceCursorIntelPT(ThreadSP thread_sp, DecodedThreadSP decoded_thread_sp) : TraceCursor(thread_sp), m_decoded_thread_sp(decoded_thread_sp) { - assert(!m_decoded_thread_sp->GetInstructions().empty() && + assert(m_decoded_thread_sp->GetInstructionsCount() > 0 && "a trace should have at least one instruction or error"); - m_pos = m_decoded_thread_sp->GetInstructions().size() - 1; + m_pos = m_decoded_thread_sp->GetInstructionsCount() - 1; m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos); } size_t TraceCursorIntelPT::GetInternalInstructionSize() { - return m_decoded_thread_sp->GetInstructions().size(); + return m_decoded_thread_sp->GetInstructionsCount(); } bool TraceCursorIntelPT::Next() { @@ -78,8 +78,8 @@ return std::abs(dist); } }; - - int64_t dist = FindDistanceAndSetPos(); + + int64_t dist = FindDistanceAndSetPos(); m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos); return dist; } @@ -93,7 +93,7 @@ } lldb::addr_t TraceCursorIntelPT::GetLoadAddress() { - return m_decoded_thread_sp->GetInstructions()[m_pos].GetLoadAddress(); + return m_decoded_thread_sp->GetInstructionLoadAddress(m_pos); } Optional @@ -109,10 +109,5 @@ TraceInstructionControlFlowType TraceCursorIntelPT::GetInstructionControlFlowType() { - lldb::addr_t next_load_address = - m_pos + 1 < GetInternalInstructionSize() - ? m_decoded_thread_sp->GetInstructions()[m_pos + 1].GetLoadAddress() - : LLDB_INVALID_ADDRESS; - return m_decoded_thread_sp->GetInstructions()[m_pos].GetControlFlowType( - next_load_address); + return m_decoded_thread_sp->GetInstructionControlFlowType(m_pos); } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -112,7 +112,7 @@ } s << "\n"; DecodedThreadSP decoded_trace_sp = Decode(thread); - size_t insn_len = decoded_trace_sp->GetInstructions().size(); + size_t insn_len = decoded_trace_sp->GetInstructionsCount(); size_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage(); s.Format(" Raw trace size: {0} KiB\n", *raw_size / 1024); diff --git a/lldb/test/API/commands/trace/TestTraceDumpInfo.py b/lldb/test/API/commands/trace/TestTraceDumpInfo.py --- a/lldb/test/API/commands/trace/TestTraceDumpInfo.py +++ b/lldb/test/API/commands/trace/TestTraceDumpInfo.py @@ -40,7 +40,7 @@ thread #1: tid = 3842849 Raw trace size: 4 KiB Total number of instructions: 21 - Total approximate memory usage: 0.98 KiB - Average memory usage per instruction: 48.00 bytes + Total approximate memory usage: 0.27 KiB + Average memory usage per instruction: 13.00 bytes Number of TSC decoding errors: 0''']) diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py --- a/lldb/test/API/commands/trace/TestTraceLoad.py +++ b/lldb/test/API/commands/trace/TestTraceLoad.py @@ -38,8 +38,10 @@ thread #1: tid = 3842849 Raw trace size: 4 KiB Total number of instructions: 21 - Total approximate memory usage: 0.98 KiB - Average memory usage per instruction: 48.00 bytes''']) + Total approximate memory usage: 0.27 KiB + Average memory usage per instruction: 13.00 bytes + + Number of TSC decoding errors: 0''']) def testLoadInvalidTraces(self): src_dir = self.getSourceDir()