zrthxn updated this revision to Diff 419656.
zrthxn edited the summary of this revision.
zrthxn added a comment.
The difference in memory usage is appreciable with large number of
instructions, as shown below
# Before (with current metrics, total memory does not include raw trace size)
Raw trace size: 2048 KiB
Total number of instructions: 900004
Total approximate memory usage: 56143.10 KiB
Average memory usage per instruction: 63.87 bytes
# After
Raw trace size: 2048 KiB
Total number of instructions: 900004
Total approximate memory usage: 42187.69 KiB
Average memory usage per instruction: 48.00 bytes
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D122603/new/
https://reviews.llvm.org/D122603
Files:
lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/test/API/commands/trace/TestTraceDumpInfo.py
lldb/test/API/commands/trace/TestTraceLoad.py
Index: lldb/test/API/commands/trace/TestTraceLoad.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceLoad.py
+++ lldb/test/API/commands/trace/TestTraceLoad.py
@@ -38,8 +38,8 @@
thread #1: tid = 3842849
Raw trace size: 4 KiB
Total number of instructions: 21
- Total approximate memory usage: 5.31 KiB
- Average memory usage per instruction: 259 bytes'''])
+ Total approximate memory usage: 0.98 KiB
+ Average memory usage per instruction: 48.00 bytes'''])
def testLoadInvalidTraces(self):
src_dir = self.getSourceDir()
Index: lldb/test/API/commands/trace/TestTraceDumpInfo.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceDumpInfo.py
+++ lldb/test/API/commands/trace/TestTraceDumpInfo.py
@@ -40,5 +40,5 @@
thread #1: tid = 3842849
Raw trace size: 4 KiB
Total number of instructions: 21
- Total approximate memory usage: 5.31 KiB
- Average memory usage per instruction: 259 bytes'''])
+ Total approximate memory usage: 0.98 KiB
+ Average memory usage per instruction: 48.00 bytes'''])
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -119,8 +119,9 @@
s.Printf(" Total number of instructions: %zu\n", insn_len);
s.Printf(" Total approximate memory usage: %0.2lf KiB\n",
(double)mem_used / 1024);
- s.Printf(" Average memory usage per instruction: %zu bytes\n",
- mem_used / insn_len);
+ if (insn_len != 0)
+ s.Printf(" Average memory usage per instruction: %0.2lf bytes\n",
+ (double)mem_used / insn_len);
return;
}
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -42,6 +42,8 @@
DecodedThreadSP m_decoded_thread_sp;
/// Internal instruction index currently pointing at.
size_t m_pos;
+ /// Tsc range covering the current instruction.
+ llvm::Optional<DecodedThread::TscRange> m_tsc_range;
};
} // namespace trace_intel_pt
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -23,6 +23,7 @@
assert(!m_decoded_thread_sp->GetInstructions().empty() &&
"a trace should have at least one instruction or error");
m_pos = m_decoded_thread_sp->GetInstructions().size() - 1;
+ m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos);
}
size_t TraceCursorIntelPT::GetInternalInstructionSize() {
@@ -40,6 +41,10 @@
while (canMoveOne()) {
m_pos += IsForwards() ? 1 : -1;
+
+ if (m_tsc_range && !m_tsc_range->InRange(m_pos))
+ m_tsc_range = IsForwards() ? m_tsc_range->Next() : m_tsc_range->Prev();
+
if (!m_ignore_errors && IsError())
return true;
if (GetInstructionControlFlowType() & m_granularity)
@@ -58,23 +63,29 @@
return std::min(std::max((int64_t)0, raw_pos), last_index);
};
- switch (origin) {
- case TraceCursor::SeekType::Set:
- m_pos = fitPosToBounds(offset);
- return m_pos;
- case TraceCursor::SeekType::End:
- m_pos = fitPosToBounds(offset + last_index);
- return last_index - m_pos;
- case TraceCursor::SeekType::Current:
- int64_t new_pos = fitPosToBounds(offset + m_pos);
- int64_t dist = m_pos - new_pos;
- m_pos = new_pos;
- return std::abs(dist);
- }
+ auto FindDistanceAndSetPos = [&]() -> int64_t {
+ switch (origin) {
+ case TraceCursor::SeekType::Set:
+ m_pos = fitPosToBounds(offset);
+ return m_pos;
+ case TraceCursor::SeekType::End:
+ m_pos = fitPosToBounds(offset + last_index);
+ return last_index - m_pos;
+ case TraceCursor::SeekType::Current:
+ int64_t new_pos = fitPosToBounds(offset + m_pos);
+ int64_t dist = m_pos - new_pos;
+ m_pos = new_pos;
+ return std::abs(dist);
+ }
+ };
+
+ int64_t dist = FindDistanceAndSetPos();
+ m_tsc_range = m_decoded_thread_sp->CalculateTscRange(m_pos);
+ return dist;
}
bool TraceCursorIntelPT::IsError() {
- return m_decoded_thread_sp->GetInstructions()[m_pos].IsError();
+ return m_decoded_thread_sp->IsInstructionAnError(m_pos);
}
const char *TraceCursorIntelPT::GetError() {
@@ -85,10 +96,14 @@
return m_decoded_thread_sp->GetInstructions()[m_pos].GetLoadAddress();
}
-Optional<uint64_t> TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) {
+Optional<uint64_t>
+TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) {
switch (counter_type) {
- case lldb::eTraceCounterTSC:
- return m_decoded_thread_sp->GetInstructions()[m_pos].GetTimestampCounter();
+ case lldb::eTraceCounterTSC:
+ if (m_tsc_range)
+ return m_tsc_range->GetTsc();
+ else
+ return llvm::None;
}
}
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -62,9 +62,6 @@
/// As mentioned, any gap is represented as an error in this class.
class IntelPTInstruction {
public:
- IntelPTInstruction(const pt_insn &pt_insn, uint64_t timestamp)
- : m_pt_insn(pt_insn), m_timestamp(timestamp), m_is_error(false) {}
-
IntelPTInstruction(const pt_insn &pt_insn)
: m_pt_insn(pt_insn), m_is_error(false) {}
@@ -85,13 +82,6 @@
/// Get the size in bytes of an instance of this class
static size_t GetMemoryUsage();
- /// Get the timestamp associated with the current instruction. The timestamp
- /// is similar to what a rdtsc instruction would return.
- ///
- /// \return
- /// The timestamp or \b llvm::None if not available.
- llvm::Optional<uint64_t> GetTimestampCounter() const;
-
/// Get the \a lldb::TraceInstructionControlFlowType categories of the
/// instruction.
///
@@ -111,9 +101,8 @@
const IntelPTInstruction &operator=(const IntelPTInstruction &other) = delete;
// When adding new members to this class, make sure to update
- // IntelPTInstruction::GetNonErrorMemoryUsage() if needed.
+ // IntelPTInstruction::GetMemoryUsage() if needed.
pt_insn m_pt_insn;
- llvm::Optional<uint64_t> m_timestamp;
bool m_is_error;
};
@@ -126,11 +115,60 @@
/// stopped at. See \a Trace::GetCursorPosition for more information.
class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
public:
+ /// \class TscRange
+ /// Class that represents the trace range associated with a given TSC.
+ /// It provides efficient iteration to the previous or next TSC range in the
+ /// decoded trace.
+ ///
+ /// TSC timestamps are emitted by the decoder infrequently, which means
+ /// that each TSC covers a range of instruction indices, which can be used to
+ /// speed up TSC lookups.
+ class TscRange {
+ public:
+ /// Check if this TSC range includes the given instruction index.
+ bool InRange(size_t insn_index);
+
+ /// Get the next range chronologically.
+ llvm::Optional<TscRange> Next();
+
+ /// Get the previous range chronologically.
+ llvm::Optional<TscRange> Prev();
+
+ /// Get the TSC value.
+ size_t GetTsc() const;
+ /// Get the smallest instruction index that has this TSC.
+ size_t GetStartInstructionIndex() const;
+ /// Get the largest instruction index that has this TSC.
+ size_t GetEndInstructionIndex() const;
+
+ private:
+ friend class DecodedThread;
+
+ TscRange(std::map<size_t, uint64_t>::const_iterator it,
+ const DecodedThread &decoded_thread);
+
+ /// The iterator pointing to the beginning of the range.
+ std::map<size_t, uint64_t>::const_iterator m_it;
+ /// The largest instruction index that has this TSC.
+ size_t m_end_index;
+
+ const DecodedThread *m_decoded_thread;
+ };
+
DecodedThread(lldb::ThreadSP thread_sp);
/// Utility constructor that initializes the trace with a provided error.
DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err);
+ /// Append a successfully decoded instruction.
+ void AppendInstruction(const pt_insn &instruction);
+
+ /// Append a sucessfully decoded instruction with an associated TSC timestamp.
+ void AppendInstruction(const pt_insn &instruction, uint64_t tsc);
+
+ /// 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()
@@ -140,20 +178,22 @@
/// The instructions of the trace.
llvm::ArrayRef<IntelPTInstruction> GetInstructions() const;
+ /// Construct the TSC range that covers the given instruction index.
+ /// This operation is O(logn) and should be used sparingly.
+ /// If the trace was collected with TSC support, all the instructions of
+ /// the trace will have associated TSCs. This means that this method will
+ /// only return \b llvm::None if there are no TSCs whatsoever in the trace.
+ llvm::Optional<TscRange> CalculateTscRange(size_t insn_index) const;
+
+ /// Check if an instruction given by its index is an error.
+ bool IsInstructionAnError(size_t insn_idx) const;
+
/// Get the error associated with a given instruction index.
///
/// \return
/// The error message of \b nullptr if the given index
/// points to a valid instruction.
- const char *GetErrorByInstructionIndex(uint64_t ins_idx);
-
- /// Append a successfully decoded instruction.
- template <typename... Ts> void AppendInstruction(Ts... instruction_args) {
- m_instructions.emplace_back(instruction_args...);
- }
-
- /// Append a decoding error (i.e. an instruction that failed to be decoded).
- void AppendError(llvm::Error &&error);
+ const char *GetErrorByInstructionIndex(size_t ins_idx);
/// Get a new cursor for the decoded thread.
lldb::TraceCursorUP GetCursor();
@@ -177,8 +217,23 @@
/// When adding new members to this class, make sure
/// to update \a CalculateApproximateMemoryUsage() accordingly.
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<IntelPTInstruction> m_instructions;
+ /// 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
+ /// are sporadic and we can think of them as ranges. If TSCs are present in
+ /// the trace, all instructions will have an associated TSC, including the
+ /// first one. Otherwise, this map will be empty.
+ std::map<size_t, uint64_t> m_instruction_timestamps;
+ /// This is the chronologically last TSC that has been added.
+ llvm::Optional<uint64_t> m_last_tsc = llvm::None;
+ // This variables stores the messages of all the error instructions in the
+ // trace. It maps `instruction index -> error message`.
llvm::DenseMap<uint64_t, std::string> m_errors;
+ /// The size in bytes of the raw buffer before decoding. It might be None if
+ /// the decoding failed.
llvm::Optional<size_t> m_raw_trace_size;
};
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -49,10 +49,6 @@
return sizeof(IntelPTInstruction);
}
-Optional<uint64_t> IntelPTInstruction::GetTimestampCounter() const {
- return m_timestamp;
-}
-
Optional<size_t> DecodedThread::GetRawTraceSize() const {
return m_raw_trace_size;
}
@@ -90,6 +86,23 @@
ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
+void DecodedThread::AppendInstruction(const pt_insn &insn) {
+ m_instructions.emplace_back(insn);
+}
+
+void DecodedThread::AppendInstruction(const pt_insn &insn, uint64_t tsc) {
+ m_instructions.emplace_back(insn);
+ if (!m_last_tsc || *m_last_tsc != tsc) {
+ // In case the first instructions are errors or did not have a TSC, we'll
+ // 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.emplace(start_index, tsc);
+ m_last_tsc = tsc;
+ }
+}
+
void DecodedThread::AppendError(llvm::Error &&error) {
m_errors.try_emplace(m_instructions.size(), toString(std::move(error)));
m_instructions.emplace_back();
@@ -99,8 +112,21 @@
return makeArrayRef(m_instructions);
}
-const char *DecodedThread::GetErrorByInstructionIndex(uint64_t idx) {
- auto it = m_errors.find(idx);
+Optional<DecodedThread::TscRange>
+DecodedThread::CalculateTscRange(size_t insn_index) const {
+ auto it = m_instruction_timestamps.upper_bound(insn_index);
+ if (it == m_instruction_timestamps.begin())
+ return None;
+
+ return TscRange(--it, *this);
+}
+
+bool DecodedThread::IsInstructionAnError(size_t insn_idx) const {
+ return m_instructions[insn_idx].IsError();
+}
+
+const char *DecodedThread::GetErrorByInstructionIndex(size_t insn_idx) {
+ auto it = m_errors.find(insn_idx);
if (it == m_errors.end())
return nullptr;
@@ -125,7 +151,47 @@
}
size_t DecodedThread::CalculateApproximateMemoryUsage() const {
- return m_raw_trace_size.getValueOr(0) +
- IntelPTInstruction::GetMemoryUsage() * m_instructions.size() +
+ return IntelPTInstruction::GetMemoryUsage() * m_instructions.size() +
m_errors.getMemorySize();
}
+
+DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
+ const DecodedThread &decoded_thread)
+ : m_it(it), m_decoded_thread(&decoded_thread) {
+ 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
+ : next_it->first - 1;
+}
+
+size_t DecodedThread::TscRange::GetTsc() const { return m_it->second; }
+
+size_t DecodedThread::TscRange::GetStartInstructionIndex() const {
+ return m_it->first;
+}
+
+size_t DecodedThread::TscRange::GetEndInstructionIndex() const {
+ return m_end_index;
+}
+
+bool DecodedThread::TscRange::InRange(size_t insn_index) {
+ return GetStartInstructionIndex() <= insn_index &&
+ insn_index <= GetEndInstructionIndex();
+}
+
+Optional<DecodedThread::TscRange> DecodedThread::TscRange::Next() {
+ auto next_it = m_it;
+ ++next_it;
+ if (next_it == m_decoded_thread->m_instruction_timestamps.end())
+ return None;
+ return TscRange(next_it, *m_decoded_thread);
+}
+
+Optional<DecodedThread::TscRange> DecodedThread::TscRange::Prev() {
+ if (m_it == m_decoded_thread->m_instruction_timestamps.begin())
+ return None;
+ auto prev_it = m_it;
+ --prev_it;
+ return TscRange(prev_it, *m_decoded_thread);
+}
\ No newline at end of file
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits