[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