From 03cc58ff2a7a88b4eb8c1409169b662b46ec85d9 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Wed, 8 Jun 2022 11:39:31 -0700 Subject: [PATCH] [trace][intelpt] Support system-wide tracing [17] - Some improvements This improves several things and addresses comments up to the diff [11] in this stack. - Simplify many functions to receive less parameters that they can identify easily - Create Storage classes for Trace and TraceIntelPT that can make it easier to reason about what can change with live process refreshes and what cannot. - Don't cache the perf zero conversion numbers in lldb-server to make sure we get the most up-to-date numbers. - Move the thread identifaction from context switches to the bundle parser, to leave TraceIntelPT simpler. This also makes sure that the constructor of TraceIntelPT is invoked when the entire data has been checked to be correct. - Normalize all bundle paths before the Processes, Threads and Modules are created, so that they can assume that all paths are correct and absolute - Fix some issues in the tests. Now they all pass. - return the specific instance when constructing PerThread and MultiCore processor tracers. - Properly implement IntelPTMultiCoreTrace::TraceStart. - Improve some comments. - Use the typedef ContextSwitchTrace more often for clarity. - Move CreateContextSwitchTracePerfEvent to Perf.h as a utility function. - Synchronize better the state of the context switch and the intel pt perf events. - Use a booblean instead of an enum for the PerfEvent state. Differential Revision: https://reviews.llvm.org/D127456 --- lldb/include/lldb/Target/Trace.h | 115 ++++++++++-------- .../Plugins/Process/Linux/IntelPTCollector.cpp | 17 ++- .../Plugins/Process/Linux/IntelPTCollector.h | 6 +- .../Process/Linux/IntelPTMultiCoreTrace.cpp | 68 ++--------- .../Plugins/Process/Linux/IntelPTMultiCoreTrace.h | 4 +- .../Process/Linux/IntelPTPerThreadProcessTrace.cpp | 5 +- .../Process/Linux/IntelPTPerThreadProcessTrace.h | 2 +- .../Plugins/Process/Linux/IntelPTProcessTrace.h | 3 +- .../Process/Linux/IntelPTSingleBufferTrace.h | 4 +- lldb/source/Plugins/Process/Linux/Perf.cpp | 83 +++++++++++-- lldb/source/Plugins/Process/Linux/Perf.h | 31 +++-- .../source/Plugins/Trace/intel-pt/TraceIntelPT.cpp | 131 +++++++------------- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h | 45 ++++--- .../Trace/intel-pt/TraceIntelPTJSONStructs.h | 4 +- .../intel-pt/TraceIntelPTMultiCoreDecoder.cpp | 40 +++--- .../Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h | 24 ++-- .../intel-pt/TraceIntelPTSessionFileParser.cpp | 135 ++++++++++++++++----- .../Trace/intel-pt/TraceIntelPTSessionFileParser.h | 18 ++- .../Trace/intel-pt/TraceIntelPTSessionSaver.cpp | 2 +- lldb/source/Target/Trace.cpp | 104 +++++++++------- .../trace/intelpt-multi-core-trace/trace.json | 8 +- .../trace_missing_threads.json | 8 +- 22 files changed, 476 insertions(+), 381 deletions(-) diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h index 67585c2..2cd4d80 100644 --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -296,10 +296,9 @@ public: OnBinaryDataReadCallback callback); /// Similar to \a OnCoreBinaryDataRead but this is able to fetch the same data - /// from multiple cores at once. - llvm::Error OnCoresBinaryDataRead(const std::set core_ids, - llvm::StringRef kind, - OnCoresBinaryDataReadCallback callback); + /// from all cores at once. + llvm::Error OnAllCoresBinaryDataRead(llvm::StringRef kind, + OnCoresBinaryDataReadCallback callback); /// \return /// All the currently traced processes. @@ -310,6 +309,11 @@ public: /// plugin. llvm::ArrayRef GetTracedCores(); + /// Helper method for reading a data file and passing its data to the given + /// callback. + static llvm::Error OnDataFileRead(FileSpec file, + OnBinaryDataReadCallback callback); + protected: /// Get the currently traced live process. /// @@ -342,10 +346,6 @@ protected: llvm::StringRef kind, OnBinaryDataReadCallback callback); - /// Helper method for reading a data file and passing its data to the given - /// callback. - llvm::Error OnDataFileRead(FileSpec file, OnBinaryDataReadCallback callback); - /// Get the file path containing data of a postmortem thread given a data /// identifier. /// @@ -498,7 +498,7 @@ protected: /// Return the list of processes traced by this instance. None of the returned /// pointers are invalid. - std::vector GetTracedProcesses() const; + std::vector GetTracedProcesses(); /// Method to be invoked by the plug-in to refresh the live process state. It /// will invoked DoRefreshLiveProcessState at some point, which should be @@ -513,52 +513,63 @@ protected: private: uint32_t m_stop_id = LLDB_INVALID_STOP_ID; + /// Process traced by this object if doing live tracing. Otherwise it's null. Process *m_live_process = nullptr; - /// Portmortem processes traced by this object if doing non-live tracing. - /// Otherwise it's empty. - std::vector m_postmortem_processes; - - /// These data kinds are returned by lldb-server when fetching the state of - /// the tracing session. The size in bytes can be used later for fetching the - /// data in batches. - /// \{ - - /// tid -> data kind -> size - llvm::DenseMap> - m_live_thread_data; - - /// core id -> data kind -> size - llvm::DenseMap> - m_live_core_data_sizes; - /// core id -> data kind -> bytes - llvm::DenseMap>> - m_live_core_data; - - /// data kind -> size - std::unordered_map m_live_process_data; - /// \} - - /// The list of cores being traced. Might be \b None depending on the plug-in. - llvm::Optional> m_cores; - - /// Postmortem traces can specific additional data files, which are - /// represented in this variable using a data kind identifier for each file. - /// \{ - - /// tid -> data kind -> file - llvm::DenseMap> - m_postmortem_thread_data; - - /// core id -> data kind -> file - llvm::DenseMap> - m_postmortem_core_data; - - /// \} - - llvm::Optional m_live_refresh_error; + /// We package all the data that can change upon process stops to make sure + /// this contract is very visible. + /// This variable should only be accessed directly by constructores or live + /// process data refreshers. + struct Storage { + /// Portmortem processes traced by this object if doing non-live tracing. + /// Otherwise it's empty. + std::vector postmortem_processes; + + /// These data kinds are returned by lldb-server when fetching the state of + /// the tracing session. The size in bytes can be used later for fetching + /// the data in batches. + /// \{ + + /// tid -> data kind -> size + llvm::DenseMap> + live_thread_data; + + /// core id -> data kind -> size + llvm::DenseMap> + live_core_data_sizes; + /// core id -> data kind -> bytes + llvm::DenseMap>> + live_core_data; + + /// data kind -> size + std::unordered_map live_process_data; + /// \} + + /// The list of cores being traced. Might be \b None depending on the + /// plug-in. + llvm::Optional> cores; + + /// Postmortem traces can specific additional data files, which are + /// represented in this variable using a data kind identifier for each file. + /// \{ + + /// tid -> data kind -> file + llvm::DenseMap> + postmortem_thread_data; + + /// core id -> data kind -> file + llvm::DenseMap> + postmortem_core_data; + + /// \} + + llvm::Optional live_refresh_error; + } m_storage; + + /// Get the storage after refreshing the data in the case of a live process. + Storage &GetUpdatedStorage(); }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp index a36d6dc..7118e6f 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp +++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp @@ -37,16 +37,13 @@ IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process) llvm::Expected IntelPTCollector::FetchPerfTscConversionParameters() { - if (!m_cached_tsc_conversion) { - if (Expected tsc_conversion = - LoadPerfTscConversionParameters()) - m_cached_tsc_conversion = std::move(*tsc_conversion); - else - return createStringError(inconvertibleErrorCode(), - "Unable to load TSC to wall time conversion: %s", - toString(tsc_conversion.takeError()).c_str()); - } - return *m_cached_tsc_conversion; + if (Expected tsc_conversion = + LoadPerfTscConversionParameters()) + return *tsc_conversion; + else + return createStringError(inconvertibleErrorCode(), + "Unable to load TSC to wall time conversion: %s", + toString(tsc_conversion.takeError()).c_str()); } Error IntelPTCollector::TraceStop(lldb::tid_t tid) { diff --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.h b/lldb/source/Plugins/Process/Linux/IntelPTCollector.h index 5777ac82..b77c434 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.h @@ -74,8 +74,7 @@ private: const TraceIntelPTStartRequest &request); /// \return - /// The conversion object between TSC and wall time. It caches the result - /// upon success. + /// The conversion object between TSC and wall time. llvm::Expected FetchPerfTscConversionParameters(); @@ -87,9 +86,6 @@ private: /// Only one instance of "process trace" can be active at a given time. /// It might be \b nullptr. IntelPTProcessTraceUP m_process_trace_up; - - /// Cached TSC to and from wall time conversion. - llvm::Optional m_cached_tsc_conversion; }; } // namespace process_linux diff --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp index 572a867..22cd860 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp +++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp @@ -33,55 +33,7 @@ static Error IncludePerfEventParanoidMessageInError(Error &&error) { toString(std::move(error)).c_str()); } -static Expected CreateContextSwitchTracePerfEvent( - bool disabled, lldb::core_id_t core_id, - IntelPTSingleBufferTrace &intelpt_core_trace) { - Log *log = GetLog(POSIXLog::Trace); -#ifndef PERF_ATTR_SIZE_VER5 - return createStringError(inconvertibleErrorCode(), - "Intel PT Linux perf event not supported"); -#else - perf_event_attr attr; - memset(&attr, 0, sizeof(attr)); - attr.size = sizeof(attr); - attr.sample_period = 0; - attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME; - attr.type = PERF_TYPE_SOFTWARE; - attr.context_switch = 1; - attr.exclude_kernel = 1; - attr.sample_id_all = 1; - attr.exclude_hv = 1; - attr.disabled = disabled; - - // The given perf configuration will product context switch records of 32 - // bytes each. Assuming that every context switch will be emitted twice (one - // for context switch ins and another one for context switch outs), and that a - // context switch will happen at least every half a millisecond per core, we - // need 500 * 32 bytes (~16 KB) for a trace of one second, which is much more - // than what a regular intel pt trace can get. Pessimistically we pick as - // 32KiB for the size of our context switch trace. - - uint64_t data_buffer_size = 32768; - uint64_t data_buffer_numpages = data_buffer_size / getpagesize(); - - LLDB_LOG(log, "Will create context switch trace buffer of size {0}", - data_buffer_size); - - if (Expected perf_event = PerfEvent::Init( - attr, /*pid=*/None, core_id, - intelpt_core_trace.GetPerfEvent().GetFd(), /*flags=*/0)) { - if (Error mmap_err = perf_event->MmapMetadataAndBuffers( - data_buffer_numpages, 0, /*data_buffer_write=*/false)) { - return std::move(mmap_err); - } - return perf_event; - } else { - return perf_event.takeError(); - } -#endif -} - -Expected +Expected> IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request, NativeProcessProtocol &process) { Expected> core_ids = GetAvailableLogicalCoreIDs(); @@ -105,8 +57,8 @@ IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request, return IncludePerfEventParanoidMessageInError(core_trace.takeError()); if (Expected context_switch_trace = - CreateContextSwitchTracePerfEvent(/*disabled=*/true, core_id, - core_trace.get())) { + CreateContextSwitchTracePerfEvent(core_id, + &core_trace->GetPerfEvent())) { traces.try_emplace(core_id, std::make_pair(std::move(*core_trace), std::move(*context_switch_trace))); @@ -115,7 +67,7 @@ IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request, } } - return IntelPTProcessTraceUP( + return std::unique_ptr( new IntelPTMultiCoreTrace(std::move(traces), process)); } @@ -129,7 +81,7 @@ void IntelPTMultiCoreTrace::ForEachCore( void IntelPTMultiCoreTrace::ForEachCore( std::function + ContextSwitchTrace &context_switch_trace)> callback) { for (auto &it : m_traces_per_core) callback(it.first, it.second.first, it.second.second); @@ -163,7 +115,7 @@ TraceIntelPTGetStateResponse IntelPTMultiCoreTrace::GetState() { state.cores.emplace(); ForEachCore([&](lldb::core_id_t core_id, const IntelPTSingleBufferTrace &core_trace, - const PerfEvent &context_switch_trace) { + const ContextSwitchTrace &context_switch_trace) { state.cores->push_back( {core_id, {{IntelPTDataKinds::kTraceBuffer, core_trace.GetTraceBufferSize()}, @@ -180,8 +132,12 @@ bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const { } llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) { - // This instance is already tracing all threads automatically. - return llvm::Error::success(); + // All the process' threads are being traced automatically. + if (!TracesThread(tid)) + return createStringError( + inconvertibleErrorCode(), + "Thread %" PRIu64 " is not part of the target process", tid); + return Error::success(); } Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) { diff --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h index ff56939..95a6da6 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h @@ -38,7 +38,7 @@ public: /// \return /// An \a IntelPTMultiCoreTrace instance if tracing was successful, or /// an \a llvm::Error otherwise. - static llvm::Expected + static llvm::Expected> StartOnAllCores(const TraceIntelPTStartRequest &request, NativeProcessProtocol &process); @@ -65,7 +65,7 @@ public: /// The perf event collecting context switches for the given core. void ForEachCore(std::function + ContextSwitchTrace &context_switch_trace)> callback); void ProcessDidStop() override; diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp index 0346233..cfda1b4 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp +++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp @@ -52,10 +52,11 @@ IntelPTPerThreadProcessTrace::TryGetBinaryData( return m_thread_traces.TryGetBinaryData(request); } -Expected +Expected> IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request, ArrayRef current_tids) { - IntelPTProcessTraceUP trace(new IntelPTPerThreadProcessTrace(request)); + std::unique_ptr trace( + new IntelPTPerThreadProcessTrace(request)); Error error = Error::success(); for (lldb::tid_t tid : current_tids) diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h index 2f28a12..e6efc50 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h @@ -32,7 +32,7 @@ public: /// \return /// An \a IntelPTMultiCoreTrace instance if tracing was successful, or /// an \a llvm::Error otherwise. - static llvm::Expected + static llvm::Expected> Start(const TraceIntelPTStartRequest &request, llvm::ArrayRef current_tids); diff --git a/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h index 1c6b44e..773151b 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h @@ -16,7 +16,8 @@ namespace lldb_private { namespace process_linux { -// Abstract class to be inherited by all the process tracing strategies. +/// Interface to be implemented by each 'process trace' strategy (per core, per +/// thread, etc). class IntelPTProcessTrace { public: virtual ~IntelPTProcessTrace() = default; diff --git a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h index 99e7e41..278ddd6 100644 --- a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h +++ b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.h @@ -40,7 +40,9 @@ public: /// The CPU core id where to trace. If \b None, then this traces all CPUs. /// /// \param[in] disabled - /// Whether to start the tracing paused. + /// If \b true, then no data is collected until \a Resume is invoked. + /// Similarly, if \b false, data is collected right away until \a Pause is + /// invoked. /// /// \return /// A \a IntelPTSingleBufferTrace instance if tracing was successful, or diff --git a/lldb/source/Plugins/Process/Linux/Perf.cpp b/lldb/source/Plugins/Process/Linux/Perf.cpp index 68bf18a..8b6b7f0 100644 --- a/lldb/source/Plugins/Process/Linux/Perf.cpp +++ b/lldb/source/Plugins/Process/Linux/Perf.cpp @@ -74,7 +74,7 @@ void resource_handle::FileDescriptorDeleter::operator()(long *ptr) { llvm::Expected PerfEvent::Init(perf_event_attr &attr, Optional pid, Optional cpu, - Optional group_fd, + Optional group_fd, unsigned long flags) { errno = 0; long fd = syscall(SYS_perf_event_open, &attr, pid.getValueOr(-1), @@ -84,8 +84,7 @@ llvm::Expected PerfEvent::Init(perf_event_attr &attr, llvm::formatv("perf event syscall failed: {0}", std::strerror(errno)); return llvm::createStringError(llvm::inconvertibleErrorCode(), err_msg); } - return PerfEvent(fd, attr.disabled ? CollectionState::Disabled - : CollectionState::Enabled); + return PerfEvent(fd, !attr.disabled); } llvm::Expected PerfEvent::Init(perf_event_attr &attr, @@ -181,7 +180,12 @@ ArrayRef PerfEvent::GetAuxBuffer() const { Expected> PerfEvent::ReadFlushedOutDataCyclicBuffer(size_t offset, size_t size) { - CollectionState previous_state = m_collection_state; + // The following code assumes that the protection level of the DATA page + // is PROT_READ. If PROT_WRITE is used, then reading would require that + // this piece of code updates some pointers. See more about data_tail + // in https://man7.org/linux/man-pages/man2/perf_event_open.2.html. + + bool was_enabled = m_enabled; if (Error err = DisableWithIoctl()) return std::move(err); @@ -220,7 +224,7 @@ PerfEvent::ReadFlushedOutDataCyclicBuffer(size_t offset, size_t size) { output.push_back(data[i]); } - if (previous_state == CollectionState::Enabled) { + if (was_enabled) { if (Error err = EnableWithIoctl()) return std::move(err); } @@ -236,7 +240,12 @@ PerfEvent::ReadFlushedOutDataCyclicBuffer(size_t offset, size_t size) { Expected> PerfEvent::ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size) { - CollectionState previous_state = m_collection_state; + // The following code assumes that the protection level of the AUX page + // is PROT_READ. If PROT_WRITE is used, then reading would require that + // this piece of code updates some pointers. See more about aux_tail + // in https://man7.org/linux/man-pages/man2/perf_event_open.2.html. + + bool was_enabled = m_enabled; if (Error err = DisableWithIoctl()) return std::move(err); @@ -271,7 +280,7 @@ PerfEvent::ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size) { for (uint64_t i = left_part_start; i < aux_head && output.size() < size; i++) output.push_back(data[i]); - if (previous_state == CollectionState::Enabled) { + if (was_enabled) { if (Error err = EnableWithIoctl()) return std::move(err); } @@ -286,7 +295,7 @@ PerfEvent::ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size) { } Error PerfEvent::DisableWithIoctl() { - if (m_collection_state == CollectionState::Disabled) + if (!m_enabled) return Error::success(); if (ioctl(*m_fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) < 0) @@ -294,12 +303,14 @@ Error PerfEvent::DisableWithIoctl() { "Can't disable perf event. %s", std::strerror(errno)); - m_collection_state = CollectionState::Disabled; + m_enabled = false; return Error::success(); } +bool PerfEvent::IsEnabled() const { return m_enabled; } + Error PerfEvent::EnableWithIoctl() { - if (m_collection_state == CollectionState::Enabled) + if (m_enabled) return Error::success(); if (ioctl(*m_fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) < 0) @@ -307,7 +318,7 @@ Error PerfEvent::EnableWithIoctl() { "Can't enable perf event. %s", std::strerror(errno)); - m_collection_state = CollectionState::Enabled; + m_enabled = true; return Error::success(); } @@ -318,3 +329,53 @@ size_t PerfEvent::GetEffectiveDataBufferSize() const { else return mmap_metadata.data_size; // The buffer has wrapped. } + +Expected +lldb_private::process_linux::CreateContextSwitchTracePerfEvent( + lldb::core_id_t core_id, const PerfEvent *parent_perf_event) { + Log *log = GetLog(POSIXLog::Trace); +#ifndef PERF_ATTR_SIZE_VER5 + return createStringError(inconvertibleErrorCode(), + "Intel PT Linux perf event not supported"); +#else + perf_event_attr attr; + memset(&attr, 0, sizeof(attr)); + attr.size = sizeof(attr); + attr.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME; + attr.type = PERF_TYPE_SOFTWARE; + attr.context_switch = 1; + attr.exclude_kernel = 1; + attr.sample_id_all = 1; + attr.exclude_hv = 1; + attr.disabled = parent_perf_event ? !parent_perf_event->IsEnabled() : false; + + // The given perf configuration will produce context switch records of 32 + // bytes each. Assuming that every context switch will be emitted twice (one + // for context switch ins and another one for context switch outs), and that a + // context switch will happen at least every half a millisecond per core, we + // need 500 * 32 bytes (~16 KB) for a trace of one second, which is much more + // than what a regular intel pt trace can get. Pessimistically we pick as + // 32KiB for the size of our context switch trace. + + uint64_t data_buffer_size = 32768; + uint64_t data_buffer_numpages = data_buffer_size / getpagesize(); + + LLDB_LOG(log, "Will create context switch trace buffer of size {0}", + data_buffer_size); + + Optional group_fd; + if (parent_perf_event) + group_fd = parent_perf_event->GetFd(); + + if (Expected perf_event = + PerfEvent::Init(attr, /*pid=*/None, core_id, group_fd, /*flags=*/0)) { + if (Error mmap_err = perf_event->MmapMetadataAndBuffers( + data_buffer_numpages, 0, /*data_buffer_write=*/false)) { + return std::move(mmap_err); + } + return perf_event; + } else { + return perf_event.takeError(); + } +#endif +} diff --git a/lldb/source/Plugins/Process/Linux/Perf.h b/lldb/source/Plugins/Process/Linux/Perf.h index cb80304..e400d53 100644 --- a/lldb/source/Plugins/Process/Linux/Perf.h +++ b/lldb/source/Plugins/Process/Linux/Perf.h @@ -80,11 +80,6 @@ using MmapUP = std::unique_ptr; /// Handles the management of the event's file descriptor and mmap'ed /// regions. class PerfEvent { - enum class CollectionState { - Enabled, - Disabled, - }; - public: /// Create a new performance monitoring event via the perf_event_open syscall. /// @@ -116,7 +111,7 @@ public: static llvm::Expected Init(perf_event_attr &attr, llvm::Optional pid, llvm::Optional cpu, - llvm::Optional group_fd, + llvm::Optional group_fd, unsigned long flags); /// Create a new performance monitoring event via the perf_event_open syscall @@ -266,17 +261,21 @@ public: /// data. size_t GetEffectiveDataBufferSize() const; + /// \return + /// \b true if and only the perf event is enabled and collecting. + bool IsEnabled() const; + private: /// Create new \a PerfEvent. /// /// \param[in] fd /// File descriptor of the perf event. /// - /// \param[in] initial_state + /// \param[in] enabled /// Initial collection state configured for this perf_event. - PerfEvent(long fd, CollectionState initial_state) + PerfEvent(long fd, bool enabled) : m_fd(new long(fd), resource_handle::FileDescriptorDeleter()), - m_collection_state(initial_state) {} + m_enabled(enabled) {} /// Wrapper for \a mmap to provide custom error messages. /// @@ -319,9 +318,21 @@ private: /// such as IntelPT. resource_handle::MmapUP m_aux_base; /// The state of the underlying perf_event. - CollectionState m_collection_state; + bool m_enabled; }; +/// Create a perf event that tracks context switches on a cpu. +/// +/// \param[in] core_id +/// The core to trace. +/// +/// \param[in] parent_perf_event +/// An optional perf event that will be grouped with the +/// new perf event. +llvm::Expected +CreateContextSwitchTracePerfEvent(lldb::core_id_t core_id, + const PerfEvent *parent_perf_event = nullptr); + /// Load \a PerfTscConversionParameters from \a perf_event_mmap_page, if /// available. llvm::Expected LoadPerfTscConversionParameters(); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp index af95c20..cdcd6eb 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -75,37 +75,22 @@ Expected TraceIntelPT::CreateInstanceForLiveProcess(Process &process) { } TraceIntelPT::TraceIntelPT(JSONTraceSession &session, - const FileSpec &session_file_dir, ArrayRef traced_processes, ArrayRef traced_threads) : Trace(traced_processes, session.GetCoreIds()), - m_cpu_info(session.cpu_info), - m_tsc_conversion(session.tsc_perf_zero_conversion) { - for (const ThreadPostMortemTraceSP &thread : traced_threads) { - m_thread_decoders.emplace(thread->GetID(), - std::make_unique(thread, *this)); - if (const Optional &trace_file = thread->GetTraceFile()) { - SetPostMortemThreadDataFile(thread->GetID(), - IntelPTDataKinds::kTraceBuffer, *trace_file); - } - } + m_cpu_info(session.cpu_info) { + m_storage.tsc_conversion = session.tsc_perf_zero_conversion; + if (session.cores) { std::vector cores; for (const JSONCore &core : *session.cores) { - FileSpec trace_buffer(core.trace_buffer); - if (trace_buffer.IsRelative()) - trace_buffer.PrependPathComponent(session_file_dir); - SetPostMortemCoreDataFile(core.core_id, IntelPTDataKinds::kTraceBuffer, - trace_buffer); + FileSpec(core.trace_buffer)); - FileSpec context_switch(core.context_switch_trace); - if (context_switch.IsRelative()) - context_switch.PrependPathComponent(session_file_dir); SetPostMortemCoreDataFile(core.core_id, IntelPTDataKinds::kPerfContextSwitchTrace, - context_switch); + FileSpec(core.context_switch_trace)); cores.push_back(core.core_id); } @@ -114,8 +99,16 @@ TraceIntelPT::TraceIntelPT(JSONTraceSession &session, for (const JSONThread &thread : process.threads) tids.push_back(thread.tid); - m_multicore_decoder.emplace(*this, cores, tids, - *session.tsc_perf_zero_conversion); + m_storage.multicore_decoder.emplace(*this); + } else { + for (const ThreadPostMortemTraceSP &thread : traced_threads) { + m_storage.thread_decoders.emplace( + thread->GetID(), std::make_unique(thread, *this)); + if (const Optional &trace_file = thread->GetTraceFile()) { + SetPostMortemThreadDataFile( + thread->GetID(), IntelPTDataKinds::kTraceBuffer, *trace_file); + } + } } } @@ -125,11 +118,12 @@ DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { thread.shared_from_this(), createStringError(inconvertibleErrorCode(), error)); - if (m_multicore_decoder) - return m_multicore_decoder->Decode(thread); + Storage &storage = GetUpdatedStorage(); + if (storage.multicore_decoder) + return storage.multicore_decoder->Decode(thread); - auto it = m_thread_decoders.find(thread.GetID()); - if (it == m_thread_decoders.end()) + auto it = storage.thread_decoders.find(thread.GetID()); + if (it == storage.thread_decoders.end()) return std::make_shared( thread.shared_from_this(), createStringError(inconvertibleErrorCode(), "thread not traced")); @@ -141,6 +135,8 @@ lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) { } void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { + Storage &storage = GetUpdatedStorage(); + lldb::tid_t tid = thread.GetID(); s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID()); if (!IsTraced(tid)) { @@ -209,12 +205,13 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { } // Multicode decoding stats - if (m_multicore_decoder) { + if (storage.multicore_decoder) { s << "\n Multi-core decoding:\n"; s.Format(" Total number of continuous executions found: {0}\n", - m_multicore_decoder->GetTotalContinuousExecutionsCount()); - s.Format(" Number of continuous executions for this thread: {0}\n", - m_multicore_decoder->GetNumContinuousExecutionsForThread(tid)); + storage.multicore_decoder->GetTotalContinuousExecutionsCount()); + s.Format( + " Number of continuous executions for this thread: {0}\n", + storage.multicore_decoder->GetNumContinuousExecutionsForThread(tid)); } // Errors @@ -234,7 +231,7 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { llvm::Expected> TraceIntelPT::GetRawTraceSize(Thread &thread) { - if (m_multicore_decoder) + if (GetUpdatedStorage().multicore_decoder) return None; // TODO: calculate the amount of intel pt raw trace associated // with the given thread. if (GetLiveProcess()) @@ -316,15 +313,17 @@ Expected TraceIntelPT::GetCPUInfo() { llvm::Optional TraceIntelPT::GetPerfZeroTscConversion() { + return GetUpdatedStorage().tsc_conversion; +} + +TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() { RefreshLiveProcessState(); - return m_tsc_conversion; + return m_storage; } Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state, StringRef json_response) { - m_thread_decoders.clear(); - m_tsc_conversion.reset(); - m_multicore_decoder.reset(); + m_storage = {}; Expected intelpt_state = json::parse(json_response, @@ -332,11 +331,13 @@ Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state, if (!intelpt_state) return intelpt_state.takeError(); + m_storage.tsc_conversion = intelpt_state->tsc_perf_zero_conversion; + if (!intelpt_state->cores) { for (const TraceThreadState &thread_state : state.traced_threads) { ThreadSP thread_sp = GetLiveProcess()->GetThreadList().FindThreadByID(thread_state.tid); - m_thread_decoders.emplace( + m_storage.thread_decoders.emplace( thread_state.tid, std::make_unique(thread_sp, *this)); } } else { @@ -351,12 +352,10 @@ Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state, if (!intelpt_state->tsc_perf_zero_conversion) return createStringError(inconvertibleErrorCode(), "Missing perf time_zero conversion values"); - m_multicore_decoder.emplace(*this, cores, tids, - *intelpt_state->tsc_perf_zero_conversion); + m_storage.multicore_decoder.emplace(*this); } - m_tsc_conversion = intelpt_state->tsc_perf_zero_conversion; - if (m_tsc_conversion) { + if (m_storage.tsc_conversion) { Log *log = GetLog(LLDBLog::Target); LLDB_LOG(log, "TraceIntelPT found TSC conversion information"); } @@ -364,10 +363,10 @@ Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state, } bool TraceIntelPT::IsTraced(lldb::tid_t tid) { - RefreshLiveProcessState(); - if (m_multicore_decoder) - return m_multicore_decoder->TracesThread(tid); - return m_thread_decoders.count(tid); + Storage &storage = GetUpdatedStorage(); + if (storage.multicore_decoder) + return storage.multicore_decoder->TracesThread(tid); + return storage.thread_decoders.count(tid); } // The information here should match the description of the intel-pt section @@ -481,46 +480,4 @@ Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kTraceBuffer, callback); } -TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; } - -Error TraceIntelPT::CreateThreadsFromContextSwitches() { - DenseMap> pids_to_tids; - - for (core_id_t core_id : GetTracedCores()) { - Error err = OnCoreBinaryDataRead( - core_id, IntelPTDataKinds::kPerfContextSwitchTrace, - [&](ArrayRef data) -> Error { - Expected> executions = - DecodePerfContextSwitchTrace(data, core_id, *m_tsc_conversion); - if (!executions) - return executions.takeError(); - for (const ThreadContinuousExecution &execution : *executions) - pids_to_tids[execution.pid].insert(execution.tid); - return Error::success(); - }); - if (err) - return err; - } - - DenseMap processes; - for (Process *proc : GetTracedProcesses()) - processes.try_emplace(proc->GetID(), proc); - - for (const auto &pid_to_tids : pids_to_tids) { - lldb::pid_t pid = pid_to_tids.first; - auto it = processes.find(pid); - if (it == processes.end()) - continue; - - Process &process = *it->second; - ThreadList &thread_list = process.GetThreadList(); - - for (lldb::tid_t tid : pid_to_tids.second) { - if (!thread_list.FindThreadByID(tid)) { - thread_list.AddThread(std::make_shared( - process, tid, /*trace_file*/ None)); - } - } - } - return Error::success(); -} +TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h index a0db4ae..53eba49 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -163,15 +163,6 @@ public: private: friend class TraceIntelPTSessionFileParser; - /// Create post-mortem threads associated with the processes traced by this - /// instance using the context switch traces. - /// - /// This does nothing if the threads already exist. - /// - /// \return - /// An \a llvm::Error in case of failures. - llvm::Error CreateThreadsFromContextSwitches(); - llvm::Expected GetCPUInfoForLiveProcess(); /// Postmortem trace constructor @@ -185,13 +176,12 @@ private: /// \param[in] trace_threads /// The threads traced in the live session. They must belong to the /// processes mentioned above. - TraceIntelPT(JSONTraceSession &session, const FileSpec &session_file_dir, + TraceIntelPT(JSONTraceSession &session, llvm::ArrayRef traced_processes, llvm::ArrayRef traced_threads); /// Constructor for live processes - TraceIntelPT(Process &live_process) - : Trace(live_process), m_thread_decoders(){}; + TraceIntelPT(Process &live_process) : Trace(live_process){}; /// Decode the trace of the given thread that, i.e. recontruct the traced /// instructions. @@ -205,20 +195,29 @@ private: /// errors are embedded in the instruction list. DecodedThreadSP Decode(Thread &thread); + /// We package all the data that can change upon process stops to make sure + /// this contract is very visible. + /// This variable should only be accessed directly by constructores or live + /// process data refreshers. + struct Storage { + llvm::Optional multicore_decoder; + /// These decoders are used for the non-per-core case + std::map> thread_decoders; + /// Helper variable used to track long running operations for telemetry. + TaskTimer task_timer; + /// It is provided by either a session file or a live process to convert TSC + /// counters to and from nanos. It might not be available on all hosts. + llvm::Optional tsc_conversion; + } m_storage; + /// It is provided by either a session file or a live process' "cpuInfo" - /// binary data. + /// binary data. We don't put it in the Storage because this variable doesn't + /// change. llvm::Optional m_cpu_info; - llvm::Optional m_multicore_decoder; - /// These decoders are used for the non-per-core case - std::map> m_thread_decoders; - /// Helper variable used to track long running operations for telemetry. - TaskTimer m_task_timer; - /// It is provided by either a session file or a live process to convert TSC - /// counters to and from nanos. It might not be available on all hosts. - llvm::Optional m_tsc_conversion; -}; -using TraceIntelPTSP = std::shared_ptr; + /// Get the storage after refreshing the data in the case of a live process. + Storage &GetUpdatedStorage(); +}; } // namespace trace_intel_pt } // namespace lldb_private diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h index 9d0fd78..39f2626 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h @@ -30,12 +30,12 @@ struct JSONModule { }; struct JSONThread { - int64_t tid; + uint64_t tid; llvm::Optional trace_buffer; }; struct JSONProcess { - int64_t pid; + uint64_t pid; llvm::Optional triple; std::vector threads; std::vector modules; diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp index 96b41e3..b6b660a 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.cpp @@ -17,11 +17,14 @@ using namespace lldb_private; using namespace lldb_private::trace_intel_pt; using namespace llvm; -TraceIntelPTMultiCoreDecoder::TraceIntelPTMultiCoreDecoder( - TraceIntelPT &trace, ArrayRef core_ids, ArrayRef tids, - const LinuxPerfZeroTscConversion &tsc_conversion) - : m_trace(trace), m_cores(core_ids.begin(), core_ids.end()), - m_tids(tids.begin(), tids.end()), m_tsc_conversion(tsc_conversion) {} +TraceIntelPTMultiCoreDecoder::TraceIntelPTMultiCoreDecoder(TraceIntelPT &trace) + : m_trace(&trace) { + for (Process *proc : trace.GetAllProcesses()) { + for (ThreadSP thread_sp : proc->GetThreadList().Threads()) { + m_tids.insert(thread_sp->GetID()); + } + } +} bool TraceIntelPTMultiCoreDecoder::TracesThread(lldb::tid_t tid) const { return m_tids.count(tid); @@ -38,12 +41,12 @@ DecodedThreadSP TraceIntelPTMultiCoreDecoder::Decode(Thread &thread) { DecodedThreadSP decoded_thread_sp = std::make_shared(thread.shared_from_this()); - Error err = m_trace.OnCoresBinaryDataRead( - m_cores, IntelPTDataKinds::kTraceBuffer, + Error err = m_trace->OnAllCoresBinaryDataRead( + IntelPTDataKinds::kTraceBuffer, [&](const DenseMap> buffers) -> Error { auto it = m_continuous_executions_per_thread->find(thread.GetID()); if (it != m_continuous_executions_per_thread->end()) - DecodeTrace(*decoded_thread_sp, m_trace, buffers, it->second); + DecodeTrace(*decoded_thread_sp, *m_trace, buffers, it->second); return Error::success(); }); @@ -60,14 +63,23 @@ TraceIntelPTMultiCoreDecoder::CorrelateContextSwitchesAndIntelPtTraces() { llvm::DenseMap> continuous_executions_per_thread; - for (core_id_t core_id : m_cores) { + Optional conv_opt = + m_trace->GetPerfZeroTscConversion(); + if (!conv_opt) + return createStringError( + inconvertibleErrorCode(), + "TSC to nanoseconds conversion values were not found"); + + LinuxPerfZeroTscConversion tsc_conversion = *conv_opt; + + for (core_id_t core_id : m_trace->GetTracedCores()) { std::vector intel_pt_executions; - Error err = m_trace.OnCoreBinaryDataRead( + Error err = m_trace->OnCoreBinaryDataRead( core_id, IntelPTDataKinds::kTraceBuffer, [&](ArrayRef data) -> Error { Expected> split_trace = - SplitTraceInContinuousExecutions(m_trace, data); + SplitTraceInContinuousExecutions(*m_trace, data); if (!split_trace) return split_trace.takeError(); @@ -96,11 +108,11 @@ TraceIntelPTMultiCoreDecoder::CorrelateContextSwitchesAndIntelPtTraces() { continuous_executions_per_thread[thread_execution.tid].push_back( execution); }; - err = m_trace.OnCoreBinaryDataRead( + err = m_trace->OnCoreBinaryDataRead( core_id, IntelPTDataKinds::kPerfContextSwitchTrace, [&](ArrayRef data) -> Error { Expected> executions = - DecodePerfContextSwitchTrace(data, core_id, m_tsc_conversion); + DecodePerfContextSwitchTrace(data, core_id, tsc_conversion); if (!executions) return executions.takeError(); for (const ThreadContinuousExecution &exec : *executions) @@ -125,7 +137,7 @@ Error TraceIntelPTMultiCoreDecoder::DecodeContextSwitchTraces() { if (m_continuous_executions_per_thread) return Error::success(); - Error err = m_trace.GetTimer().ForGlobal().TimeTask( + Error err = m_trace->GetTimer().ForGlobal().TimeTask( "Context switch and Intel PT traces correlation", [&]() -> Error { if (auto correlation = CorrelateContextSwitchesAndIntelPtTraces()) { m_continuous_executions_per_thread.emplace(std::move(*correlation)); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h index 2e01aa6..00dea87 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCoreDecoder.h @@ -24,20 +24,14 @@ namespace trace_intel_pt { /// to contention or race conditions. Finally, it assumes that a tid is not /// repeated twice for two different threads because of the shortness of the /// intel pt trace. +/// +/// This object should be recreated after every stop in the case of live +/// processes. class TraceIntelPTMultiCoreDecoder { public: - /// \param[in] core_ids - /// The list of cores where the traced programs were running on. - /// - /// \param[in] tids - /// The full list of tids that were traced. - /// - /// \param[in] tsc_conversion - /// The conversion values for converting between nanoseconds and TSCs. - TraceIntelPTMultiCoreDecoder( - TraceIntelPT &trace, llvm::ArrayRef core_ids, - llvm::ArrayRef tids, - const LinuxPerfZeroTscConversion &tsc_conversion); + /// \param[in] TraceIntelPT + /// The trace object to be decoded + TraceIntelPTMultiCoreDecoder(TraceIntelPT &trace); /// \return /// A \a DecodedThread for the \p thread by decoding its instructions on all @@ -68,16 +62,14 @@ private: llvm::DenseMap>> CorrelateContextSwitchesAndIntelPtTraces(); - TraceIntelPT &m_trace; - std::set m_cores; + TraceIntelPT *m_trace; std::set m_tids; llvm::Optional< llvm::DenseMap>> m_continuous_executions_per_thread; llvm::DenseMap m_decoded_threads; - LinuxPerfZeroTscConversion m_tsc_conversion; /// This variable will be non-None if a severe error happened during the setup - /// of the decoder. + /// of the decoder and we don't want decoding to be reattempted. llvm::Optional m_setup_error; uint64_t m_unattributed_intelpt_subtraces; }; diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp index 50ef4b4..56ef530 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp @@ -22,39 +22,45 @@ using namespace lldb_private; using namespace lldb_private::trace_intel_pt; using namespace llvm; -void TraceIntelPTSessionFileParser::NormalizePath( - lldb_private::FileSpec &file_spec) { +FileSpec TraceIntelPTSessionFileParser::NormalizePath(const std::string &path) { + FileSpec file_spec(path); if (file_spec.IsRelative()) file_spec.PrependPathComponent(m_session_file_dir); + return file_spec; } Error TraceIntelPTSessionFileParser::ParseModule(lldb::TargetSP &target_sp, const JSONModule &module) { - FileSpec system_file_spec(module.system_path); - NormalizePath(system_file_spec); + auto do_parse = [&]() -> Error { + FileSpec system_file_spec(module.system_path); - FileSpec local_file_spec(module.file.hasValue() ? *module.file - : module.system_path); - NormalizePath(local_file_spec); + FileSpec local_file_spec(module.file.hasValue() ? *module.file + : module.system_path); - ModuleSpec module_spec; - module_spec.GetFileSpec() = local_file_spec; - module_spec.GetPlatformFileSpec() = system_file_spec; + ModuleSpec module_spec; + module_spec.GetFileSpec() = local_file_spec; + module_spec.GetPlatformFileSpec() = system_file_spec; - if (module.uuid.hasValue()) - module_spec.GetUUID().SetFromStringRef(*module.uuid); + if (module.uuid.hasValue()) + module_spec.GetUUID().SetFromStringRef(*module.uuid); - Status error; - ModuleSP module_sp = - target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error); + Status error; + ModuleSP module_sp = + target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error); - if (error.Fail()) - return error.ToError(); + if (error.Fail()) + return error.ToError(); - bool load_addr_changed = false; - module_sp->SetLoadAddress(*target_sp, module.load_address, false, - load_addr_changed); - return llvm::Error::success(); + bool load_addr_changed = false; + module_sp->SetLoadAddress(*target_sp, module.load_address, false, + load_addr_changed); + return Error::success(); + }; + if (Error err = do_parse()) + return createStringError( + inconvertibleErrorCode(), "Error when parsing module %s. %s", + module.system_path.c_str(), toString(std::move(err)).c_str()); + return Error::success(); } Error TraceIntelPTSessionFileParser::CreateJSONError(json::Path::Root &root, @@ -73,10 +79,8 @@ TraceIntelPTSessionFileParser::ParseThread(ProcessSP &process_sp, lldb::tid_t tid = static_cast(thread.tid); Optional trace_file; - if (thread.trace_buffer) { - trace_file.emplace(*thread.trace_buffer); - NormalizePath(*trace_file); - } + if (thread.trace_buffer) + trace_file = FileSpec(*thread.trace_buffer); ThreadPostMortemTraceSP thread_sp = std::make_shared(*process_sp, tid, trace_file); @@ -225,6 +229,54 @@ Notes: return schema; } +Error TraceIntelPTSessionFileParser::AugmentThreadsFromContextSwitches( + JSONTraceSession &session) { + if (!session.cores) + return Error::success(); + + if (!session.tsc_perf_zero_conversion) + return createStringError(inconvertibleErrorCode(), + "TSC to nanos conversion values are needed when " + "context switch information is provided."); + + DenseMap indexed_processes; + DenseMap> indexed_threads; + + for (JSONProcess &process : session.processes) { + indexed_processes[process.pid] = &process; + for (JSONThread &thread : process.threads) + indexed_threads[&process].insert(thread.tid); + } + + auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) { + auto proc = indexed_processes.find(pid); + if (proc == indexed_processes.end()) + return; + if (indexed_threads[proc->second].count(tid)) + return; + indexed_threads[proc->second].insert(tid); + proc->second->threads.push_back({tid, /*trace_buffer=*/None}); + }; + + for (const JSONCore &core : *session.cores) { + Error err = Trace::OnDataFileRead( + FileSpec(core.context_switch_trace), + [&](ArrayRef data) -> Error { + Expected> executions = + DecodePerfContextSwitchTrace(data, core.core_id, + *session.tsc_perf_zero_conversion); + if (!executions) + return executions.takeError(); + for (const ThreadContinuousExecution &execution : *executions) + on_thread_seen(execution.pid, execution.tid); + return Error::success(); + }); + if (err) + return err; + } + return Error::success(); +} + Expected TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance( JSONTraceSession &session, std::vector &parsed_processes) { std::vector threads; @@ -235,17 +287,33 @@ Expected TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance( parsed_process.threads.end()); } - TraceIntelPTSP trace_instance(new TraceIntelPT( - session, FileSpec(m_session_file_dir), processes, threads)); + TraceSP trace_instance(new TraceIntelPT(session, processes, threads)); for (const ParsedProcess &parsed_process : parsed_processes) parsed_process.target_sp->SetTrace(trace_instance); + return trace_instance; +} + +void TraceIntelPTSessionFileParser::NormalizeAllPaths( + JSONTraceSession &session) { + for (JSONProcess &process : session.processes) { + for (JSONModule &module : process.modules) { + module.system_path = NormalizePath(module.system_path).GetPath(); + if (module.file) + module.file = NormalizePath(*module.file).GetPath(); + } + for (JSONThread &thread : process.threads) { + if (thread.trace_buffer) + thread.trace_buffer = NormalizePath(*thread.trace_buffer).GetPath(); + } + } if (session.cores) { - if (Error err = trace_instance->CreateThreadsFromContextSwitches()) - return std::move(err); + for (JSONCore &core : *session.cores) { + core.context_switch_trace = + NormalizePath(core.context_switch_trace).GetPath(); + core.trace_buffer = NormalizePath(core.trace_buffer).GetPath(); + } } - - return trace_instance; } Expected TraceIntelPTSessionFileParser::Parse() { @@ -254,6 +322,11 @@ Expected TraceIntelPTSessionFileParser::Parse() { if (!fromJSON(m_trace_session_file, session, root)) return CreateJSONError(root, m_trace_session_file); + NormalizeAllPaths(session); + + if (Error err = AugmentThreadsFromContextSwitches(session)) + return std::move(err); + if (Expected> parsed_processes = ParseSessionFile(session)) return CreateTraceIntelPTInstance(session, *parsed_processes); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h index 04d9811..d000ad2 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h @@ -57,9 +57,8 @@ public: std::vector &parsed_processes); private: - /// Resolve non-absolute paths relative to the session file folder. It - /// modifies the given file_spec. - void NormalizePath(lldb_private::FileSpec &file_spec); + /// Resolve non-absolute paths relative to the session file folder. + FileSpec NormalizePath(const std::string &path); lldb::ThreadPostMortemTraceSP ParseThread(lldb::ProcessSP &process_sp, const JSONThread &thread); @@ -92,6 +91,19 @@ private: llvm::Expected> ParseSessionFile(const JSONTraceSession &session); + /// When applicable, augment the list of threads in the session file by + /// inspecting the context switch trace. This only applies for threads of + /// processes already specified in this session file. + /// + /// \return + /// An \a llvm::Error in case if failures, or \a llvm::Error::success + /// otherwise. + llvm::Error AugmentThreadsFromContextSwitches(JSONTraceSession &session); + + /// Modifiy the session file by normalizing all the paths relative to the + /// session file directory. + void NormalizeAllPaths(JSONTraceSession &session); + Debugger &m_debugger; const llvm::json::Value &m_trace_session_file; std::string m_session_file_dir; diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp index caae817..1cf254d 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp @@ -262,7 +262,7 @@ BuildProcessSection(Process &process, const FileSpec &directory) { return json_modules.takeError(); return JSONProcess{ - static_cast(process.GetID()), + process.GetID(), process.GetTarget().GetArchitecture().GetTriple().getTriple(), json_threads.get(), json_modules.get()}; } diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp index b6c84de..24ad72f 100644 --- a/lldb/source/Target/Trace.cpp +++ b/lldb/source/Target/Trace.cpp @@ -116,8 +116,9 @@ Expected Trace::GetLiveProcessState() { Optional Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, llvm::StringRef kind) { - auto it = m_live_thread_data.find(tid); - if (it == m_live_thread_data.end()) + Storage &storage = GetUpdatedStorage(); + auto it = storage.live_thread_data.find(tid); + if (it == storage.live_thread_data.end()) return None; std::unordered_map &single_thread_data = it->second; auto single_thread_data_it = single_thread_data.find(kind.str()); @@ -128,8 +129,9 @@ Optional Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, Optional Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id, llvm::StringRef kind) { - auto it = m_live_core_data_sizes.find(core_id); - if (it == m_live_core_data_sizes.end()) + Storage &storage = GetUpdatedStorage(); + auto it = storage.live_core_data_sizes.find(core_id); + if (it == storage.live_core_data_sizes.end()) return None; std::unordered_map &single_core_data = it->second; auto single_thread_data_it = single_core_data.find(kind.str()); @@ -139,8 +141,9 @@ Optional Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id, } Optional Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { - auto data_it = m_live_process_data.find(kind.str()); - if (data_it == m_live_process_data.end()) + Storage &storage = GetUpdatedStorage(); + auto data_it = storage.live_process_data.find(kind.str()); + if (data_it == storage.live_process_data.end()) return None; return data_it->second; } @@ -197,6 +200,11 @@ Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { return m_live_process->TraceGetBinaryData(request); } +Trace::Storage &Trace::GetUpdatedStorage() { + RefreshLiveProcessState(); + return m_storage; +} + const char *Trace::RefreshLiveProcessState() { if (!m_live_process) return nullptr; @@ -209,15 +217,11 @@ const char *Trace::RefreshLiveProcessState() { LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked"); m_stop_id = new_stop_id; - m_live_thread_data.clear(); - m_live_refresh_error.reset(); - m_live_core_data_sizes.clear(); - m_live_core_data.clear(); - m_cores.reset(); + m_storage = Trace::Storage(); auto HandleError = [&](Error &&err) -> const char * { - m_live_refresh_error = toString(std::move(err)); - return m_live_refresh_error->c_str(); + m_storage.live_refresh_error = toString(std::move(err)); + return m_storage.live_refresh_error->c_str(); }; Expected json_string = GetLiveProcessState(); @@ -237,18 +241,19 @@ const char *Trace::RefreshLiveProcessState() { for (const TraceThreadState &thread_state : live_process_state->traced_threads) { for (const TraceBinaryData &item : thread_state.binary_data) - m_live_thread_data[thread_state.tid][item.kind] = item.size; + m_storage.live_thread_data[thread_state.tid][item.kind] = item.size; } LLDB_LOG(log, "== Found {0} threads being traced", live_process_state->traced_threads.size()); if (live_process_state->cores) { - m_cores.emplace(); + m_storage.cores.emplace(); for (const TraceCoreState &core_state : *live_process_state->cores) { - m_cores->push_back(core_state.core_id); + m_storage.cores->push_back(core_state.core_id); for (const TraceBinaryData &item : core_state.binary_data) - m_live_core_data_sizes[core_state.core_id][item.kind] = item.size; + m_storage.live_core_data_sizes[core_state.core_id][item.kind] = + item.size; } LLDB_LOG(log, "== Found {0} cpu cores being traced", live_process_state->cores->size()); @@ -256,7 +261,7 @@ const char *Trace::RefreshLiveProcessState() { for (const TraceBinaryData &item : live_process_state->process_binary_data) - m_live_process_data[item.kind] = item.size; + m_storage.live_process_data[item.kind] = item.size; if (Error err = DoRefreshLiveProcessState(std::move(*live_process_state), *json_string)) @@ -268,14 +273,14 @@ const char *Trace::RefreshLiveProcessState() { Trace::Trace(ArrayRef postmortem_processes, Optional> postmortem_cores) { for (ProcessSP process_sp : postmortem_processes) - m_postmortem_processes.push_back(process_sp.get()); - m_cores = postmortem_cores; + m_storage.postmortem_processes.push_back(process_sp.get()); + m_storage.cores = postmortem_cores; } Process *Trace::GetLiveProcess() { return m_live_process; } ArrayRef Trace::GetPostMortemProcesses() { - return m_postmortem_processes; + return m_storage.postmortem_processes; } std::vector Trace::GetAllProcesses() { @@ -298,8 +303,9 @@ Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { tid, kind)); }; - auto it = m_postmortem_thread_data.find(tid); - if (it == m_postmortem_thread_data.end()) + Storage &storage = GetUpdatedStorage(); + auto it = storage.postmortem_thread_data.find(tid); + if (it == storage.postmortem_thread_data.end()) return NotFoundError(); std::unordered_map &data_kind_to_file_spec_map = @@ -320,8 +326,9 @@ Trace::GetPostMortemCoreDataFile(lldb::core_id_t core_id, core_id, kind)); }; - auto it = m_postmortem_core_data.find(core_id); - if (it == m_postmortem_core_data.end()) + Storage &storage = GetUpdatedStorage(); + auto it = storage.postmortem_core_data.find(core_id); + if (it == storage.postmortem_core_data.end()) return NotFoundError(); std::unordered_map &data_kind_to_file_spec_map = @@ -334,13 +341,15 @@ Trace::GetPostMortemCoreDataFile(lldb::core_id_t core_id, void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind, FileSpec file_spec) { - m_postmortem_thread_data[tid][kind.str()] = file_spec; + Storage &storage = GetUpdatedStorage(); + storage.postmortem_thread_data[tid][kind.str()] = file_spec; } void Trace::SetPostMortemCoreDataFile(lldb::core_id_t core_id, llvm::StringRef kind, FileSpec file_spec) { - m_postmortem_core_data[core_id][kind.str()] = file_spec; + Storage &storage = GetUpdatedStorage(); + storage.postmortem_core_data[core_id][kind.str()] = file_spec; } llvm::Error @@ -355,8 +364,9 @@ Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, llvm::Error Trace::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id, llvm::StringRef kind, OnBinaryDataReadCallback callback) { - auto core_data_entries = m_live_core_data.find(core_id); - if (core_data_entries != m_live_core_data.end()) { + Storage &storage = GetUpdatedStorage(); + auto core_data_entries = storage.live_core_data.find(core_id); + if (core_data_entries != storage.live_core_data.end()) { auto core_data = core_data_entries->second.find(kind.str()); if (core_data != core_data_entries->second.end()) return callback(core_data->second); @@ -365,7 +375,8 @@ llvm::Error Trace::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id, Expected> data = GetLiveCoreBinaryData(core_id, kind); if (!data) return data.takeError(); - auto it = m_live_core_data[core_id].insert({kind.str(), std::move(*data)}); + auto it = + storage.live_core_data[core_id].insert({kind.str(), std::move(*data)}); return callback(it.first->second); } @@ -374,7 +385,9 @@ llvm::Error Trace::OnDataFileRead(FileSpec file, ErrorOr> trace_or_error = MemoryBuffer::getFile(file.GetPath()); if (std::error_code err = trace_or_error.getError()) - return errorCodeToError(err); + return createStringError( + inconvertibleErrorCode(), "Failed fetching trace-related file %s. %s", + file.GetCString(), toString(errorCodeToError(err)).c_str()); MemoryBuffer &data = **trace_or_error; ArrayRef array_ref( @@ -404,7 +417,6 @@ Trace::OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id, llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, OnBinaryDataReadCallback callback) { - RefreshLiveProcessState(); if (m_live_process) return OnLiveThreadBinaryDataRead(tid, kind, callback); else @@ -412,14 +424,16 @@ llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, } llvm::Error -Trace::OnCoresBinaryDataRead(const std::set core_ids, - llvm::StringRef kind, - OnCoresBinaryDataReadCallback callback) { +Trace::OnAllCoresBinaryDataRead(llvm::StringRef kind, + OnCoresBinaryDataReadCallback callback) { DenseMap> buffers; + Storage &storage = GetUpdatedStorage(); + if (!storage.cores) + return Error::success(); - std::function::iterator)> process_core = - [&](std::set::iterator core_id) -> Error { - if (core_id == core_ids.end()) + std::function::iterator)> process_core = + [&](std::vector::iterator core_id) -> Error { + if (core_id == storage.cores->end()) return callback(buffers); return OnCoreBinaryDataRead(*core_id, kind, @@ -430,13 +444,12 @@ Trace::OnCoresBinaryDataRead(const std::set core_ids, return process_core(next_id); }); }; - return process_core(core_ids.begin()); + return process_core(storage.cores->begin()); } llvm::Error Trace::OnCoreBinaryDataRead(lldb::core_id_t core_id, llvm::StringRef kind, OnBinaryDataReadCallback callback) { - RefreshLiveProcessState(); if (m_live_process) return OnLiveCoreBinaryDataRead(core_id, kind, callback); else @@ -444,16 +457,17 @@ llvm::Error Trace::OnCoreBinaryDataRead(lldb::core_id_t core_id, } ArrayRef Trace::GetTracedCores() { - RefreshLiveProcessState(); - if (m_cores) - return *m_cores; + Storage &storage = GetUpdatedStorage(); + if (storage.cores) + return *storage.cores; return {}; } -std::vector Trace::GetTracedProcesses() const { +std::vector Trace::GetTracedProcesses() { std::vector processes; + Storage &storage = GetUpdatedStorage(); - for (Process *proc : m_postmortem_processes) + for (Process *proc : storage.postmortem_processes) processes.push_back(proc); if (m_live_process) diff --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json index 4bbb427..b8e77d7 100644 --- a/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json +++ b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace.json @@ -1,14 +1,14 @@ { "cores": [ { - "contextSwitchTrace": "/tmp/trace8/cores/45.perf_context_switch_trace", + "contextSwitchTrace": "cores/45.perf_context_switch_trace", "coreId": 45, - "traceBuffer": "/tmp/trace8/cores/45.intelpt_trace" + "traceBuffer": "cores/45.intelpt_trace" }, { - "contextSwitchTrace": "/tmp/trace8/cores/51.perf_context_switch_trace", + "contextSwitchTrace": "cores/51.perf_context_switch_trace", "coreId": 51, - "traceBuffer": "/tmp/trace8/cores/51.intelpt_trace" + "traceBuffer": "cores/51.intelpt_trace" } ], "cpuInfo": { diff --git a/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace_missing_threads.json b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace_missing_threads.json index e03c8e4..e77fb87 100644 --- a/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace_missing_threads.json +++ b/lldb/test/API/commands/trace/intelpt-multi-core-trace/trace_missing_threads.json @@ -1,14 +1,14 @@ { "cores": [ { - "contextSwitchTrace": "/tmp/trace8/cores/45.perf_context_switch_trace", + "contextSwitchTrace": "cores/45.perf_context_switch_trace", "coreId": 45, - "traceBuffer": "/tmp/trace8/cores/45.intelpt_trace" + "traceBuffer": "cores/45.intelpt_trace" }, { - "contextSwitchTrace": "/tmp/trace8/cores/51.perf_context_switch_trace", + "contextSwitchTrace": "cores/51.perf_context_switch_trace", "coreId": 51, - "traceBuffer": "/tmp/trace8/cores/51.intelpt_trace" + "traceBuffer": "cores/51.intelpt_trace" } ], "cpuInfo": { -- 2.7.4