[trace][intelpt] Support system-wide tracing [2] - Add a dummy --per-core-tracing option

This updates the documentation of the gdb-remote protocol, as well as the help messages, to include the new --per-core-tracing option.

Differential Revision: https://reviews.llvm.org/D124640
This commit is contained in:
Walter Erquinigo 2022-04-28 14:00:44 -07:00
parent 67d0bc27c0
commit b8d1776fc5
13 changed files with 204 additions and 145 deletions

View File

@ -283,7 +283,7 @@ read packet: {"name":<name>, "description":<description>}/E<error code>;AAAAAAAA
// Tracing technology name, e.g. intel-pt, arm-coresight. // Tracing technology name, e.g. intel-pt, arm-coresight.
// //
// /* thread tracing only */ // /* thread tracing only */
// "tids": [<decimal integer>], // "tids"?: [<decimal integer>],
// Individual threads to trace. // Individual threads to trace.
// //
// ... other parameters specific to the provided tracing type // ... other parameters specific to the provided tracing type
@ -298,16 +298,25 @@ read packet: {"name":<name>, "description":<description>}/E<error code>;AAAAAAAA
// INTEL-PT // INTEL-PT
// intel-pt supports both "thread tracing" and "process tracing". // intel-pt supports both "thread tracing" and "process tracing".
// //
// "Process tracing" is implemented by tracing each thread individually, but // "Process tracing" is implemented in two different ways. If the
// managed by the same "process trace" instance. // "perCoreTracing" option is false, then each thread is traced individually
// Each actual thread trace, either from "process tracing" or "thread tracing", // but managed by the same "process trace" instance. This means that the
// amount of trace buffers used is proportional to the number of running
// threads. This is the recommended option unless the number of threads is
// huge. If "perCoreTracing" is true, then each cpu core is traced invidually
// instead of each thread, which uses a fixed number of trace buffers, but
// might result in less data available for less frequent threads. See
// "perCoreTracing" below for more information.
//
// Each actual trace buffer, either from "process tracing" or "thread tracing",
// is stored in an in-memory circular buffer, which keeps the most recent data. // is stored in an in-memory circular buffer, which keeps the most recent data.
// //
// Additional params in the input schema: // Additional params in the input schema:
// { // {
// "threadBufferSize": <decimal integer>, // "traceBufferSize": <decimal integer>,
// Trace buffer size per thread in bytes. It must be a power of 2 // Size in bytes used by each individual per-thread or per-core trace
// greater than or equal to 4096 (2^12) bytes. // buffer. It must be a power of 2 greater than or equal to 4096 (2^12)
// bytes.
// //
// "enableTsc": <boolean>, // "enableTsc": <boolean>,
// Whether to enable TSC timestamps or not. This is supported on // Whether to enable TSC timestamps or not. This is supported on
@ -342,15 +351,35 @@ read packet: {"name":<name>, "description":<description>}/E<error code>;AAAAAAAA
// 0 if supported. // 0 if supported.
// //
// /* process tracing only */ // /* process tracing only */
// "perCoreTracing": <boolean>
// Instead of having an individual trace buffer per thread, this option
// triggers the collection on a per cpu core basis. This effectively
// traces the entire activity on all cores. At decoding time, in order
// to correctly associate a decoded instruction with a thread, the
// context switch trace of each core is needed, as well as a record per
// cpu indicating which thread was running on each core when tracing
// started. These secondary traces are correlated with the intel-pt
// trace by comparing TSC timestamps.
//
// This option forces the capture of TSC timestamps (see "enableTsc").
//
// Note: This option can't be used simulatenously with any other trace
// sessions because of its system-wide nature.
//
// /* process tracing only */
// "processBufferSizeLimit": <decimal integer>, // "processBufferSizeLimit": <decimal integer>,
// Maximum total buffer size per process in bytes. // Maximum total buffer size per process in bytes.
// This limit applies to the sum of the sizes of all trace buffers for // This limit applies to the sum of the sizes of all trace buffers for
// the current process, excluding the ones started with "thread tracing". // the current process, excluding the ones started with "thread tracing".
// //
// Whenever a thread is attempted to be traced due to "process tracing" // If "perCoreTracing" is false, whenever a thread is attempted to be
// and the limit would be reached, the process is stopped with a // traced due to "process tracing" and the limit would be reached, the
// "tracing" reason along with a meaningful description, so that the // process is stopped with a "tracing" reason along with a meaningful
// user can retrace the process if needed. // description, so that the user can retrace the process if needed.
//
// If "perCoreTracing" is true, then starting the system-wide trace
// session fails if all the individual per-core trace buffers require
// in total more memory that the limit impossed by this parameter.
// } // }
// //
// Notes: // Notes:

