[Lldb-commits] [lldb] fc5ef57 - [trace][intelpt] Support system-wide tracing [12] - Support multi-core trace load and save
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Wed Jun 15 13:28:44 PDT 2022
Author: Walter Erquinigo
Date: 2022-06-15T13:28:36-07:00
New Revision: fc5ef57c7df5805c828de2e3e334c2cf74648e58
URL: https://github.com/llvm/llvm-project/commit/fc5ef57c7df5805c828de2e3e334c2cf74648e58
DIFF: https://github.com/llvm/llvm-project/commit/fc5ef57c7df5805c828de2e3e334c2cf74648e58.diff
LOG: [trace][intelpt] Support system-wide tracing [12] - Support multi-core trace load and save
:q!
This diff is massive, but it's because it connects the client with lldb-server
and also ensures that the postmortem case works.
- Flatten the postmortem trace schema. The reason is that the schema has become quite complex due to the new multicore case, which defeats the original purpose of having a schema that could work for every trace plug-in. At this point, it's better that each trace plug-in defines it's own full schema. This means that the only common field is "type".
-- Because of this new approach, I merged the "common" trace load and saving functionalities into the IntelPT one. This simplified the code quite a bit. If we eventually implement another trace plug-in, we can see then what we could reuse.
-- The new schema, which is flattened, has now better comments and is parsed better. A change I did was to disallow hex addresses, because they are a bit error prone. I'm asking now to print the address in decimal.
-- Renamed "intel" to "GenuineIntel" in the schema because that's what you see in /proc/cpuinfo.
- Implemented reading the context switch trace data buffer. I had to do
some refactors to do that cleanly.
-- A major change that I did here was to simplify the perf_event circular buffer reading logic. It was too complex. Maybe the original Intel author had something different in mind.
- Implemented all the necessary bits to read trace.json files with per-core data.
- Implemented all the necessary bits to save to disk per-core trace session.
- Added a test that ensures that parsing and saving to disk works.
Differential Revision: https://reviews.llvm.org/D126015
Added:
Modified:
lldb/docs/lldb-gdb-remote.txt
lldb/docs/use/intel_pt.rst
lldb/include/lldb/Target/Trace.h
lldb/include/lldb/Utility/TraceGDBRemotePackets.h
lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
lldb/source/Plugins/Process/Linux/Perf.cpp
lldb/source/Plugins/Process/Linux/Perf.h
lldb/source/Plugins/Trace/common/CMakeLists.txt
lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
lldb/source/Target/Trace.cpp
lldb/source/Utility/TraceGDBRemotePackets.cpp
lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
lldb/test/API/commands/trace/TestTraceLoad.py
lldb/test/API/commands/trace/TestTraceSave.py
lldb/test/API/commands/trace/TestTraceSchema.py
lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
lldb/test/API/commands/trace/intelpt-trace/trace.json
lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
lldb/unittests/Process/Linux/PerfTests.cpp
lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp
Removed:
lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
lldb/source/Plugins/Trace/common/TraceJSONStructs.h
lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp
lldb/source/Plugins/Trace/common/TraceSessionSaver.h
################################################################################
diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt
index 1996e3f8fac9d..e62707bd7db4d 100644
--- a/lldb/docs/lldb-gdb-remote.txt
+++ b/lldb/docs/lldb-gdb-remote.txt
@@ -534,7 +534,7 @@ read packet: OK/E<error code>;AAAAAAAAA
// "tscPerfZeroConversion": {
// "timeMult": <decimal integer>,
// "timeShift": <decimal integer>,
-// "timeSero": <decimal integer>,
+// "timeZero": <decimal integer>,
// }
//----------------------------------------------------------------------
diff --git a/lldb/docs/use/intel_pt.rst b/lldb/docs/use/intel_pt.rst
index 8b80b4a25b75b..ae6f72011ace4 100644
--- a/lldb/docs/use/intel_pt.rst
+++ b/lldb/docs/use/intel_pt.rst
@@ -182,14 +182,12 @@ For example
::
{
- "trace": {
- "type": "intel-pt",
- "pt_cpu": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -198,14 +196,14 @@ For example
"threads": [
{
"tid": 815455,
- "traceFile": "trace.file" # raw thread-specific trace from the AUX buffer
+ "traceBuffer": "trace.file" # raw thread-specific trace from the AUX buffer
}
],
"modules": [ # this are all the shared libraries + the main executable
{
"file": "a.out", # optional if it's the same as systemPath
"systemPath": "a.out",
- "loadAddress": "0x0000000000400000",
+ "loadAddress": 4194304,
},
{
"file": "libfoo.so",
@@ -251,4 +249,4 @@ References
- Some details about how Meta is using Intel Processor Trace can be found in this blog_ post.
.. _document: https://docs.google.com/document/d/1cOVTGp1sL_HBXjP9eB7qjVtDNr5xnuZvUUtv43G5eVI
-.. _blog: https://engineering.fb.com/2021/04/27/developer-tools/reverse-debugging/
\ No newline at end of file
+.. _blog: https://engineering.fb.com/2021/04/27/developer-tools/reverse-debugging/
diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index 88af9d3b221dc..534c87a2d5b6c 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -240,6 +240,7 @@ class Trace : public PluginInterface,
using OnBinaryDataReadCallback =
std::function<llvm::Error(llvm::ArrayRef<uint8_t> data)>;
+
/// Fetch binary data associated with a thread, either live or postmortem, and
/// pass it to the given callback. The reason of having a callback is to free
/// the caller from having to manage the life cycle of the data and to hide
@@ -265,23 +266,77 @@ class Trace : public PluginInterface,
llvm::Error OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
OnBinaryDataReadCallback callback);
- /// Get the current traced live process.
+ /// Fetch binary data associated with a core, either live or postmortem, and
+ /// pass it to the given callback. The reason of having a callback is to free
+ /// the caller from having to manage the life cycle of the data and to hide
+ /// the
diff erent data fetching procedures that exist for live and post mortem
+ /// cores.
+ ///
+ /// The fetched data is not persisted after the callback is invoked.
+ ///
+ /// \param[in] core_id
+ /// The core who owns the data.
+ ///
+ /// \param[in] kind
+ /// The kind of data to read.
+ ///
+ /// \param[in] callback
+ /// The callback to be invoked once the data was successfully read. Its
+ /// return value, which is an \a llvm::Error, is returned by this
+ /// function.
///
/// \return
- /// The current traced live process. If it's not a live process,
- /// return \a nullptr.
- Process *GetLiveProcess();
+ /// An \a llvm::Error if the data couldn't be fetched, or the return value
+ /// of the callback, otherwise.
+ llvm::Error OnCoreBinaryDataRead(lldb::core_id_t core_id,
+ llvm::StringRef kind,
+ OnBinaryDataReadCallback callback);
+
+ /// \return
+ /// All the currently traced processes.
+ std::vector<Process *> GetAllProcesses();
+
+ /// \return
+ /// The list of cores being traced. Might be empty depending on the
+ /// plugin.
+ llvm::ArrayRef<lldb::core_id_t> GetTracedCores();
protected:
+ /// Get the currently traced live process.
+ ///
+ /// \return
+ /// If it's not a live process, return \a nullptr.
+ Process *GetLiveProcess();
+
+ /// Get the currently traced postmortem processes.
+ ///
+ /// \return
+ /// If it's not a live process session, return an empty list.
+ llvm::ArrayRef<Process *> GetPostMortemProcesses();
+
/// Implementation of \a OnThreadBinaryDataRead() for live threads.
llvm::Error OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
OnBinaryDataReadCallback callback);
+ /// Implementation of \a OnLiveBinaryDataRead() for live cores.
+ llvm::Error OnLiveCoreBinaryDataRead(lldb::core_id_t core,
+ llvm::StringRef kind,
+ OnBinaryDataReadCallback callback);
+
/// Implementation of \a OnThreadBinaryDataRead() for post mortem threads.
llvm::Error
OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
OnBinaryDataReadCallback callback);
+ /// Implementation of \a OnCoreBinaryDataRead() for post mortem cores.
+ llvm::Error OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id,
+ 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.
///
@@ -297,6 +352,21 @@ class Trace : public PluginInterface,
llvm::Expected<FileSpec> GetPostMortemThreadDataFile(lldb::tid_t tid,
llvm::StringRef kind);
+ /// Get the file path containing data of a postmortem core given a data
+ /// identifier.
+ ///
+ /// \param[in] core_id
+ /// The core whose data is requested.
+ ///
+ /// \param[in] kind
+ /// The kind of data requested.
+ ///
+ /// \return
+ /// The file spec containing the requested data, or an \a llvm::Error in
+ /// case of failures.
+ llvm::Expected<FileSpec> GetPostMortemCoreDataFile(lldb::core_id_t core_id,
+ llvm::StringRef kind);
+
/// Associate a given thread with a data file using a data identifier.
///
/// \param[in] tid
@@ -310,6 +380,19 @@ class Trace : public PluginInterface,
void SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind,
FileSpec file_spec);
+ /// Associate a given core with a data file using a data identifier.
+ ///
+ /// \param[in] core_id
+ /// The core associated with the data file.
+ ///
+ /// \param[in] kind
+ /// The kind of data being registered.
+ ///
+ /// \param[in] file_spec
+ /// The path of the data file.
+ void SetPostMortemCoreDataFile(lldb::core_id_t core_id, llvm::StringRef kind,
+ FileSpec file_spec);
+
/// Get binary data of a live thread given a data identifier.
///
/// \param[in] tid
@@ -324,6 +407,20 @@ class Trace : public PluginInterface,
llvm::Expected<std::vector<uint8_t>>
GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind);
+ /// Get binary data of a live core given a data identifier.
+ ///
+ /// \param[in] core_id
+ /// The core whose data is requested.
+ ///
+ /// \param[in] kind
+ /// The kind of data requested.
+ ///
+ /// \return
+ /// A vector of bytes with the requested data, or an \a llvm::Error in
+ /// case of failures.
+ llvm::Expected<std::vector<uint8_t>>
+ GetLiveCoreBinaryData(lldb::core_id_t core_id, llvm::StringRef kind);
+
/// Get binary data of the current process given a data identifier.
///
/// \param[in] kind
@@ -339,10 +436,16 @@ class Trace : public PluginInterface,
llvm::Optional<size_t> GetLiveThreadBinaryDataSize(lldb::tid_t tid,
llvm::StringRef kind);
+ /// Get the size of the data returned by \a GetLiveCoreBinaryData
+ llvm::Optional<size_t> GetLiveCoreBinaryDataSize(lldb::core_id_t core_id,
+ llvm::StringRef kind);
+
/// Get the size of the data returned by \a GetLiveProcessBinaryData
llvm::Optional<size_t> GetLiveProcessBinaryDataSize(llvm::StringRef kind);
+
/// Constructor for post mortem processes
- Trace() = default;
+ Trace(llvm::ArrayRef<lldb::ProcessSP> postmortem_processes,
+ llvm::Optional<std::vector<lldb::core_id_t>> postmortem_cores);
/// Constructor for a live process
Trace(Process &live_process) : m_live_process(&live_process) {}
@@ -400,6 +503,10 @@ class Trace : public PluginInterface,
/// 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<Process *> 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.
@@ -409,16 +516,30 @@ class Trace : public PluginInterface,
llvm::DenseMap<lldb::tid_t, std::unordered_map<std::string, size_t>>
m_live_thread_data;
+ /// core id -> data kind -> size
+ llvm::DenseMap<lldb::core_id_t, std::unordered_map<std::string, size_t>>
+ m_live_core_data;
/// data kind -> size
std::unordered_map<std::string, size_t> m_live_process_data;
/// \}
+ /// The list of cores being traced. Might be \b None depending on the plug-in.
+ llvm::Optional<std::vector<lldb::core_id_t>> 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<lldb::tid_t, std::unordered_map<std::string, FileSpec>>
m_postmortem_thread_data;
+ /// core id -> data kind -> file
+ llvm::DenseMap<lldb::core_id_t, std::unordered_map<std::string, FileSpec>>
+ m_postmortem_core_data;
+
+ /// \}
+
llvm::Optional<std::string> m_live_refresh_error;
};
diff --git a/lldb/include/lldb/Utility/TraceGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
index d7b5a37e4cccb..2854f2f48129d 100644
--- a/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/TraceGDBRemotePackets.h
@@ -133,7 +133,9 @@ struct TraceGetStateResponse {
std::vector<TraceThreadState> traced_threads;
std::vector<TraceBinaryData> process_binary_data;
llvm::Optional<std::vector<TraceCoreState>> cores;
- std::vector<std::string> warnings;
+ llvm::Optional<std::vector<std::string>> warnings;
+
+ void AddWarning(llvm::StringRef warning);
};
bool fromJSON(const llvm::json::Value &value, TraceGetStateResponse &packet,
@@ -151,6 +153,8 @@ struct TraceGetBinaryDataRequest {
std::string kind;
/// Optional tid if the data is related to a thread.
llvm::Optional<lldb::tid_t> tid;
+ /// Optional core id if the data is related to a cpu core.
+ llvm::Optional<lldb::tid_t> core_id;
/// Offset in bytes from where to start reading the data.
uint64_t offset;
/// Number of bytes to read.
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
index 29ff87f5b4df1..a36d6dcce6fad 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTCollector.cpp
@@ -182,32 +182,39 @@ Expected<json::Value> IntelPTCollector::GetState() {
FetchPerfTscConversionParameters())
state.tsc_perf_zero_conversion = *tsc_conversion;
else
- state.warnings.push_back(toString(tsc_conversion.takeError()));
+ state.AddWarning(toString(tsc_conversion.takeError()));
return toJSON(state);
}
Expected<std::vector<uint8_t>>
IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) {
- if (request.kind == IntelPTDataKinds::kTraceBuffer) {
- if (!request.tid)
- return createStringError(
- inconvertibleErrorCode(),
- "Getting a trace buffer without a tid is currently unsupported");
+ if (request.kind == IntelPTDataKinds::kProcFsCpuInfo)
+ return GetProcfsCpuInfo();
- if (m_process_trace_up && m_process_trace_up->TracesThread(*request.tid))
- return m_process_trace_up->GetBinaryData(request);
+ if (m_process_trace_up) {
+ Expected<Optional<std::vector<uint8_t>>> data =
+ m_process_trace_up->TryGetBinaryData(request);
+ if (!data)
+ return data.takeError();
+ if (*data)
+ return **data;
+ }
- if (Expected<IntelPTSingleBufferTrace &> trace =
- m_thread_traces.GetTracedThread(*request.tid))
- return trace->GetTraceBuffer(request.offset, request.size);
- else
- return trace.takeError();
- } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) {
- return GetProcfsCpuInfo();
+ {
+ Expected<Optional<std::vector<uint8_t>>> data =
+ m_thread_traces.TryGetBinaryData(request);
+ if (!data)
+ return data.takeError();
+ if (*data)
+ return **data;
}
- return createStringError(inconvertibleErrorCode(),
- "Unsuported trace binary data kind: %s",
- request.kind.c_str());
+
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("Can't fetch data kind {0} for core_id {1}, tid {2} and "
+ "\"process tracing\" mode {3}",
+ request.kind, request.core_id, request.tid,
+ m_process_trace_up ? "enabled" : "not enabled"));
}
bool IntelPTCollector::IsSupported() {
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
index ea58cf6a6e315..572a867ed4c18 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp
@@ -70,8 +70,8 @@ static Expected<PerfEvent> CreateContextSwitchTracePerfEvent(
if (Expected<PerfEvent> 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)) {
+ if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
+ data_buffer_numpages, 0, /*data_buffer_write=*/false)) {
return std::move(mmap_err);
}
return perf_event;
@@ -190,7 +190,21 @@ Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) {
"per-core process tracing is enabled.");
}
-Expected<std::vector<uint8_t>>
-IntelPTMultiCoreTrace::GetBinaryData(const TraceGetBinaryDataRequest &request) {
- return createStringError(inconvertibleErrorCode(), "Unimplemented");
+Expected<Optional<std::vector<uint8_t>>>
+IntelPTMultiCoreTrace::TryGetBinaryData(
+ const TraceGetBinaryDataRequest &request) {
+ if (!request.core_id)
+ return None;
+ auto it = m_traces_per_core.find(*request.core_id);
+ if (it == m_traces_per_core.end())
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("Core {0} is not being traced", *request.core_id));
+
+ if (request.kind == IntelPTDataKinds::kTraceBuffer)
+ return it->second.first.GetTraceBuffer(request.offset, request.size);
+ if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace)
+ return it->second.second.ReadFlushedOutDataCyclicBuffer(request.offset,
+ request.size);
+ return None;
}
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
index 30fb5dea8075d..ff5693938614e 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.h
@@ -80,8 +80,8 @@ class IntelPTMultiCoreTrace : public IntelPTProcessTrace {
llvm::Error TraceStop(lldb::tid_t tid) override;
- llvm::Expected<std::vector<uint8_t>>
- GetBinaryData(const TraceGetBinaryDataRequest &request) override;
+ llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+ TryGetBinaryData(const TraceGetBinaryDataRequest &request) override;
private:
/// This assumes that all underlying perf_events for each core are part of the
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
index 1545b213851a7..0346233f41315 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.cpp
@@ -46,13 +46,10 @@ TraceIntelPTGetStateResponse IntelPTPerThreadProcessTrace::GetState() {
return state;
}
-Expected<std::vector<uint8_t>> IntelPTPerThreadProcessTrace::GetBinaryData(
+Expected<llvm::Optional<std::vector<uint8_t>>>
+IntelPTPerThreadProcessTrace::TryGetBinaryData(
const TraceGetBinaryDataRequest &request) {
- if (Expected<IntelPTSingleBufferTrace &> trace =
- m_thread_traces.GetTracedThread(*request.tid))
- return trace->GetTraceBuffer(request.offset, request.size);
- else
- return trace.takeError();
+ return m_thread_traces.TryGetBinaryData(request);
}
Expected<IntelPTProcessTraceUP>
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
index f1ae8ef16954b..2f28a12fc4f60 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTPerThreadProcessTrace.h
@@ -44,8 +44,8 @@ class IntelPTPerThreadProcessTrace : public IntelPTProcessTrace {
TraceIntelPTGetStateResponse GetState() override;
- llvm::Expected<std::vector<uint8_t>>
- GetBinaryData(const TraceGetBinaryDataRequest &request) override;
+ llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+ TryGetBinaryData(const TraceGetBinaryDataRequest &request) override;
private:
IntelPTPerThreadProcessTrace(const TraceIntelPTStartRequest &request)
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
index 06fc346e92db1..1c6b44e2395bb 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTProcessTrace.h
@@ -36,9 +36,12 @@ class IntelPTProcessTrace {
/// \copydoc IntelPTThreadTraceCollection::TraceStop()
virtual llvm::Error TraceStop(lldb::tid_t tid) = 0;
- /// Get binary data owned by this instance.
- virtual llvm::Expected<std::vector<uint8_t>>
- GetBinaryData(const TraceGetBinaryDataRequest &request) = 0;
+ /// \return
+ /// \b None if this instance doesn't support the requested data, an \a
+ /// llvm::Error if this isntance supports it but fails at fetching it, and
+ /// \b Error::success() otherwise.
+ virtual llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+ TryGetBinaryData(const TraceGetBinaryDataRequest &request) = 0;
};
using IntelPTProcessTraceUP = std::unique_ptr<IntelPTProcessTrace>;
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
index 2d3aad52dbb12..8f7909b4072c7 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTSingleBufferTrace.cpp
@@ -270,7 +270,8 @@ IntelPTSingleBufferTrace::Start(const TraceIntelPTStartRequest &request,
if (Expected<PerfEvent> perf_event = PerfEvent::Init(*attr, tid, core_id)) {
if (Error mmap_err = perf_event->MmapMetadataAndBuffers(
- /*num_data_pages=*/0, aux_buffer_numpages)) {
+ /*num_data_pages=*/0, aux_buffer_numpages,
+ /*data_buffer_write=*/true)) {
return std::move(mmap_err);
}
return IntelPTSingleBufferTrace(std::move(*perf_event));
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
index d7206b193e8f6..bd06e033f1606 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.cpp
@@ -71,3 +71,21 @@ void IntelPTThreadTraceCollection::Clear() {
size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const {
return m_thread_traces.size();
}
+
+llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+IntelPTThreadTraceCollection::TryGetBinaryData(
+ const TraceGetBinaryDataRequest &request) {
+ if (!request.tid)
+ return None;
+ if (request.kind != IntelPTDataKinds::kTraceBuffer)
+ return None;
+
+ if (!TracesThread(*request.tid))
+ return None;
+
+ if (Expected<IntelPTSingleBufferTrace &> trace =
+ GetTracedThread(*request.tid))
+ return trace->GetTraceBuffer(request.offset, request.size);
+ else
+ return trace.takeError();
+}
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
index f343e436d692a..8a6b5a26352ac 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
@@ -59,6 +59,10 @@ class IntelPTThreadTraceCollection {
size_t GetTracedThreadsCount() const;
+ /// \copydoc IntelPTProcessTrace::TryGetBinaryData()
+ llvm::Expected<llvm::Optional<std::vector<uint8_t>>>
+ TryGetBinaryData(const TraceGetBinaryDataRequest &request);
+
private:
llvm::DenseMap<lldb::tid_t, IntelPTSingleBufferTrace> m_thread_traces;
/// Total actual thread buffer size in bytes
diff --git a/lldb/source/Plugins/Process/Linux/Perf.cpp b/lldb/source/Plugins/Process/Linux/Perf.cpp
index d11a54e702592..2caf5c1d6905d 100644
--- a/lldb/source/Plugins/Process/Linux/Perf.cpp
+++ b/lldb/source/Plugins/Process/Linux/Perf.cpp
@@ -24,54 +24,6 @@ using namespace lldb_private;
using namespace process_linux;
using namespace llvm;
-void lldb_private::process_linux::ReadCyclicBuffer(
- llvm::MutableArrayRef<uint8_t> &dst, llvm::ArrayRef<uint8_t> src,
- size_t src_cyc_index, size_t offset) {
-
- Log *log = GetLog(POSIXLog::Trace);
-
- if (dst.empty() || src.empty()) {
- dst = dst.drop_back(dst.size());
- return;
- }
-
- if (dst.data() == nullptr || src.data() == nullptr) {
- dst = dst.drop_back(dst.size());
- return;
- }
-
- if (src_cyc_index > src.size()) {
- dst = dst.drop_back(dst.size());
- return;
- }
-
- if (offset >= src.size()) {
- LLDB_LOG(log, "Too Big offset ");
- dst = dst.drop_back(dst.size());
- return;
- }
-
- llvm::SmallVector<ArrayRef<uint8_t>, 2> parts = {
- src.slice(src_cyc_index), src.take_front(src_cyc_index)};
-
- if (offset > parts[0].size()) {
- parts[1] = parts[1].slice(offset - parts[0].size());
- parts[0] = parts[0].drop_back(parts[0].size());
- } else if (offset == parts[0].size()) {
- parts[0] = parts[0].drop_back(parts[0].size());
- } else {
- parts[0] = parts[0].slice(offset);
- }
- auto next = dst.begin();
- auto bytes_left = dst.size();
- for (auto part : parts) {
- size_t chunk_size = std::min(part.size(), bytes_left);
- next = std::copy_n(part.begin(), chunk_size, next);
- bytes_left -= chunk_size;
- }
- dst = dst.drop_back(bytes_left);
-}
-
Expected<LinuxPerfZeroTscConversion>
lldb_private::process_linux::LoadPerfTscConversionParameters() {
lldb::pid_t pid = getpid();
@@ -84,8 +36,10 @@ lldb_private::process_linux::LoadPerfTscConversionParameters() {
Expected<PerfEvent> perf_event = PerfEvent::Init(attr, pid);
if (!perf_event)
return perf_event.takeError();
- if (Error mmap_err = perf_event->MmapMetadataAndBuffers(/*num_data_pages*/ 0,
- /*num_aux_pages*/ 0))
+ if (Error mmap_err =
+ perf_event->MmapMetadataAndBuffers(/*num_data_pages=*/0,
+ /*num_aux_pages=*/0,
+ /*data_buffer_write=*/false))
return std::move(mmap_err);
perf_event_mmap_page &mmap_metada = perf_event->GetMetadataPage();
@@ -155,11 +109,12 @@ PerfEvent::DoMmap(void *addr, size_t length, int prot, int flags,
return resource_handle::MmapUP(mmap_result, length);
}
-llvm::Error PerfEvent::MmapMetadataAndDataBuffer(size_t num_data_pages) {
+llvm::Error PerfEvent::MmapMetadataAndDataBuffer(size_t num_data_pages,
+ bool data_buffer_write) {
size_t mmap_size = (num_data_pages + 1) * getpagesize();
- if (Expected<resource_handle::MmapUP> mmap_metadata_data =
- DoMmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, 0,
- "metadata and data buffer")) {
+ if (Expected<resource_handle::MmapUP> mmap_metadata_data = DoMmap(
+ nullptr, mmap_size, PROT_READ | (data_buffer_write ? PROT_WRITE : 0),
+ MAP_SHARED, 0, "metadata and data buffer")) {
m_metadata_data_base = std::move(mmap_metadata_data.get());
return Error::success();
} else
@@ -171,6 +126,7 @@ llvm::Error PerfEvent::MmapAuxBuffer(size_t num_aux_pages) {
return Error::success();
perf_event_mmap_page &metadata_page = GetMetadataPage();
+
metadata_page.aux_offset =
metadata_page.data_offset + metadata_page.data_size;
metadata_page.aux_size = num_aux_pages * getpagesize();
@@ -185,7 +141,8 @@ llvm::Error PerfEvent::MmapAuxBuffer(size_t num_aux_pages) {
}
llvm::Error PerfEvent::MmapMetadataAndBuffers(size_t num_data_pages,
- size_t num_aux_pages) {
+ size_t num_aux_pages,
+ bool data_buffer_write) {
if (num_data_pages != 0 && !isPowerOf2_64(num_data_pages))
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
@@ -196,7 +153,7 @@ llvm::Error PerfEvent::MmapMetadataAndBuffers(size_t num_data_pages,
llvm::inconvertibleErrorCode(),
llvm::formatv("Number of aux pages must be a power of 2, got: {0}",
num_aux_pages));
- if (Error err = MmapMetadataAndDataBuffer(num_data_pages))
+ if (Error err = MmapMetadataAndDataBuffer(num_data_pages, data_buffer_write))
return err;
if (Error err = MmapAuxBuffer(num_aux_pages))
return err;
@@ -222,18 +179,74 @@ ArrayRef<uint8_t> PerfEvent::GetAuxBuffer() const {
static_cast<size_t>(mmap_metadata.aux_size)};
}
+Expected<std::vector<uint8_t>>
+PerfEvent::ReadFlushedOutDataCyclicBuffer(size_t offset, size_t size) {
+ CollectionState previous_state = m_collection_state;
+ if (Error err = DisableWithIoctl())
+ return std::move(err);
+
+ /**
+ * The data buffer and aux buffer have
diff erent implementations
+ * with respect to their definition of head pointer. In the case
+ * of Aux data buffer the head always wraps around the aux buffer
+ * and we don't need to care about it, whereas the data_head keeps
+ * increasing and needs to be wrapped by modulus operator
+ */
+ perf_event_mmap_page &mmap_metadata = GetMetadataPage();
+
+ ArrayRef<uint8_t> data = GetDataBuffer();
+ uint64_t data_head = mmap_metadata.data_head;
+ uint64_t data_size = mmap_metadata.data_size;
+ std::vector<uint8_t> output;
+ output.reserve(size);
+
+ if (data_head > data_size) {
+ uint64_t actual_data_head = data_head % data_size;
+ // The buffer has wrapped
+ for (uint64_t i = actual_data_head + offset;
+ i < data_size && output.size() < size; i++)
+ output.push_back(data[i]);
+
+ // We need to find the starting position for the left part if the offset was
+ // too big
+ uint64_t left_part_start = actual_data_head + offset >= data_size
+ ? actual_data_head + offset - data_size
+ : 0;
+ for (uint64_t i = left_part_start;
+ i < actual_data_head && output.size() < size; i++)
+ output.push_back(data[i]);
+ } else {
+ for (uint64_t i = offset; i < data_head && output.size() < size; i++)
+ output.push_back(data[i]);
+ }
+
+ if (previous_state == CollectionState::Enabled) {
+ if (Error err = EnableWithIoctl())
+ return std::move(err);
+ }
+
+ if (output.size() != size)
+ return createStringError(inconvertibleErrorCode(),
+ formatv("Requested {0} bytes of perf_event data "
+ "buffer but only {1} are available",
+ size, output.size()));
+
+ return data;
+}
+
Expected<std::vector<uint8_t>>
PerfEvent::ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size) {
CollectionState previous_state = m_collection_state;
if (Error err = DisableWithIoctl())
return std::move(err);
- std::vector<uint8_t> data(size, 0);
perf_event_mmap_page &mmap_metadata = GetMetadataPage();
- Log *log = GetLog(POSIXLog::Trace);
- uint64_t head = mmap_metadata.aux_head;
- LLDB_LOG(log, "Aux size -{0} , Head - {1}", mmap_metadata.aux_size, head);
+ ArrayRef<uint8_t> data = GetAuxBuffer();
+ uint64_t aux_head = mmap_metadata.aux_head;
+ uint64_t aux_size = mmap_metadata.aux_size;
+ std::vector<uint8_t> output;
+ output.reserve(size);
/**
* When configured as ring buffer, the aux buffer keeps wrapping around
@@ -247,14 +260,28 @@ PerfEvent::ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size) {
*
* */
- MutableArrayRef<uint8_t> buffer(data);
- ReadCyclicBuffer(buffer, GetAuxBuffer(), static_cast<size_t>(head), offset);
+ for (uint64_t i = aux_head + offset; i < aux_size && output.size() < size;
+ i++)
+ output.push_back(data[i]);
+
+ // We need to find the starting position for the left part if the offset was
+ // too big
+ uint64_t left_part_start =
+ aux_head + offset >= aux_size ? aux_head + offset - aux_size : 0;
+ 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 (Error err = EnableWithIoctl())
return std::move(err);
}
+ if (output.size() != size)
+ return createStringError(inconvertibleErrorCode(),
+ formatv("Requested {0} bytes of perf_event aux "
+ "buffer but only {1} are available",
+ size, output.size()));
+
return data;
}
@@ -286,7 +313,7 @@ Error PerfEvent::EnableWithIoctl() {
size_t PerfEvent::GetEffectiveDataBufferSize() const {
perf_event_mmap_page &mmap_metadata = GetMetadataPage();
- if (mmap_metadata.data_head < mmap_metadata.data_size)
+ if (mmap_metadata.data_head <= mmap_metadata.data_size)
return mmap_metadata.data_head;
else
return mmap_metadata.data_size; // The buffer has wrapped.
diff --git a/lldb/source/Plugins/Process/Linux/Perf.h b/lldb/source/Plugins/Process/Linux/Perf.h
index 6bb943a30b891..cb80304c7b9e2 100644
--- a/lldb/source/Plugins/Process/Linux/Perf.h
+++ b/lldb/source/Plugins/Process/Linux/Perf.h
@@ -74,24 +74,6 @@ using MmapUP = std::unique_ptr<void, resource_handle::MmapDeleter>;
} // namespace resource_handle
-/// Read data from a cyclic buffer
-///
-/// \param[in] [out] buf
-/// Destination buffer, the buffer will be truncated to written size.
-///
-/// \param[in] src
-/// Source buffer which must be a cyclic buffer.
-///
-/// \param[in] src_cyc_index
-/// The index pointer (start of the valid data in the cyclic
-/// buffer).
-///
-/// \param[in] offset
-/// The offset to begin reading the data in the cyclic buffer.
-void ReadCyclicBuffer(llvm::MutableArrayRef<uint8_t> &dst,
- llvm::ArrayRef<uint8_t> src, size_t src_cyc_index,
- size_t offset);
-
/// Thin wrapper of the perf_event_open API.
///
/// Exposes the metadata page and data and aux buffers of a perf event.
@@ -174,11 +156,16 @@ class PerfEvent {
/// A value of 0 effectively is a no-op and no data is mmap'ed for this
/// buffer.
///
+ /// \param[in] data_buffer_write
+ /// Whether to mmap the data buffer with WRITE permissions. This changes
+ /// the behavior of how the kernel writes to the data buffer.
+ ///
/// \return
/// \a llvm::Error::success if the mmap operations succeeded,
/// or an \a llvm::Error otherwise.
llvm::Error MmapMetadataAndBuffers(size_t num_data_pages,
- size_t num_aux_pages);
+ size_t num_aux_pages,
+ bool data_buffer_write);
/// Get the file descriptor associated with the perf event.
long GetFd() const;
@@ -237,6 +224,25 @@ class PerfEvent {
llvm::Expected<std::vector<uint8_t>>
ReadFlushedOutAuxCyclicBuffer(size_t offset, size_t size);
+ /// Read the data buffer managed by this perf event. To ensure that the
+ /// data is up-to-date and is not corrupted by read-write race conditions, the
+ /// underlying perf_event is paused during read, and later it's returned to
+ /// its initial state. The returned data will be linear, i.e. it will fix the
+ /// circular wrapping the might exist int he buffer.
+ ///
+ /// \param[in] offset
+ /// Offset of the data to read.
+ ///
+ /// \param[in] size
+ /// Number of bytes to read.
+ ///
+ /// \return
+ /// A vector with the requested binary data. The vector will have the
+ /// size of the requested \a size. Non-available positions will be
+ /// filled with zeroes.
+ llvm::Expected<std::vector<uint8_t>>
+ ReadFlushedOutDataCyclicBuffer(size_t offset, size_t size);
+
/// Use the ioctl API to disable the perf event and all the events in its
/// group. This doesn't terminate the perf event.
///
@@ -290,7 +296,12 @@ class PerfEvent {
/// Number of pages in the data buffer to mmap, must be a power of 2.
/// A value of 0 is useful for "dummy" events that only want to access
/// the metadata, \a perf_event_mmap_page, or the aux buffer.
- llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages);
+ ///
+ /// \param[in] data_buffer_write
+ /// Whether to mmap the data buffer with WRITE permissions. This changes
+ /// the behavior of how the kernel writes to the data buffer.
+ llvm::Error MmapMetadataAndDataBuffer(size_t num_data_pages,
+ bool data_buffer_write);
/// Mmap the aux buffer of the perf event.
///
diff --git a/lldb/source/Plugins/Trace/common/CMakeLists.txt b/lldb/source/Plugins/Trace/common/CMakeLists.txt
index 279115222f8e0..ee7afddb15f72 100644
--- a/lldb/source/Plugins/Trace/common/CMakeLists.txt
+++ b/lldb/source/Plugins/Trace/common/CMakeLists.txt
@@ -1,8 +1,5 @@
add_lldb_library(lldbPluginTraceCommon
ThreadPostMortemTrace.cpp
- TraceJSONStructs.cpp
- TraceSessionFileParser.cpp
- TraceSessionSaver.cpp
LINK_LIBS
lldbCore
diff --git a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
index 45d6f3b0e0983..37239b9d4c710 100644
--- a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
+++ b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.cpp
@@ -16,6 +16,7 @@
using namespace lldb;
using namespace lldb_private;
+using namespace llvm;
void ThreadPostMortemTrace::RefreshStateAfterStop() {}
@@ -36,6 +37,6 @@ ThreadPostMortemTrace::CreateRegisterContextForFrame(StackFrame *frame) {
bool ThreadPostMortemTrace::CalculateStopInfo() { return false; }
-const FileSpec &ThreadPostMortemTrace::GetTraceFile() const {
+const Optional<FileSpec> &ThreadPostMortemTrace::GetTraceFile() const {
return m_trace_file;
}
diff --git a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
index 9cfe754ae0e4e..14ea2f1f5f7d6 100644
--- a/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
+++ b/lldb/source/Plugins/Trace/common/ThreadPostMortemTrace.h
@@ -32,7 +32,7 @@ class ThreadPostMortemTrace : public Thread {
/// The file that contains the list of instructions that were traced when
/// this thread was being executed.
ThreadPostMortemTrace(Process &process, lldb::tid_t tid,
- const FileSpec &trace_file)
+ const llvm::Optional<FileSpec> &trace_file)
: Thread(process, tid), m_trace_file(trace_file) {}
void RefreshStateAfterStop() override;
@@ -44,7 +44,7 @@ class ThreadPostMortemTrace : public Thread {
/// \return
/// The trace file of this thread.
- const FileSpec &GetTraceFile() const;
+ const llvm::Optional<FileSpec> &GetTraceFile() const;
protected:
bool CalculateStopInfo() override;
@@ -52,7 +52,7 @@ class ThreadPostMortemTrace : public Thread {
lldb::RegisterContextSP m_thread_reg_ctx_sp;
private:
- FileSpec m_trace_file;
+ llvm::Optional<FileSpec> m_trace_file;
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp b/lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
deleted file mode 100644
index 1773a60037013..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceJSONStructs.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//===-- TraceSessionFileStructs.cpp ---------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===/
-
-#include "TraceJSONStructs.h"
-#include "ThreadPostMortemTrace.h"
-#include "lldb/Core/Debugger.h"
-#include "lldb/Core/Module.h"
-#include "lldb/Target/Process.h"
-#include "lldb/Target/Target.h"
-#include <sstream>
-
-using namespace lldb_private;
-namespace llvm {
-namespace json {
-
-llvm::json::Value toJSON(const JSONModule &module) {
- llvm::json::Object json_module;
- json_module["systemPath"] = module.system_path;
- if (module.file)
- json_module["file"] = *module.file;
- std::ostringstream oss;
- oss << "0x" << std::hex << module.load_address.value;
- json_module["loadAddress"] = oss.str();
- if (module.uuid)
- json_module["uuid"] = *module.uuid;
- return std::move(json_module);
-}
-
-llvm::json::Value toJSON(const JSONThread &thread) {
- return Value(Object{{"tid", thread.tid}, {"traceFile", thread.trace_file}});
-}
-
-llvm::json::Value toJSON(const JSONProcess &process) {
- llvm::json::Object json_process;
- json_process["pid"] = process.pid;
- json_process["triple"] = process.triple;
-
- llvm::json::Array threads_arr;
- for (JSONThread e : process.threads)
- threads_arr.push_back(toJSON(e));
-
- json_process["threads"] = llvm::json::Value(std::move(threads_arr));
-
- llvm::json::Array modules_arr;
- for (JSONModule e : process.modules)
- modules_arr.push_back(toJSON(e));
-
- json_process["modules"] = llvm::json::Value(std::move(modules_arr));
-
- return std::move(json_process);
-}
-
-llvm::json::Value toJSON(const JSONTraceSessionBase &session) {
- llvm::json::Array arr;
- for (JSONProcess e : session.processes)
- arr.push_back(toJSON(e));
-
- return std::move(arr);
-}
-
-bool fromJSON(const Value &value, JSONAddress &address, Path path) {
- Optional<StringRef> s = value.getAsString();
- if (s.hasValue() && !s->getAsInteger(0, address.value))
- return true;
-
- path.report("expected numeric string");
- return false;
-}
-
-bool fromJSON(const Value &value, JSONModule &module, Path path) {
- ObjectMapper o(value, path);
- return o && o.map("systemPath", module.system_path) &&
- o.map("file", module.file) &&
- o.map("loadAddress", module.load_address) &&
- o.map("uuid", module.uuid);
-}
-
-bool fromJSON(const Value &value, JSONThread &thread, Path path) {
- ObjectMapper o(value, path);
- return o && o.map("tid", thread.tid) && o.map("traceFile", thread.trace_file);
-}
-
-bool fromJSON(const Value &value, JSONProcess &process, Path path) {
- ObjectMapper o(value, path);
- return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
- o.map("threads", process.threads) && o.map("modules", process.modules);
-}
-
-bool fromJSON(const Value &value, JSONTracePluginSettings &plugin_settings,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("type", plugin_settings.type);
-}
-
-bool fromJSON(const Value &value, JSONTraceSessionBase &session, Path path) {
- ObjectMapper o(value, path);
- return o && o.map("processes", session.processes);
-}
-
-} // namespace json
-} // namespace llvm
diff --git a/lldb/source/Plugins/Trace/common/TraceJSONStructs.h b/lldb/source/Plugins/Trace/common/TraceJSONStructs.h
deleted file mode 100644
index e01c33bf0d6af..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceJSONStructs.h
+++ /dev/null
@@ -1,98 +0,0 @@
-//===-- TraceJSONStruct.h ---------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_TARGET_TRACEJSONSTRUCTS_H
-#define LLDB_TARGET_TRACEJSONSTRUCTS_H
-
-#include "lldb/lldb-types.h"
-#include "llvm/Support/JSON.h"
-
-namespace lldb_private {
-
-struct JSONAddress {
- lldb::addr_t value;
-};
-
-struct JSONModule {
- std::string system_path;
- llvm::Optional<std::string> file;
- JSONAddress load_address;
- llvm::Optional<std::string> uuid;
-};
-
-struct JSONThread {
- int64_t tid;
- std::string trace_file;
-};
-
-struct JSONProcess {
- int64_t pid;
- std::string triple;
- std::vector<JSONThread> threads;
- std::vector<JSONModule> modules;
-};
-
-struct JSONTracePluginSettings {
- std::string type;
-};
-
-struct JSONTraceSessionBase {
- std::vector<JSONProcess> processes;
-};
-
-/// The trace plug-in implementation should provide its own TPluginSettings,
-/// which corresponds to the "trace" section of the schema.
-template <class TPluginSettings>
-struct JSONTraceSession : JSONTraceSessionBase {
- TPluginSettings trace;
-};
-
-} // namespace lldb_private
-
-namespace llvm {
-namespace json {
-
-llvm::json::Value toJSON(const lldb_private::JSONModule &module);
-
-llvm::json::Value toJSON(const lldb_private::JSONThread &thread);
-
-llvm::json::Value toJSON(const lldb_private::JSONProcess &process);
-
-llvm::json::Value
-toJSON(const lldb_private::JSONTraceSessionBase &session_base);
-
-bool fromJSON(const Value &value, lldb_private::JSONAddress &address,
- Path path);
-
-bool fromJSON(const Value &value, lldb_private::JSONModule &module, Path path);
-
-bool fromJSON(const Value &value, lldb_private::JSONThread &thread, Path path);
-
-bool fromJSON(const Value &value, lldb_private::JSONProcess &process,
- Path path);
-
-bool fromJSON(const Value &value,
- lldb_private::JSONTracePluginSettings &plugin_settings,
- Path path);
-
-bool fromJSON(const Value &value, lldb_private::JSONTraceSessionBase &session,
- Path path);
-
-template <class TPluginSettings>
-bool fromJSON(const Value &value,
- lldb_private::JSONTraceSession<TPluginSettings> &session,
- Path path) {
- ObjectMapper o(value, path);
- return o && o.map("trace", session.trace) &&
- fromJSON(value, (lldb_private::JSONTraceSessionBase &)session, path);
-}
-
-} // namespace json
-} // namespace llvm
-
-#endif // LLDB_TARGET_TRACEJSONSTRUCTS_H
diff --git a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp b/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
deleted file mode 100644
index b26704ca34be8..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-//===-- TraceSessionFileParser.cpp ---------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===/
-
-#include "TraceSessionFileParser.h"
-#include "ThreadPostMortemTrace.h"
-
-#include <sstream>
-
-#include "lldb/Core/Debugger.h"
-#include "lldb/Core/Module.h"
-#include "lldb/Target/Process.h"
-#include "lldb/Target/Target.h"
-
-using namespace lldb;
-using namespace lldb_private;
-using namespace llvm;
-
-void TraceSessionFileParser::NormalizePath(lldb_private::FileSpec &file_spec) {
- if (file_spec.IsRelative())
- file_spec.PrependPathComponent(m_session_file_dir);
-}
-
-Error TraceSessionFileParser::ParseModule(lldb::TargetSP &target_sp,
- const JSONModule &module) {
- FileSpec system_file_spec(module.system_path);
- NormalizePath(system_file_spec);
-
- FileSpec local_file_spec(module.file.hasValue() ? *module.file
- : module.system_path);
- NormalizePath(local_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);
-
- Status error;
- ModuleSP module_sp =
- target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
-
- if (error.Fail())
- return error.ToError();
-
- bool load_addr_changed = false;
- module_sp->SetLoadAddress(*target_sp, module.load_address.value, false,
- load_addr_changed);
- return llvm::Error::success();
-}
-
-Error TraceSessionFileParser::CreateJSONError(json::Path::Root &root,
- const json::Value &value) {
- std::string err;
- raw_string_ostream os(err);
- root.printErrorContext(value, os);
- return createStringError(
- std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
- toString(root.getError()).c_str(), os.str().c_str(), m_schema.data());
-}
-
-std::string TraceSessionFileParser::BuildSchema(StringRef plugin_schema) {
- std::ostringstream schema_builder;
- schema_builder << "{\n \"trace\": ";
- schema_builder << plugin_schema.data() << ",";
- schema_builder << R"(
- "processes": [
- {
- "pid": integer,
- "triple": string, // llvm-triple
- "threads": [
- {
- "tid": integer,
- "traceFile": string
- }
- ],
- "modules": [
- {
- "systemPath": string, // original path of the module at runtime
- "file"?: string, // copy of the file if not available at "systemPath"
- "loadAddress": string, // string address in hex or decimal form
- "uuid"?: string,
- }
- ]
- }
- ]
- // Notes:
- // All paths are either absolute or relative to the session file.
-}
-)";
- return schema_builder.str();
-}
-
-ThreadPostMortemTraceSP
-TraceSessionFileParser::ParseThread(ProcessSP &process_sp,
- const JSONThread &thread) {
- lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
-
- FileSpec trace_file(thread.trace_file);
- NormalizePath(trace_file);
-
- ThreadPostMortemTraceSP thread_sp =
- std::make_shared<ThreadPostMortemTrace>(*process_sp, tid, trace_file);
- process_sp->GetThreadList().AddThread(thread_sp);
- return thread_sp;
-}
-
-Expected<TraceSessionFileParser::ParsedProcess>
-TraceSessionFileParser::ParseProcess(const JSONProcess &process) {
- TargetSP target_sp;
- Status error = m_debugger.GetTargetList().CreateTarget(
- m_debugger, /*user_exe_path*/ StringRef(), process.triple,
- eLoadDependentsNo,
- /*platform_options*/ nullptr, target_sp);
-
- if (!target_sp)
- return error.ToError();
-
- ParsedProcess parsed_process;
- parsed_process.target_sp = target_sp;
-
- ProcessSP process_sp = target_sp->CreateProcess(
- /*listener*/ nullptr, "trace",
- /*crash_file*/ nullptr,
- /*can_connect*/ false);
-
- process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
-
- for (const JSONThread &thread : process.threads)
- parsed_process.threads.push_back(ParseThread(process_sp, thread));
-
- for (const JSONModule &module : process.modules)
- if (Error err = ParseModule(target_sp, module))
- return std::move(err);
-
- if (!process.threads.empty())
- process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
-
- // We invoke DidAttach to create a correct stopped state for the process and
- // its threads.
- ArchSpec process_arch;
- process_sp->DidAttach(process_arch);
-
- return parsed_process;
-}
-
-Expected<std::vector<TraceSessionFileParser::ParsedProcess>>
-TraceSessionFileParser::ParseCommonSessionFile(
- const JSONTraceSessionBase &session) {
- std::vector<ParsedProcess> parsed_processes;
-
- auto onError = [&]() {
- // Delete all targets that were created so far in case of failures
- for (ParsedProcess &parsed_process : parsed_processes)
- m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
- };
-
- for (const JSONProcess &process : session.processes) {
- if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
- parsed_processes.push_back(std::move(*parsed_process));
- else {
- onError();
- return parsed_process.takeError();
- }
- }
- return parsed_processes;
-}
diff --git a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.h b/lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
deleted file mode 100644
index 19cc2f59ded73..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionFileParser.h
+++ /dev/null
@@ -1,93 +0,0 @@
-//===-- TraceSessionFileParser.h --------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_TARGET_TRACESESSIONPARSER_H
-#define LLDB_TARGET_TRACESESSIONPARSER_H
-
-#include "ThreadPostMortemTrace.h"
-#include "TraceJSONStructs.h"
-
-namespace lldb_private {
-
-/// \class TraceSessionFileParser TraceSessionFileParser.h
-///
-/// Base class for parsing the common information of JSON trace session files.
-/// Contains the basic C++ structs that represent the JSON data, which include
-/// \a JSONTraceSession as the root object.
-///
-/// See \a Trace::FindPlugin for more information regarding these JSON files.
-class TraceSessionFileParser {
-public:
-
- /// Helper struct holding the objects created when parsing a process
- struct ParsedProcess {
- lldb::TargetSP target_sp;
- std::vector<lldb::ThreadPostMortemTraceSP> threads;
- };
-
- TraceSessionFileParser(Debugger &debugger, llvm::StringRef session_file_dir,
- llvm::StringRef schema)
- : m_debugger(debugger), m_session_file_dir(session_file_dir),
- m_schema(schema) {}
-
- /// Build the full schema for a Trace plug-in.
- ///
- /// \param[in] plugin_schema
- /// The subschema that corresponds to the "trace" section of the schema.
- ///
- /// \return
- /// The full schema containing the common attributes and the plug-in
- /// specific attributes.
- static std::string BuildSchema(llvm::StringRef plugin_schema);
-
- /// Parse the fields common to all trace session schemas.
- ///
- /// \param[in] session
- /// The session json objects already deserialized.
- ///
- /// \return
- /// A list of \a ParsedProcess containing all threads and targets created
- /// during the parsing, or an error in case of failures. In case of
- /// errors, no side effects are produced.
- llvm::Expected<std::vector<ParsedProcess>>
- ParseCommonSessionFile(const JSONTraceSessionBase &session);
-
-protected:
- /// Resolve non-absolute paths relative to the session file folder. It
- /// modifies the given file_spec.
- void NormalizePath(lldb_private::FileSpec &file_spec);
-
- lldb::ThreadPostMortemTraceSP ParseThread(lldb::ProcessSP &process_sp,
- const JSONThread &thread);
-
- llvm::Expected<ParsedProcess> ParseProcess(const JSONProcess &process);
-
- llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module);
-
- /// Create a user-friendly error message upon a JSON-parsing failure using the
- /// \a json::ObjectMapper functionality.
- ///
- /// \param[in] root
- /// The \a llvm::json::Path::Root used to parse the JSON \a value.
- ///
- /// \param[in] value
- /// The json value that failed to parse.
- ///
- /// \return
- /// An \a llvm::Error containing the user-friendly error message.
- llvm::Error CreateJSONError(llvm::json::Path::Root &root,
- const llvm::json::Value &value);
-
- Debugger &m_debugger;
- std::string m_session_file_dir;
- llvm::StringRef m_schema;
-};
-} // namespace lldb_private
-
-
-#endif // LLDB_TARGET_TRACESESSIONPARSER_H
diff --git a/lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp b/lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp
deleted file mode 100644
index e1d62062b8e79..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-//===-- TraceSessionSaver.cpp ---------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "TraceSessionSaver.h"
-
-#include "lldb/Core/Module.h"
-#include "lldb/Core/Value.h"
-#include "lldb/Target/Process.h"
-#include "lldb/Target/SectionLoadList.h"
-#include "lldb/Target/Target.h"
-#include "lldb/lldb-types.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/JSON.h"
-
-#include <fstream>
-
-using namespace lldb;
-using namespace lldb_private;
-using namespace llvm;
-
-llvm::Error TraceSessionSaver::WriteSessionToFile(
- const llvm::json::Value &trace_session_description, FileSpec directory) {
-
- FileSpec trace_path = directory;
- trace_path.AppendPathComponent("trace.json");
- std::ofstream os(trace_path.GetPath());
- os << std::string(formatv("{0:2}", trace_session_description));
- os.close();
- if (!os)
- return createStringError(inconvertibleErrorCode(),
- formatv("couldn't write to the file {0}",
- trace_path.GetPath().c_str()));
- return Error::success();
-}
-
-llvm::Expected<JSONTraceSessionBase> TraceSessionSaver::BuildProcessesSection(
- Process &live_process, llvm::StringRef raw_thread_trace_data_kind,
- FileSpec directory) {
-
- JSONTraceSessionBase json_session_description;
- Expected<std::vector<JSONThread>> json_threads =
- BuildThreadsSection(live_process, raw_thread_trace_data_kind, directory);
- if (!json_threads)
- return json_threads.takeError();
-
- Expected<std::vector<JSONModule>> json_modules =
- BuildModulesSection(live_process, directory);
- if (!json_modules)
- return json_modules.takeError();
-
- json_session_description.processes.push_back(JSONProcess{
- static_cast<int64_t>(live_process.GetID()),
- live_process.GetTarget().GetArchitecture().GetTriple().getTriple(),
- json_threads.get(), json_modules.get()});
- return json_session_description;
-}
-
-llvm::Expected<std::vector<JSONThread>> TraceSessionSaver::BuildThreadsSection(
- Process &live_process, llvm::StringRef raw_thread_trace_data_kind,
- FileSpec directory) {
- std::vector<JSONThread> json_threads;
- for (ThreadSP thread_sp : live_process.Threads()) {
- TraceSP trace_sp = live_process.GetTarget().GetTrace();
- lldb::tid_t tid = thread_sp->GetID();
- if (!trace_sp->IsTraced(tid))
- continue;
-
- // resolve the directory just in case
- FileSystem::Instance().Resolve(directory);
- FileSpec raw_trace_path = directory;
- raw_trace_path.AppendPathComponent(std::to_string(tid) + ".trace");
- json_threads.push_back(JSONThread{static_cast<int64_t>(tid),
- raw_trace_path.GetPath().c_str()});
-
- llvm::Error err =
- live_process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
- tid, raw_thread_trace_data_kind,
- [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
- std::basic_fstream<char> raw_trace_fs =
- std::fstream(raw_trace_path.GetPath().c_str(),
- std::ios::out | std::ios::binary);
- raw_trace_fs.write(reinterpret_cast<const char *>(&data[0]),
- data.size() * sizeof(uint8_t));
- raw_trace_fs.close();
- if (!raw_trace_fs)
- return createStringError(
- inconvertibleErrorCode(),
- formatv("couldn't write to the file {0}",
- raw_trace_path.GetPath().c_str()));
- return Error::success();
- });
- if (err)
- return std::move(err);
- }
- return json_threads;
-}
-
-llvm::Expected<std::vector<JSONModule>>
-TraceSessionSaver::BuildModulesSection(Process &live_process,
- FileSpec directory) {
- std::vector<JSONModule> json_modules;
- ModuleList module_list = live_process.GetTarget().GetImages();
- for (size_t i = 0; i < module_list.GetSize(); ++i) {
- ModuleSP module_sp(module_list.GetModuleAtIndex(i));
- if (!module_sp)
- continue;
- std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
- // TODO: support memory-only libraries like [vdso]
- if (!module_sp->GetFileSpec().IsAbsolute())
- continue;
-
- std::string file = module_sp->GetFileSpec().GetPath();
- ObjectFile *objfile = module_sp->GetObjectFile();
- if (objfile == nullptr)
- continue;
-
- lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
- Address base_addr(objfile->GetBaseAddress());
- if (base_addr.IsValid() &&
- !live_process.GetTarget().GetSectionLoadList().IsEmpty())
- load_addr = base_addr.GetLoadAddress(&live_process.GetTarget());
-
- if (load_addr == LLDB_INVALID_ADDRESS)
- continue;
-
- FileSystem::Instance().Resolve(directory);
- FileSpec path_to_copy_module = directory;
- path_to_copy_module.AppendPathComponent("modules");
- path_to_copy_module.AppendPathComponent(system_path);
- sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString());
-
- if (std::error_code ec = llvm::sys::fs::copy_file(
- system_path, path_to_copy_module.GetPath()))
- return createStringError(
- inconvertibleErrorCode(),
- formatv("couldn't write to the file. {0}", ec.message()));
-
- json_modules.push_back(
- JSONModule{system_path, path_to_copy_module.GetPath(),
- JSONAddress{load_addr}, module_sp->GetUUID().GetAsString()});
- }
- return json_modules;
-}
diff --git a/lldb/source/Plugins/Trace/common/TraceSessionSaver.h b/lldb/source/Plugins/Trace/common/TraceSessionSaver.h
deleted file mode 100644
index c490fd747ffc2..0000000000000
--- a/lldb/source/Plugins/Trace/common/TraceSessionSaver.h
+++ /dev/null
@@ -1,102 +0,0 @@
-//===-- SessionSaver.h ----------------------------------------*- C++ //-*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_TARGET_TRACESESSIONSAVER_H
-#define LLDB_TARGET_TRACESESSIONSAVER_H
-
-#include "TraceJSONStructs.h"
-
-namespace lldb_private {
-
-class TraceSessionSaver {
-
-public:
- /// Save the trace session description JSON object inside the given directory
- /// as a file named \a trace.json.
- ///
- /// \param[in] trace_session_description
- /// The trace's session, as JSON Object.
- ///
- /// \param[in] directory
- /// The directory where the JSON file will be saved.
- ///
- /// \return
- /// \a llvm::success if the operation was successful, or an \a llvm::Error
- /// otherwise.
- static llvm::Error
- WriteSessionToFile(const llvm::json::Value &trace_session_description,
- FileSpec directory);
-
- /// Build the processes section of the trace session description file. Besides
- /// returning the processes information, this method saves to disk all modules
- /// and raw traces corresponding to the traced threads of the given process.
- ///
- /// \param[in] live_process
- /// The process being traced.
- ///
- /// \param[in] raw_thread_trace_data_kind
- /// Identifier for the data kind of the raw trace for each thread.
- ///
- /// \param[in] directory
- /// The directory where files will be saved when building the processes
- /// section.
- ///
- /// \return
- /// The processes section or \a llvm::Error in case of failures.
- static llvm::Expected<JSONTraceSessionBase>
- BuildProcessesSection(Process &live_process,
- llvm::StringRef raw_thread_trace_data_kind,
- FileSpec directory);
-
- /// Build the threads sub-section of the trace session description file.
- /// For each traced thread, its raw trace is also written to the file
- /// \a thread_id_.trace inside of the given directory.
- ///
- /// \param[in] live_process
- /// The process being traced.
- ///
- /// \param[in] raw_thread_trace_data_kind
- /// Identifier for the data kind of the raw trace for each thread.
- ///
- /// \param[in] directory
- /// The directory where files will be saved when building the threads
- /// section.
- ///
- /// \return
- /// The threads section or \a llvm::Error in case of failures.
- static llvm::Expected<std::vector<JSONThread>>
- BuildThreadsSection(Process &live_process,
- llvm::StringRef raw_thread_trace_data_kind,
- FileSpec directory);
-
- /// Build modules sub-section of the trace's session. The original modules
- /// will be copied over to the \a <directory/modules> folder. Invalid modules
- /// are skipped.
- /// Copying the modules has the benefit of making these trace session
- /// directories self-contained, as the raw traces and modules are part of the
- /// output directory and can be sent to another machine, where lldb can load
- /// them and replicate exactly the same trace session.
- ///
- /// \param[in] live_process
- /// The process being traced.
- ///
- /// \param[in] directory
- /// The directory where the modules files will be saved when building
- /// the modules section.
- /// Example: If a module \a libbar.so exists in the path
- /// \a /usr/lib/foo/libbar.so, then it will be copied to
- /// \a <directory>/modules/usr/lib/foo/libbar.so.
- ///
- /// \return
- /// The modules section or \a llvm::Error in case of failures.
- static llvm::Expected<std::vector<JSONModule>>
- BuildModulesSection(Process &live_process, FileSpec directory);
-};
-} // namespace lldb_private
-
-#endif // LLDB_TARGET_TRACESESSIONSAVER_H
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index df3192a8bd9bf..997b46b709e93 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -74,15 +74,28 @@ Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
return instance;
}
-TraceIntelPT::TraceIntelPT(
- const pt_cpu &cpu_info,
- const std::vector<ThreadPostMortemTraceSP> &traced_threads)
- : m_cpu_info(cpu_info) {
+TraceIntelPT::TraceIntelPT(JSONTraceSession &session,
+ ArrayRef<ProcessSP> traced_processes,
+ ArrayRef<ThreadPostMortemTraceSP> 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<ThreadDecoder>(thread, *this));
- SetPostMortemThreadDataFile(thread->GetID(), IntelPTDataKinds::kTraceBuffer,
- thread->GetTraceFile());
+ if (const Optional<FileSpec> &trace_file = thread->GetTraceFile()) {
+ SetPostMortemThreadDataFile(thread->GetID(),
+ IntelPTDataKinds::kTraceBuffer, *trace_file);
+ }
+ }
+ if (session.cores) {
+ for (const JSONCore &core : *session.cores) {
+ SetPostMortemCoreDataFile(core.core_id, IntelPTDataKinds::kTraceBuffer,
+ FileSpec(core.trace_buffer));
+ SetPostMortemCoreDataFile(core.core_id,
+ IntelPTDataKinds::kPerfContextSwitchTrace,
+ FileSpec(core.context_switch_trace));
+ }
}
}
@@ -240,6 +253,12 @@ Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
return *m_cpu_info;
}
+llvm::Optional<LinuxPerfZeroTscConversion>
+TraceIntelPT::GetPerfZeroTscConversion() {
+ RefreshLiveProcessState();
+ return m_tsc_conversion;
+}
+
Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state,
StringRef json_response) {
m_thread_decoders.clear();
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index dd4f3e8430445..608f4184d1b71 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -146,8 +146,11 @@ class TraceIntelPT : public Trace {
llvm::Error OnThreadBufferRead(lldb::tid_t tid,
OnBinaryDataReadCallback callback);
+ /// Get or fetch the cpu information from, for example, /proc/cpuinfo.
llvm::Expected<pt_cpu> GetCPUInfo();
+ /// Get or fetch the values used to convert to and from TSCs and nanos.
+ llvm::Optional<LinuxPerfZeroTscConversion> GetPerfZeroTscConversion();
/// \return
/// The timer object for this trace.
@@ -158,12 +161,20 @@ class TraceIntelPT : public Trace {
llvm::Expected<pt_cpu> GetCPUInfoForLiveProcess();
+ /// Postmortem trace constructor
+ ///
+ /// \param[in] session
+ /// The definition file for the postmortem session.
+ ///
+ /// \param[in] traces_proceses
+ /// The processes traced in the live session.
+ ///
/// \param[in] trace_threads
- /// ThreadTrace instances, which are not live-processes and whose trace
- /// files are fixed.
- TraceIntelPT(
- const pt_cpu &cpu_info,
- const std::vector<lldb::ThreadPostMortemTraceSP> &traced_threads);
+ /// The threads traced in the live session. They must belong to the
+ /// processes mentioned above.
+ TraceIntelPT(JSONTraceSession &session,
+ llvm::ArrayRef<lldb::ProcessSP> traced_processes,
+ llvm::ArrayRef<lldb::ThreadPostMortemTraceSP> traced_threads);
/// Constructor for live processes
TraceIntelPT(Process &live_process)
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
index e36751e235dca..b637666504956 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
@@ -15,45 +15,130 @@ using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::trace_intel_pt;
using namespace llvm;
+using namespace llvm::json;
-namespace llvm {
-namespace json {
+namespace lldb_private {
+namespace trace_intel_pt {
-bool fromJSON(const Value &value, JSONTraceIntelPTSettings &plugin_settings,
- Path path) {
+Optional<std::vector<lldb::core_id_t>> JSONTraceSession::GetCoreIds() {
+ if (!cores)
+ return None;
+ std::vector<lldb::core_id_t> core_ids;
+ for (const JSONCore &core : *cores)
+ core_ids.push_back(core.core_id);
+ return core_ids;
+}
+
+json::Value toJSON(const JSONModule &module) {
+ json::Object json_module;
+ json_module["systemPath"] = module.system_path;
+ if (module.file)
+ json_module["file"] = *module.file;
+ json_module["loadAddress"] = module.load_address;
+ if (module.uuid)
+ json_module["uuid"] = *module.uuid;
+ return std::move(json_module);
+}
+
+bool fromJSON(const json::Value &value, JSONModule &module, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("systemPath", module.system_path) &&
+ o.map("file", module.file) &&
+ o.map("loadAddress", module.load_address) &&
+ o.map("uuid", module.uuid);
+}
+
+json::Value toJSON(const JSONThread &thread) {
+ return json::Object{{"tid", thread.tid},
+ {"traceBuffer", thread.trace_buffer}};
+}
+
+bool fromJSON(const json::Value &value, JSONThread &thread, Path path) {
+ ObjectMapper o(value, path);
+ return o && o.map("tid", thread.tid) &&
+ o.map("traceBuffer", thread.trace_buffer);
+}
+
+json::Value toJSON(const JSONProcess &process) {
+ return Object{
+ {"pid", process.pid},
+ {"triple", process.triple},
+ {"threads", process.threads},
+ {"modules", process.modules},
+ };
+}
+
+bool fromJSON(const json::Value &value, JSONProcess &process, Path path) {
ObjectMapper o(value, path);
- return o && o.map("cpuInfo", plugin_settings.cpuInfo) &&
- fromJSON(value, (JSONTracePluginSettings &)plugin_settings, path);
+ return o && o.map("pid", process.pid) && o.map("triple", process.triple) &&
+ o.map("threads", process.threads) && o.map("modules", process.modules);
+}
+
+json::Value toJSON(const JSONCore &core) {
+ return Object{
+ {"coreId", core.core_id},
+ {"traceBuffer", core.trace_buffer},
+ {"contextSwitchTrace", core.context_switch_trace},
+ };
}
-bool fromJSON(const json::Value &value, JSONTraceIntelPTCPUInfo &cpu_info,
- Path path) {
+bool fromJSON(const json::Value &value, JSONCore &core, Path path) {
ObjectMapper o(value, path);
- return o && o.map("vendor", cpu_info.vendor) &&
- o.map("family", cpu_info.family) && o.map("model", cpu_info.model) &&
- o.map("stepping", cpu_info.stepping);
+ uint64_t core_id;
+ if (!o || !o.map("coreId", core_id) ||
+ !o.map("traceBuffer", core.trace_buffer) ||
+ !o.map("contextSwitchTrace", core.context_switch_trace))
+ return false;
+ core.core_id = core_id;
+ return true;
}
-Value toJSON(const JSONTraceIntelPTCPUInfo &cpu_info) {
- return Value(Object{{"family", cpu_info.family},
- {"model", cpu_info.model},
- {"stepping", cpu_info.stepping},
- {"vendor", cpu_info.vendor}});
+json::Value toJSON(const pt_cpu &cpu_info) {
+ return Object{
+ {"vendor", cpu_info.vendor == pcv_intel ? "GenuineIntel" : "Unknown"},
+ {"family", cpu_info.family},
+ {"model", cpu_info.model},
+ {"stepping", cpu_info.stepping},
+ };
}
-llvm::json::Value toJSON(const JSONTraceIntelPTTrace &trace) {
- llvm::json::Object json_trace;
- json_trace["type"] = trace.type;
- json_trace["cpuInfo"] = toJSON(trace.cpuInfo);
- return std::move(json_trace);
+bool fromJSON(const json::Value &value, pt_cpu &cpu_info, Path path) {
+ ObjectMapper o(value, path);
+ std::string vendor;
+ uint64_t family, model, stepping;
+ if (!o || !o.map("vendor", vendor) || !o.map("family", family) ||
+ !o.map("model", model) || !o.map("stepping", stepping))
+ return false;
+ cpu_info.vendor = vendor == "GenuineIntel" ? pcv_intel : pcv_unknown;
+ cpu_info.family = family;
+ cpu_info.model = model;
+ cpu_info.stepping = stepping;
+ return true;
}
-llvm::json::Value toJSON(const JSONTraceIntelPTSession &session) {
- llvm::json::Object json_session;
- json_session["trace"] = toJSON(session.ipt_trace);
- json_session["processes"] = toJSON(session.session_base);
- return std::move(json_session);
+json::Value toJSON(const JSONTraceSession &session) {
+ return Object{{"type", session.type},
+ {"processes", session.processes},
+ // We have to do this because the compiler fails at doing it
+ // automatically because pt_cpu is not in a namespace
+ {"cpuInfo", toJSON(session.cpu_info)},
+ {"cores", session.cores},
+ {"tscPerfZeroConversion", session.tsc_perf_zero_conversion}};
+}
+
+bool fromJSON(const json::Value &value, JSONTraceSession &session, Path path) {
+ ObjectMapper o(value, path);
+ if (!o || !o.map("processes", session.processes) ||
+ !o.map("type", session.type) || !o.map("cores", session.cores) ||
+ !o.map("tscPerfZeroConversion", session.tsc_perf_zero_conversion))
+ return false;
+ // We have to do this because the compiler fails at doing it automatically
+ // because pt_cpu is not in a namespace
+ if (!fromJSON(*value.getAsObject()->get("cpuInfo"), session.cpu_info,
+ path.field("cpuInfo")))
+ return false;
+ return true;
}
-} // namespace json
-} // namespace llvm
+} // 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 ec024f27b8c9d..af4c365d1c49e 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
@@ -9,67 +9,84 @@
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
-#include "../common/TraceJSONStructs.h"
+#include "lldb/lldb-types.h"
+
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/JSON.h"
+
#include <intel-pt.h>
+#include <vector>
namespace lldb_private {
namespace trace_intel_pt {
-struct JSONTraceIntelPTCPUInfo {
- JSONTraceIntelPTCPUInfo() = default;
-
- JSONTraceIntelPTCPUInfo(pt_cpu cpu_info) {
- family = static_cast<int64_t>(cpu_info.family);
- model = static_cast<int64_t>(cpu_info.model);
- stepping = static_cast<int64_t>(cpu_info.stepping);
- vendor = cpu_info.vendor == pcv_intel ? "intel" : "Unknown";
- }
+struct JSONModule {
+ std::string system_path;
+ llvm::Optional<std::string> file;
+ uint64_t load_address;
+ llvm::Optional<std::string> uuid;
+};
- int64_t family;
- int64_t model;
- int64_t stepping;
- std::string vendor;
+struct JSONThread {
+ int64_t tid;
+ llvm::Optional<std::string> trace_buffer;
};
-struct JSONTraceIntelPTTrace {
- std::string type;
- JSONTraceIntelPTCPUInfo cpuInfo;
+struct JSONProcess {
+ int64_t pid;
+ std::string triple;
+ std::vector<JSONThread> threads;
+ std::vector<JSONModule> modules;
};
-struct JSONTraceIntelPTSession {
- JSONTraceIntelPTTrace ipt_trace;
- JSONTraceSessionBase session_base;
+struct JSONCore {
+ lldb::core_id_t core_id;
+ std::string trace_buffer;
+ std::string context_switch_trace;
};
-struct JSONTraceIntelPTSettings : JSONTracePluginSettings {
- JSONTraceIntelPTCPUInfo cpuInfo;
+struct JSONTraceSession {
+ std::string type;
+ pt_cpu cpu_info;
+ std::vector<JSONProcess> processes;
+ llvm::Optional<std::vector<JSONCore>> cores;
+ llvm::Optional<LinuxPerfZeroTscConversion> tsc_perf_zero_conversion;
+
+ llvm::Optional<std::vector<lldb::core_id_t>> GetCoreIds();
};
-} // namespace trace_intel_pt
-} // namespace lldb_private
+llvm::json::Value toJSON(const JSONModule &module);
+
+llvm::json::Value toJSON(const JSONThread &thread);
-namespace llvm {
-namespace json {
+llvm::json::Value toJSON(const JSONProcess &process);
-bool fromJSON(
- const Value &value,
- lldb_private::trace_intel_pt::JSONTraceIntelPTSettings &plugin_settings,
- Path path);
+llvm::json::Value toJSON(const JSONCore &core);
-bool fromJSON(const llvm::json::Value &value,
- lldb_private::trace_intel_pt::JSONTraceIntelPTCPUInfo &packet,
+llvm::json::Value toJSON(const pt_cpu &cpu_info);
+
+llvm::json::Value toJSON(const JSONTraceSession &session);
+
+bool fromJSON(const llvm::json::Value &value, JSONModule &module,
llvm::json::Path path);
-llvm::json::Value
-toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTCPUInfo &cpu_info);
+bool fromJSON(const llvm::json::Value &value, JSONThread &thread,
+ llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, JSONProcess &process,
+ llvm::json::Path path);
-llvm::json::Value
-toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTTrace &trace);
+bool fromJSON(const llvm::json::Value &value, JSONCore &core,
+ llvm::json::Path path);
-llvm::json::Value
-toJSON(const lldb_private::trace_intel_pt::JSONTraceIntelPTSession &session);
+bool fromJSON(const llvm::json::Value &value, pt_cpu &cpu_info,
+ llvm::json::Path path);
-} // namespace json
-} // namespace llvm
+bool fromJSON(const llvm::json::Value &value, JSONTraceSession &session,
+ llvm::json::Path path);
+} // namespace trace_intel_pt
+} // namespace lldb_private
#endif // LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
index 7e2c39a20255c..10ed88e8a06d7 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -10,44 +10,221 @@
#include "../common/ThreadPostMortemTrace.h"
#include "TraceIntelPT.h"
+#include "TraceIntelPTJSONStructs.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::trace_intel_pt;
using namespace llvm;
+void TraceIntelPTSessionFileParser::NormalizePath(
+ lldb_private::FileSpec &file_spec) {
+ if (file_spec.IsRelative())
+ file_spec.PrependPathComponent(m_session_file_dir);
+}
+
+Error TraceIntelPTSessionFileParser::ParseModule(lldb::TargetSP &target_sp,
+ const JSONModule &module) {
+ FileSpec system_file_spec(module.system_path);
+ NormalizePath(system_file_spec);
+
+ FileSpec local_file_spec(module.file.hasValue() ? *module.file
+ : module.system_path);
+ NormalizePath(local_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);
+
+ Status error;
+ ModuleSP module_sp =
+ target_sp->GetOrCreateModule(module_spec, /*notify*/ false, &error);
+
+ 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();
+}
+
+Error TraceIntelPTSessionFileParser::CreateJSONError(json::Path::Root &root,
+ const json::Value &value) {
+ std::string err;
+ raw_string_ostream os(err);
+ root.printErrorContext(value, os);
+ return createStringError(
+ std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s",
+ toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data());
+}
+
+ThreadPostMortemTraceSP
+TraceIntelPTSessionFileParser::ParseThread(ProcessSP &process_sp,
+ const JSONThread &thread) {
+ lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid);
+
+ Optional<FileSpec> trace_file;
+ if (thread.trace_buffer) {
+ trace_file.emplace(*thread.trace_buffer);
+ NormalizePath(*trace_file);
+ }
+
+ ThreadPostMortemTraceSP thread_sp =
+ std::make_shared<ThreadPostMortemTrace>(*process_sp, tid, trace_file);
+ process_sp->GetThreadList().AddThread(thread_sp);
+ return thread_sp;
+}
+
+Expected<TraceIntelPTSessionFileParser::ParsedProcess>
+TraceIntelPTSessionFileParser::ParseProcess(const JSONProcess &process) {
+ TargetSP target_sp;
+ Status error = m_debugger.GetTargetList().CreateTarget(
+ m_debugger, /*user_exe_path*/ StringRef(), process.triple,
+ eLoadDependentsNo,
+ /*platform_options*/ nullptr, target_sp);
+
+ if (!target_sp)
+ return error.ToError();
+
+ ParsedProcess parsed_process;
+ parsed_process.target_sp = target_sp;
+
+ ProcessSP process_sp = target_sp->CreateProcess(
+ /*listener*/ nullptr, "trace",
+ /*crash_file*/ nullptr,
+ /*can_connect*/ false);
+
+ process_sp->SetID(static_cast<lldb::pid_t>(process.pid));
+
+ for (const JSONThread &thread : process.threads)
+ parsed_process.threads.push_back(ParseThread(process_sp, thread));
+
+ for (const JSONModule &module : process.modules)
+ if (Error err = ParseModule(target_sp, module))
+ return std::move(err);
+
+ if (!process.threads.empty())
+ process_sp->GetThreadList().SetSelectedThreadByIndexID(0);
+
+ // We invoke DidAttach to create a correct stopped state for the process and
+ // its threads.
+ ArchSpec process_arch;
+ process_sp->DidAttach(process_arch);
+
+ return parsed_process;
+}
+
+Expected<std::vector<TraceIntelPTSessionFileParser::ParsedProcess>>
+TraceIntelPTSessionFileParser::ParseSessionFile(
+ const JSONTraceSession &session) {
+ std::vector<ParsedProcess> parsed_processes;
+
+ auto HandleError = [&](Error &&err) {
+ // Delete all targets that were created so far in case of failures
+ for (ParsedProcess &parsed_process : parsed_processes)
+ m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp);
+ return std::move(err);
+ };
+
+ for (const JSONProcess &process : session.processes) {
+ if (Expected<ParsedProcess> parsed_process = ParseProcess(process))
+ parsed_processes.push_back(std::move(*parsed_process));
+ else
+ return HandleError(parsed_process.takeError());
+ }
+ return parsed_processes;
+}
+
StringRef TraceIntelPTSessionFileParser::GetSchema() {
static std::string schema;
if (schema.empty()) {
- schema = TraceSessionFileParser::BuildSchema(R"({
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel" | "unknown",
- "family": integer,
- "model": integer,
- "stepping": integer
+ schema = R"({
+ "type": "intel-pt",
+ "cpuInfo": {
+ // CPU information gotten from, for example, /proc/cpuinfo.
+
+ "vendor": "GenuineIntel" | "unknown",
+ "family": integer,
+ "model": integer,
+ "stepping": integer
+ },
+ "processes": [
+ {
+ "pid": integer,
+ "triple": string,
+ // clang/llvm target triple.
+ "threads": [
+ {
+ "tid": integer,
+ "traceBuffer"?: string
+ // Path to the raw Intel PT buffer file for this thread.
+ }
+ ],
+ "modules": [
+ {
+ "systemPath": string,
+ // Original path of the module at runtime.
+ "file"?: string,
+ // Path to a copy of the file if not available at "systemPath".
+ "loadAddress": integer,
+ // Lowest address of the sections of the module loaded on memory.
+ "uuid"?: string,
+ // Build UUID for the file for sanity checks.
+ }
+ ]
+ }
+ ],
+ "cores"?: [
+ {
+ "coreId": integer,
+ // Id of this CPU core.
+ "traceBuffer": string,
+ // Path to the raw Intel PT buffer for this core.
+ "contextSwitchTrace": string,
+ // Path to the raw perf_event_open context switch trace file for this core.
}
- })");
+ ],
+ "tscPerfZeroConversion"?: {
+ // Values used to convert between TSCs and nanoseconds. See the time_zero
+ // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html
+ // for for information.
+
+ "timeMult": integer,
+ "timeShift": integer,
+ "timeZero": integer,
}
- return schema;
}
-pt_cpu TraceIntelPTSessionFileParser::ParsePTCPU(
- const JSONTraceIntelPTCPUInfo &cpu_info) {
- return {cpu_info.vendor.compare("intel") == 0 ? pcv_intel : pcv_unknown,
- static_cast<uint16_t>(cpu_info.family),
- static_cast<uint8_t>(cpu_info.model),
- static_cast<uint8_t>(cpu_info.stepping)};
+Notes:
+
+- All paths are either absolute or relative to folder containing the session file.
+- "cores" is provided if and only if processes[].threads[].traceBuffer is not provided.
+- "tscPerfZeroConversion" must be provided if "cores" is provided.
+ })";
+ }
+ return schema;
}
TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
- const pt_cpu &cpu_info, std::vector<ParsedProcess> &parsed_processes) {
+ JSONTraceSession &session, std::vector<ParsedProcess> &parsed_processes) {
std::vector<ThreadPostMortemTraceSP> threads;
- for (const ParsedProcess &parsed_process : parsed_processes)
+ std::vector<ProcessSP> processes;
+ for (const ParsedProcess &parsed_process : parsed_processes) {
+ processes.push_back(parsed_process.target_sp->GetProcessSP());
threads.insert(threads.end(), parsed_process.threads.begin(),
parsed_process.threads.end());
+ }
- TraceSP trace_instance(new TraceIntelPT(cpu_info, threads));
+ TraceSP trace_instance(new TraceIntelPT(session, processes, threads));
for (const ParsedProcess &parsed_process : parsed_processes)
parsed_process.target_sp->SetTrace(trace_instance);
@@ -56,14 +233,13 @@ TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
Expected<TraceSP> TraceIntelPTSessionFileParser::Parse() {
json::Path::Root root("traceSession");
- JSONTraceSession<JSONTraceIntelPTSettings> session;
- if (!json::fromJSON(m_trace_session_file, session, root))
+ JSONTraceSession session;
+ if (!fromJSON(m_trace_session_file, session, root))
return CreateJSONError(root, m_trace_session_file);
if (Expected<std::vector<ParsedProcess>> parsed_processes =
- ParseCommonSessionFile(session))
- return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.cpuInfo),
- *parsed_processes);
+ ParseSessionFile(session))
+ return CreateTraceIntelPTInstance(session, *parsed_processes);
else
return parsed_processes.takeError();
}
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
index 6bf39cad1e168..9a54cc1854d13 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -9,7 +9,7 @@
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTSESSIONFILEPARSER_H
-#include "../common/TraceSessionFileParser.h"
+#include "../common/ThreadPostMortemTrace.h"
#include "TraceIntelPTJSONStructs.h"
namespace lldb_private {
@@ -17,16 +17,28 @@ namespace trace_intel_pt {
class TraceIntelPT;
-class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
+class TraceIntelPTSessionFileParser {
public:
+ /// Helper struct holding the objects created when parsing a process
+ struct ParsedProcess {
+ lldb::TargetSP target_sp;
+ std::vector<lldb::ThreadPostMortemTraceSP> threads;
+ };
- /// See \a TraceSessionFileParser::TraceSessionFileParser for the description
- /// of these fields.
+ /// \param[in] debugger
+ /// The debugger that will own the targets to create.
+ ///
+ /// \param[in] trace_session_file
+ /// The contents of the main trace session definition file that follows the
+ /// schema of the intel pt trace plug-in.
+ ///
+ /// \param[in] session_file_dir
+ /// The folder where the trace session file is located.
TraceIntelPTSessionFileParser(Debugger &debugger,
const llvm::json::Value &trace_session_file,
llvm::StringRef session_file_dir)
- : TraceSessionFileParser(debugger, session_file_dir, GetSchema()),
- m_trace_session_file(trace_session_file) {}
+ : m_debugger(debugger), m_trace_session_file(trace_session_file),
+ m_session_file_dir(session_file_dir) {}
/// \return
/// The JSON schema for the session data.
@@ -41,13 +53,41 @@ class TraceIntelPTSessionFileParser : public TraceSessionFileParser {
llvm::Expected<lldb::TraceSP> Parse();
lldb::TraceSP
- CreateTraceIntelPTInstance(const pt_cpu &cpu_info,
+ CreateTraceIntelPTInstance(JSONTraceSession &session,
std::vector<ParsedProcess> &parsed_processes);
private:
- static pt_cpu ParsePTCPU(const JSONTraceIntelPTCPUInfo &cpu_info);
+ /// Resolve non-absolute paths relative to the session file folder. It
+ /// modifies the given file_spec.
+ void NormalizePath(lldb_private::FileSpec &file_spec);
+
+ lldb::ThreadPostMortemTraceSP ParseThread(lldb::ProcessSP &process_sp,
+ const JSONThread &thread);
+
+ llvm::Expected<ParsedProcess> ParseProcess(const JSONProcess &process);
+
+ llvm::Error ParseModule(lldb::TargetSP &target_sp, const JSONModule &module);
+
+ /// Create a user-friendly error message upon a JSON-parsing failure using the
+ /// \a json::ObjectMapper functionality.
+ ///
+ /// \param[in] root
+ /// The \a llvm::json::Path::Root used to parse the JSON \a value.
+ ///
+ /// \param[in] value
+ /// The json value that failed to parse.
+ ///
+ /// \return
+ /// An \a llvm::Error containing the user-friendly error message.
+ llvm::Error CreateJSONError(llvm::json::Path::Root &root,
+ const llvm::json::Value &value);
+
+ llvm::Expected<std::vector<ParsedProcess>>
+ ParseSessionFile(const JSONTraceSession &session);
+ Debugger &m_debugger;
const llvm::json::Value &m_trace_session_file;
+ std::string m_session_file_dir;
};
} // namespace trace_intel_pt
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
index 9d6d7e4f68ee8..15e3ead908032 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "TraceIntelPTSessionSaver.h"
-#include "../common/TraceSessionSaver.h"
#include "TraceIntelPT.h"
#include "TraceIntelPTJSONStructs.h"
#include "lldb/Core/Module.h"
@@ -31,42 +30,280 @@ using namespace lldb_private;
using namespace lldb_private::trace_intel_pt;
using namespace llvm;
-llvm::Error TraceIntelPTSessionSaver::SaveToDisk(TraceIntelPT &trace_ipt,
- FileSpec directory) {
- Process *live_process = trace_ipt.GetLiveProcess();
- if (live_process == nullptr)
+static llvm::Error WriteBytesToDisk(FileSpec &output_file,
+ ArrayRef<uint8_t> data) {
+ std::basic_fstream<char> out_fs = std::fstream(
+ output_file.GetPath().c_str(), std::ios::out | std::ios::binary);
+ out_fs.write(reinterpret_cast<const char *>(&data[0]),
+ data.size() * sizeof(uint8_t));
+ out_fs.close();
+ if (!out_fs)
return createStringError(inconvertibleErrorCode(),
- "Saving a trace requires a live process.");
+ formatv("couldn't write to the file {0}",
+ output_file.GetPath().c_str()));
+ return Error::success();
+}
- if (std::error_code ec =
- sys::fs::create_directories(directory.GetPath().c_str()))
- return llvm::errorCodeToError(ec);
+/// Save the trace session description JSON object inside the given directory
+/// as a file named \a trace.json.
+///
+/// \param[in] trace_session_json
+/// The trace's session, as JSON Object.
+///
+/// \param[in] directory
+/// The directory where the JSON file will be saved.
+///
+/// \return
+/// \a llvm::Success if the operation was successful, or an \a llvm::Error
+/// otherwise.
+static llvm::Error
+WriteSessionToFile(const llvm::json::Value &trace_session_json,
+ const FileSpec &directory) {
+ FileSpec trace_path = directory;
+ trace_path.AppendPathComponent("trace.json");
+ std::ofstream os(trace_path.GetPath());
+ os << std::string(formatv("{0:2}", trace_session_json));
+ os.close();
+ if (!os)
+ return createStringError(inconvertibleErrorCode(),
+ formatv("couldn't write to the file {0}",
+ trace_path.GetPath().c_str()));
+ return Error::success();
+}
+
+/// Build the threads sub-section of the trace session description file.
+/// Any associated binary files are created inside the given directory.
+///
+/// \param[in] process
+/// The process being traced.
+///
+/// \param[in] directory
+/// The directory where files will be saved when building the threads
+/// section.
+///
+/// \return
+/// The threads section or \a llvm::Error in case of failures.
+static llvm::Expected<std::vector<JSONThread>>
+BuildThreadsSection(Process &process, FileSpec directory) {
+ std::vector<JSONThread> json_threads;
+ TraceSP trace_sp = process.GetTarget().GetTrace();
+
+ FileSpec threads_dir = directory;
+ threads_dir.AppendPathComponent("threads");
+ FileSystem::Instance().Resolve(threads_dir);
+ sys::fs::create_directories(threads_dir.GetCString());
+
+ for (ThreadSP thread_sp : process.Threads()) {
+ lldb::tid_t tid = thread_sp->GetID();
+ if (!trace_sp->IsTraced(tid))
+ continue;
+
+ JSONThread json_thread;
+ json_thread.tid = tid;
+
+ if (trace_sp->GetTracedCores().empty()) {
+ FileSpec output_file = threads_dir;
+ output_file.AppendPathComponent(std::to_string(tid) + ".intelpt_trace");
+ json_thread.trace_buffer = output_file.GetPath();
+
+ llvm::Error err = process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
+ tid, IntelPTDataKinds::kTraceBuffer,
+ [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+ return WriteBytesToDisk(output_file, data);
+ });
+ if (err)
+ return std::move(err);
+ }
+
+ json_threads.push_back(std::move(json_thread));
+ }
+ return json_threads;
+}
+
+static llvm::Expected<llvm::Optional<std::vector<JSONCore>>>
+BuildCoresSection(TraceIntelPT &trace_ipt, FileSpec directory) {
+ if (trace_ipt.GetTracedCores().empty())
+ return None;
+
+ std::vector<JSONCore> json_cores;
+ FileSpec cores_dir = directory;
+ cores_dir.AppendPathComponent("cores");
+ FileSystem::Instance().Resolve(cores_dir);
+ sys::fs::create_directories(cores_dir.GetCString());
+
+ for (lldb::core_id_t core_id : trace_ipt.GetTracedCores()) {
+ JSONCore json_core;
+ json_core.core_id = core_id;
+
+ {
+ FileSpec output_trace = cores_dir;
+ output_trace.AppendPathComponent(std::to_string(core_id) +
+ ".intelpt_trace");
+ json_core.trace_buffer = output_trace.GetPath();
+
+ llvm::Error err = trace_ipt.OnCoreBinaryDataRead(
+ core_id, IntelPTDataKinds::kTraceBuffer,
+ [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+ return WriteBytesToDisk(output_trace, data);
+ });
+ if (err)
+ return std::move(err);
+ }
- llvm::Expected<JSONTraceIntelPTTrace> json_intel_pt_trace =
- BuildTraceSection(trace_ipt);
- if (!json_intel_pt_trace)
- return json_intel_pt_trace.takeError();
+ {
+ FileSpec output_context_switch_trace = cores_dir;
+ output_context_switch_trace.AppendPathComponent(
+ std::to_string(core_id) + ".perf_context_switch_trace");
+ json_core.context_switch_trace = output_context_switch_trace.GetPath();
- llvm::Expected<JSONTraceSessionBase> json_session_description =
- TraceSessionSaver::BuildProcessesSection(
- *live_process, IntelPTDataKinds::kTraceBuffer, directory);
+ llvm::Error err = trace_ipt.OnCoreBinaryDataRead(
+ core_id, IntelPTDataKinds::kPerfContextSwitchTrace,
+ [&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
+ return WriteBytesToDisk(output_context_switch_trace, data);
+ });
+ if (err)
+ return std::move(err);
+ }
+ json_cores.push_back(std::move(json_core));
+ }
+ return json_cores;
+}
+
+/// Build modules sub-section of the trace's session. The original modules
+/// will be copied over to the \a <directory/modules> folder. Invalid modules
+/// are skipped.
+/// Copying the modules has the benefit of making these trace session
+/// directories self-contained, as the raw traces and modules are part of the
+/// output directory and can be sent to another machine, where lldb can load
+/// them and replicate exactly the same trace session.
+///
+/// \param[in] process
+/// The process being traced.
+///
+/// \param[in] directory
+/// The directory where the modules files will be saved when building
+/// the modules section.
+/// Example: If a module \a libbar.so exists in the path
+/// \a /usr/lib/foo/libbar.so, then it will be copied to
+/// \a <directory>/modules/usr/lib/foo/libbar.so.
+///
+/// \return
+/// The modules section or \a llvm::Error in case of failures.
+static llvm::Expected<std::vector<JSONModule>>
+BuildModulesSection(Process &process, FileSpec directory) {
+ std::vector<JSONModule> json_modules;
+ ModuleList module_list = process.GetTarget().GetImages();
+ for (size_t i = 0; i < module_list.GetSize(); ++i) {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(i));
+ if (!module_sp)
+ continue;
+ std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
+ // TODO: support memory-only libraries like [vdso]
+ if (!module_sp->GetFileSpec().IsAbsolute())
+ continue;
+
+ std::string file = module_sp->GetFileSpec().GetPath();
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile == nullptr)
+ continue;
+
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ Address base_addr(objfile->GetBaseAddress());
+ if (base_addr.IsValid() &&
+ !process.GetTarget().GetSectionLoadList().IsEmpty())
+ load_addr = base_addr.GetLoadAddress(&process.GetTarget());
+
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ continue;
- if (!json_session_description)
- return json_session_description.takeError();
+ FileSystem::Instance().Resolve(directory);
+ FileSpec path_to_copy_module = directory;
+ path_to_copy_module.AppendPathComponent("modules");
+ path_to_copy_module.AppendPathComponent(system_path);
+ sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString());
- JSONTraceIntelPTSession json_intel_pt_session{json_intel_pt_trace.get(),
- json_session_description.get()};
+ if (std::error_code ec = llvm::sys::fs::copy_file(
+ system_path, path_to_copy_module.GetPath()))
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("couldn't write to the file. {0}", ec.message()));
- return TraceSessionSaver::WriteSessionToFile(
- llvm::json::toJSON(json_intel_pt_session), directory);
+ json_modules.push_back(JSONModule{system_path,
+ path_to_copy_module.GetPath(), load_addr,
+ module_sp->GetUUID().GetAsString()});
+ }
+ return json_modules;
}
-llvm::Expected<JSONTraceIntelPTTrace>
-TraceIntelPTSessionSaver::BuildTraceSection(TraceIntelPT &trace_ipt) {
- llvm::Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
+/// Build the processes section of the trace session description file. Besides
+/// returning the processes information, this method saves to disk all modules
+/// and raw traces corresponding to the traced threads of the given process.
+///
+/// \param[in] process
+/// The process being traced.
+///
+/// \param[in] directory
+/// The directory where files will be saved when building the processes
+/// section.
+///
+/// \return
+/// The processes section or \a llvm::Error in case of failures.
+static llvm::Expected<JSONProcess>
+BuildProcessSection(Process &process, const FileSpec &directory) {
+ Expected<std::vector<JSONThread>> json_threads =
+ BuildThreadsSection(process, directory);
+ if (!json_threads)
+ return json_threads.takeError();
+
+ Expected<std::vector<JSONModule>> json_modules =
+ BuildModulesSection(process, directory);
+ if (!json_modules)
+ return json_modules.takeError();
+
+ return JSONProcess{
+ static_cast<int64_t>(process.GetID()),
+ process.GetTarget().GetArchitecture().GetTriple().getTriple(),
+ json_threads.get(), json_modules.get()};
+}
+
+/// See BuildProcessSection()
+static llvm::Expected<std::vector<JSONProcess>>
+BuildProcessesSection(TraceIntelPT &trace_ipt, const FileSpec &directory) {
+ std::vector<JSONProcess> processes;
+ for (Process *process : trace_ipt.GetAllProcesses()) {
+ if (llvm::Expected<JSONProcess> json_process =
+ BuildProcessSection(*process, directory))
+ processes.push_back(std::move(*json_process));
+ else
+ return json_process.takeError();
+ }
+ return processes;
+}
+
+Error TraceIntelPTSessionSaver::SaveToDisk(TraceIntelPT &trace_ipt,
+ FileSpec directory) {
+ if (std::error_code ec =
+ sys::fs::create_directories(directory.GetPath().c_str()))
+ return llvm::errorCodeToError(ec);
+
+ Expected<pt_cpu> cpu_info = trace_ipt.GetCPUInfo();
if (!cpu_info)
return cpu_info.takeError();
- return JSONTraceIntelPTTrace{"intel-pt",
- JSONTraceIntelPTCPUInfo(cpu_info.get())};
+ Expected<std::vector<JSONProcess>> json_processes =
+ BuildProcessesSection(trace_ipt, directory);
+
+ if (!json_processes)
+ return json_processes.takeError();
+
+ Expected<Optional<std::vector<JSONCore>>> json_cores =
+ BuildCoresSection(trace_ipt, directory);
+ if (!json_cores)
+ return json_cores.takeError();
+
+ JSONTraceSession json_intel_pt_session{"intel-pt", *cpu_info, *json_processes,
+ *json_cores,
+ trace_ipt.GetPerfZeroTscConversion()};
+
+ return WriteSessionToFile(toJSON(json_intel_pt_session), directory);
}
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
index 943519f959e9d..e7ddff813992f 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.h
@@ -11,15 +11,12 @@
#include "TraceIntelPT.h"
-#include "../common/TraceJSONStructs.h"
+#include "TraceIntelPTJSONStructs.h"
namespace lldb_private {
namespace trace_intel_pt {
-class TraceIntelPT;
-
class TraceIntelPTSessionSaver {
-
public:
/// Save the Intel PT trace of a live process to the specified directory,
/// which will be created if needed. This will also create a file
@@ -38,17 +35,6 @@ class TraceIntelPTSessionSaver {
/// \a llvm::success if the operation was successful, or an \a llvm::Error
/// otherwise.
llvm::Error SaveToDisk(TraceIntelPT &trace_ipt, FileSpec directory);
-
-private:
- /// Build trace section of the intel-pt trace session description file.
- ///
- /// \param[in] trace_ipt
- /// The Intel PT trace.
- ///
- /// \return
- /// The trace section an \a llvm::Error in case of failures.
- llvm::Expected<JSONTraceIntelPTTrace>
- BuildTraceSection(TraceIntelPT &trace_ipt);
};
} // namespace trace_intel_pt
diff --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
index 16d816e640181..5c16482acae3a 100644
--- a/lldb/source/Target/Trace.cpp
+++ b/lldb/source/Target/Trace.cpp
@@ -27,26 +27,16 @@ using namespace llvm;
// Helper structs used to extract the type of a trace session json without
// having to parse the entire object.
-struct JSONSimplePluginSettings {
- std::string type;
-};
-
struct JSONSimpleTraceSession {
- JSONSimplePluginSettings trace;
+ std::string type;
};
namespace llvm {
namespace json {
-bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings,
- Path path) {
- json::ObjectMapper o(value, path);
- return o && o.map("type", plugin_settings.type);
-}
-
bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) {
json::ObjectMapper o(value, path);
- return o && o.map("trace", session.trace);
+ return o && o.map("type", session.type);
}
} // namespace json
@@ -69,10 +59,10 @@ Trace::FindPluginForPostMortemProcess(Debugger &debugger,
return root.getError();
if (auto create_callback =
- PluginManager::GetTraceCreateCallback(json_session.trace.type))
+ PluginManager::GetTraceCreateCallback(json_session.type))
return create_callback(trace_session_file, session_file_dir, debugger);
- return createInvalidPlugInError(json_session.trace.type);
+ return createInvalidPlugInError(json_session.type);
}
Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name,
@@ -136,6 +126,18 @@ Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
return single_thread_data_it->second;
}
+Optional<size_t> Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id,
+ llvm::StringRef kind) {
+ auto it = m_live_core_data.find(core_id);
+ if (it == m_live_core_data.end())
+ return None;
+ std::unordered_map<std::string, size_t> &single_core_data = it->second;
+ auto single_thread_data_it = single_core_data.find(kind.str());
+ if (single_thread_data_it == single_core_data.end())
+ return None;
+ return single_thread_data_it->second;
+}
+
Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) {
auto data_it = m_live_process_data.find(kind.str());
if (data_it == m_live_process_data.end())
@@ -155,8 +157,26 @@ Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) {
"Tracing data \"%s\" is not available for thread %" PRIu64 ".",
kind.data(), tid);
- TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, 0,
- *size};
+ TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid,
+ /*core_id=*/None, /*offset=*/0, *size};
+ return m_live_process->TraceGetBinaryData(request);
+}
+
+Expected<std::vector<uint8_t>>
+Trace::GetLiveCoreBinaryData(lldb::core_id_t core_id, llvm::StringRef kind) {
+ if (!m_live_process)
+ return createStringError(inconvertibleErrorCode(),
+ "Tracing requires a live process.");
+ llvm::Optional<size_t> size = GetLiveCoreBinaryDataSize(core_id, kind);
+ if (!size)
+ return createStringError(
+ inconvertibleErrorCode(),
+ "Tracing data \"%s\" is not available for core_id %" PRIu64 ".",
+ kind.data(), core_id);
+
+ TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(),
+ /*tid=*/None, core_id,
+ /*offset=*/0, *size};
return m_live_process->TraceGetBinaryData(request);
}
@@ -171,8 +191,9 @@ Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
inconvertibleErrorCode(),
"Tracing data \"%s\" is not available for the process.", kind.data());
- TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), None, 0,
- *size};
+ TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(),
+ /*tid=*/None, /*core_id*/ None,
+ /*offset=*/0, *size};
return m_live_process->TraceGetBinaryData(request);
}
@@ -190,6 +211,7 @@ const char *Trace::RefreshLiveProcessState() {
m_stop_id = new_stop_id;
m_live_thread_data.clear();
m_live_refresh_error.reset();
+ m_cores.reset();
auto HandleError = [&](Error &&err) -> const char * {
m_live_refresh_error = toString(std::move(err));
@@ -205,8 +227,10 @@ const char *Trace::RefreshLiveProcessState() {
if (!live_process_state)
return HandleError(live_process_state.takeError());
- for (std::string &warning : live_process_state->warnings)
- LLDB_LOG(log, "Warning when fetching the trace state: {0}", warning);
+ if (live_process_state->warnings) {
+ for (std::string &warning : *live_process_state->warnings)
+ LLDB_LOG(log, "== Warning when fetching the trace state: {0}", warning);
+ }
for (const TraceThreadState &thread_state :
live_process_state->traced_threads) {
@@ -214,6 +238,21 @@ const char *Trace::RefreshLiveProcessState() {
m_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();
+ for (const TraceCoreState &core_state : *live_process_state->cores) {
+ m_cores->push_back(core_state.core_id);
+ for (const TraceBinaryData &item : core_state.binary_data)
+ m_live_core_data[core_state.core_id][item.kind] = item.size;
+ }
+ LLDB_LOG(log, "== Found {0} cpu cores being traced",
+ live_process_state->cores->size());
+ }
+
+
for (const TraceBinaryData &item : live_process_state->process_binary_data)
m_live_process_data[item.kind] = item.size;
@@ -224,8 +263,25 @@ const char *Trace::RefreshLiveProcessState() {
return nullptr;
}
+Trace::Trace(ArrayRef<ProcessSP> postmortem_processes,
+ Optional<std::vector<lldb::core_id_t>> postmortem_cores) {
+ for (ProcessSP process_sp : postmortem_processes)
+ m_postmortem_processes.push_back(process_sp.get());
+ m_cores = postmortem_cores;
+}
+
Process *Trace::GetLiveProcess() { return m_live_process; }
+ArrayRef<Process *> Trace::GetPostMortemProcesses() {
+ return m_postmortem_processes;
+}
+
+std::vector<Process *> Trace::GetAllProcesses() {
+ if (Process *proc = GetLiveProcess())
+ return {proc};
+ return GetPostMortemProcesses();
+}
+
uint32_t Trace::GetStopID() {
RefreshLiveProcessState();
return m_stop_id;
@@ -252,11 +308,39 @@ Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) {
return it2->second;
}
+llvm::Expected<FileSpec>
+Trace::GetPostMortemCoreDataFile(lldb::core_id_t core_id,
+ llvm::StringRef kind) {
+ auto NotFoundError = [&]() {
+ return createStringError(
+ inconvertibleErrorCode(),
+ formatv("The core with id={0} doesn't have the tracing data {1}",
+ core_id, kind));
+ };
+
+ auto it = m_postmortem_core_data.find(core_id);
+ if (it == m_postmortem_core_data.end())
+ return NotFoundError();
+
+ std::unordered_map<std::string, FileSpec> &data_kind_to_file_spec_map =
+ it->second;
+ auto it2 = data_kind_to_file_spec_map.find(kind.str());
+ if (it2 == data_kind_to_file_spec_map.end())
+ return NotFoundError();
+ return it2->second;
+}
+
void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind,
FileSpec file_spec) {
m_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;
+}
+
llvm::Error
Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
OnBinaryDataReadCallback callback) {
@@ -266,14 +350,19 @@ Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
return callback(*data);
}
-llvm::Error
-Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
- OnBinaryDataReadCallback callback) {
- Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind);
- if (!file)
- return file.takeError();
+llvm::Error Trace::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id,
+ llvm::StringRef kind,
+ OnBinaryDataReadCallback callback) {
+ Expected<std::vector<uint8_t>> data = GetLiveCoreBinaryData(core_id, kind);
+ if (!data)
+ return data.takeError();
+ return callback(*data);
+}
+
+llvm::Error Trace::OnDataFileRead(FileSpec file,
+ OnBinaryDataReadCallback callback) {
ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
- MemoryBuffer::getFile(file->GetPath());
+ MemoryBuffer::getFile(file.GetPath());
if (std::error_code err = trace_or_error.getError())
return errorCodeToError(err);
@@ -284,10 +373,47 @@ Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
return callback(array_ref);
}
+llvm::Error
+Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
+ OnBinaryDataReadCallback callback) {
+ Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind);
+ if (!file)
+ return file.takeError();
+ return OnDataFileRead(*file, callback);
+}
+
+llvm::Error
+Trace::OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id,
+ llvm::StringRef kind,
+ OnBinaryDataReadCallback callback) {
+ Expected<FileSpec> file = GetPostMortemCoreDataFile(core_id, kind);
+ if (!file)
+ return file.takeError();
+ return OnDataFileRead(*file, callback);
+}
+
llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind,
OnBinaryDataReadCallback callback) {
+ RefreshLiveProcessState();
if (m_live_process)
return OnLiveThreadBinaryDataRead(tid, kind, callback);
else
return OnPostMortemThreadBinaryDataRead(tid, kind, callback);
}
+
+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
+ return OnPostMortemCoreBinaryDataRead(core_id, kind, callback);
+}
+
+ArrayRef<lldb::core_id_t> Trace::GetTracedCores() {
+ RefreshLiveProcessState();
+ if (m_cores)
+ return *m_cores;
+ return {};
+}
diff --git a/lldb/source/Utility/TraceGDBRemotePackets.cpp b/lldb/source/Utility/TraceGDBRemotePackets.cpp
index 313de1a271f4e..944cfd78e36c8 100644
--- a/lldb/source/Utility/TraceGDBRemotePackets.cpp
+++ b/lldb/source/Utility/TraceGDBRemotePackets.cpp
@@ -101,7 +101,7 @@ bool fromJSON(const json::Value &value, TraceGetStateResponse &packet,
return o && o.map("tracedThreads", packet.traced_threads) &&
o.map("processBinaryData", packet.process_binary_data) &&
o.map("cores", packet.cores) &&
- o.mapOptional("warnings", packet.warnings);
+ o.map("warnings", packet.warnings);
}
json::Value toJSON(const TraceGetStateResponse &packet) {
@@ -111,6 +111,12 @@ json::Value toJSON(const TraceGetStateResponse &packet) {
{"warnings", packet.warnings}});
}
+void TraceGetStateResponse::AddWarning(StringRef warning) {
+ if (!warnings)
+ warnings.emplace();
+ warnings->push_back(warning.data());
+}
+
bool fromJSON(const json::Value &value, TraceCoreState &packet,
json::Path path) {
ObjectMapper o(value, path);
@@ -135,6 +141,7 @@ json::Value toJSON(const TraceGetBinaryDataRequest &packet) {
{"kind", packet.kind},
{"offset", packet.offset},
{"tid", packet.tid},
+ {"coreId", packet.core_id},
{"size", packet.size}});
}
@@ -143,7 +150,7 @@ bool fromJSON(const json::Value &value, TraceGetBinaryDataRequest &packet,
ObjectMapper o(value, path);
return o && o.map("type", packet.type) && o.map("kind", packet.kind) &&
o.map("tid", packet.tid) && o.map("offset", packet.offset) &&
- o.map("size", packet.size);
+ o.map("size", packet.size) && o.map("coreId", packet.core_id);
}
/// \}
diff --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
index 505d7262b643a..6ffc3e95cb37f 100644
--- a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
+++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -60,7 +60,6 @@ std::chrono::nanoseconds LinuxPerfZeroTscConversion::ToNanos(uint64_t tsc) {
json::Value toJSON(const LinuxPerfZeroTscConversion &packet) {
return json::Value(json::Object{
- {"kind", "tscPerfZeroConversion"},
{"timeMult", packet.time_mult},
{"timeShift", packet.time_shift},
{"timeZero", packet.time_zero},
diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index 731cade7587bc..d1351c09ef6e1 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -62,23 +62,24 @@ def testLoadInvalidTraces(self):
Context:
{
+ "cpuInfo": { ... },
"processes": [
/* error: expected object */
123
],
- "trace": { ... }
+ "type": "intel-pt"
}
Schema:
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel" | "unknown",
- "family": integer,
- "model": integer,
- "stepping": integer
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ // CPU information gotten from, for example, /proc/cpuinfo.
+
+ "vendor": "GenuineIntel" | "unknown",
+ "family": integer,
+ "model": integer,
+ "stepping": integer
},'''])
# Now we test a missing field in the global session file
@@ -87,25 +88,22 @@ def testLoadInvalidTraces(self):
# Now we test a missing field in the intel-pt settings
self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad4.json"), error=True,
- substrs=['''error: missing value at traceSession.trace.cpuInfo.family
+ substrs=['''error: missing value at traceSession.cpuInfo.family
Context:
{
+ "cpuInfo": /* error: missing value */ {
+ "model": 79,
+ "stepping": 1,
+ "vendor": "GenuineIntel"
+ },
"processes": [],
- "trace": {
- "cpuInfo": /* error: missing value */ {
- "model": 79,
- "stepping": 1,
- "vendor": "intel"
- },
- "type": "intel-pt"
- }
+ "type": "intel-pt"
}''', "Schema"])
# Now we test an incorrect load address in the intel-pt settings
self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad5.json"), error=True,
- substrs=['error: expected numeric string at traceSession.processes[0].modules[0].loadAddress',
- '"loadAddress": /* error: expected numeric string */ 400000,', "Schema"])
+ substrs=['error: missing value at traceSession.processes[1].pid', "Schema"])
# The following wrong schema will have a valid target and an invalid one. In the case of failure,
# no targets should be created.
diff --git a/lldb/test/API/commands/trace/TestTraceSave.py b/lldb/test/API/commands/trace/TestTraceSave.py
index 4037c729ca924..9b82e636d0ae6 100644
--- a/lldb/test/API/commands/trace/TestTraceSave.py
+++ b/lldb/test/API/commands/trace/TestTraceSave.py
@@ -1,9 +1,15 @@
import lldb
+import json
from intelpt_testcase import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
from lldbsuite.test.decorators import *
+def find(predicate, seq):
+ for item in seq:
+ if predicate(item):
+ return item
+
class TestTraceSave(TraceIntelPTTestCaseBase):
mydir = TestBase.compute_mydir(__file__)
@@ -54,10 +60,79 @@ def testSaveWhenNotLiveTrace(self):
# Check the output when not doing live tracing
self.expect("process trace save -d " +
- os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir"),
- substrs=["error: Saving a trace requires a live process."],
- error=True)
+ os.path.join(self.getBuildDir(), "intelpt-trace", "trace_not_live_dir"))
+
+ def testSaveMultiCoreTrace(self):
+ '''
+ This test starts a per-core tracing session, then saves the session to disk, and
+ finally it loads it again.
+ '''
+ self.skipIfPerCoreTracingIsNotSupported()
+
+ self.expect("target create " +
+ os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+ self.expect("b main")
+ self.expect("r")
+ self.expect("process trace start --per-core-tracing")
+ self.expect("b 7")
+ output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")
+ self.expect("process trace save -d " + output_dir)
+
+ def checkSessionBundle(session_file_path):
+ with open(session_file_path) as session_file:
+ session = json.load(session_file)
+ # We expect tsc conversion info
+ self.assertTrue("tscPerfZeroConversion" in session)
+ # We expect at least one core
+ self.assertGreater(len(session["cores"]), 0)
+
+ # We expect the required trace files to be created
+ for core in session["cores"]:
+ core_files_prefix = os.path.join(output_dir, "cores", str(core["coreId"]))
+ self.assertTrue(os.path.exists(core_files_prefix + ".intelpt_trace"))
+ self.assertTrue(os.path.exists(core_files_prefix + ".perf_context_switch_trace"))
+
+ # We expect at least one one process
+ self.assertGreater(len(session["processes"]), 0)
+ for process in session["processes"]:
+ # We expect at least one thread
+ self.assertGreater(len(process["threads"]), 0)
+ # We don't expect thread traces
+ for thread in process["threads"]:
+ self.assertTrue(("traceBuffer" not in thread) or (thread["traceBuffer"] is None))
+
+ original_trace_session_file = os.path.join(output_dir, "trace.json")
+ checkSessionBundle(original_trace_session_file)
+
+ output_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "trace_save")
+ self.expect("trace load " + os.path.join(output_dir, "trace.json"))
+ output_copy_dir = os.path.join(self.getBuildDir(), "intelpt-trace", "copy_trace_save")
+ self.expect("process trace save -d " + output_copy_dir)
+
+ # We now check that the new bundle is correct on its own
+ copied_trace_session_file = os.path.join(output_copy_dir, "trace.json")
+ checkSessionBundle(copied_trace_session_file)
+
+ # We finally check that the new bundle has the same information as the original one
+ with open(original_trace_session_file) as original_file:
+ original = json.load(original_file)
+ with open(copied_trace_session_file) as copy_file:
+ copy = json.load(copy_file)
+
+ self.assertEqual(len(original["processes"]), len(copy["processes"]))
+
+ for process in original["processes"]:
+ copied_process = find(lambda proc : proc["pid"] == process["pid"], copy["processes"])
+ self.assertTrue(copied_process is not None)
+
+ for thread in process["threads"]:
+ copied_thread = find(lambda thr : thr["tid"] == thread["tid"], copied_process["threads"])
+ self.assertTrue(copied_thread is not None)
+
+ for core in original["cores"]:
+ copied_core = find(lambda cor : cor["coreId"] == core["coreId"], copy["cores"])
+ self.assertTrue(copied_core is not None)
def testSaveTrace(self):
self.expect("target create " +
diff --git a/lldb/test/API/commands/trace/TestTraceSchema.py b/lldb/test/API/commands/trace/TestTraceSchema.py
index cd80c5479f72c..8ba2809537b7a 100644
--- a/lldb/test/API/commands/trace/TestTraceSchema.py
+++ b/lldb/test/API/commands/trace/TestTraceSchema.py
@@ -9,7 +9,7 @@ class TestTraceLoad(TraceIntelPTTestCaseBase):
mydir = TestBase.compute_mydir(__file__)
def testSchema(self):
- self.expect("trace schema intel-pt", substrs=["trace", "triple", "threads", "traceFile"])
+ self.expect("trace schema intel-pt", substrs=["triple", "threads", "traceBuffer"])
def testInvalidPluginSchema(self):
self.expect("trace schema invalid-plugin", error=True,
@@ -17,12 +17,12 @@ def testInvalidPluginSchema(self):
def testAllSchemas(self):
self.expect("trace schema all", substrs=['''{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel" | "unknown",
- "family": integer,
- "model": integer,
- "stepping": integer
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ // CPU information gotten from, for example, /proc/cpuinfo.
+
+ "vendor": "GenuineIntel" | "unknown",
+ "family": integer,
+ "model": integer,
+ "stepping": integer
},'''])
diff --git a/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json b/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
index b8c6d32473dbd..6b44b92edd58e 100644
--- a/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
+++ b/lldb/test/API/commands/trace/intelpt-trace-multi-file/multi-file-no-ld.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -15,26 +13,26 @@
"threads": [
{
"tid": 815455,
- "traceFile": "multi-file.trace"
+ "traceBuffer": "multi-file.trace"
}
],
"modules": [
{
"file": "a.out",
"systemPath": "a.out",
- "loadAddress": "0x0000000000400000",
+ "loadAddress": 4194304,
"uuid": "D2414468-7112-B7C5-408D-FF07E30D5B17-A5BFD2C4"
},
{
"file": "libfoo.so",
"systemPath": "libfoo.so",
- "loadAddress": "0x00007ffff7bd9000",
+ "loadAddress": 140737349783552,
"uuid": "B30FFEDA-8BB2-3D08-4580-C5937ED11E2B-21BE778C"
},
{
"file": "libbar.so",
"systemPath": "libbar.so",
- "loadAddress": "0x00007ffff79d7000",
+ "loadAddress": 140737347678208,
"uuid": "6633B038-EA73-D1A6-FF9A-7D0C0EDF733D-95FEA2CC"
}
]
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace.json b/lldb/test/API/commands/trace/intelpt-trace/trace.json
index 9104c572e3fb7..d284817591094 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -15,14 +13,14 @@
"threads": [
{
"tid": 3842849,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": [
{
"file": "a.out",
"systemPath": "a.out",
- "loadAddress": "0x0000000000400000",
+ "loadAddress": 4194304,
"uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
}
]
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
index f73944d1fa186..5bd3d35ec08b6 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_2threads.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -15,18 +13,18 @@
"threads": [
{
"tid": 3842849,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
},
{
"tid": 3842850,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": [
{
"file": "a.out",
"systemPath": "a.out",
- "loadAddress": "0x0000000000400000",
+ "loadAddress": 4194304,
"uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
}
]
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
index 0232f9ffb80d4..5da88abc6ec1c 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
123
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
index f944c7362d048..46fa01abf45b9 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad2.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -15,14 +13,14 @@
"threads": [
{
"tid": 5678,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": [
{
"file": "a.out",
"systemPath": "a.out",
- "loadAddress": "0x0000000000400000",
+ "loadAddress": 4194304,
"uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
}
]
@@ -32,7 +30,7 @@
"threads": [
{
"tid": 56789,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": []
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
index 63eafbc13efbf..ae33077c8ff09 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad3.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -15,14 +13,14 @@
"threads": [
{
"tid": 5678,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": [
{
"file": "a.out",
"systemPath": "a.out",
- "loadAddress": "0x0000000000400000",
+ "loadAddress": 4194304,
"uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
}
]
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
index 7224ee5f1f3c2..89245edb9aad8 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad4.json
@@ -1,11 +1,9 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "model": 79,
+ "stepping": 1
},
"processes": [
]
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
index 1664072aa3b8d..d70b176af878b 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad5.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -15,7 +13,7 @@
"threads": [
{
"tid": 5678,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": [
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json b/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
index 901f41901842c..84e4bf783f021 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_bad_image.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 6,
- "model": 79,
- "stepping": 1
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
},
"processes": [
{
@@ -15,14 +13,14 @@
"threads": [
{
"tid": 3842849,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": [
{
"file": "a.out",
"systemPath": "a.out",
- "loadAddress": "0x0000000000FFFFF0",
+ "loadAddress": 16777200,
"uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
}
]
diff --git a/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json b/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
index 318aaba8154ff..e55af9f1cacc4 100644
--- a/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
+++ b/lldb/test/API/commands/trace/intelpt-trace/trace_wrong_cpu.json
@@ -1,12 +1,10 @@
{
- "trace": {
- "type": "intel-pt",
- "cpuInfo": {
- "vendor": "intel",
- "family": 2123123,
- "model": 12123123,
- "stepping": 1231231
- }
+ "type": "intel-pt",
+ "cpuInfo": {
+ "vendor": "GenuineIntel",
+ "family": 2123123,
+ "model": 12123123,
+ "stepping": 1231231
},
"processes": [
{
@@ -15,14 +13,14 @@
"threads": [
{
"tid": 3842849,
- "traceFile": "3842849.trace"
+ "traceBuffer": "3842849.trace"
}
],
"modules": [
{
"file": "a.out",
"systemPath": "a.out",
- "loadAddress": "0x0000000000400000",
+ "loadAddress": 4194304,
"uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
}
]
diff --git a/lldb/unittests/Process/Linux/PerfTests.cpp b/lldb/unittests/Process/Linux/PerfTests.cpp
index 39bbc43c1da53..c12aa097dd3ca 100644
--- a/lldb/unittests/Process/Linux/PerfTests.cpp
+++ b/lldb/unittests/Process/Linux/PerfTests.cpp
@@ -6,8 +6,6 @@
//
//===----------------------------------------------------------------------===//
-#ifdef __x86_64__
-
#include "Perf.h"
#include "llvm/Support/Error.h"
@@ -85,136 +83,3 @@ TEST(Perf, TscConversion) {
ASSERT_LT(converted_tsc_
diff .count(),
(SLEEP_NANOS + acceptable_overhead).count());
}
-
-size_t ReadCylicBufferWrapper(void *buf, size_t buf_size, void *cyc_buf,
- size_t cyc_buf_size, size_t cyc_start,
- size_t offset) {
- llvm::MutableArrayRef<uint8_t> dst(reinterpret_cast<uint8_t *>(buf),
- buf_size);
- llvm::ArrayRef<uint8_t> src(reinterpret_cast<uint8_t *>(cyc_buf),
- cyc_buf_size);
- ReadCyclicBuffer(dst, src, cyc_start, offset);
- return dst.size();
-}
-
-TEST(CyclicBuffer, EdgeCases) {
- size_t bytes_read;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char smaller_buffer[4] = {};
-
- // empty buffer to read into
- bytes_read = ReadCylicBufferWrapper(smaller_buffer, 0, cyclic_buffer,
- sizeof(cyclic_buffer), 3, 0);
- ASSERT_EQ(0u, bytes_read);
-
- // empty cyclic buffer
- bytes_read = ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, 0, 3, 0);
- ASSERT_EQ(0u, bytes_read);
-
- // bigger offset
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 6);
- ASSERT_EQ(0u, bytes_read);
-
- // wrong offset
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
- ASSERT_EQ(0u, bytes_read);
-
- // wrong start
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 7);
- ASSERT_EQ(0u, bytes_read);
-}
-
-TEST(CyclicBuffer, EqualSizeBuffer) {
- size_t bytes_read = 0;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- char cyclic[] = "cyclic";
- for (size_t i = 0; i < sizeof(cyclic); i++) {
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char equal_size_buffer[7] = {};
- bytes_read =
- ReadCylicBufferWrapper(equal_size_buffer, sizeof(cyclic_buffer),
- cyclic_buffer, sizeof(cyclic_buffer), 3, i);
- ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
- ASSERT_STREQ(equal_size_buffer, (cyclic + i));
- }
-}
-
-TEST(CyclicBuffer, SmallerSizeBuffer) {
- size_t bytes_read;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char smaller_buffer[4] = {};
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 0);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "cyc");
-
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 1);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "ycl");
-
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 2);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "cli");
-
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 3);
- ASSERT_EQ(3u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "lic");
-
- {
- char smaller_buffer[4] = {};
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 4);
- ASSERT_EQ(2u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "ic");
- }
- {
- char smaller_buffer[4] = {};
- bytes_read =
- ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, 5);
- ASSERT_EQ(1u, bytes_read);
- ASSERT_STREQ(smaller_buffer, "c");
- }
-}
-
-TEST(CyclicBuffer, BiggerSizeBuffer) {
- size_t bytes_read = 0;
- uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'};
-
- char cyclic[] = "cyclic";
- for (size_t i = 0; i < sizeof(cyclic); i++) {
- // We will always leave the last bytes untouched
- // so that string comparisons work.
- char bigger_buffer[10] = {};
- bytes_read =
- ReadCylicBufferWrapper(bigger_buffer, (sizeof(bigger_buffer) - 1),
- cyclic_buffer, sizeof(cyclic_buffer), 3, i);
- ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read);
- ASSERT_STREQ(bigger_buffer, (cyclic + i));
- }
-}
-
-#endif // __x86_64__
diff --git a/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp b/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp
index 94b6ec14ef840..b3f8538ee8b68 100644
--- a/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/TraceGDBRemotePacketsTest.cpp
@@ -38,8 +38,7 @@ TEST(TraceGDBRemotePacketsTest, IntelPTGetStateResponse) {
// Create TraceIntelPTGetStateResponse.
TraceIntelPTGetStateResponse response;
- response.tsc_conversion = std::make_unique<LinuxPerfZeroTscConversion>(
- test_time_mult, test_time_shift, test_time_zero);
+ response.tsc_perf_zero_conversion = LinuxPerfZeroTscConversion{test_time_mult, test_time_shift, test_time_zero};
// Serialize then deserialize.
Expected<TraceIntelPTGetStateResponse> deserialized_response =
@@ -57,9 +56,9 @@ TEST(TraceGDBRemotePacketsTest, IntelPTGetStateResponse) {
const uint64_t EXPECTED_NANOS = 9223372039007304983u;
uint64_t pre_serialization_conversion =
- response.tsc_conversion->Convert(TSC).count();
+ response.tsc_perf_zero_conversion->ToNanos(TSC).count();
uint64_t post_serialization_conversion =
- deserialized_response->tsc_conversion->Convert(TSC).count();
+ deserialized_response->tsc_perf_zero_conversion->ToNanos(TSC).count();
// Check equality:
// Ensure that both the TraceGetStateResponse and TraceIntelPTGetStateResponse
@@ -95,7 +94,7 @@ TEST(TraceGDBRemotePacketsTest, IntelPTGetStateResponseEmpty) {
// portions of the JSON representation are unchanged.
ASSERT_EQ(toJSON(response), toJSON(*deserialized_response));
// Ensure that the tsc_conversion's are nullptr.
- ASSERT_EQ(response.tsc_conversion.get(), nullptr);
- ASSERT_EQ(response.tsc_conversion.get(),
- deserialized_response->tsc_conversion.get());
+ ASSERT_EQ(response.tsc_perf_zero_conversion, None);
+ ASSERT_EQ(response.tsc_perf_zero_conversion,
+ deserialized_response->tsc_perf_zero_conversion);
}
More information about the lldb-commits
mailing list