blob: 96c4a89bd1cb83a0af4d958d81a0316ade7799fa [file] [log] [blame]
//===-- TraceIntelPT.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "TraceIntelPT.h"
#include "CommandObjectTraceStartIntelPT.h"
#include "TraceIntelPTSessionFileParser.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadPostMortemTrace.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::trace_intel_pt;
using namespace llvm;
LLDB_PLUGIN_DEFINE(TraceIntelPT)
lldb::CommandObjectSP
TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) {
return CommandObjectSP(
new CommandObjectProcessTraceStartIntelPT(*this, interpreter));
}
lldb::CommandObjectSP
TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
return CommandObjectSP(
new CommandObjectThreadTraceStartIntelPT(*this, interpreter));
}
void TraceIntelPT::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace",
CreateInstanceForSessionFile,
CreateInstanceForLiveProcess,
TraceIntelPTSessionFileParser::GetSchema());
}
void TraceIntelPT::Terminate() {
PluginManager::UnregisterPlugin(CreateInstanceForSessionFile);
}
ConstString TraceIntelPT::GetPluginNameStatic() {
static ConstString g_name("intel-pt");
return g_name;
}
StringRef TraceIntelPT::GetSchema() {
return TraceIntelPTSessionFileParser::GetSchema();
}
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); }
uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
void TraceIntelPT::Dump(Stream *s) const {}
Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile(
const json::Value &trace_session_file, StringRef session_file_dir,
Debugger &debugger) {
return TraceIntelPTSessionFileParser(debugger, trace_session_file,
session_file_dir)
.Parse();
}
Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
TraceSP instance(new TraceIntelPT(process));
process.GetTarget().SetTrace(instance);
return instance;
}
TraceIntelPT::TraceIntelPT(
const pt_cpu &cpu_info,
const std::vector<ThreadPostMortemTraceSP> &traced_threads)
: m_cpu_info(cpu_info) {
for (const ThreadPostMortemTraceSP &thread : traced_threads)
m_thread_decoders.emplace(
thread.get(), std::make_unique<PostMortemThreadDecoder>(thread, *this));
}
const DecodedThread *TraceIntelPT::Decode(Thread &thread) {
RefreshLiveProcessState();
if (m_failed_live_threads_decoder.hasValue())
return &*m_failed_live_threads_decoder;
auto it = m_thread_decoders.find(&thread);
if (it == m_thread_decoders.end())
return nullptr;
return &it->second->Decode();
}
size_t TraceIntelPT::GetCursorPosition(Thread &thread) {
const DecodedThread *decoded_thread = Decode(thread);
if (!decoded_thread)
return 0;
return decoded_thread->GetCursorPosition();
}
void TraceIntelPT::TraverseInstructions(
Thread &thread, size_t position, TraceDirection direction,
std::function<bool(size_t index, Expected<lldb::addr_t> load_addr)>
callback) {
const DecodedThread *decoded_thread = Decode(thread);
if (!decoded_thread)
return;
ArrayRef<IntelPTInstruction> instructions = decoded_thread->GetInstructions();
ssize_t delta = direction == TraceDirection::Forwards ? 1 : -1;
for (ssize_t i = position; i < (ssize_t)instructions.size() && i >= 0;
i += delta)
if (!callback(i, instructions[i].GetLoadAddress()))
break;
}
size_t TraceIntelPT::GetInstructionCount(Thread &thread) {
if (const DecodedThread *decoded_thread = Decode(thread))
return decoded_thread->GetInstructions().size();
else
return 0;
}
Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo");
if (!cpu_info)
return cpu_info.takeError();
int64_t cpu_family = -1;
int64_t model = -1;
int64_t stepping = -1;
std::string vendor_id;
StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
cpu_info->size());
while (!rest.empty()) {
StringRef line;
std::tie(line, rest) = rest.split('\n');
SmallVector<StringRef, 2> columns;
line.split(columns, StringRef(":"), -1, false);
if (columns.size() < 2)
continue; // continue searching
columns[1] = columns[1].trim(" ");
if (columns[0].contains("cpu family") &&
columns[1].getAsInteger(10, cpu_family))
continue;
else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
continue;
else if (columns[0].contains("stepping") &&
columns[1].getAsInteger(10, stepping))
continue;
else if (columns[0].contains("vendor_id")) {
vendor_id = columns[1].str();
if (!vendor_id.empty())
continue;
}
if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
(!vendor_id.empty())) {
return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
static_cast<uint16_t>(cpu_family),
static_cast<uint8_t>(model),
static_cast<uint8_t>(stepping)};
}
}
return createStringError(inconvertibleErrorCode(),
"Failed parsing the target's /proc/cpuinfo file");
}
Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
if (!m_cpu_info) {
if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
m_cpu_info = *cpu_info;
else
return cpu_info.takeError();
}
return *m_cpu_info;
}
void TraceIntelPT::DoRefreshLiveProcessState(
Expected<TraceGetStateResponse> state) {
m_thread_decoders.clear();
if (!state) {
m_failed_live_threads_decoder = DecodedThread(state.takeError());
return;
}
for (const TraceThreadState &thread_state : state->tracedThreads) {
Thread &thread =
*m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
m_thread_decoders.emplace(
&thread, std::make_unique<LiveThreadDecoder>(thread, *this));
}
}
bool TraceIntelPT::IsTraced(const Thread &thread) {
return m_thread_decoders.count(&thread);
}
Error TraceIntelPT::Start(size_t thread_buffer_size,
size_t total_buffer_size_limit) {
TraceIntelPTStartRequest request;
request.threadBufferSize = thread_buffer_size;
request.processBufferSizeLimit = total_buffer_size_limit;
request.type = GetPluginName().AsCString();
return Trace::Start(toJSON(request));
}
llvm::Error TraceIntelPT::Start(const std::vector<lldb::tid_t> &tids,
size_t thread_buffer_size) {
TraceIntelPTStartRequest request;
request.threadBufferSize = thread_buffer_size;
request.type = GetPluginName().AsCString();
request.tids.emplace();
for (lldb::tid_t tid : tids)
request.tids->push_back(tid);
return Trace::Start(toJSON(request));
}
Expected<std::vector<uint8_t>>
TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) {
return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer");
}