View File

@ -28,20 +28,23 @@ struct IntelPTDataKinds {
/// \{ /// \{
struct TraceIntelPTStartRequest : TraceStartRequest { struct TraceIntelPTStartRequest : TraceStartRequest {
/// Size in bytes to use for each thread's trace buffer. /// Size in bytes to use for each thread's trace buffer.
int64_t threadBufferSize; int64_t trace_buffer_size;
/// Whether to enable TSC /// Whether to enable TSC
bool enableTsc; bool enable_tsc;
/// PSB packet period /// PSB packet period
llvm::Optional<int64_t> psbPeriod; llvm::Optional<int64_t> psb_period;
/// Required when doing "process tracing". /// Required when doing "process tracing".
/// ///
/// Limit in bytes on all the thread traces started by this "process trace" /// Limit in bytes on all the thread traces started by this "process trace"
/// instance. When a thread is about to be traced and the limit would be hit, /// instance. When a thread is about to be traced and the limit would be hit,
/// then a "tracing" stop event is triggered. /// then a "tracing" stop event is triggered.
llvm::Optional<int64_t> processBufferSizeLimit; llvm::Optional<int64_t> process_buffer_size_limit;
/// Whether to have a trace buffer per thread or per cpu core.
llvm::Optional<bool> per_core_tracing;
}; };
bool fromJSON(const llvm::json::Value &value, TraceIntelPTStartRequest &packet, bool fromJSON(const llvm::json::Value &value, TraceIntelPTStartRequest &packet,

View File

@ -45,37 +45,38 @@ class TraceIntelPTTestCaseBase(TestBase):
else: else:
self.assertSuccess(sberror) self.assertSuccess(sberror)
def createConfiguration(self, threadBufferSize=None, def createConfiguration(self, traceBufferSize=None,
processBufferSizeLimit=None, enableTsc=False, processBufferSizeLimit=None, enableTsc=False,
psbPeriod=None): psbPeriod=None, perCoreTracing=False):
obj = {} obj = {}
if processBufferSizeLimit is not None: if processBufferSizeLimit is not None:
obj["processBufferSizeLimit"] = processBufferSizeLimit obj["processBufferSizeLimit"] = processBufferSizeLimit
if threadBufferSize is not None: if traceBufferSize is not None:
obj["threadBufferSize"] = threadBufferSize obj["traceBufferSize"] = traceBufferSize
if psbPeriod is not None: if psbPeriod is not None:
obj["psbPeriod"] = psbPeriod obj["psbPeriod"] = psbPeriod
obj["enableTsc"] = enableTsc obj["enableTsc"] = enableTsc
obj["perCoreTracing"] = perCoreTracing
configuration = lldb.SBStructuredData() configuration = lldb.SBStructuredData()
configuration.SetFromJSON(json.dumps(obj)) configuration.SetFromJSON(json.dumps(obj))
return configuration return configuration
def traceStartThread(self, thread=None, error=False, substrs=None, def traceStartThread(self, thread=None, error=False, substrs=None,
threadBufferSize=None, enableTsc=False, psbPeriod=None): traceBufferSize=None, enableTsc=False, psbPeriod=None):
if self.USE_SB_API: if self.USE_SB_API:
trace = self.getTraceOrCreate() trace = self.getTraceOrCreate()
thread = thread if thread is not None else self.thread() thread = thread if thread is not None else self.thread()
configuration = self.createConfiguration( configuration = self.createConfiguration(
threadBufferSize=threadBufferSize, enableTsc=enableTsc, traceBufferSize=traceBufferSize, enableTsc=enableTsc,
psbPeriod=psbPeriod) psbPeriod=psbPeriod)
self.assertSBError(trace.Start(thread, configuration), error) self.assertSBError(trace.Start(thread, configuration), error)
else: else:
command = "thread trace start" command = "thread trace start"
if thread is not None: if thread is not None:
command += " " + str(thread.GetIndexID()) command += " " + str(thread.GetIndexID())
if threadBufferSize is not None: if traceBufferSize is not None:
command += " -s " + str(threadBufferSize) command += " -s " + str(traceBufferSize)
if enableTsc: if enableTsc:
command += " --tsc" command += " --tsc"
if psbPeriod is not None: if psbPeriod is not None:
@ -83,12 +84,13 @@ class TraceIntelPTTestCaseBase(TestBase):
self.expect(command, error=error, substrs=substrs) self.expect(command, error=error, substrs=substrs)
def traceStartProcess(self, processBufferSizeLimit=None, error=False, def traceStartProcess(self, processBufferSizeLimit=None, error=False,
substrs=None, enableTsc=False, psbPeriod=None): substrs=None, enableTsc=False, psbPeriod=None,
perCoreTracing=False):
if self.USE_SB_API: if self.USE_SB_API:
trace = self.getTraceOrCreate() trace = self.getTraceOrCreate()
configuration = self.createConfiguration( configuration = self.createConfiguration(
processBufferSizeLimit=processBufferSizeLimit, enableTsc=enableTsc, processBufferSizeLimit=processBufferSizeLimit, enableTsc=enableTsc,
psbPeriod=psbPeriod) psbPeriod=psbPeriod, perCoreTracing=perCoreTracing)
self.assertSBError(trace.Start(configuration), error=error) self.assertSBError(trace.Start(configuration), error=error)
else: else:
command = "process trace start" command = "process trace start"
@ -98,6 +100,8 @@ class TraceIntelPTTestCaseBase(TestBase):
command += " --tsc" command += " --tsc"
if psbPeriod is not None: if psbPeriod is not None:
command += " --psb-period " + str(psbPeriod) command += " --psb-period " + str(psbPeriod)
if perCoreTracing:
command += " --per-core-tracing"
self.expect(command, error=error, substrs=substrs) self.expect(command, error=error, substrs=substrs)
def traceStopProcess(self): def traceStopProcess(self):

View File

@ -443,8 +443,8 @@ Error IntelPTThreadTraceCollection::TraceStart(
"Thread %" PRIu64 " already traced", tid); "Thread %" PRIu64 " already traced", tid);
Expected<IntelPTThreadTraceUP> trace_up = IntelPTThreadTrace::Create( Expected<IntelPTThreadTraceUP> trace_up = IntelPTThreadTrace::Create(
m_pid, tid, request.threadBufferSize, request.enableTsc, m_pid, tid, request.trace_buffer_size, request.enable_tsc,
request.psbPeriod.map([](int64_t period) { return (size_t)period; })); request.psb_period.map([](int64_t period) { return (size_t)period; }));
if (!trace_up) if (!trace_up)
return trace_up.takeError(); return trace_up.takeError();
@ -490,8 +490,9 @@ Error IntelPTProcessTrace::TraceStop(lldb::tid_t tid) {
} }
Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) { Error IntelPTProcessTrace::TraceStart(lldb::tid_t tid) {
if (m_thread_traces.GetTotalBufferSize() + m_tracing_params.threadBufferSize > if (m_thread_traces.GetTotalBufferSize() +
static_cast<size_t>(*m_tracing_params.processBufferSizeLimit)) m_tracing_params.trace_buffer_size >
static_cast<size_t>(*m_tracing_params.process_buffer_size_limit))
return createStringError( return createStringError(
inconvertibleErrorCode(), inconvertibleErrorCode(),
"Thread %" PRIu64 " can't be traced as the process trace size limit " "Thread %" PRIu64 " can't be traced as the process trace size limit "
@ -548,6 +549,10 @@ Error IntelPTCollector::TraceStart(
inconvertibleErrorCode(), inconvertibleErrorCode(),
"Process currently traced. Stop process tracing first"); "Process currently traced. Stop process tracing first");
} }
if (request.per_core_tracing.getValueOr(false)) {
return createStringError(inconvertibleErrorCode(),
"Per-core tracing is not supported.");
}
m_process_trace = IntelPTProcessTrace(m_pid, request); m_process_trace = IntelPTProcessTrace(m_pid, request);
Error error = Error::success(); Error error = Error::success();

View File

@ -32,13 +32,13 @@ Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue(
switch (short_option) { switch (short_option) {
case 's': { case 's': {
int64_t thread_buffer_size; int64_t trace_buffer_size;
if (option_arg.empty() || option_arg.getAsInteger(0, thread_buffer_size) || if (option_arg.empty() || option_arg.getAsInteger(0, trace_buffer_size) ||
thread_buffer_size < 0) trace_buffer_size < 0)
error.SetErrorStringWithFormat("invalid integer value for option '%s'", error.SetErrorStringWithFormat("invalid integer value for option '%s'",
option_arg.str().c_str()); option_arg.str().c_str());
else else
m_thread_buffer_size = thread_buffer_size; m_trace_buffer_size = trace_buffer_size;
break; break;
} }
case 't': { case 't': {
@ -63,7 +63,7 @@ Status CommandObjectThreadTraceStartIntelPT::CommandOptions::SetOptionValue(
void CommandObjectThreadTraceStartIntelPT::CommandOptions:: void CommandObjectThreadTraceStartIntelPT::CommandOptions::
OptionParsingStarting(ExecutionContext *execution_context) { OptionParsingStarting(ExecutionContext *execution_context) {
m_thread_buffer_size = kDefaultThreadBufferSize; m_trace_buffer_size = kDefaultTraceBufferSize;
m_enable_tsc = kDefaultEnableTscValue; m_enable_tsc = kDefaultEnableTscValue;
m_psb_period = kDefaultPsbPeriod; m_psb_period = kDefaultPsbPeriod;
} }
@ -76,7 +76,7 @@ CommandObjectThreadTraceStartIntelPT::CommandOptions::GetDefinitions() {
bool CommandObjectThreadTraceStartIntelPT::DoExecuteOnThreads( bool CommandObjectThreadTraceStartIntelPT::DoExecuteOnThreads(
Args &command, CommandReturnObject &result, Args &command, CommandReturnObject &result,
llvm::ArrayRef<lldb::tid_t> tids) { 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_trace_buffer_size,
m_options.m_enable_tsc, m_options.m_psb_period)) m_options.m_enable_tsc, m_options.m_psb_period))
result.SetError(Status(std::move(err))); result.SetError(Status(std::move(err)));
else else
@ -98,13 +98,13 @@ Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
switch (short_option) { switch (short_option) {
case 's': { case 's': {
int64_t thread_buffer_size; int64_t trace_buffer_size;
if (option_arg.empty() || option_arg.getAsInteger(0, thread_buffer_size) || if (option_arg.empty() || option_arg.getAsInteger(0, trace_buffer_size) ||
thread_buffer_size < 0) trace_buffer_size < 0)
error.SetErrorStringWithFormat("invalid integer value for option '%s'", error.SetErrorStringWithFormat("invalid integer value for option '%s'",
option_arg.str().c_str()); option_arg.str().c_str());
else else
m_thread_buffer_size = thread_buffer_size; m_trace_buffer_size = trace_buffer_size;
break; break;
} }
case 'l': { case 'l': {
@ -122,6 +122,10 @@ Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
m_enable_tsc = true; m_enable_tsc = true;
break; break;
} }
case 'c': {
m_per_core_tracing = true;
break;
}
case 'p': { case 'p': {
int64_t psb_period; int64_t psb_period;
if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) || if (option_arg.empty() || option_arg.getAsInteger(0, psb_period) ||
@ -140,10 +144,11 @@ Status CommandObjectProcessTraceStartIntelPT::CommandOptions::SetOptionValue(
void CommandObjectProcessTraceStartIntelPT::CommandOptions:: void CommandObjectProcessTraceStartIntelPT::CommandOptions::
OptionParsingStarting(ExecutionContext *execution_context) { OptionParsingStarting(ExecutionContext *execution_context) {
m_thread_buffer_size = kDefaultThreadBufferSize; m_trace_buffer_size = kDefaultTraceBufferSize;
m_process_buffer_size_limit = kDefaultProcessBufferSizeLimit; m_process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
m_enable_tsc = kDefaultEnableTscValue; m_enable_tsc = kDefaultEnableTscValue;
m_psb_period = kDefaultPsbPeriod; m_psb_period = kDefaultPsbPeriod;
m_per_core_tracing = kDefaultPerCoreTracing;
} }
llvm::ArrayRef<OptionDefinition> llvm::ArrayRef<OptionDefinition>
@ -153,9 +158,10 @@ CommandObjectProcessTraceStartIntelPT::CommandOptions::GetDefinitions() {
bool CommandObjectProcessTraceStartIntelPT::DoExecute( bool CommandObjectProcessTraceStartIntelPT::DoExecute(
Args &command, CommandReturnObject &result) { Args &command, CommandReturnObject &result) {
if (Error err = m_trace.Start(m_options.m_thread_buffer_size, if (Error err = m_trace.Start(m_options.m_trace_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)) m_options.m_enable_tsc, m_options.m_psb_period,
m_options.m_per_core_tracing))
result.SetError(Status(std::move(err))); result.SetError(Status(std::move(err)));
else else
result.SetStatus(eReturnStatusSuccessFinishResult); result.SetStatus(eReturnStatusSuccessFinishResult);

View File

@ -31,7 +31,7 @@ public:
llvm::ArrayRef<OptionDefinition> GetDefinitions() override; llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
size_t m_thread_buffer_size; size_t m_trace_buffer_size;
bool m_enable_tsc; bool m_enable_tsc;
llvm::Optional<size_t> m_psb_period; llvm::Optional<size_t> m_psb_period;
}; };
@ -74,10 +74,11 @@ public:
llvm::ArrayRef<OptionDefinition> GetDefinitions() override; llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
size_t m_thread_buffer_size; size_t m_trace_buffer_size;
size_t m_process_buffer_size_limit; size_t m_process_buffer_size_limit;
bool m_enable_tsc; bool m_enable_tsc;
llvm::Optional<size_t> m_psb_period; llvm::Optional<size_t> m_psb_period;
bool m_per_core_tracing;
}; };
CommandObjectProcessTraceStartIntelPT(TraceIntelPT &trace, CommandObjectProcessTraceStartIntelPT(TraceIntelPT &trace,
@ -85,10 +86,14 @@ public:
: CommandObjectParsed( : CommandObjectParsed(
interpreter, "process trace start", interpreter, "process trace start",
"Start tracing this process with intel-pt, including future " "Start tracing this process with intel-pt, including future "
"threads. " "threads. If --per-core-tracing is not provided, this traces each "
"This is implemented by tracing each thread independently. " "thread independently, thus using a trace buffer per thread. "
"Threads traced with the \"thread trace start\" command are left " "Threads traced with the \"thread trace start\" command are left "
"unaffected ant not retraced.", "unaffected ant not retraced. This is the recommended option "
"unless the number of threads is huge. If --per-core-tracing is "
"passed, each cpu core is traced instead of each thread, which "
"uses a fixed number of trace buffers, but might result in less "
"data available for less frequent threads.",
"process trace start [<intel-pt-options>]", "process trace start [<intel-pt-options>]",
lldb::eCommandRequiresProcess | lldb::eCommandTryTargetAPILock | lldb::eCommandRequiresProcess | lldb::eCommandTryTargetAPILock |
lldb::eCommandProcessMustBeLaunched | lldb::eCommandProcessMustBeLaunched |

View File

@ -271,101 +271,79 @@ bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
// documentation file. Similarly, it should match the CLI help messages of the // documentation file. Similarly, it should match the CLI help messages of the
// TraceIntelPTOptions.td file. // TraceIntelPTOptions.td file.
const char *TraceIntelPT::GetStartConfigurationHelp() { const char *TraceIntelPT::GetStartConfigurationHelp() {
return R"(Parameters: static Optional<std::string> message;
if (!message) {
message.emplace(formatv(R"(Parameters:
Note: If a parameter is not specified, a default value will be used. See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a
description of each parameter below.
- int threadBufferSize (defaults to 4096 bytes): - int traceBufferSize (defaults to {0} bytes):
[process and thread tracing] [process and thread tracing]
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
the most recent data.
- boolean enableTsc (default to false): - boolean enableTsc (default to {1}):
[process and thread tracing] [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): - int psbPeriod (defaults to {2}):
[process and thread tracing] [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 - boolean perCoreTracing (default to {3}):
/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] [process tracing only]
Maximum total trace size per process in bytes. This limit applies
to the sum of the sizes of all thread traces of this process, - int processBufferSizeLimit (defaults to {4} MiB):
excluding the ones created explicitly with "thread tracing". [process tracing only])",
Whenever a thread is attempted to be traced due to this command kDefaultTraceBufferSize, kDefaultEnableTscValue,
and the limit would be reached, the process is stopped with a kDefaultPsbPeriod, kDefaultPerCoreTracing,
"processor trace" reason, so that the user can retrace the process kDefaultProcessBufferSizeLimit / 1024 / 1024));
if needed.)"; }
return message->c_str();
} }
Error TraceIntelPT::Start(size_t thread_buffer_size, Error TraceIntelPT::Start(size_t trace_buffer_size,
size_t total_buffer_size_limit, bool enable_tsc, size_t total_buffer_size_limit, bool enable_tsc,
Optional<size_t> psb_period) { Optional<size_t> psb_period, bool per_core_tracing) {
TraceIntelPTStartRequest request; TraceIntelPTStartRequest request;
request.threadBufferSize = thread_buffer_size; request.trace_buffer_size = trace_buffer_size;
request.processBufferSizeLimit = total_buffer_size_limit; request.process_buffer_size_limit = total_buffer_size_limit;
request.enableTsc = enable_tsc; request.enable_tsc = enable_tsc;
request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); request.psb_period = psb_period.map([](size_t val) { return (int64_t)val; });
request.type = GetPluginName().str(); request.type = GetPluginName().str();
request.per_core_tracing = per_core_tracing;
return Trace::Start(toJSON(request)); return Trace::Start(toJSON(request));
} }
Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) { Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
size_t thread_buffer_size = kDefaultThreadBufferSize; size_t trace_buffer_size = kDefaultTraceBufferSize;
size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit; size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
bool enable_tsc = kDefaultEnableTscValue; bool enable_tsc = kDefaultEnableTscValue;
Optional<size_t> psb_period = kDefaultPsbPeriod; Optional<size_t> psb_period = kDefaultPsbPeriod;
bool per_core_tracing = kDefaultPerCoreTracing;
if (configuration) { if (configuration) {
if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); dict->GetValueForKeyAsInteger("traceBufferSize", trace_buffer_size);
dict->GetValueForKeyAsInteger("processBufferSizeLimit", dict->GetValueForKeyAsInteger("processBufferSizeLimit",
process_buffer_size_limit); process_buffer_size_limit);
dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
dict->GetValueForKeyAsInteger("psbPeriod", psb_period); dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
dict->GetValueForKeyAsBoolean("perCoreTracing", per_core_tracing);
} else { } else {
return createStringError(inconvertibleErrorCode(), return createStringError(inconvertibleErrorCode(),
"configuration object is not a dictionary"); "configuration object is not a dictionary");
} }
} }
return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc, return Start(trace_buffer_size, process_buffer_size_limit, enable_tsc,
psb_period); psb_period, per_core_tracing);
} }
llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
size_t thread_buffer_size, bool enable_tsc, size_t trace_buffer_size, bool enable_tsc,
Optional<size_t> psb_period) { Optional<size_t> psb_period) {
TraceIntelPTStartRequest request; TraceIntelPTStartRequest request;
request.threadBufferSize = thread_buffer_size; request.trace_buffer_size = trace_buffer_size;
request.enableTsc = enable_tsc; request.enable_tsc = enable_tsc;
request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; }); request.psb_period = psb_period.map([](size_t val) { return (int64_t)val; });
request.type = GetPluginName().str(); request.type = GetPluginName().str();
request.tids.emplace(); request.tids.emplace();
for (lldb::tid_t tid : tids) for (lldb::tid_t tid : tids)
@ -375,13 +353,13 @@ llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids, Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
StructuredData::ObjectSP configuration) { StructuredData::ObjectSP configuration) {
size_t thread_buffer_size = kDefaultThreadBufferSize; size_t trace_buffer_size = kDefaultTraceBufferSize;
bool enable_tsc = kDefaultEnableTscValue; bool enable_tsc = kDefaultEnableTscValue;
Optional<size_t> psb_period = kDefaultPsbPeriod; Optional<size_t> psb_period = kDefaultPsbPeriod;
if (configuration) { if (configuration) {
if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) { if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size); dict->GetValueForKeyAsInteger("traceBufferSize", trace_buffer_size);
dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc); dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
dict->GetValueForKeyAsInteger("psbPeriod", psb_period); dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
} else { } else {
@ -390,7 +368,7 @@ Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
} }
} }
return Start(tids, thread_buffer_size, enable_tsc, psb_period); return Start(tids, trace_buffer_size, enable_tsc, psb_period);
} }
Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid, Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,

