xref: /llvm-project/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp (revision a50ea2f76f993f65c8756067f7ad5a21e560b0c9)
1cfd96f05SWalter Erquinigo //===-- DecodedThread.cpp -------------------------------------------------===//
2cfd96f05SWalter Erquinigo //
3cfd96f05SWalter Erquinigo // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4cfd96f05SWalter Erquinigo // See https://llvm.org/LICENSE.txt for license information.
5cfd96f05SWalter Erquinigo // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6cfd96f05SWalter Erquinigo //
7cfd96f05SWalter Erquinigo //===----------------------------------------------------------------------===//
8cfd96f05SWalter Erquinigo 
9cfd96f05SWalter Erquinigo #include "DecodedThread.h"
10b0aa7076SWalter Erquinigo #include "TraceCursorIntelPT.h"
11c4fb631cSWalter Erquinigo #include <intel-pt.h>
126423b502SWalter Erquinigo #include <memory>
13f190ce62SKazu Hirata #include <optional>
14cfd96f05SWalter Erquinigo 
15b0aa7076SWalter Erquinigo using namespace lldb;
16cfd96f05SWalter Erquinigo using namespace lldb_private;
17cfd96f05SWalter Erquinigo using namespace lldb_private::trace_intel_pt;
18cfd96f05SWalter Erquinigo using namespace llvm;
19cfd96f05SWalter Erquinigo 
20cfd96f05SWalter Erquinigo char IntelPTError::ID;
21cfd96f05SWalter Erquinigo 
IntelPTError(int libipt_error_code,lldb::addr_t address)22cfd96f05SWalter Erquinigo IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
23cfd96f05SWalter Erquinigo     : m_libipt_error_code(libipt_error_code), m_address(address) {
24cfd96f05SWalter Erquinigo   assert(libipt_error_code < 0);
25cfd96f05SWalter Erquinigo }
26cfd96f05SWalter Erquinigo 
log(llvm::raw_ostream & OS) const27cfd96f05SWalter Erquinigo void IntelPTError::log(llvm::raw_ostream &OS) const {
28a7d6c3efSWalter Erquinigo   OS << pt_errstr(pt_errcode(m_libipt_error_code));
29a7d6c3efSWalter Erquinigo   if (m_address != LLDB_INVALID_ADDRESS && m_address > 0)
30a7d6c3efSWalter Erquinigo     OS << formatv(": {0:x+16}", m_address);
31cfd96f05SWalter Erquinigo }
32cfd96f05SWalter Erquinigo 
InRange(uint64_t item_index) const334f676c25SWalter Erquinigo bool DecodedThread::TSCRange::InRange(uint64_t item_index) const {
344f676c25SWalter Erquinigo   return item_index >= first_item_index &&
354f676c25SWalter Erquinigo          item_index < first_item_index + items_count;
36059f39d2SWalter Erquinigo }
37059f39d2SWalter Erquinigo 
InRange(uint64_t item_index) const384f676c25SWalter Erquinigo bool DecodedThread::NanosecondsRange::InRange(uint64_t item_index) const {
394f676c25SWalter Erquinigo   return item_index >= first_item_index &&
404f676c25SWalter Erquinigo          item_index < first_item_index + items_count;
414f676c25SWalter Erquinigo }
424f676c25SWalter Erquinigo 
GetInterpolatedTime(uint64_t item_index,uint64_t begin_of_time_nanos,const LinuxPerfZeroTscConversion & tsc_conversion) const434f676c25SWalter Erquinigo double DecodedThread::NanosecondsRange::GetInterpolatedTime(
444f676c25SWalter Erquinigo     uint64_t item_index, uint64_t begin_of_time_nanos,
454f676c25SWalter Erquinigo     const LinuxPerfZeroTscConversion &tsc_conversion) const {
464f676c25SWalter Erquinigo   uint64_t items_since_last_tsc = item_index - first_item_index;
474f676c25SWalter Erquinigo 
484f676c25SWalter Erquinigo   auto interpolate = [&](uint64_t next_range_start_ns) {
494f676c25SWalter Erquinigo     if (next_range_start_ns == nanos) {
504f676c25SWalter Erquinigo       // If the resolution of the conversion formula is bad enough to consider
514f676c25SWalter Erquinigo       // these two timestamps as equal, then we just increase the next one by 1
524f676c25SWalter Erquinigo       // for correction
534f676c25SWalter Erquinigo       next_range_start_ns++;
544f676c25SWalter Erquinigo     }
554f676c25SWalter Erquinigo     long double item_duration =
564f676c25SWalter Erquinigo         static_cast<long double>(items_count) / (next_range_start_ns - nanos);
574f676c25SWalter Erquinigo     return (nanos - begin_of_time_nanos) + items_since_last_tsc * item_duration;
584f676c25SWalter Erquinigo   };
594f676c25SWalter Erquinigo 
604f676c25SWalter Erquinigo   if (!next_range) {
614f676c25SWalter Erquinigo     // If this is the last TSC range, so we have to extrapolate. In this case,
624f676c25SWalter Erquinigo     // we assume that each instruction took one TSC, which is what an
634f676c25SWalter Erquinigo     // instruction would take if no parallelism is achieved and the frequency
644f676c25SWalter Erquinigo     // multiplier is 1.
654f676c25SWalter Erquinigo     return interpolate(tsc_conversion.ToNanos(tsc + items_count));
664f676c25SWalter Erquinigo   }
674f676c25SWalter Erquinigo   if (items_count < (next_range->tsc - tsc)) {
684f676c25SWalter Erquinigo     // If the numbers of items in this range is less than the total TSC duration
694f676c25SWalter Erquinigo     // of this range, i.e. each instruction taking longer than 1 TSC, then we
704f676c25SWalter Erquinigo     // can assume that something else happened between these TSCs (e.g. a
714f676c25SWalter Erquinigo     // context switch, change to kernel, decoding errors, etc). In this case, we
724f676c25SWalter Erquinigo     // also assume that each instruction took 1 TSC. A proper way to improve
734f676c25SWalter Erquinigo     // this would be to analize the next events in the trace looking for context
744f676c25SWalter Erquinigo     // switches or trace disablement events, but for now, as we only want an
754f676c25SWalter Erquinigo     // approximation, we keep it simple. We are also guaranteed that the time in
764f676c25SWalter Erquinigo     // nanos of the next range is different to the current one, just because of
774f676c25SWalter Erquinigo     // the definition of a NanosecondsRange.
784f676c25SWalter Erquinigo     return interpolate(
794f676c25SWalter Erquinigo         std::min(tsc_conversion.ToNanos(tsc + items_count), next_range->nanos));
804f676c25SWalter Erquinigo   }
814f676c25SWalter Erquinigo 
824f676c25SWalter Erquinigo   // In this case, each item took less than 1 TSC, so some parallelism was
834f676c25SWalter Erquinigo   // achieved, which is an indication that we didn't suffered of any kind of
844f676c25SWalter Erquinigo   // interruption.
854f676c25SWalter Erquinigo   return interpolate(next_range->nanos);
864f676c25SWalter Erquinigo }
874f676c25SWalter Erquinigo 
GetItemsCount() const88*a50ea2f7SNicholas Mosier uint64_t DecodedThread::GetItemsCount() const { return m_item_data.size(); }
894f676c25SWalter Erquinigo 
904f676c25SWalter Erquinigo lldb::addr_t
GetInstructionLoadAddress(uint64_t item_index) const914f676c25SWalter Erquinigo DecodedThread::GetInstructionLoadAddress(uint64_t item_index) const {
92*a50ea2f7SNicholas Mosier   return std::get<lldb::addr_t>(m_item_data[item_index]);
93cfd96f05SWalter Erquinigo }
94cfd96f05SWalter Erquinigo 
95e17cae07SWalter Erquinigo lldb::addr_t
GetSyncPointOffsetByIndex(uint64_t item_index) const96e17cae07SWalter Erquinigo DecodedThread::GetSyncPointOffsetByIndex(uint64_t item_index) const {
97e17cae07SWalter Erquinigo   return m_psb_offsets.find(item_index)->second;
98e17cae07SWalter Erquinigo }
99e17cae07SWalter Erquinigo 
GetThread()100bcf1978aSAlisamar Husain ThreadSP DecodedThread::GetThread() { return m_thread_sp; }
101bcf1978aSAlisamar Husain 
102*a50ea2f7SNicholas Mosier template <typename Data>
103a7d6c3efSWalter Erquinigo DecodedThread::TraceItemStorage &
CreateNewTraceItem(lldb::TraceItemKind kind,Data && data)104*a50ea2f7SNicholas Mosier DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind, Data &&data) {
105*a50ea2f7SNicholas Mosier   m_item_data.emplace_back(data);
106*a50ea2f7SNicholas Mosier 
1074f676c25SWalter Erquinigo   if (m_last_tsc)
1084f676c25SWalter Erquinigo     (*m_last_tsc)->second.items_count++;
1094f676c25SWalter Erquinigo   if (m_last_nanoseconds)
1104f676c25SWalter Erquinigo     (*m_last_nanoseconds)->second.items_count++;
111*a50ea2f7SNicholas Mosier 
112a7d6c3efSWalter Erquinigo   return m_item_data.back();
113a7d6c3efSWalter Erquinigo }
114a7d6c3efSWalter Erquinigo 
NotifySyncPoint(lldb::addr_t psb_offset)115e17cae07SWalter Erquinigo void DecodedThread::NotifySyncPoint(lldb::addr_t psb_offset) {
116e17cae07SWalter Erquinigo   m_psb_offsets.try_emplace(GetItemsCount(), psb_offset);
117e17cae07SWalter Erquinigo   AppendEvent(lldb::eTraceEventSyncPoint);
118e17cae07SWalter Erquinigo }
119e17cae07SWalter Erquinigo 
NotifyTsc(TSC tsc)1204f676c25SWalter Erquinigo void DecodedThread::NotifyTsc(TSC tsc) {
1214f676c25SWalter Erquinigo   if (m_last_tsc && (*m_last_tsc)->second.tsc == tsc)
1224f676c25SWalter Erquinigo     return;
123e17cae07SWalter Erquinigo   if (m_last_tsc)
124e17cae07SWalter Erquinigo     assert(tsc >= (*m_last_tsc)->second.tsc &&
125e17cae07SWalter Erquinigo            "We can't have decreasing times");
1264f676c25SWalter Erquinigo 
1274f676c25SWalter Erquinigo   m_last_tsc =
1284f676c25SWalter Erquinigo       m_tscs.emplace(GetItemsCount(), TSCRange{tsc, 0, GetItemsCount()}).first;
1294f676c25SWalter Erquinigo 
1304f676c25SWalter Erquinigo   if (m_tsc_conversion) {
1314f676c25SWalter Erquinigo     uint64_t nanos = m_tsc_conversion->ToNanos(tsc);
1324f676c25SWalter Erquinigo     if (!m_last_nanoseconds || (*m_last_nanoseconds)->second.nanos != nanos) {
1334f676c25SWalter Erquinigo       m_last_nanoseconds =
1344f676c25SWalter Erquinigo           m_nanoseconds
1354f676c25SWalter Erquinigo               .emplace(GetItemsCount(), NanosecondsRange{nanos, tsc, nullptr, 0,
1364f676c25SWalter Erquinigo                                                          GetItemsCount()})
1374f676c25SWalter Erquinigo               .first;
1384f676c25SWalter Erquinigo       if (*m_last_nanoseconds != m_nanoseconds.begin()) {
1394f676c25SWalter Erquinigo         auto prev_range = prev(*m_last_nanoseconds);
1404f676c25SWalter Erquinigo         prev_range->second.next_range = &(*m_last_nanoseconds)->second;
141ca922a35SAlisamar Husain       }
142ca922a35SAlisamar Husain     }
1434f676c25SWalter Erquinigo   }
1444f676c25SWalter Erquinigo   AppendEvent(lldb::eTraceEventHWClockTick);
1454f676c25SWalter Erquinigo }
146ca922a35SAlisamar Husain 
NotifyCPU(lldb::cpu_id_t cpu_id)1474a843d92SWalter Erquinigo void DecodedThread::NotifyCPU(lldb::cpu_id_t cpu_id) {
1484a843d92SWalter Erquinigo   if (!m_last_cpu || *m_last_cpu != cpu_id) {
1494f676c25SWalter Erquinigo     m_cpus.emplace(GetItemsCount(), cpu_id);
1504a843d92SWalter Erquinigo     m_last_cpu = cpu_id;
1514a843d92SWalter Erquinigo     AppendEvent(lldb::eTraceEventCPUChanged);
1524a843d92SWalter Erquinigo   }
1534a843d92SWalter Erquinigo }
1544a843d92SWalter Erquinigo 
GetCPUByIndex(uint64_t item_index) const155f9b4ea0cSJakob Johnson lldb::cpu_id_t DecodedThread::GetCPUByIndex(uint64_t item_index) const {
1564f676c25SWalter Erquinigo   auto it = m_cpus.upper_bound(item_index);
157f9b4ea0cSJakob Johnson   return it == m_cpus.begin() ? LLDB_INVALID_CPU_ID : prev(it)->second;
1584a843d92SWalter Erquinigo }
1594a843d92SWalter Erquinigo 
1602fe83274SKazu Hirata std::optional<DecodedThread::TSCRange>
GetTSCRangeByIndex(uint64_t item_index) const1614f676c25SWalter Erquinigo DecodedThread::GetTSCRangeByIndex(uint64_t item_index) const {
1624f676c25SWalter Erquinigo   auto next_it = m_tscs.upper_bound(item_index);
1634f676c25SWalter Erquinigo   if (next_it == m_tscs.begin())
164d920ab4aSKazu Hirata     return std::nullopt;
1654f676c25SWalter Erquinigo   return prev(next_it)->second;
1664f676c25SWalter Erquinigo }
1674f676c25SWalter Erquinigo 
1682fe83274SKazu Hirata std::optional<DecodedThread::NanosecondsRange>
GetNanosecondsRangeByIndex(uint64_t item_index)1694f676c25SWalter Erquinigo DecodedThread::GetNanosecondsRangeByIndex(uint64_t item_index) {
1704f676c25SWalter Erquinigo   auto next_it = m_nanoseconds.upper_bound(item_index);
1714f676c25SWalter Erquinigo   if (next_it == m_nanoseconds.begin())
172d920ab4aSKazu Hirata     return std::nullopt;
1734f676c25SWalter Erquinigo   return prev(next_it)->second;
1744f676c25SWalter Erquinigo }
1754f676c25SWalter Erquinigo 
GetTotalInstructionCount() const176c49d14acSWalter Erquinigo uint64_t DecodedThread::GetTotalInstructionCount() const {
177c49d14acSWalter Erquinigo   return m_insn_count;
178c49d14acSWalter Erquinigo }
179c49d14acSWalter Erquinigo 
AppendEvent(lldb::TraceEvent event)180a7d6c3efSWalter Erquinigo void DecodedThread::AppendEvent(lldb::TraceEvent event) {
181*a50ea2f7SNicholas Mosier   CreateNewTraceItem(lldb::eTraceItemKindEvent, event);
182a7d6c3efSWalter Erquinigo   m_events_stats.RecordEvent(event);
183a7d6c3efSWalter Erquinigo }
184a7d6c3efSWalter Erquinigo 
AppendInstruction(const pt_insn & insn)185a7d6c3efSWalter Erquinigo void DecodedThread::AppendInstruction(const pt_insn &insn) {
186*a50ea2f7SNicholas Mosier   CreateNewTraceItem(lldb::eTraceItemKindInstruction, insn.ip);
187c49d14acSWalter Erquinigo   m_insn_count++;
188a7d6c3efSWalter Erquinigo }
189a7d6c3efSWalter Erquinigo 
AppendError(const IntelPTError & error)190a7d6c3efSWalter Erquinigo void DecodedThread::AppendError(const IntelPTError &error) {
191*a50ea2f7SNicholas Mosier   CreateNewTraceItem(lldb::eTraceItemKindError, error.message());
192c49d14acSWalter Erquinigo   m_error_stats.RecordError(/*fatal=*/false);
1931e5083a5SWalter Erquinigo }
1941e5083a5SWalter Erquinigo 
AppendCustomError(StringRef err,bool fatal)195c49d14acSWalter Erquinigo void DecodedThread::AppendCustomError(StringRef err, bool fatal) {
196*a50ea2f7SNicholas Mosier   CreateNewTraceItem(lldb::eTraceItemKindError, err.str());
197c49d14acSWalter Erquinigo   m_error_stats.RecordError(fatal);
1981e5083a5SWalter Erquinigo }
1991e5083a5SWalter Erquinigo 
GetEventByIndex(int item_index) const200a7d6c3efSWalter Erquinigo lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const {
201*a50ea2f7SNicholas Mosier   return std::get<lldb::TraceEvent>(m_item_data[item_index]);
202059f39d2SWalter Erquinigo }
203059f39d2SWalter Erquinigo 
GetEventsStats() const204059f39d2SWalter Erquinigo const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
205059f39d2SWalter Erquinigo   return m_events_stats;
206059f39d2SWalter Erquinigo }
207059f39d2SWalter Erquinigo 
RecordEvent(lldb::TraceEvent event)208a7d6c3efSWalter Erquinigo void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) {
209059f39d2SWalter Erquinigo   events_counts[event]++;
210059f39d2SWalter Erquinigo   total_count++;
2111e5083a5SWalter Erquinigo }
2121e5083a5SWalter Erquinigo 
GetTotalCount() const213c49d14acSWalter Erquinigo uint64_t DecodedThread::ErrorStats::GetTotalCount() const {
214c49d14acSWalter Erquinigo   uint64_t total = 0;
215c49d14acSWalter Erquinigo   for (const auto &[kind, count] : libipt_errors)
216c49d14acSWalter Erquinigo     total += count;
217c49d14acSWalter Erquinigo 
218c49d14acSWalter Erquinigo   return total + other_errors + fatal_errors;
219c49d14acSWalter Erquinigo }
220c49d14acSWalter Erquinigo 
RecordError(bool fatal)221c49d14acSWalter Erquinigo void DecodedThread::ErrorStats::RecordError(bool fatal) {
222c49d14acSWalter Erquinigo   if (fatal)
223c49d14acSWalter Erquinigo     fatal_errors++;
224c49d14acSWalter Erquinigo   else
225c49d14acSWalter Erquinigo     other_errors++;
226c49d14acSWalter Erquinigo }
227c49d14acSWalter Erquinigo 
RecordError(int libipt_error_code)228c49d14acSWalter Erquinigo void DecodedThread::ErrorStats::RecordError(int libipt_error_code) {
229c49d14acSWalter Erquinigo   libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;
230c49d14acSWalter Erquinigo }
231c49d14acSWalter Erquinigo 
GetErrorStats() const232c49d14acSWalter Erquinigo const DecodedThread::ErrorStats &DecodedThread::GetErrorStats() const {
233c49d14acSWalter Erquinigo   return m_error_stats;
234c49d14acSWalter Erquinigo }
235c49d14acSWalter Erquinigo 
2364f676c25SWalter Erquinigo lldb::TraceItemKind
GetItemKindByIndex(uint64_t item_index) const2374f676c25SWalter Erquinigo DecodedThread::GetItemKindByIndex(uint64_t item_index) const {
238*a50ea2f7SNicholas Mosier   return std::visit(
239*a50ea2f7SNicholas Mosier       llvm::makeVisitor(
240*a50ea2f7SNicholas Mosier           [](const std::string &) { return lldb::eTraceItemKindError; },
241*a50ea2f7SNicholas Mosier           [](lldb::TraceEvent) { return lldb::eTraceItemKindEvent; },
242*a50ea2f7SNicholas Mosier           [](lldb::addr_t) { return lldb::eTraceItemKindInstruction; }),
243*a50ea2f7SNicholas Mosier       m_item_data[item_index]);
244ca922a35SAlisamar Husain }
245ca922a35SAlisamar Husain 
GetErrorByIndex(uint64_t item_index) const2464bae7066SAlex Langford llvm::StringRef DecodedThread::GetErrorByIndex(uint64_t item_index) const {
2474bae7066SAlex Langford   if (item_index >= m_item_data.size())
2484bae7066SAlex Langford     return llvm::StringRef();
249*a50ea2f7SNicholas Mosier   return std::get<std::string>(m_item_data[item_index]);
250cfd96f05SWalter Erquinigo }
2510b697561SWalter Erquinigo 
DecodedThread(ThreadSP thread_sp,const std::optional<LinuxPerfZeroTscConversion> & tsc_conversion)2524f676c25SWalter Erquinigo DecodedThread::DecodedThread(
2534f676c25SWalter Erquinigo     ThreadSP thread_sp,
2542fe83274SKazu Hirata     const std::optional<LinuxPerfZeroTscConversion> &tsc_conversion)
2554f676c25SWalter Erquinigo     : m_thread_sp(thread_sp), m_tsc_conversion(tsc_conversion) {}
25637a466ddSAlisamar Husain 
CalculateApproximateMemoryUsage() const25737a466ddSAlisamar Husain size_t DecodedThread::CalculateApproximateMemoryUsage() const {
258a7d6c3efSWalter Erquinigo   return sizeof(TraceItemStorage) * m_item_data.size() +
2594f676c25SWalter Erquinigo          (sizeof(uint64_t) + sizeof(TSC)) * m_tscs.size() +
2604f676c25SWalter Erquinigo          (sizeof(uint64_t) + sizeof(uint64_t)) * m_nanoseconds.size() +
2614f676c25SWalter Erquinigo          (sizeof(uint64_t) + sizeof(lldb::cpu_id_t)) * m_cpus.size();
262ca922a35SAlisamar Husain }
263