D104422 added the interface for TraceCursor, which is the main way to traverse instructions in a trace. This diff implements the corresponding cursor class for Intel PT and deletes the now obsolete code. Besides that, the logic for the "thread trace dump instructions" was adapted to use this cursor (pretty much I ended up moving code from Trace.cpp to TraceCursor.cpp). The command by default traverses the instructions backwards, and if the user passes --forwards, then it's not forwards. More information about that is in the Options.td file. Regarding the Intel PT cursor. All Intel PT cursors for the same thread share the same DecodedThread instance. I'm not yet implementing lazy decoding because we don't need it. That'll be for later. For the time being, the entire thread trace is decoded when the first cursor for that thread is requested. Differential Revision: https://reviews.llvm.org/D105531
223 lines
7.3 KiB
C++
223 lines
7.3 KiB
C++
//===-- Trace.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 "lldb/Target/Trace.h"
|
|
|
|
#include "llvm/Support/Format.h"
|
|
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/SectionLoadList.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Target/ThreadPostMortemTrace.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace llvm;
|
|
|
|
// Helper structs used to extract the type of a trace session json without
|
|
// having to parse the entire object.
|
|
|
|
struct JSONSimplePluginSettings {
|
|
std::string type;
|
|
};
|
|
|
|
struct JSONSimpleTraceSession {
|
|
JSONSimplePluginSettings trace;
|
|
};
|
|
|
|
namespace llvm {
|
|
namespace json {
|
|
|
|
bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings,
|
|
Path path) {
|
|
json::ObjectMapper o(value, path);
|
|
return o && o.map("type", plugin_settings.type);
|
|
}
|
|
|
|
bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) {
|
|
json::ObjectMapper o(value, path);
|
|
return o && o.map("trace", session.trace);
|
|
}
|
|
|
|
} // namespace json
|
|
} // namespace llvm
|
|
|
|
static Error createInvalidPlugInError(StringRef plugin_name) {
|
|
return createStringError(
|
|
std::errc::invalid_argument,
|
|
"no trace plug-in matches the specified type: \"%s\"",
|
|
plugin_name.data());
|
|
}
|
|
|
|
Expected<lldb::TraceSP>
|
|
Trace::FindPluginForPostMortemProcess(Debugger &debugger,
|
|
const json::Value &trace_session_file,
|
|
StringRef session_file_dir) {
|
|
JSONSimpleTraceSession json_session;
|
|
json::Path::Root root("traceSession");
|
|
if (!json::fromJSON(trace_session_file, json_session, root))
|
|
return root.getError();
|
|
|
|
ConstString plugin_name(json_session.trace.type);
|
|
if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name))
|
|
return create_callback(trace_session_file, session_file_dir, debugger);
|
|
|
|
return createInvalidPlugInError(json_session.trace.type);
|
|
}
|
|
|
|
Expected<lldb::TraceSP>
|
|
Trace::FindPluginForLiveProcess(llvm::StringRef plugin_name, Process &process) {
|
|
if (!process.IsLiveDebugSession())
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Can't trace non-live processes");
|
|
|
|
ConstString name(plugin_name);
|
|
if (auto create_callback =
|
|
PluginManager::GetTraceCreateCallbackForLiveProcess(name))
|
|
return create_callback(process);
|
|
|
|
return createInvalidPlugInError(plugin_name);
|
|
}
|
|
|
|
Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
|
|
ConstString plugin_name(name);
|
|
StringRef schema = PluginManager::GetTraceSchema(plugin_name);
|
|
if (!schema.empty())
|
|
return schema;
|
|
|
|
return createInvalidPlugInError(name);
|
|
}
|
|
|
|
Error Trace::Start(const llvm::json::Value &request) {
|
|
if (!m_live_process)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Tracing requires a live process.");
|
|
return m_live_process->TraceStart(request);
|
|
}
|
|
|
|
Error Trace::Stop() {
|
|
if (!m_live_process)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Tracing requires a live process.");
|
|
return m_live_process->TraceStop(
|
|
TraceStopRequest(GetPluginName().AsCString()));
|
|
}
|
|
|
|
Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) {
|
|
if (!m_live_process)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Tracing requires a live process.");
|
|
return m_live_process->TraceStop(
|
|
TraceStopRequest(GetPluginName().AsCString(), tids));
|
|
}
|
|
|
|
Expected<std::string> Trace::GetLiveProcessState() {
|
|
if (!m_live_process)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Tracing requires a live process.");
|
|
return m_live_process->TraceGetState(GetPluginName().AsCString());
|
|
}
|
|
|
|
Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
|
|
llvm::StringRef kind) {
|
|
auto it = m_live_thread_data.find(tid);
|
|
if (it == m_live_thread_data.end())
|
|
return None;
|
|
std::unordered_map<std::string, size_t> &single_thread_data = it->second;
|
|
auto single_thread_data_it = single_thread_data.find(kind.str());
|
|
if (single_thread_data_it == single_thread_data.end())
|
|
return None;
|
|
return single_thread_data_it->second;
|
|
}
|
|
|
|
Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) {
|
|
auto data_it = m_live_process_data.find(kind.str());
|
|
if (data_it == m_live_process_data.end())
|
|
return None;
|
|
return data_it->second;
|
|
}
|
|
|
|
Expected<ArrayRef<uint8_t>>
|
|
Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) {
|
|
if (!m_live_process)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Tracing requires a live process.");
|
|
llvm::Optional<size_t> size = GetLiveThreadBinaryDataSize(tid, kind);
|
|
if (!size)
|
|
return createStringError(
|
|
inconvertibleErrorCode(),
|
|
"Tracing data \"%s\" is not available for thread %" PRIu64 ".",
|
|
kind.data(), tid);
|
|
|
|
TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
|
|
static_cast<int64_t>(tid), 0,
|
|
static_cast<int64_t>(*size)};
|
|
return m_live_process->TraceGetBinaryData(request);
|
|
}
|
|
|
|
Expected<ArrayRef<uint8_t>>
|
|
Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
|
|
if (!m_live_process)
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Tracing requires a live process.");
|
|
llvm::Optional<size_t> size = GetLiveProcessBinaryDataSize(kind);
|
|
if (!size)
|
|
return createStringError(
|
|
inconvertibleErrorCode(),
|
|
"Tracing data \"%s\" is not available for the process.", kind.data());
|
|
|
|
TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
|
|
None, 0, static_cast<int64_t>(*size)};
|
|
return m_live_process->TraceGetBinaryData(request);
|
|
}
|
|
|
|
void Trace::RefreshLiveProcessState() {
|
|
if (!m_live_process)
|
|
return;
|
|
|
|
uint32_t new_stop_id = m_live_process->GetStopID();
|
|
if (new_stop_id == m_stop_id)
|
|
return;
|
|
|
|
m_stop_id = new_stop_id;
|
|
m_live_thread_data.clear();
|
|
|
|
Expected<std::string> json_string = GetLiveProcessState();
|
|
if (!json_string) {
|
|
DoRefreshLiveProcessState(json_string.takeError());
|
|
return;
|
|
}
|
|
Expected<TraceGetStateResponse> live_process_state =
|
|
json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse");
|
|
if (!live_process_state) {
|
|
DoRefreshLiveProcessState(live_process_state.takeError());
|
|
return;
|
|
}
|
|
|
|
for (const TraceThreadState &thread_state :
|
|
live_process_state->tracedThreads) {
|
|
for (const TraceBinaryData &item : thread_state.binaryData)
|
|
m_live_thread_data[thread_state.tid][item.kind] = item.size;
|
|
}
|
|
|
|
for (const TraceBinaryData &item : live_process_state->processBinaryData)
|
|
m_live_process_data[item.kind] = item.size;
|
|
|
|
DoRefreshLiveProcessState(std::move(live_process_state));
|
|
}
|
|
|
|
uint32_t Trace::GetStopID() {
|
|
RefreshLiveProcessState();
|
|
return m_stop_id;
|
|
}
|