View File

@ -84,27 +84,31 @@ public:
/// Start tracing a live process. /// Start tracing a live process.
/// ///
/// \param[in] thread_buffer_size /// More information on the parameters below can be found in the
/// jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt.
///
/// \param[in] trace_buffer_size
/// Trace size per thread in bytes. /// Trace size per thread in bytes.
/// ///
/// \param[in] total_buffer_size_limit /// \param[in] total_buffer_size_limit
/// Maximum total trace size per process in bytes. /// Maximum total trace size per process in bytes.
/// More information in TraceIntelPT::GetStartConfigurationHelp().
/// ///
/// \param[in] enable_tsc /// \param[in] enable_tsc
/// Whether to use enable TSC timestamps or not. /// Whether to use enable TSC timestamps or not.
/// More information in TraceIntelPT::GetStartConfigurationHelp().
/// ///
/// \param[in] psb_period /// \param[in] psb_period
///
/// This value defines the period in which PSB packets will be generated. /// This value defines the period in which PSB packets will be generated.
/// More information in TraceIntelPT::GetStartConfigurationHelp(); ///
/// \param[in] per_core_tracing
/// This value defines whether to have a trace buffer per thread or per
/// cpu core.
/// ///
/// \return /// \return
/// \a llvm::Error::success if the operation was successful, or /// \a llvm::Error::success if the operation was successful, or
/// \a llvm::Error otherwise. /// \a llvm::Error otherwise.
llvm::Error Start(size_t thread_buffer_size, size_t total_buffer_size_limit, llvm::Error Start(size_t trace_buffer_size, size_t total_buffer_size_limit,
bool enable_tsc, llvm::Optional<size_t> psb_period); bool enable_tsc, llvm::Optional<size_t> psb_period,
bool m_per_core_tracing);
/// \copydoc Trace::Start /// \copydoc Trace::Start
llvm::Error Start(StructuredData::ObjectSP configuration = llvm::Error Start(StructuredData::ObjectSP configuration =
@ -112,25 +116,25 @@ public:
/// Start tracing live threads. /// Start tracing live threads.
/// ///
/// More information on the parameters below can be found in the
/// jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt.
///
/// \param[in] tids /// \param[in] tids
/// Threads to trace. /// Threads to trace.
/// ///
/// \param[in] thread_buffer_size /// \param[in] trace_buffer_size
/// Trace size per thread in bytes. /// Trace size per thread or per core in bytes.
/// ///
/// \param[in] enable_tsc /// \param[in] enable_tsc
/// Whether to use enable TSC timestamps or not. /// Whether to use enable TSC timestamps or not.
/// More information in TraceIntelPT::GetStartConfigurationHelp().
/// ///
/// \param[in] psb_period /// \param[in] psb_period
///
/// This value defines the period in which PSB packets will be generated. /// This value defines the period in which PSB packets will be generated.
/// More information in TraceIntelPT::GetStartConfigurationHelp().
/// ///
/// \return /// \return
/// \a llvm::Error::success if the operation was successful, or /// \a llvm::Error::success if the operation was successful, or
/// \a llvm::Error otherwise. /// \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 trace_buffer_size,
bool enable_tsc, llvm::Optional<size_t> psb_period); bool enable_tsc, llvm::Optional<size_t> psb_period);
/// \copydoc Trace::Start /// \copydoc Trace::Start

