jj10306 updated this revision to Diff 413985.
jj10306 marked 2 inline comments as done.
jj10306 edited the summary of this revision.
jj10306 added a comment.
Addressed most of the previous diff's comments (other than the ones from
@wallace and @davidca that were explicitly mentioned in my previous comment).
The naming of the conversion values is currently inconsistent, but I will
update the naming and make it consistent across all the new changes once the
name is agreed upon.
- Add client and server side caching of conversion values
- Update `DecodedThread` to allow for access to the conversion values from the
`TraceCursor`
- Add `GetNanos` (to be renamed) API to `TraceCursor` to allow for conversion
of a trace instruction's TSC value to nanoseconds
- Update `trace schema` and `trace save` commands to accommodate schema changes
- Add `resource_handle` namespace to allow for reuse of file descriptor and
munmap resource handles
- Rename IntelPTManager to IntelPTCollector
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D120595/new/
https://reviews.llvm.org/D120595
Files:
lldb/docs/lldb-gdb-remote.txt
lldb/include/lldb/Target/Trace.h
lldb/include/lldb/Target/TraceCursor.h
lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
lldb/source/Plugins/Process/Linux/IntelPTManager.h
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/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/Target/Trace.cpp
lldb/source/Target/TraceInstructionDumper.cpp
lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
Index: lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
===================================================================
--- lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
+++ lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
+#include "lldb/Utility/TraceGDBRemotePackets.h"
using namespace llvm;
using namespace llvm::json;
@@ -43,4 +44,51 @@
return base;
}
+uint64_t PerfTimestampCounterRate::ToNanos(uint64_t tsc) {
+ // See 'time_zero' section of https://man7.org/linux/man-pages/man2/perf_event_open.2.html
+ uint64_t quot = tsc >> m_time_shift;
+ uint64_t rem_flag = (((uint64_t)1 << m_time_shift) - 1);
+ uint64_t rem = tsc & rem_flag;
+ return m_time_zero + quot * m_time_mult + ((rem * m_time_mult) >> m_time_shift);
+}
+
+bool fromJSON(const llvm::json::Value &value, TimestampCounterRateSP &tsc_rate, llvm::json::Path path) {
+ std::string tsc_rate_kind;
+ ObjectMapper o(value, path);
+
+ if(!o.map("kind", tsc_rate_kind))
+ return false;
+
+ if (tsc_rate_kind == "perf") {
+ int64_t time_mult, time_shift, time_zero;
+ if (!o.map("time_mult", time_mult) || !o.map("time_shift", time_shift) || !o.map("time_zero", time_zero))
+ return false;
+ tsc_rate = std::make_shared<PerfTimestampCounterRate>((uint32_t)time_mult, (uint16_t)time_shift, (uint64_t)time_zero);
+ return true;
+ }
+ path.report("Unsupported TSC rate kind");
+ return false;
+}
+
+bool fromJSON(const json::Value &value, TraceIntelPTGetStateResponse &packet, Path path) {
+ ObjectMapper o(value, path);
+ return o && fromJSON(value, (TraceGetStateResponse &)packet, path) && o.mapOptional("tsc_rate", packet.tsc_rate);
+}
+
+llvm::json::Value PerfTimestampCounterRate::toJSON() {
+ return json::Value(json::Object{
+ {"kind", "perf"},
+ {"time_mult", (int64_t) m_time_mult},
+ {"time_shift", (int64_t) m_time_shift},
+ {"time_zero", (int64_t) m_time_zero},
+ });
+}
+
+json::Value toJSON(const TraceIntelPTGetStateResponse &packet) {
+ json::Value base = toJSON((const TraceGetStateResponse &)packet);
+ if (packet.tsc_rate)
+ base.getAsObject()->try_emplace("tsc_rate", packet.tsc_rate->toJSON());
+
+ return base;
+}
} // namespace lldb_private
Index: lldb/source/Target/TraceInstructionDumper.cpp
===================================================================
--- lldb/source/Target/TraceInstructionDumper.cpp
+++ lldb/source/Target/TraceInstructionDumper.cpp
@@ -185,6 +185,10 @@
if (Optional<uint64_t> timestamp = m_cursor_up->GetTimestampCounter())
s.Printf("0x%016" PRIx64, *timestamp);
+ // TODO: temporary hacky way to see new timestamp functionality
+ // How do we want to incorporate the nanosecon values into the dump output?
+ if (Optional<uint64_t> timestamp_nanos = m_cursor_up->GetNanos())
+ s.Printf(" NANOS 0x%016" PRIx64, *timestamp_nanos);
else
s.Printf("unavailable");
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -18,6 +18,7 @@
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/TraceGDBRemotePackets.h"
using namespace lldb;
using namespace lldb_private;
@@ -187,20 +188,12 @@
m_stop_id = new_stop_id;
m_live_thread_data.clear();
- Expected<std::string> json_string = GetLiveProcessState();
- if (!json_string) {
- DoRefreshLiveProcessState(json_string.takeError());
+ llvm::Optional<TraceGetStateResponse> live_process_state = DoRefreshLiveProcessState(GetLiveProcessState());
+ if (!live_process_state)
return;
- }
- Expected<TraceGetStateResponse> live_process_state =
- json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse");
- if (!live_process_state) {
- DoRefreshLiveProcessState(live_process_state.takeError());
- return;
- }
for (const TraceThreadState &thread_state :
- live_process_state->tracedThreads) {
+ live_process_state->tracedThreads) {
for (const TraceBinaryData &item : thread_state.binaryData)
m_live_thread_data[thread_state.tid][item.kind] = item.size;
}
@@ -208,7 +201,6 @@
for (const TraceBinaryData &item : live_process_state->processBinaryData)
m_live_process_data[item.kind] = item.size;
- DoRefreshLiveProcessState(std::move(live_process_state));
}
uint32_t Trace::GetStopID() {
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionSaver.cpp
@@ -75,5 +75,6 @@
return cpu_info.takeError();
return JSONTraceIntelPTTrace{"intel-pt",
- JSONTraceIntelPTCPUInfo(cpu_info.get())};
+ JSONTraceIntelPTCPUInfo(cpu_info.get()),
+ trace_ipt.GetTimestampCounterRate()};
}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.h
@@ -43,7 +43,8 @@
lldb::TraceSP
CreateTraceIntelPTInstance(const pt_cpu &cpu_info,
- std::vector<ParsedProcess> &parsed_processes);
+ std::vector<ParsedProcess> &parsed_processes,
+ TimestampCounterRateSP tsc_conversion_params_sp);
private:
static pt_cpu ParsePTCPU(const JSONTraceIntelPTCPUInfo &cpu_info);
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTSessionFileParser.cpp
@@ -19,6 +19,10 @@
StringRef TraceIntelPTSessionFileParser::GetSchema() {
static std::string schema;
if (schema.empty()) {
+ // TODO: how to cleanly describe tsc_conversion_params in the schema strings
+ // given its potential different variants and it being optional?
+ // For now just putting perf variant until
+ // feedback is gathered
schema = TraceSessionFileParser::BuildSchema(R"({
"type": "intel-pt",
"cpuInfo": {
@@ -26,6 +30,12 @@
"family": integer,
"model": integer,
"stepping": integer
+ },
+ "tsc_conversion_params"?: {
+ "kind": "perf",
+ "time_mult": integer,
+ "time_shift": integer,
+ "time_zero": integer
}
})");
}
@@ -41,13 +51,14 @@
}
TraceSP TraceIntelPTSessionFileParser::CreateTraceIntelPTInstance(
- const pt_cpu &cpu_info, std::vector<ParsedProcess> &parsed_processes) {
+ const pt_cpu &cpu_info, std::vector<ParsedProcess> &parsed_processes,
+ TimestampCounterRateSP tsc_conversion_params_sp) {
std::vector<ThreadPostMortemTraceSP> threads;
for (const ParsedProcess &parsed_process : parsed_processes)
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(cpu_info, threads, tsc_conversion_params_sp));
for (const ParsedProcess &parsed_process : parsed_processes)
parsed_process.target_sp->SetTrace(trace_instance);
@@ -63,7 +74,7 @@
if (Expected<std::vector<ParsedProcess>> parsed_processes =
ParseCommonSessionFile(session))
return CreateTraceIntelPTInstance(ParsePTCPU(session.trace.cpuInfo),
- *parsed_processes);
+ *parsed_processes, session.trace.tsc_conversion_params);
else
return parsed_processes.takeError();
}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.h
@@ -9,6 +9,7 @@
#ifndef LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
#define LLDB_SOURCE_PLUGINS_TRACE_INTEL_PT_TRACEINTELPTJSONSTRUCTS_H
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
#include "../common/TraceJSONStructs.h"
#include <intel-pt.h>
@@ -34,6 +35,7 @@
struct JSONTraceIntelPTTrace {
std::string type;
JSONTraceIntelPTCPUInfo cpuInfo;
+ TimestampCounterRateSP tsc_conversion_params;
};
struct JSONTraceIntelPTSession {
@@ -43,6 +45,7 @@
struct JSONTraceIntelPTSettings : JSONTracePluginSettings {
JSONTraceIntelPTCPUInfo cpuInfo;
+ TimestampCounterRateSP tsc_conversion_params;
};
} // namespace trace_intel_pt
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPTJSONStructs.cpp
@@ -22,8 +22,9 @@
bool fromJSON(const Value &value, JSONTraceIntelPTSettings &plugin_settings,
Path path) {
ObjectMapper o(value, path);
- return o && o.map("cpuInfo", plugin_settings.cpuInfo) &&
- fromJSON(value, (JSONTracePluginSettings &)plugin_settings, path);
+ return o && o.map("cpuInfo", plugin_settings.cpuInfo)
+ && o.mapOptional("tsc_conversion_params", plugin_settings.tsc_conversion_params)
+ && fromJSON(value, (JSONTracePluginSettings &)plugin_settings, path);
}
bool fromJSON(const json::Value &value, JSONTraceIntelPTCPUInfo &cpu_info,
@@ -45,6 +46,7 @@
llvm::json::Object json_trace;
json_trace["type"] = trace.type;
json_trace["cpuInfo"] = toJSON(trace.cpuInfo);
+ json_trace["tsc_conversion_params"] = trace.tsc_conversion_params->toJSON();
return std::move(json_trace);
}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -12,6 +12,7 @@
#include "IntelPTDecoder.h"
#include "TraceIntelPTSessionFileParser.h"
#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/TraceIntelPTGDBRemotePackets.h"
#include "lldb/lldb-types.h"
#include "llvm/Support/raw_ostream.h"
@@ -74,8 +75,8 @@
llvm::Optional<size_t> GetRawTraceSize(Thread &thread);
- void DoRefreshLiveProcessState(
- llvm::Expected<TraceGetStateResponse> state) override;
+ llvm::Optional<TraceGetStateResponse> DoRefreshLiveProcessState(
+ llvm::Expected<std::string> json_string) override;
bool IsTraced(lldb::tid_t tid) override;
@@ -142,6 +143,9 @@
llvm::Expected<pt_cpu> GetCPUInfo();
+ // TODO: rename and add docs after naming convention is agreed upon
+ TimestampCounterRateSP GetTimestampCounterRate();
+
/// Get the current traced live process.
///
/// \return
@@ -159,7 +163,8 @@
/// files are fixed.
TraceIntelPT(
const pt_cpu &cpu_info,
- const std::vector<lldb::ThreadPostMortemTraceSP> &traced_threads);
+ const std::vector<lldb::ThreadPostMortemTraceSP> &traced_threads,
+ const TimestampCounterRateSP tsc_conversion_params_sp);
/// Constructor for live processes
TraceIntelPT(Process &live_process)
@@ -183,8 +188,13 @@
std::map<lldb::tid_t, std::unique_ptr<ThreadDecoder>> m_thread_decoders;
/// Error gotten after a failed live process update, if any.
llvm::Optional<std::string> m_live_refresh_error;
+ /// \a llvm::None indicates that the program has not yet attempted to fetch the timestamp conversion rate.
+ /// After attempting to fetch, this represents the timestamp counter rate if one exists, otherwise `nullptr`.
+ llvm::Optional<TimestampCounterRateSP> m_tsc_rate;
};
+using TraceIntelPTSP = std::shared_ptr<TraceIntelPT>;
+
} // namespace trace_intel_pt
} // namespace lldb_private
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -17,6 +17,7 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
+#include "lldb/Utility/TraceGDBRemotePackets.h"
#include "llvm/ADT/None.h"
using namespace lldb;
@@ -76,8 +77,9 @@
TraceIntelPT::TraceIntelPT(
const pt_cpu &cpu_info,
- const std::vector<ThreadPostMortemTraceSP> &traced_threads)
- : m_cpu_info(cpu_info) {
+ const std::vector<ThreadPostMortemTraceSP> &traced_threads,
+ const TimestampCounterRateSP tsc_conversion_params_sp)
+ : m_cpu_info(cpu_info), m_tsc_rate(tsc_conversion_params_sp) {
for (const ThreadPostMortemTraceSP &thread : traced_threads)
m_thread_decoders.emplace(
thread->GetID(),
@@ -183,15 +185,28 @@
return *m_cpu_info;
}
+TimestampCounterRateSP TraceIntelPT::GetTimestampCounterRate() {
+ return m_tsc_rate ? *m_tsc_rate : nullptr;
+}
+
Process *TraceIntelPT::GetLiveProcess() { return m_live_process; }
-void TraceIntelPT::DoRefreshLiveProcessState(
- Expected<TraceGetStateResponse> state) {
+llvm::Optional<TraceGetStateResponse> TraceIntelPT::DoRefreshLiveProcessState(
+ Expected<std::string> json_string) {
+
+ if (!json_string) {
+ m_live_refresh_error = toString(json_string.takeError());
+ return llvm::None;
+ }
+
m_thread_decoders.clear();
+ Expected<TraceIntelPTGetStateResponse> state =
+ json::parse<TraceIntelPTGetStateResponse>(*json_string, "TraceIntelPTGetStateResponse");
+
if (!state) {
m_live_refresh_error = toString(state.takeError());
- return;
+ return llvm::None;
}
for (const TraceThreadState &thread_state : state->tracedThreads) {
@@ -200,6 +215,8 @@
m_thread_decoders.emplace(
thread_state.tid, std::make_unique<LiveThreadDecoder>(thread, *this));
}
+ m_tsc_rate = state->tsc_rate;
+ return state.get();
}
bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -30,6 +30,8 @@
llvm::Optional<uint64_t> GetTimestampCounter() override;
+ llvm::Optional<uint64_t> GetNanos() override;
+
lldb::TraceInstructionControlFlowType
GetInstructionControlFlowType() override;
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -7,11 +7,13 @@
//===----------------------------------------------------------------------===//
#include "TraceCursorIntelPT.h"
+
#include "DecodedThread.h"
-#include "TraceIntelPT.h"
+#include <cstdint>
#include <cstdlib>
+
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::trace_intel_pt;
@@ -89,6 +91,14 @@
return m_decoded_thread_sp->GetInstructions()[m_pos].GetTimestampCounter();
}
+Optional<uint64_t> TraceCursorIntelPT::GetNanos() {
+ if (Optional<uint64_t> tsc = GetTimestampCounter()) {
+ if (TimestampCounterRateSP conversion_params_sp = m_decoded_thread_sp->GetTscConversionParams())
+ return conversion_params_sp->ToNanos(*tsc);
+ }
+ return llvm::None;
+}
+
TraceInstructionControlFlowType
TraceCursorIntelPT::GetInstructionControlFlowType() {
lldb::addr_t next_load_address =
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -258,7 +258,7 @@
m_trace_thread->GetTraceFile(), raw_trace_size))
return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
std::move(*instructions),
- raw_trace_size);
+ raw_trace_size, m_trace);
else
return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
instructions.takeError());
@@ -272,7 +272,7 @@
if (Expected<std::vector<IntelPTInstruction>> instructions =
DecodeLiveThread(*m_thread_sp, m_trace, raw_trace_size))
return std::make_shared<DecodedThread>(
- m_thread_sp, std::move(*instructions), raw_trace_size);
+ m_thread_sp, std::move(*instructions), raw_trace_size, m_trace);
else
return std::make_shared<DecodedThread>(m_thread_sp,
instructions.takeError());
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -22,6 +22,9 @@
namespace lldb_private {
namespace trace_intel_pt {
+// Forward declaration for use in DecodedThread class
+class TraceIntelPT;
+
/// Class for representing a libipt decoding error.
class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
public:
@@ -128,7 +131,8 @@
public:
DecodedThread(lldb::ThreadSP thread_sp,
std::vector<IntelPTInstruction> &&instructions,
- size_t raw_trace_size);
+ size_t raw_trace_size,
+ TraceIntelPT &trace);
/// Constructor with a single error signaling a complete failure of the
/// decoding process.
@@ -144,6 +148,9 @@
/// Get a new cursor for the decoded thread.
lldb::TraceCursorUP GetCursor();
+ /// Get the TSC conversion params for the decoded thread.
+ TimestampCounterRateSP GetTscConversionParams() const;
+
/// Get the size in bytes of the corresponding Intel PT raw trace
///
/// \return
@@ -154,6 +161,7 @@
lldb::ThreadSP m_thread_sp;
std::vector<IntelPTInstruction> m_instructions;
size_t m_raw_trace_size;
+ TimestampCounterRateSP m_tsc_conversion_params;
};
using DecodedThreadSP = std::shared_ptr<DecodedThread>;
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -105,9 +105,10 @@
DecodedThread::DecodedThread(ThreadSP thread_sp,
std::vector<IntelPTInstruction> &&instructions,
- size_t raw_trace_size)
+ size_t raw_trace_size,
+ TraceIntelPT &trace)
: m_thread_sp(thread_sp), m_instructions(std::move(instructions)),
- m_raw_trace_size(raw_trace_size) {
+ m_raw_trace_size(raw_trace_size), m_tsc_conversion_params(trace.GetTimestampCounterRate()) {
if (m_instructions.empty())
m_instructions.emplace_back(
createStringError(inconvertibleErrorCode(), "empty trace"));
@@ -116,3 +117,7 @@
lldb::TraceCursorUP DecodedThread::GetCursor() {
return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
}
+
+TimestampCounterRateSP DecodedThread::GetTscConversionParams() const {
+ return m_tsc_conversion_params;
+}
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -241,7 +241,7 @@
Status PopulateMemoryRegionCache();
/// Manages Intel PT process and thread traces.
- IntelPTManager m_intel_pt_manager;
+ IntelPTCollector m_intel_pt_collector;
// Handle a clone()-like event.
bool MonitorClone(NativeThreadLinux &parent, lldb::pid_t child_pid,
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -312,7 +312,7 @@
const ArchSpec &arch, MainLoop &mainloop,
llvm::ArrayRef<::pid_t> tids)
: NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch),
- m_main_loop(mainloop), m_intel_pt_manager(pid) {
+ m_main_loop(mainloop), m_intel_pt_collector(pid) {
if (m_terminal_fd != -1) {
Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
assert(status.Success());
@@ -983,7 +983,7 @@
e; // Save the error, but still attempt to detach from other threads.
}
- m_intel_pt_manager.Clear();
+ m_intel_pt_collector.Clear();
return error;
}
@@ -1666,7 +1666,7 @@
Status NativeProcessLinux::NotifyTracersOfNewThread(lldb::tid_t tid) {
Log *log = GetLog(POSIXLog::Thread);
- Status error(m_intel_pt_manager.OnThreadCreated(tid));
+ Status error(m_intel_pt_collector.OnThreadCreated(tid));
if (error.Fail())
LLDB_LOG(log, "Failed to trace a new thread with intel-pt, tid = {0}. {1}",
tid, error.AsCString());
@@ -1675,7 +1675,7 @@
Status NativeProcessLinux::NotifyTracersOfThreadDestroyed(lldb::tid_t tid) {
Log *log = GetLog(POSIXLog::Thread);
- Status error(m_intel_pt_manager.OnThreadDestroyed(tid));
+ Status error(m_intel_pt_collector.OnThreadDestroyed(tid));
if (error.Fail())
LLDB_LOG(log,
"Failed to stop a destroyed thread with intel-pt, tid = {0}. {1}",
@@ -1938,7 +1938,7 @@
}
llvm::Expected<TraceSupportedResponse> NativeProcessLinux::TraceSupported() {
- if (IntelPTManager::IsSupported())
+ if (IntelPTCollector::IsSupported())
return TraceSupportedResponse{"intel-pt", "Intel Processor Trace"};
return NativeProcessProtocol::TraceSupported();
}
@@ -1951,7 +1951,7 @@
std::vector<lldb::tid_t> process_threads;
for (auto &thread : m_threads)
process_threads.push_back(thread->GetID());
- return m_intel_pt_manager.TraceStart(*request, process_threads);
+ return m_intel_pt_collector.TraceStart(*request, process_threads);
} else
return request.takeError();
}
@@ -1961,19 +1961,19 @@
Error NativeProcessLinux::TraceStop(const TraceStopRequest &request) {
if (request.type == "intel-pt")
- return m_intel_pt_manager.TraceStop(request);
+ return m_intel_pt_collector.TraceStop(request);
return NativeProcessProtocol::TraceStop(request);
}
Expected<json::Value> NativeProcessLinux::TraceGetState(StringRef type) {
if (type == "intel-pt")
- return m_intel_pt_manager.GetState();
+ return m_intel_pt_collector.GetState();
return NativeProcessProtocol::TraceGetState(type);
}
Expected<std::vector<uint8_t>> NativeProcessLinux::TraceGetBinaryData(
const TraceGetBinaryDataRequest &request) {
if (request.type == "intel-pt")
- return m_intel_pt_manager.GetBinaryData(request);
+ return m_intel_pt_collector.GetBinaryData(request);
return NativeProcessProtocol::TraceGetBinaryData(request);
}
Index: lldb/source/Plugins/Process/Linux/IntelPTManager.h
===================================================================
--- lldb/source/Plugins/Process/Linux/IntelPTManager.h
+++ lldb/source/Plugins/Process/Linux/IntelPTManager.h
@@ -23,6 +23,36 @@
namespace process_linux {
+namespace resource_handle {
+
+class munmap_delete {
+ size_t m_length;
+
+public:
+ munmap_delete(size_t length) : m_length(length) {}
+ void operator()(void *ptr) {
+ if (m_length)
+ munmap(ptr, m_length);
+ }
+};
+
+class file_close {
+
+public:
+ file_close() = default;
+ void operator()(int *ptr) {
+ if (ptr == nullptr)
+ return;
+ if (*ptr == -1)
+ return;
+ close(*ptr);
+ std::default_delete<int>()(ptr);
+}
+};
+
+} // namespace resource_handle
+
+
/// This class keeps track of one tracing instance of
/// Intel(R) Processor Trace on Linux OS at thread level.
///
@@ -32,34 +62,9 @@
class IntelPTThreadTrace {
- class munmap_delete {
- size_t m_length;
-
- public:
- munmap_delete(size_t length) : m_length(length) {}
- void operator()(void *ptr) {
- if (m_length)
- munmap(ptr, m_length);
- }
- };
-
- class file_close {
-
- public:
- file_close() = default;
- void operator()(int *ptr) {
- if (ptr == nullptr)
- return;
- if (*ptr == -1)
- return;
- close(*ptr);
- std::default_delete<int>()(ptr);
- }
- };
-
- std::unique_ptr<perf_event_mmap_page, munmap_delete> m_mmap_meta;
- std::unique_ptr<uint8_t, munmap_delete> m_mmap_aux;
- std::unique_ptr<int, file_close> m_fd;
+ std::unique_ptr<perf_event_mmap_page, resource_handle::munmap_delete> m_mmap_meta;
+ std::unique_ptr<uint8_t, resource_handle::munmap_delete> m_mmap_aux;
+ std::unique_ptr<int, resource_handle::file_close> m_fd;
lldb::tid_t m_tid;
/// Start tracing a thread
@@ -88,8 +93,8 @@
llvm::MutableArrayRef<uint8_t> GetDataBuffer() const;
IntelPTThreadTrace()
- : m_mmap_meta(nullptr, munmap_delete(0)),
- m_mmap_aux(nullptr, munmap_delete(0)), m_fd(nullptr, file_close()) {}
+ : m_mmap_meta(nullptr, resource_handle::munmap_delete(0)),
+ m_mmap_aux(nullptr, resource_handle::munmap_delete(0)), m_fd(nullptr, resource_handle::file_close()) {}
public:
/// Get the content of /proc/cpuinfo that can be later used to decode traces.
@@ -203,11 +208,20 @@
};
/// Main class that manages intel-pt process and thread tracing.
-class IntelPTManager {
+class IntelPTCollector {
public:
- IntelPTManager(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {}
+ IntelPTCollector(lldb::pid_t pid) : m_pid(pid), m_thread_traces(pid) {}
static bool IsSupported();
+ /// Start tracing a thread
+ ///
+ /// \param[in] pid
+ /// The process id of the thread being traced.
+ ///
+ /// \return
+ /// \a The timestamp counter rate if one exists, \a nullptr if no conversion exists.
+ static TimestampCounterRateSP GetTscRate(lldb::pid_t pid);
+
/// If "process tracing" is enabled, then trace the given thread.
llvm::Error OnThreadCreated(lldb::tid_t tid);
@@ -235,6 +249,9 @@
/// Dispose of all traces
void Clear();
+ /// Server-side cache of timestamp counter rate
+ static llvm::Optional<TimestampCounterRateSP> g_tsc_rate;
+
private:
llvm::Error TraceStop(lldb::tid_t tid);
Index: lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
+++ lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
@@ -8,8 +8,10 @@
#include <algorithm>
#include <fstream>
+#include <linux/perf_event.h>
#include <sstream>
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MathExtras.h"
@@ -18,8 +20,10 @@
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
#include "lldb/Host/linux/Support.h"
#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-types.h"
#include <sys/ioctl.h>
+#include <sys/mman.h>
#include <sys/syscall.h>
using namespace lldb;
@@ -42,6 +46,8 @@
const char *kPSBPeriodBitOffsetFile =
"/sys/bus/event_source/devices/intel_pt/format/psb_period";
+llvm::Optional<TimestampCounterRateSP> IntelPTCollector::g_tsc_rate = llvm::None;
+
enum IntelPTConfigFileType {
Hex = 0,
// 0 or 1
@@ -236,7 +242,7 @@
"perf event syscall failed");
}
- m_fd = std::unique_ptr<int, file_close>(new int(fd), file_close());
+ m_fd = std::unique_ptr<int, resource_handle::file_close>(new int(fd), resource_handle::file_close());
errno = 0;
auto base =
@@ -248,9 +254,9 @@
"Meta buffer allocation failed");
}
- m_mmap_meta = std::unique_ptr<perf_event_mmap_page, munmap_delete>(
+ m_mmap_meta = std::unique_ptr<perf_event_mmap_page, resource_handle::munmap_delete>(
reinterpret_cast<perf_event_mmap_page *>(base),
- munmap_delete(buffer_size + page_size));
+ resource_handle::munmap_delete(buffer_size + page_size));
m_mmap_meta->aux_offset = m_mmap_meta->data_offset + m_mmap_meta->data_size;
m_mmap_meta->aux_size = buffer_size;
@@ -264,8 +270,8 @@
return createStringError(inconvertibleErrorCode(),
"Trace buffer allocation failed");
}
- m_mmap_aux = std::unique_ptr<uint8_t, munmap_delete>(
- reinterpret_cast<uint8_t *>(mmap_aux), munmap_delete(buffer_size));
+ m_mmap_aux = std::unique_ptr<uint8_t, resource_handle::munmap_delete>(
+ reinterpret_cast<uint8_t *>(mmap_aux), resource_handle::munmap_delete(buffer_size));
return Error::success();
#endif
}
@@ -564,15 +570,15 @@
return m_thread_traces;
}
-/// IntelPTManager
+/// IntelPTCollector
-Error IntelPTManager::TraceStop(lldb::tid_t tid) {
+Error IntelPTCollector::TraceStop(lldb::tid_t tid) {
if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
return m_process_trace->TraceStop(tid);
return m_thread_traces.TraceStop(tid);
}
-Error IntelPTManager::TraceStop(const TraceStopRequest &request) {
+Error IntelPTCollector::TraceStop(const TraceStopRequest &request) {
if (request.IsProcessTracing()) {
Clear();
return Error::success();
@@ -585,7 +591,7 @@
}
}
-Error IntelPTManager::TraceStart(
+Error IntelPTCollector::TraceStart(
const TraceIntelPTStartRequest &request,
const std::vector<lldb::tid_t> &process_threads) {
if (request.IsProcessTracing()) {
@@ -609,13 +615,13 @@
}
}
-Error IntelPTManager::OnThreadCreated(lldb::tid_t tid) {
+Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) {
if (!IsProcessTracingEnabled())
return Error::success();
return m_process_trace->TraceStart(tid);
}
-Error IntelPTManager::OnThreadDestroyed(lldb::tid_t tid) {
+Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
return m_process_trace->TraceStop(tid);
else if (m_thread_traces.TracesThread(tid))
@@ -623,12 +629,12 @@
return Error::success();
}
-Expected<json::Value> IntelPTManager::GetState() const {
+Expected<json::Value> IntelPTCollector::GetState() const {
Expected<ArrayRef<uint8_t>> cpu_info = IntelPTThreadTrace::GetCPUInfo();
if (!cpu_info)
return cpu_info.takeError();
- TraceGetStateResponse state;
+ TraceIntelPTGetStateResponse state;
state.processBinaryData.push_back(
{"cpuInfo", static_cast<int64_t>(cpu_info->size())});
@@ -642,18 +648,20 @@
state.tracedThreads.insert(state.tracedThreads.end(), thread_states.begin(),
thread_states.end());
}
+
+ state.tsc_rate = IntelPTCollector::GetTscRate(m_pid);
return toJSON(state);
}
Expected<const IntelPTThreadTrace &>
-IntelPTManager::GetTracedThread(lldb::tid_t tid) const {
+IntelPTCollector::GetTracedThread(lldb::tid_t tid) const {
if (IsProcessTracingEnabled() && m_process_trace->TracesThread(tid))
return m_process_trace->GetThreadTraces().GetTracedThread(tid);
return m_thread_traces.GetTracedThread(tid);
}
Expected<std::vector<uint8_t>>
-IntelPTManager::GetBinaryData(const TraceGetBinaryDataRequest &request) const {
+IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) const {
if (request.kind == "threadTraceBuffer") {
if (Expected<const IntelPTThreadTrace &> trace =
GetTracedThread(*request.tid))
@@ -668,9 +676,9 @@
request.kind.c_str());
}
-void IntelPTManager::ClearProcessTracing() { m_process_trace = None; }
+void IntelPTCollector::ClearProcessTracing() { m_process_trace = None; }
-bool IntelPTManager::IsSupported() {
+bool IntelPTCollector::IsSupported() {
Expected<uint32_t> intel_pt_type = GetOSEventType();
if (!intel_pt_type) {
llvm::consumeError(intel_pt_type.takeError());
@@ -679,11 +687,70 @@
return true;
}
-bool IntelPTManager::IsProcessTracingEnabled() const {
+bool IntelPTCollector::IsProcessTracingEnabled() const {
return (bool)m_process_trace;
}
-void IntelPTManager::Clear() {
+void IntelPTCollector::Clear() {
ClearProcessTracing();
m_thread_traces.Clear();
}
+
+llvm::Expected<TimestampCounterRateSP> PerfGetTscRate(lldb::pid_t pid) {
+ Log *log = GetLog(POSIXLog::Ptrace);
+ perf_event_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ // Set the minimum attributes necessary to access the mmap metadata page.
+ attr.size = sizeof(attr);
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.config = PERF_COUNT_SW_DUMMY;
+ attr.sample_type = PERF_SAMPLE_TIME;
+
+ errno = 0;
+ auto fd =
+ syscall(SYS_perf_event_open, &attr, pid, -1, -1, 0);
+ if (fd == -1) {
+ LLDB_LOG(log, "syscall error {0}", errno);
+ return createStringError(inconvertibleErrorCode(), "perf event syscall failed");
+ }
+ auto fd_handle = std::unique_ptr<int, resource_handle::file_close>(new int(fd), resource_handle::file_close());
+
+ uint64_t page_size = getpagesize();
+ errno = 0;
+ auto base =
+ mmap(nullptr, page_size, PROT_READ, MAP_SHARED, fd, 0);
+
+ if (base == MAP_FAILED) {
+ // TODO: should logging be done in this function or should the error just be returned here and let
+ // the caller log/consume the error?
+ LLDB_LOG(log, "mmap base error {0}", errno);
+ return createStringError(inconvertibleErrorCode(), "perf mmap meta allocation failed");
+ }
+ auto perf_mmap_meta_handle =
+ std::unique_ptr<perf_event_mmap_page, resource_handle::munmap_delete>(
+ reinterpret_cast<perf_event_mmap_page *>(base),
+ resource_handle::munmap_delete(page_size));
+
+ if (perf_mmap_meta_handle->cap_user_time && perf_mmap_meta_handle->cap_user_time_zero) {
+ return std::make_shared<PerfTimestampCounterRate>(perf_mmap_meta_handle->time_mult, perf_mmap_meta_handle->time_shift, perf_mmap_meta_handle->time_zero);
+ } else {
+ auto err_cap = !perf_mmap_meta_handle->cap_user_time ? "cap_user_time" : "cap_user_time_zero";
+ LLDB_LOG(log, "{0} not supported. TSC cannot be converted to time unit", err_cap);
+ return createStringError(inconvertibleErrorCode(), "TSC cannot be converted to time unit due to unsupported capabilities");
+ }
+}
+
+TimestampCounterRateSP IntelPTCollector::GetTscRate(lldb::pid_t pid) {
+#ifndef PERF_ATTR_SIZE_VER5
+ llvm_unreachable("Intel PT Linux perf event not supported");
+#else
+ if (!g_tsc_rate) {
+ g_tsc_rate = nullptr;
+ // Try to get a TSC rate from each different source until one succeeds
+ // Add additional sources of TSC rate, as necessary
+ if (llvm::Expected<TimestampCounterRateSP> perf_tsc_rate = PerfGetTscRate(pid))
+ g_tsc_rate = *perf_tsc_rate;
+ }
+ return *g_tsc_rate;
+#endif
+}
Index: lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
===================================================================
--- lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
+++ lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
@@ -38,6 +38,50 @@
llvm::json::Path path);
llvm::json::Value toJSON(const TraceIntelPTStartRequest &packet);
+
+/// Class that can be extended to represent different CPU Timestamp Counter (TSC) conversion
+/// rates, which can be used to convert TSCs to common units of time, such as nanoseconds. More
+/// on TSCs can be found here https://en.wikipedia.org/wiki/Time_Stamp_Counter.
+// TODO: update after naming convention is agreed upon
+class TimestampCounterRate {
+ public:
+ virtual ~TimestampCounterRate() = default;
+ /// Convert a TSC value to nanoseconds. This represents the number of nanoseconds since
+ /// the TSC's reset.
+ virtual uint64_t ToNanos(uint64_t tsc) = 0;
+ virtual llvm::json::Value toJSON() = 0;
+};
+
+using TimestampCounterRateSP = std::shared_ptr<TimestampCounterRate> ;
+
+/// TSC to nanoseconds conversion values defined by the Linux perf_event API when the
+/// capibilities cap_user_time and cap_user_time_zero are set. See the documentation of
+/// `time_zero` in https://man7.org/linux/man-pages/man2/perf_event_open.2.html for more information.
+class PerfTimestampCounterRate : public TimestampCounterRate {
+ public:
+ PerfTimestampCounterRate(uint32_t time_mult, uint16_t time_shift, uint64_t time_zero) :
+ m_time_mult(time_mult), m_time_shift(time_shift), m_time_zero(time_zero) {}
+ uint64_t ToNanos(uint64_t tsc) override;
+ llvm::json::Value toJSON() override;
+
+ private:
+ uint32_t m_time_mult;
+ uint16_t m_time_shift;
+ uint64_t m_time_zero;
+};
+
+/// jLLDBTraceGetState gdb-remote packet
+struct TraceIntelPTGetStateResponse : TraceGetStateResponse {
+ /// Timestamp counter rate if one exists, otherwise `nullptr`.
+ TimestampCounterRateSP tsc_rate;
+};
+
+bool fromJSON(const llvm::json::Value &value, TimestampCounterRateSP &tsc_converter, llvm::json::Path path);
+
+bool fromJSON(const llvm::json::Value &value, TraceIntelPTGetStateResponse &packet, llvm::json::Path path);
+
+llvm::json::Value toJSON(const TraceIntelPTGetStateResponse &packet);
+
/// \}
} // namespace lldb_private
Index: lldb/include/lldb/Target/TraceCursor.h
===================================================================
--- lldb/include/lldb/Target/TraceCursor.h
+++ lldb/include/lldb/Target/TraceCursor.h
@@ -189,6 +189,9 @@
/// The timestamp or \b llvm::None if not available.
virtual llvm::Optional<uint64_t> GetTimestampCounter() = 0;
+ // TODO: add docs and rename once naming convention is agreed upon
+ virtual llvm::Optional<uint64_t> GetNanos() = 0;
+
/// \return
/// The \a lldb::TraceInstructionControlFlowType categories the
/// instruction the cursor is pointing at falls into. If the cursor points
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -302,10 +302,14 @@
///
/// This is invoked by RefreshLiveProcessState when a new state is found.
///
- /// \param[in] state
- /// The jLLDBTraceGetState response.
- virtual void
- DoRefreshLiveProcessState(llvm::Expected<TraceGetStateResponse> state) = 0;
+ /// \param[in] json_string
+ /// JSON string representing the jLLDBTraceGetState response,
+ /// it may be invalid.
+ ///
+ /// \return
+ /// The packet response, None if response parsing failed.
+ virtual llvm::Optional<TraceGetStateResponse>
+ DoRefreshLiveProcessState(llvm::Expected<std::string> json_string) = 0;
/// Method to be invoked by the plug-in to refresh the live process state.
///
Index: lldb/docs/lldb-gdb-remote.txt
===================================================================
--- lldb/docs/lldb-gdb-remote.txt
+++ lldb/docs/lldb-gdb-remote.txt
@@ -451,7 +451,16 @@
// "size": <decimal integer>,
// Size in bytes of this thread data.
// },
-// }]
+// }],
+// "tsc_conversion"?: Optional<{
+// "kind": <string>,
+// Timestamp counter conversion rate kind, e.g. perf.
+// The kind strings can be found in the overriden toJSON method
+// of each TimestampCounterRate subclass. The kind determines
+// what fields are available in the remainder of the response.
+//
+// ... additional parameters specific to the provided tracing type and tsc_conversion kind
+// }>
// }
//
// NOTES
@@ -463,6 +472,33 @@
// Binary data kinds:
// - threadTraceBuffer: trace buffer for a thread.
// - cpuInfo: contents of the /proc/cpuinfo file.
+//
+// Additional params in the tsc_conversion output schema when kind == "perf":
+// {
+// "time_mult": <decimal integer>,
+// time_mult field of the Linux perf_event_mmap_page struct available when
+// cap_usr_time is set. Used in TSC to nanosecond conversion calculation defined
+// by the Linux perf_event API.
+//
+// "time_shift": <decimal integer>,
+// time_shift field of the Linux perf_event_mmap_page struct available when
+// cap_usr_time is set. Used in TSC to nanosecond conversion calculation defined
+// by the Linux perf_event API.
+//
+// "time_zero": <decimal integer>,
+// time_zero field of the Linux perf_event_mmap_page struct available when
+// cap_usr_time_zero is set. Used in TSC to nanosecond conversion calculation defined
+// by the Linux perf_event API.
+// }
+//
+// Notes:
+// - See https://en.wikipedia.org/wiki/Time_Stamp_Counter for information on the timestamp counter (TSC).
+// - "tsc_conversion" optionally represents the information for converting the timestamp counter to the
+// number of nanoseconds since its reset.
+// - "time_mult", "time_shift" and "time_zero" are the three values needed to convert TSC to nanoseconds defined by
+// the Linux perf_event API when cap_user_time and cap_user_time_zero are set. See the documentation of time_zero in
+// https://man7.org/linux/man-pages/man2/perf_event_open.2.html for the conversion calculation.
+//
//----------------------------------------------------------------------
send packet: jLLDBTraceGetState:{"type":<type>}]
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits