[Lldb-commits] [lldb] 0419584 - [intel pt] Add TSC timestamps
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Tue Jul 20 16:37:45 PDT 2021
Author: Walter Erquinigo
Date: 2021-07-20T16:29:17-07:00
New Revision: 04195843ef91d012dbaa1611b348447f8136c271
URL: https://github.com/llvm/llvm-project/commit/04195843ef91d012dbaa1611b348447f8136c271
DIFF: https://github.com/llvm/llvm-project/commit/04195843ef91d012dbaa1611b348447f8136c271.diff
LOG: [intel pt] Add TSC timestamps
Differential Revision: https://reviews.llvm.org/D106328
Added:
lldb/test/API/commands/trace/TestTraceTimestampCounters.py
Modified:
lldb/docs/lldb-gdb-remote.txt
lldb/include/lldb/Target/TraceCursor.h
lldb/include/lldb/Target/TraceInstructionDumper.h
lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
lldb/source/Commands/CommandObjectThread.cpp
lldb/source/Commands/Options.td
lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
lldb/source/Plugins/Process/Linux/IntelPTManager.h
lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.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/TraceIntelPTConstants.h
lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
lldb/source/Target/TraceInstructionDumper.cpp
lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
Removed:
################################################################################
diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt
index d4ec112f3936c..3eb3dc51c0280 100644
--- a/lldb/docs/lldb-gdb-remote.txt
+++ b/lldb/docs/lldb-gdb-remote.txt
@@ -309,6 +309,38 @@ read packet: {"name":<name>, "description":<description>}/E<error code>;AAAAAAAA
// Trace buffer size per thread in bytes. It must be a power of 2
// greater than or equal to 4096 (2^12) bytes.
//
+// "enableTsc": <boolean>,
+// Whether to enable TSC timestamps or not. This is supported on
+// all devices that support intel-pt. A TSC timestamp is generated along
+// with PSB (synchronization) packets, whose frequency can be configured
+// with the "psbPeriod" parameter.
+//
+// "psbPeriod"?: <Optional decimal integer>,
+// This value defines the period in which PSB packets will be generated.
+// A PSB packet is a synchronization packet that contains a TSC
+// timestamp and the current absolute instruction pointer.
+//
+// This parameter can only be used if
+//
+// /sys/bus/event_source/devices/intel_pt/caps/psb_cyc
+//
+// is 1. Otherwise, the PSB period will be defined by the processor.
+//
+// If supported, valid values for this period can be found in
+/
+// /sys/bus/event_source/devices/intel_pt/caps/psb_periods
+//
+// which contains a hexadecimal number, whose bits represent valid
+// values e.g. if bit 2 is set, then value 2 is valid.
+//
+// The psb_period value is converted to the approximate number of
+// raw trace bytes between PSB packets as:
+//
+// 2 ^ (value + 11)
+//
+// e.g. value 3 means 16KiB between PSB packets. Defaults to
+// 0 if supported.
+//
// /* process tracing only */
// "processBufferSizeLimit": <decimal integer>,
// Maximum total buffer size per process in bytes.
@@ -871,7 +903,7 @@ osminor: optional, specifies the minor version number of the OS (e.g. for macOS
ospatch: optional, specifies the patch level number of the OS (e.g. for macOS 10.12.2, it would be 2)
vm-page-size: optional, specifies the target system VM page size, base 10.
Needed for the "dirty-pages:" list in the qMemoryRegionInfo
- packet, where a list of dirty pages is sent from the remote
+ packet, where a list of dirty pages is sent from the remote
stub. This page size tells lldb how large each dirty page is.
addressing_bits: optional, specifies how many bits in addresses are
significant for addressing, base 10. If bits 38..0
@@ -1185,8 +1217,8 @@ tuples to return are:
//
// If the stub supports identifying dirty pages within a
// memory region, this key should always be present for all
- // qMemoryRegionInfo replies. This key with no pages
- // listed ("dirty-pages:;") indicates no dirty pages in
+ // qMemoryRegionInfo replies. This key with no pages
+ // listed ("dirty-pages:;") indicates no dirty pages in
// this memory region. The *absence* of this key means
// that this stub cannot determine dirty pages.
diff --git a/lldb/include/lldb/Target/TraceCursor.h b/lldb/include/lldb/Target/TraceCursor.h
index e15ced82a470f..14fc00d5f95b1 100644
--- a/lldb/include/lldb/Target/TraceCursor.h
+++ b/lldb/include/lldb/Target/TraceCursor.h
@@ -180,6 +180,15 @@ class TraceCursor {
/// LLDB_INVALID_ADDRESS.
virtual lldb::addr_t GetLoadAddress() = 0;
+ /// Get the timestamp counter associated with the current instruction.
+ /// Modern Intel, ARM and AMD processors support this counter. However, a
+ /// trace plugin might decide to use a
diff erent time unit instead of an
+ /// actual TSC.
+ ///
+ /// \return
+ /// The timestamp or \b llvm::None if not available.
+ virtual llvm::Optional<uint64_t> GetTimestampCounter() = 0;
+
/// \return
/// The \a lldb::TraceInstructionControlFlowType categories the
/// instruction the cursor is pointing at falls into. If the cursor points
diff --git a/lldb/include/lldb/Target/TraceInstructionDumper.h b/lldb/include/lldb/Target/TraceInstructionDumper.h
index 388e5063d1725..c4878bfd3fd59 100644
--- a/lldb/include/lldb/Target/TraceInstructionDumper.h
+++ b/lldb/include/lldb/Target/TraceInstructionDumper.h
@@ -30,8 +30,12 @@ class TraceInstructionDumper {
/// \param[in] raw
/// Dump only instruction addresses without disassembly nor symbol
/// information.
+ ///
+ /// \param[in] show_tsc
+ /// For each instruction, print the corresponding timestamp counter if
+ /// available.
TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up, int initial_index = 0,
- bool raw = false);
+ bool raw = false, bool show_tsc = false);
/// Dump \a count instructions of the thread trace starting at the current
/// cursor position.
@@ -63,6 +67,7 @@ class TraceInstructionDumper {
lldb::TraceCursorUP m_cursor_up;
int m_index;
bool m_raw;
+ bool m_show_tsc;
/// If \b true, all the instructions have been traversed.
bool m_no_more_data = false;
};
diff --git a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
index 95f4eeb27d3fa..8f4947b1f189c 100644
--- a/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/TraceIntelPTGDBRemotePackets.h
@@ -19,6 +19,13 @@ namespace lldb_private {
struct TraceIntelPTStartRequest : TraceStartRequest {
/// Size in bytes to use for each thread's trace buffer.
int64_t threadBufferSize;
+
+ /// Whether to enable TSC
+ bool enableTsc;
+
+ /// PSB packet period
+ llvm::Optional<int64_t> psbPeriod;
+
/// Required when doing "process tracing".
///
/// Limit in bytes on all the thread traces started by this "process trace"
diff --git a/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
index 2b7dfb16d34ab..a2f63bc88bd10 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
@@ -45,22 +45,30 @@ def assertSBError(self, sberror, error=False):
else:
self.assertSuccess(sberror)
- def createConfiguration(self, threadBufferSize=None, processBufferSizeLimit=None):
+ def createConfiguration(self, threadBufferSize=None,
+ processBufferSizeLimit=None, enableTsc=False,
+ psbPeriod=None):
obj = {}
if processBufferSizeLimit is not None:
obj["processBufferSizeLimit"] = processBufferSizeLimit
if threadBufferSize is not None:
- obj["threadBufferSize"] = threadBufferSize
+ obj["threadBufferSize"] = threadBufferSize
+ if psbPeriod is not None:
+ obj["psbPeriod"] = psbPeriod
+ obj["enableTsc"] = enableTsc
configuration = lldb.SBStructuredData()
configuration.SetFromJSON(json.dumps(obj))
return configuration
- def traceStartThread(self, thread=None, error=False, substrs=None, threadBufferSize=None):
+ def traceStartThread(self, thread=None, error=False, substrs=None,
+ threadBufferSize=None, enableTsc=False, psbPeriod=None):
if self.USE_SB_API:
trace = self.getTraceOrCreate()
thread = thread if thread is not None else self.thread()
- configuration = self.createConfiguration(threadBufferSize=threadBufferSize)
+ configuration = self.createConfiguration(
+ threadBufferSize=threadBufferSize, enableTsc=enableTsc,
+ psbPeriod=psbPeriod)
self.assertSBError(trace.Start(thread, configuration), error)
else:
command = "thread trace start"
@@ -68,17 +76,28 @@ def traceStartThread(self, thread=None, error=False, substrs=None, threadBufferS
command += " " + str(thread.GetIndexID())
if threadBufferSize is not None:
command += " -s " + str(threadBufferSize)
+ if enableTsc:
+ command += " --tsc"
+ if psbPeriod is not None:
+ command += " --psb-period " + str(psbPeriod)
self.expect(command, error=error, substrs=substrs)
- def traceStartProcess(self, processBufferSizeLimit=None, error=False, substrs=None):
+ def traceStartProcess(self, processBufferSizeLimit=None, error=False,
+ substrs=None, enableTsc=False, psbPeriod=None):
if self.USE_SB_API:
trace = self.getTraceOrCreate()
- configuration = self.createConfiguration(processBufferSizeLimit=processBufferSizeLimit)
+ configuration = self.createConfiguration(
+ processBufferSizeLimit=processBufferSizeLimit, enableTsc=enableTsc,
+ psbPeriod=psbPeriod)
self.assertSBError(trace.Start(configuration), error=error)
else:
command = "process trace start"
if processBufferSizeLimit != None:
command += " -l " + str(processBufferSizeLimit)
+ if enableTsc:
+ command += " --tsc"
+ if psbPeriod is not None:
+ command += " --psb-period " + str(psbPeriod)
self.expect(command, error=error, substrs=substrs)
def traceStopProcess(self):
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index e4cf8a411b22b..2f8772953af40 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -2024,6 +2024,10 @@ class CommandObjectTraceDumpInstructions
m_forwards = true;
break;
}
+ case 't': {
+ m_show_tsc = true;
+ break;
+ }
default:
llvm_unreachable("Unimplemented option");
}
@@ -2035,6 +2039,7 @@ class CommandObjectTraceDumpInstructions
m_skip = 0;
m_raw = false;
m_forwards = false;
+ m_show_tsc = false;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -2048,6 +2053,7 @@ class CommandObjectTraceDumpInstructions
size_t m_skip;
bool m_raw;
bool m_forwards;
+ bool m_show_tsc;
};
CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
@@ -2109,7 +2115,8 @@ class CommandObjectTraceDumpInstructions
int initial_index = setUpCursor();
auto dumper = std::make_unique<TraceInstructionDumper>(
- std::move(cursor_up), initial_index, m_options.m_raw);
+ std::move(cursor_up), initial_index, m_options.m_raw,
+ m_options.m_show_tsc);
// This happens when the seek value was more than the number of available
// instructions.
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 36b5a82a8831b..d0bc80c74d55d 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1065,6 +1065,9 @@ let Command = "thread trace dump instructions" in {
def thread_trace_dump_instructions_raw : Option<"raw", "r">,
Group<1>,
Desc<"Dump only instruction address without disassembly nor symbol information.">;
+ def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">,
+ Group<1>,
+ Desc<"For each instruction, print the corresponding timestamp counter if available.">;
}
let Command = "type summary add" in {
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTManager.cpp b/lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
index f3680a190c411..0bd48933d4d3e 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
+++ b/lldb/source/Plugins/Process/Linux/IntelPTManager.cpp
@@ -8,6 +8,7 @@
#include <algorithm>
#include <fstream>
+#include <sstream>
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
@@ -29,32 +30,151 @@ using namespace llvm;
const char *kOSEventIntelPTTypeFile =
"/sys/bus/event_source/devices/intel_pt/type";
-/// Return the Linux perf event type for Intel PT.
-static Expected<uint32_t> GetOSEventType() {
- auto intel_pt_type_text =
- llvm::MemoryBuffer::getFileAsStream(kOSEventIntelPTTypeFile);
+const char *kPSBPeriodCapFile =
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc";
+
+const char *kPSBPeriodValidValuesFile =
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_periods";
+
+const char *kTSCBitOffsetFile =
+ "/sys/bus/event_source/devices/intel_pt/format/tsc";
+
+const char *kPSBPeriodBitOffsetFile =
+ "/sys/bus/event_source/devices/intel_pt/format/psb_period";
- if (!intel_pt_type_text)
+enum IntelPTConfigFileType {
+ Hex = 0,
+ // 0 or 1
+ ZeroOne,
+ Decimal,
+ // a bit index file always starts with the prefix config: following by an int,
+ // which represents the offset of the perf_event_attr.config value where to
+ // store a given configuration.
+ BitOffset
+};
+
+static Expected<uint32_t> ReadIntelPTConfigFile(const char *file,
+ IntelPTConfigFileType type) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> stream =
+ MemoryBuffer::getFileAsStream(file);
+
+ if (!stream)
return createStringError(inconvertibleErrorCode(),
- "Can't open the file '%s'",
- kOSEventIntelPTTypeFile);
+ "Can't open the file '%s'", file);
+
+ uint32_t value = 0;
+ StringRef text_buffer = stream.get()->getBuffer();
+
+ if (type == BitOffset) {
+ const char *prefix = "config:";
+ if (!text_buffer.startswith(prefix))
+ return createStringError(inconvertibleErrorCode(),
+ "The file '%s' contents doesn't start with '%s'",
+ file, prefix);
+ text_buffer = text_buffer.substr(strlen(prefix));
+ }
+
+ auto getRadix = [&]() {
+ switch (type) {
+ case Hex:
+ return 16;
+ case ZeroOne:
+ case Decimal:
+ case BitOffset:
+ return 10;
+ }
+ };
- uint32_t intel_pt_type = 0;
- StringRef buffer = intel_pt_type_text.get()->getBuffer();
- if (buffer.trim().getAsInteger(10, intel_pt_type))
+ auto createError = [&](const char *expected_value_message) {
return createStringError(
inconvertibleErrorCode(),
- "The file '%s' has a invalid value. It should be an unsigned int.",
- kOSEventIntelPTTypeFile);
- return intel_pt_type;
+ "The file '%s' has an invalid value. It should be %s.", file,
+ expected_value_message);
+ };
+
+ if (text_buffer.trim().consumeInteger(getRadix(), value) ||
+ (type == ZeroOne && value != 0 && value != 1)) {
+ switch (type) {
+ case Hex:
+ return createError("an unsigned hexadecimal int");
+ case ZeroOne:
+ return createError("0 or 1");
+ case Decimal:
+ case BitOffset:
+ return createError("an unsigned decimal int");
+ }
+ }
+ return value;
+}
+/// Return the Linux perf event type for Intel PT.
+static Expected<uint32_t> GetOSEventType() {
+ return ReadIntelPTConfigFile(kOSEventIntelPTTypeFile,
+ IntelPTConfigFileType::Decimal);
+}
+
+static Error CheckPsbPeriod(size_t psb_period) {
+ Expected<uint32_t> cap =
+ ReadIntelPTConfigFile(kPSBPeriodCapFile, IntelPTConfigFileType::ZeroOne);
+ if (!cap)
+ return cap.takeError();
+ if (*cap == 0)
+ return createStringError(inconvertibleErrorCode(),
+ "psb_period is unsupported in the system.");
+
+ Expected<uint32_t> valid_values = ReadIntelPTConfigFile(
+ kPSBPeriodValidValuesFile, IntelPTConfigFileType::Hex);
+ if (!valid_values)
+ return valid_values.takeError();
+
+ if (valid_values.get() & (1 << psb_period))
+ return Error::success();
+
+ std::ostringstream error;
+ // 0 is always a valid value
+ error << "Invalid psb_period. Valid values are: 0";
+ uint32_t mask = valid_values.get();
+ while (mask) {
+ int index = __builtin_ctz(mask);
+ if (index > 0)
+ error << ", " << index;
+ // clear the lowest bit
+ mask &= mask - 1;
+ }
+ error << ".";
+ return createStringError(inconvertibleErrorCode(), error.str().c_str());
}
size_t IntelPTThreadTrace::GetTraceBufferSize() const {
return m_mmap_meta->aux_size;
}
+static Expected<uint64_t>
+GeneratePerfEventConfigValue(bool enable_tsc, Optional<size_t> psb_period) {
+ uint64_t config = 0;
+ // tsc is always supported
+ if (enable_tsc) {
+ if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
+ kTSCBitOffsetFile, IntelPTConfigFileType::BitOffset))
+ config |= 1 << *offset;
+ else
+ return offset.takeError();
+ }
+ if (psb_period) {
+ if (Error error = CheckPsbPeriod(*psb_period))
+ return std::move(error);
+
+ if (Expected<uint32_t> offset = ReadIntelPTConfigFile(
+ kPSBPeriodBitOffsetFile, IntelPTConfigFileType::BitOffset))
+ config |= *psb_period << *offset;
+ else
+ return offset.takeError();
+ }
+ return config;
+}
+
Error IntelPTThreadTrace::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
- uint64_t buffer_size) {
+ uint64_t buffer_size, bool enable_tsc,
+ Optional<size_t> psb_period) {
#ifndef PERF_ATTR_SIZE_VER5
llvm_unreachable("Intel PT Linux perf event not supported");
#else
@@ -85,15 +205,21 @@ Error IntelPTThreadTrace::StartTrace(lldb::pid_t pid, lldb::tid_t tid,
attr.exclude_hv = 1;
attr.exclude_idle = 1;
attr.mmap = 1;
- attr.config = 0;
- Expected<uint32_t> intel_pt_type = GetOSEventType();
+ if (Expected<uint64_t> config_value =
+ GeneratePerfEventConfigValue(enable_tsc, psb_period)) {
+ attr.config = *config_value;
+ LLDB_LOG(log, "intel pt config {0}", attr.config);
+ } else {
+ return config_value.takeError();
+ }
- if (!intel_pt_type)
+ if (Expected<uint32_t> intel_pt_type = GetOSEventType()) {
+ attr.type = *intel_pt_type;
+ LLDB_LOG(log, "intel pt type {0}", attr.type);
+ } else {
return intel_pt_type.takeError();
-
- LLDB_LOG(log, "intel pt type {0}", *intel_pt_type);
- attr.type = *intel_pt_type;
+ }
LLDB_LOG(log, "buffer size {0} ", buffer_size);
@@ -174,11 +300,12 @@ Expected<ArrayRef<uint8_t>> IntelPTThreadTrace::GetCPUInfo() {
}
llvm::Expected<IntelPTThreadTraceUP>
-IntelPTThreadTrace::Create(lldb::pid_t pid, lldb::tid_t tid,
- size_t buffer_size) {
+IntelPTThreadTrace::Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size,
+ bool enable_tsc, Optional<size_t> psb_period) {
IntelPTThreadTraceUP thread_trace_up(new IntelPTThreadTrace());
- if (llvm::Error err = thread_trace_up->StartTrace(pid, tid, buffer_size))
+ if (llvm::Error err = thread_trace_up->StartTrace(pid, tid, buffer_size,
+ enable_tsc, psb_period))
return std::move(err);
return std::move(thread_trace_up);
@@ -368,8 +495,9 @@ Error IntelPTThreadTraceCollection::TraceStart(
return createStringError(inconvertibleErrorCode(),
"Thread %" PRIu64 " already traced", tid);
- Expected<IntelPTThreadTraceUP> trace_up =
- IntelPTThreadTrace::Create(m_pid, tid, request.threadBufferSize);
+ Expected<IntelPTThreadTraceUP> trace_up = IntelPTThreadTrace::Create(
+ m_pid, tid, request.threadBufferSize, request.enableTsc,
+ request.psbPeriod.map([](int64_t period) { return (size_t)period; }));
if (!trace_up)
return trace_up.takeError();
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTManager.h b/lldb/source/Plugins/Process/Linux/IntelPTManager.h
index 807798a811db6..38566a221077a 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTManager.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTManager.h
@@ -70,11 +70,19 @@ class IntelPTThreadTrace {
/// \param[in] buffer_size
/// Size of the thread buffer in bytes.
///
+ /// \param[in] enable_tsc
+ /// Whether to use enable TSC timestamps or not.
+ /// More information in TraceIntelPT::GetStartConfigurationHelp().
+ ///
+ /// \param[in] psb_period
+ /// This value defines the period in which PSB packets will be generated.
+ /// More information in TraceIntelPT::GetStartConfigurationHelp().
+ ///
/// \return
/// \a llvm::Error::success if tracing was successful, or an
/// \a llvm::Error otherwise.
- llvm::Error StartTrace(lldb::pid_t pid, lldb::tid_t tid,
- uint64_t buffer_size);
+ llvm::Error StartTrace(lldb::pid_t pid, lldb::tid_t tid, uint64_t buffer_size,
+ bool enable_tsc, llvm::Optional<size_t> psb_period);
llvm::MutableArrayRef<uint8_t> GetAuxBuffer() const;
llvm::MutableArrayRef<uint8_t> GetDataBuffer() const;
@@ -95,7 +103,8 @@ class IntelPTThreadTrace {
/// A \a IntelPTThreadTrace instance if tracing was successful, or
/// an \a llvm::Error otherwise.
static llvm::Expected<IntelPTThreadTraceUP>
- Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size);
+ Create(lldb::pid_t pid, lldb::tid_t tid, size_t buffer_size, bool enable_tsc,
+ llvm::Optional<size_t> psb_period);
/// Read the trace buffer of the currently traced thread.
///
diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
index e615b2051f977..5650af657c5e4 100644
--- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.cpp
@@ -41,6 +41,20 @@ Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue(
m_thread_buffer_size = thread_buffer_size;
break;
}
+ case 't': {
+ m_enable_tsc = true;
+ break;
+ }
+ case 'p': {
+ int64_t psb_period;
+ if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) ||
+ psb_period < 0)
+ error.SetErrorStringWithFormat("invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_psb_period = psb_period;
+ break;
+ }
default:
llvm_unreachable("Unimplemented option");
}
@@ -49,7 +63,9 @@ Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue(
void CommandObjectThreadTraceStartIntelPT::CommandOptions::
OptionParsingStarting(ExecutionContext *execution_context) {
- m_thread_buffer_size = kThreadBufferSize;
+ m_thread_buffer_size = kDefaultThreadBufferSize;
+ m_enable_tsc = kDefaultEnableTscValue;
+ m_psb_period = kDefaultPsbPeriod;
}
llvm::ArrayRef<OptionDefinition>
@@ -60,7 +76,8 @@ CommandObjectThreadTraceStartIntelPT::CommandOptions::GetDefinitions() {
bool CommandObjectThreadTraceStartIntelPT::DoExecuteOnThreads(
Args &command, CommandReturnObject &result,
llvm::ArrayRef<lldb::tid_t> tids) {
- if (Error err = m_trace.Start(tids, m_options.m_thread_buffer_size))
+ if (Error err = m_trace.Start(tids, m_options.m_thread_buffer_size,
+ m_options.m_enable_tsc, m_options.m_psb_period))
result.SetError(Status(std::move(err)));
else
result.SetStatus(eReturnStatusSuccessFinishResult);
@@ -101,6 +118,20 @@ Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
m_process_buffer_size_limit = process_buffer_size_limit;
break;
}
+ case 't': {
+ m_enable_tsc = true;
+ break;
+ }
+ case 'p': {
+ int64_t psb_period;
+ if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) ||
+ psb_period < 0)
+ error.SetErrorStringWithFormat("invalid integer value for option '%s'",
+ option_arg.str().c_str());
+ else
+ m_psb_period = psb_period;
+ break;
+ }
default:
llvm_unreachable("Unimplemented option");
}
@@ -109,8 +140,10 @@ Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
void CommandObjectProcessTraceStartIntelPT::CommandOptions::
OptionParsingStarting(ExecutionContext *execution_context) {
- m_thread_buffer_size = kThreadBufferSize;
- m_process_buffer_size_limit = kProcessBufferSizeLimit;
+ m_thread_buffer_size = kDefaultThreadBufferSize;
+ m_process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
+ m_enable_tsc = kDefaultEnableTscValue;
+ m_psb_period = kDefaultPsbPeriod;
}
llvm::ArrayRef<OptionDefinition>
@@ -121,7 +154,8 @@ CommandObjectProcessTraceStartIntelPT::CommandOptions::GetDefinitions() {
bool CommandObjectProcessTraceStartIntelPT::DoExecute(
Args &command, CommandReturnObject &result) {
if (Error err = m_trace.Start(m_options.m_thread_buffer_size,
- m_options.m_process_buffer_size_limit))
+ m_options.m_process_buffer_size_limit,
+ m_options.m_enable_tsc, m_options.m_psb_period))
result.SetError(Status(std::move(err)));
else
result.SetStatus(eReturnStatusSuccessFinishResult);
diff --git a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
index 34edbd2995f29..2f3d53a864067 100644
--- a/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/CommandObjectTraceStartIntelPT.h
@@ -32,6 +32,8 @@ class CommandObjectThreadTraceStartIntelPT
llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
size_t m_thread_buffer_size;
+ bool m_enable_tsc;
+ llvm::Optional<size_t> m_psb_period;
};
CommandObjectThreadTraceStartIntelPT(TraceIntelPT &trace,
@@ -74,6 +76,8 @@ class CommandObjectProcessTraceStartIntelPT : public CommandObjectParsed {
size_t m_thread_buffer_size;
size_t m_process_buffer_size_limit;
+ bool m_enable_tsc;
+ llvm::Optional<size_t> m_psb_period;
};
CommandObjectProcessTraceStartIntelPT(TraceIntelPT &trace,
diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
index 5c15e663fd638..970a7970c0fb3 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -48,6 +48,10 @@ bool IntelPTInstruction::IsError() const { return (bool)m_error; }
lldb::addr_t IntelPTInstruction::GetLoadAddress() const { return m_pt_insn.ip; }
+Optional<uint64_t> IntelPTInstruction::GetTimestampCounter() const {
+ return m_timestamp;
+}
+
Error IntelPTInstruction::ToError() const {
if (!IsError())
return Error::success();
diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
index d5983c363367b..135d938dfc57b 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -61,6 +61,9 @@ class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
/// As mentioned, any gap is represented as an error in this class.
class IntelPTInstruction {
public:
+ IntelPTInstruction(const pt_insn &pt_insn, uint64_t timestamp)
+ : m_pt_insn(pt_insn), m_timestamp(timestamp) {}
+
IntelPTInstruction(const pt_insn &pt_insn) : m_pt_insn(pt_insn) {}
/// Error constructor
@@ -84,6 +87,13 @@ class IntelPTInstruction {
/// \a llvm::Error::success otherwise.
llvm::Error ToError() const;
+ /// Get the timestamp associated with the current instruction. The timestamp
+ /// is similar to what a rdtsc instruction would return.
+ ///
+ /// \return
+ /// The timestamp or \b llvm::None if not available.
+ llvm::Optional<uint64_t> GetTimestampCounter() const;
+
/// Get the \a lldb::TraceInstructionControlFlowType categories of the
/// instruction.
///
@@ -103,6 +113,7 @@ class IntelPTInstruction {
const IntelPTInstruction &operator=(const IntelPTInstruction &other) = delete;
pt_insn m_pt_insn;
+ llvm::Optional<uint64_t> m_timestamp;
std::unique_ptr<llvm::ErrorInfoBase> m_error;
};
diff --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
index fa9e42131f0a0..8c331841b54e6 100644
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -9,6 +9,7 @@
#include "llvm/Support/MemoryBuffer.h"
+#include "DecodedThread.h"
#include "TraceIntelPT.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Section.h"
@@ -136,7 +137,23 @@ DecodeInstructions(pt_insn_decoder &decoder) {
break;
}
- instructions.emplace_back(insn);
+ uint64_t time;
+ int time_error = pt_insn_time(&decoder, &time, nullptr, nullptr);
+ if (time_error == -pte_invalid) {
+ // This happens if we invoke the pt_insn_time method incorrectly,
+ // but the instruction is good though.
+ instructions.emplace_back(
+ make_error<IntelPTError>(time_error, insn.ip));
+ instructions.emplace_back(insn);
+ break;
+ }
+ if (time_error == -pte_no_time) {
+ // We simply don't have time information, i.e. None of TSC, MTC or CYC
+ // was enabled.
+ instructions.emplace_back(insn);
+ } else {
+ instructions.emplace_back(insn, time);
+ }
}
}
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
index 25c2446162994..edefdd0d3486e 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -85,6 +85,10 @@ lldb::addr_t TraceCursorIntelPT::GetLoadAddress() {
return m_decoded_thread_sp->GetInstructions()[m_pos].GetLoadAddress();
}
+Optional<uint64_t> TraceCursorIntelPT::GetTimestampCounter() {
+ return m_decoded_thread_sp->GetInstructions()[m_pos].GetTimestampCounter();
+}
+
TraceInstructionControlFlowType
TraceCursorIntelPT::GetInstructionControlFlowType() {
lldb::addr_t next_load_address =
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
index 4eb4e638905fa..29d3792bb489e 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -28,6 +28,8 @@ class TraceCursorIntelPT : public TraceCursor {
lldb::addr_t GetLoadAddress() override;
+ llvm::Optional<uint64_t> GetTimestampCounter() override;
+
lldb::TraceInstructionControlFlowType
GetInstructionControlFlowType() override;
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index 527e0d5e66287..efbc0e2ed7f47 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -192,6 +192,10 @@ bool TraceIntelPT::IsTraced(const Thread &thread) {
return m_thread_decoders.count(&thread);
}
+// The information here should match the description of the intel-pt section
+// of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt
+// documentation file. Similarly, it should match the CLI help messages of the
+// TraceIntelPTOptions.td file.
const char *TraceIntelPT::GetStartConfigurationHelp() {
return R"(Parameters:
@@ -203,6 +207,38 @@ const char *TraceIntelPT::GetStartConfigurationHelp() {
than or equal to 4096 (2^12). The trace is circular keeping the
the most recent data.
+ - boolean enableTsc (default to false):
+ [process and thread tracing]
+ Whether to use enable TSC timestamps or not. This is supported on
+ all devices that support intel-pt.
+
+ - psbPeriod (defaults to null):
+ [process and thread tracing]
+ This value defines the period in which PSB packets will be generated.
+ A PSB packet is a synchronization packet that contains a TSC
+ timestamp and the current absolute instruction pointer.
+
+ This parameter can only be used if
+
+ /sys/bus/event_source/devices/intel_pt/caps/psb_cyc
+
+ is 1. Otherwise, the PSB period will be defined by the processor.
+
+ If supported, valid values for this period can be found in
+
+ /sys/bus/event_source/devices/intel_pt/caps/psb_periods
+
+ which contains a hexadecimal number, whose bits represent
+ valid values e.g. if bit 2 is set, then value 2 is valid.
+
+ The psb_period value is converted to the approximate number of
+ raw trace bytes between PSB packets as:
+
+ 2 ^ (value + 11)
+
+ e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if
+ supported.
+
- int processBufferSizeLimit (defaults to 500 MB):
[process tracing only]
Maximum total trace size per process in bytes. This limit applies
@@ -215,36 +251,47 @@ const char *TraceIntelPT::GetStartConfigurationHelp() {
}
Error TraceIntelPT::Start(size_t thread_buffer_size,
- size_t total_buffer_size_limit) {
+ size_t total_buffer_size_limit, bool enable_tsc,
+ Optional<size_t> psb_period) {
TraceIntelPTStartRequest request;
request.threadBufferSize = thread_buffer_size;
request.processBufferSizeLimit = total_buffer_size_limit;
+ request.enableTsc = enable_tsc;
+ request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
request.type = GetPluginName().AsCString();
return Trace::Start(toJSON(request));
}
Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
- size_t thread_buffer_size = kThreadBufferSize;
- size_t process_buffer_size_limit = kProcessBufferSizeLimit;
+ size_t thread_buffer_size = kDefaultThreadBufferSize;
+ size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
+ bool enable_tsc = kDefaultEnableTscValue;
+ Optional<size_t> psb_period = kDefaultPsbPeriod;
if (configuration) {
if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
dict->GetValueForKeyAsInteger("processBufferSizeLimit",
process_buffer_size_limit);
+ dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
+ dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
} else {
return createStringError(inconvertibleErrorCode(),
"configuration object is not a dictionary");
}
}
- return Start(thread_buffer_size, process_buffer_size_limit);
+ return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc,
+ psb_period);
}
llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
- size_t thread_buffer_size) {
+ size_t thread_buffer_size, bool enable_tsc,
+ Optional<size_t> psb_period) {
TraceIntelPTStartRequest request;
request.threadBufferSize = thread_buffer_size;
+ request.enableTsc = enable_tsc;
+ request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
request.type = GetPluginName().AsCString();
request.tids.emplace();
for (lldb::tid_t tid : tids)
@@ -254,18 +301,22 @@ llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
StructuredData::ObjectSP configuration) {
- size_t thread_buffer_size = kThreadBufferSize;
+ size_t thread_buffer_size = kDefaultThreadBufferSize;
+ bool enable_tsc = kDefaultEnableTscValue;
+ Optional<size_t> psb_period = kDefaultPsbPeriod;
if (configuration) {
if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
+ dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
+ dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
} else {
return createStringError(inconvertibleErrorCode(),
"configuration object is not a dictionary");
}
}
- return Start(tids, thread_buffer_size);
+ return Start(tids, thread_buffer_size, enable_tsc, psb_period);
}
Expected<std::vector<uint8_t>>
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index 9433ab8601f13..cd6fedb2e1616 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -80,18 +80,23 @@ class TraceIntelPT : public Trace {
/// Trace size per thread in bytes.
///
/// \param[in] total_buffer_size_limit
- /// Maximum total trace size per process in bytes. This limit applies to
- /// the sum of the sizes of all thread traces of this process, excluding
- /// the threads traced explicitly.
+ /// Maximum total trace size per process in bytes.
+ /// More information in TraceIntelPT::GetStartConfigurationHelp().
///
- /// Whenever a thread is attempted to be traced due to this operation and
- /// the limit would be reached, the process is stopped with a "tracing"
- /// reason, so that the user can retrace the process if needed.
+ /// \param[in] enable_tsc
+ /// Whether to use enable TSC timestamps or not.
+ /// More information in TraceIntelPT::GetStartConfigurationHelp().
+ ///
+ /// \param[in] psb_period
+ ///
+ /// This value defines the period in which PSB packets will be generated.
+ /// More information in TraceIntelPT::GetStartConfigurationHelp();
///
/// \return
/// \a llvm::Error::success if the operation was successful, or
/// \a llvm::Error otherwise.
- llvm::Error Start(size_t thread_buffer_size, size_t total_buffer_size_limit);
+ llvm::Error Start(size_t thread_buffer_size, size_t total_buffer_size_limit,
+ bool enable_tsc, llvm::Optional<size_t> psb_period);
/// \copydoc Trace::Start
llvm::Error Start(StructuredData::ObjectSP configuration =
@@ -105,11 +110,20 @@ class TraceIntelPT : public Trace {
/// \param[in] thread_buffer_size
/// Trace size per thread in bytes.
///
+ /// \param[in] enable_tsc
+ /// Whether to use enable TSC timestamps or not.
+ /// More information in TraceIntelPT::GetStartConfigurationHelp().
+ ///
+ /// \param[in] psb_period
+ ///
+ /// This value defines the period in which PSB packets will be generated.
+ /// More information in TraceIntelPT::GetStartConfigurationHelp().
+ ///
/// \return
/// \a llvm::Error::success if the operation was successful, or
/// \a llvm::Error otherwise.
- llvm::Error Start(llvm::ArrayRef<lldb::tid_t> tids,
- size_t thread_buffer_size);
+ llvm::Error Start(llvm::ArrayRef<lldb::tid_t> tids, size_t thread_buffer_size,
+ bool enable_tsc, llvm::Optional<size_t> psb_period);
/// \copydoc Trace::Start
llvm::Error Start(llvm::ArrayRef<lldb::tid_t> tids,
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
index a9d0d0e30b054..c2bc1b57b2bd2 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTConstants.h
@@ -11,11 +11,15 @@
#include <cstddef>
+#include <llvm/ADT/Optional.h>
+
namespace lldb_private {
namespace trace_intel_pt {
-const size_t kThreadBufferSize = 4 * 1024; // 4KB
-const size_t kProcessBufferSizeLimit = 5 * 1024 * 1024; // 500MB
+const size_t kDefaultThreadBufferSize = 4 * 1024; // 4KB
+const size_t kDefaultProcessBufferSizeLimit = 5 * 1024 * 1024; // 500MB
+const bool kDefaultEnableTscValue = false;
+const llvm::Optional<size_t> kDefaultPsbPeriod = llvm::None;
} // namespace trace_intel_pt
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
index 85350e4c585c3..9e8cab1ee5c4c 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTOptions.td
@@ -1,5 +1,10 @@
include "../../../../source/Commands/OptionsBase.td"
+// The information of the start commands here should match the description of
+// the intel-pt section of the jLLDBTraceStart packet in the
+// lldb/docs/lldb-gdb-remote.txt documentation file. Similarly, it should match
+// the API help message of TraceIntelPT::GetStartConfigurationHelp().
+
let Command = "thread trace start intel pt" in {
def thread_trace_start_intel_pt_size: Option<"size", "s">,
Group<1>,
@@ -7,6 +12,26 @@ let Command = "thread trace start intel pt" in {
Desc<"Trace size in bytes per thread. It must be a power of 2 greater "
"than or equal to 4096 (2^12). The trace is circular keeping "
"the most recent data. Defaults to 4096 bytes.">;
+ def thread_trace_start_intel_pt_tsc: Option<"tsc", "t">,
+ Group<1>,
+ Desc<"Enable the use of TSC timestamps. This is supported on all devices "
+ "that support intel-pt.">;
+ def thread_trace_start_intel_pt_psb_period: Option<"psb-period", "p">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<"This value defines the period in which PSB packets will be "
+ "generated. A PSB packet is a synchronization packet that contains a "
+ "TSC timestamp and the current absolute instruction pointer. "
+ "This parameter can only be used if "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc is 1. Otherwise, "
+ "the PSB period will be defined by the processor. If supported, valid "
+ "values for this period can be found in "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_periods which "
+ "contains a hexadecimal number, whose bits represent valid values "
+ "e.g. if bit 2 is set, then value 2 is valid. The psb_period value is "
+ "converted to the approximate number of raw trace bytes between PSB "
+ "packets as: 2 ^ (value + 11), e.g. value 3 means 16KiB between PSB "
+ "packets. Defaults to 0 if supported.">;
}
let Command = "process trace start intel pt" in {
@@ -26,4 +51,24 @@ let Command = "process trace start intel pt" in {
"the limit would be reached, the process is stopped with a "
"\"processor trace\" reason, so that the user can retrace the process "
"if needed. Defaults to 500MB.">;
+ def process_trace_start_intel_pt_tsc: Option<"tsc", "t">,
+ Group<1>,
+ Desc<"Enable the use of TSC timestamps. This is supported on all devices "
+ "that support intel-pt.">;
+ def process_trace_start_intel_pt_psb_period: Option<"psb-period", "p">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<"This value defines the period in which PSB packets will be "
+ "generated. A PSB packet is a synchronization packet that contains a "
+ "TSC timestamp and the current absolute instruction pointer. "
+ "This parameter can only be used if "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc is 1. Otherwise, "
+ "the PSB period will be defined by the processor. If supported, valid "
+ "values for this period can be found in "
+ "/sys/bus/event_source/devices/intel_pt/caps/psb_periods which "
+ "contains a hexadecimal number, whose bits represent valid values "
+ "e.g. if bit 2 is set, then value 2 is valid. The psb_period value is "
+ "converted to the approximate number of raw trace bytes between PSB "
+ "packets as: 2 ^ (value + 11), e.g. value 3 means 16KiB between PSB "
+ "packets. Defaults to 0 if supported.">;
}
diff --git a/lldb/source/Target/TraceInstructionDumper.cpp b/lldb/source/Target/TraceInstructionDumper.cpp
index a8525d6a65c45..dc1e86481c363 100644
--- a/lldb/source/Target/TraceInstructionDumper.cpp
+++ b/lldb/source/Target/TraceInstructionDumper.cpp
@@ -19,8 +19,10 @@ using namespace lldb_private;
using namespace llvm;
TraceInstructionDumper::TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up,
- int initial_index, bool raw)
- : m_cursor_up(std::move(cursor_up)), m_index(initial_index), m_raw(raw) {}
+ int initial_index, bool raw,
+ bool show_tsc)
+ : m_cursor_up(std::move(cursor_up)), m_index(initial_index), m_raw(raw),
+ m_show_tsc(show_tsc) {}
/// \return
/// Return \b true if the cursor could move one step.
@@ -177,6 +179,17 @@ void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
auto printInstructionIndex = [&]() {
s.Printf(" [%*d] ", digits_count, m_index);
+
+ if (m_show_tsc) {
+ s.Printf("[tsc=");
+
+ if (Optional<uint64_t> timestamp = m_cursor_up->GetTimestampCounter())
+ s.Printf("0x%016" PRIx64, *timestamp);
+ else
+ s.Printf("unavailable");
+
+ s.Printf("] ");
+ }
};
InstructionSymbolInfo prev_insn_info;
diff --git a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
index 69f773f673bb2..dbb93d8d1c5c6 100644
--- a/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
+++ b/lldb/source/Utility/TraceIntelPTGDBRemotePackets.cpp
@@ -17,6 +17,8 @@ bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet,
Path path) {
ObjectMapper o(value, path);
if (!o || !fromJSON(value, (TraceStartRequest &)packet, path) ||
+ !o.map("enableTsc", packet.enableTsc) ||
+ !o.map("psbPeriod", packet.psbPeriod) ||
!o.map("threadBufferSize", packet.threadBufferSize) ||
!o.map("processBufferSizeLimit", packet.processBufferSizeLimit))
return false;
@@ -36,6 +38,8 @@ json::Value toJSON(const TraceIntelPTStartRequest &packet) {
base.getAsObject()->try_emplace("threadBufferSize", packet.threadBufferSize);
base.getAsObject()->try_emplace("processBufferSizeLimit",
packet.processBufferSizeLimit);
+ base.getAsObject()->try_emplace("psbPeriod", packet.psbPeriod);
+ base.getAsObject()->try_emplace("enableTsc", packet.enableTsc);
return base;
}
diff --git a/lldb/test/API/commands/trace/TestTraceTimestampCounters.py b/lldb/test/API/commands/trace/TestTraceTimestampCounters.py
new file mode 100644
index 0000000000000..8126e36990133
--- /dev/null
+++ b/lldb/test/API/commands/trace/TestTraceTimestampCounters.py
@@ -0,0 +1,100 @@
+import lldb
+from intelpt_testcase import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceTimestampCounters(TraceIntelPTTestCaseBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @testSBAPIAndCommands
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testTscPerThread(self):
+ self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+ self.expect("b main")
+ self.expect("r")
+
+ self.traceStartThread(enableTsc=True)
+
+ self.expect("n")
+ self.expect("thread trace dump instructions --tsc -c 1",
+ patterns=["\[0\] \[tsc=0x[0-9a-fA-F]+\] 0x0000000000400511 movl"])
+
+ @testSBAPIAndCommands
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testTscPerProcess(self):
+ self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+ self.expect("b main")
+ self.expect("r")
+
+ self.traceStartProcess(enableTsc=True)
+
+ self.expect("n")
+ self.expect("thread trace dump instructions --tsc -c 1",
+ patterns=["\[0\] \[tsc=0x[0-9a-fA-F]+\] 0x0000000000400511 movl"])
+
+ @testSBAPIAndCommands
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testDumpingAfterTracingWithoutTsc(self):
+ self.expect("file " + os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+ self.expect("b main")
+ self.expect("r")
+
+ self.traceStartThread(enableTsc=False)
+
+ self.expect("n")
+ self.expect("thread trace dump instructions --tsc -c 1",
+ patterns=["\[0\] \[tsc=unavailable\] 0x0000000000400511 movl"])
+
+ @testSBAPIAndCommands
+ @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
+ def testPSBPeriod(self):
+ def isPSBSupported():
+ caps_file = "/sys/bus/event_source/devices/intel_pt/caps/psb_cyc"
+ if not os.path.exists(caps_file):
+ return False
+ with open(caps_file, "r") as f:
+ val = int(f.readline())
+ if val != 1:
+ return False
+ return True
+
+ def getValidPSBValues():
+ values_file = "/sys/bus/event_source/devices/intel_pt/caps/psb_periods"
+ values = []
+ with open(values_file, "r") as f:
+ mask = int(f.readline(), 16)
+ for i in range(0, 32):
+ if (1 << i) & mask:
+ values.append(i)
+ return values
+
+
+ if not isPSBSupported():
+ self.skipTest("PSB period unsupported")
+
+ valid_psb_values = getValidPSBValues()
+ # 0 should always be valid, and it's assumed by lldb-server
+ self.assertEqual(valid_psb_values[0], 0)
+
+ self.expect("file " + (os.path.join(self.getSourceDir(), "intelpt-trace", "a.out")))
+ self.expect("b main")
+ self.expect("r")
+
+ # it's enough to test with two valid values
+ for psb_period in (valid_psb_values[0], valid_psb_values[-1]):
+ # we first test at thread level
+ self.traceStartThread(psbPeriod=psb_period)
+ self.traceStopThread()
+
+ # we now test at process level
+ self.traceStartProcess(psbPeriod=psb_period)
+ self.traceStopProcess()
+
+ # we now test invalid values
+ self.traceStartThread(psbPeriod=valid_psb_values[-1] + 1, error=True,
+ substrs=["Invalid psb_period. Valid values are: 0"])
+
+ # TODO: dump the perf_event_attr.config as part of the upcoming "trace dump info"
+ # command and check that the psb period is included there.
More information about the lldb-commits
mailing list