View File

@ -16,10 +16,11 @@
namespace lldb_private { namespace lldb_private {
namespace trace_intel_pt { namespace trace_intel_pt {
const size_t kDefaultThreadBufferSize = 4 * 1024; // 4KB const size_t kDefaultTraceBufferSize = 4 * 1024; // 4KB
const size_t kDefaultProcessBufferSizeLimit = 5 * 1024 * 1024; // 500MB const size_t kDefaultProcessBufferSizeLimit = 5 * 1024 * 1024; // 500MB
const bool kDefaultEnableTscValue = false; const bool kDefaultEnableTscValue = false;
const llvm::Optional<size_t> kDefaultPsbPeriod = llvm::None; const llvm::Optional<size_t> kDefaultPsbPeriod = llvm::None;
const bool kDefaultPerCoreTracing = false;
} // namespace trace_intel_pt } // namespace trace_intel_pt
} // namespace lldb_private } // namespace lldb_private

View File

@ -35,12 +35,24 @@ let Command = "thread trace start intel pt" in {
} }
let Command = "process trace start intel pt" in { let Command = "process trace start intel pt" in {
def process_trace_start_intel_pt_thread_size: Option<"thread-size", "s">, def process_trace_start_intel_pt_buffer_size: Option<"buffer-size", "s">,
Group<1>, Group<1>,
Arg<"Value">, Arg<"Value">,
Desc<"Trace size in bytes per thread. It must be a power of 2 greater " Desc<"Size in bytes used by each individual per-thread or per-core trace "
"than or equal to 4096 (2^12). The trace is circular keeping " "buffer. It must be a power of 2 greater than or equal to 4096 (2^12) "
"the most recent data. Defaults to 4096 bytes.">; "bytes.">;
def process_trace_start_intel_pt_per_core_tracing:
Option<"per-core-tracing", "c">,
Group<1>,
Desc<"Instead of having an individual trace buffer per thread, which uses "
"a number trace buffers proportional to the number of running "
"threads, this option triggers the collection on a per cpu core "
"basis. This effectively traces the entire activity on all cores "
"using a limited amount of trace buffers regardless of the number of "
"threads. This might cause data loss for less frequent threads. This "
"option forces the capture of TSC timestamps (see --tsc). Also, this "
"option can't be used simulatenously with any other trace sessions "
"because of its system-wide nature.">;
def process_trace_start_intel_pt_process_size_limit: Option<"total-size-limit", "l">, def process_trace_start_intel_pt_process_size_limit: Option<"total-size-limit", "l">,
Group<1>, Group<1>,
Arg<"Value">, Arg<"Value">,

View File

@ -20,29 +20,27 @@ bool fromJSON(const json::Value &value, TraceIntelPTStartRequest &packet,
Path path) { Path path) {
ObjectMapper o(value, path); ObjectMapper o(value, path);
if (!o || !fromJSON(value, (TraceStartRequest &)packet, path) || if (!o || !fromJSON(value, (TraceStartRequest &)packet, path) ||
!o.map("enableTsc", packet.enableTsc) || !o.map("enableTsc", packet.enable_tsc) ||
!o.map("psbPeriod", packet.psbPeriod) || !o.map("psbPeriod", packet.psb_period) ||
!o.map("threadBufferSize", packet.threadBufferSize) || !o.map("traceBufferSize", packet.trace_buffer_size))
!o.map("processBufferSizeLimit", packet.processBufferSizeLimit))
return false;
if (packet.tids && packet.processBufferSizeLimit) {
path.report("processBufferSizeLimit must be provided");
return false;
}
if (!packet.tids && !packet.processBufferSizeLimit) {
path.report("processBufferSizeLimit must not be provided");
return false; return false;
if (packet.IsProcessTracing()) {
if (!o.map("processBufferSizeLimit", packet.process_buffer_size_limit) ||
!o.map("perCoreTracing", packet.per_core_tracing))
return false;
} }
return true; return true;
} }
json::Value toJSON(const TraceIntelPTStartRequest &packet) { json::Value toJSON(const TraceIntelPTStartRequest &packet) {
json::Value base = toJSON((const TraceStartRequest &)packet); json::Value base = toJSON((const TraceStartRequest &)packet);
base.getAsObject()->try_emplace("threadBufferSize", packet.threadBufferSize); json::Object &obj = *base.getAsObject();
base.getAsObject()->try_emplace("processBufferSizeLimit", obj.try_emplace("traceBufferSize", packet.trace_buffer_size);
packet.processBufferSizeLimit); obj.try_emplace("processBufferSizeLimit", packet.process_buffer_size_limit);
base.getAsObject()->try_emplace("psbPeriod", packet.psbPeriod); obj.try_emplace("psbPeriod", packet.psb_period);
base.getAsObject()->try_emplace("enableTsc", packet.enableTsc); obj.try_emplace("enableTsc", packet.enable_tsc);
obj.try_emplace("perCoreTracing", packet.per_core_tracing);
return base; return base;
} }

View File

@ -35,18 +35,18 @@ class TestTraceStartStop(TraceIntelPTTestCaseBase):
self.expect("r") self.expect("r")
self.traceStartThread( self.traceStartThread(
error=True, threadBufferSize=2000, error=True, traceBufferSize=2000,
substrs=["The trace buffer size must be a power of 2", "It was 2000"]) substrs=["The trace buffer size must be a power of 2", "It was 2000"])
self.traceStartThread( self.traceStartThread(
error=True, threadBufferSize=5000, error=True, traceBufferSize=5000,
substrs=["The trace buffer size must be a power of 2", "It was 5000"]) substrs=["The trace buffer size must be a power of 2", "It was 5000"])
self.traceStartThread( self.traceStartThread(
error=True, threadBufferSize=0, error=True, traceBufferSize=0,
substrs=["The trace buffer size must be a power of 2", "It was 0"]) substrs=["The trace buffer size must be a power of 2", "It was 0"])
self.traceStartThread(threadBufferSize=1048576) self.traceStartThread(traceBufferSize=1048576)
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
def testSBAPIHelp(self): def testSBAPIHelp(self):
@ -55,7 +55,7 @@ class TestTraceStartStop(TraceIntelPTTestCaseBase):
self.expect("r") self.expect("r")
help = self.getTraceOrCreate().GetStartConfigurationHelp() help = self.getTraceOrCreate().GetStartConfigurationHelp()
self.assertIn("threadBufferSize", help) self.assertIn("traceBufferSize", help)
self.assertIn("processBufferSizeLimit", help) self.assertIn("processBufferSizeLimit", help)
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))

View File

@ -152,3 +152,17 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase):
self.expect("c", substrs=['Thread', "can't be traced"]) self.expect("c", substrs=['Thread', "can't be traced"])
self.traceStopProcess() self.traceStopProcess()
@skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64']))
@testSBAPIAndCommands
def testStartPerCoreSession(self):
self.build()
exe = self.getBuildArtifact("a.out")
self.dbg.CreateTarget(exe)
self.expect("b main")
self.expect("r")
self.traceStartProcess(
error=True, perCoreTracing=True,
substrs=["Per-core tracing is not supported"])