Files
clang-p2996/lldb/source/Plugins/Trace/common/TraceSessionSaver.cpp
Walter Erquinigo e0cfe20ad2 [trace][intel pt] Create a common accessor for live and postmortem data
Some parts of the code have to distinguish between live and postmortem threads
to figure out how to get some data, e.g. thread trace buffers. This makes the
code less generic and more error prone. An example of that is that we have
two different decoders: LiveThreadDecoder and PostMortemThreadDecoder. They
exist because getting the trace bufer is different for each case.

The problem doesn't stop there. Soon we'll have even more kinds of data, like
the context switch trace, whose fetching will be different for live and post-
mortem processes.

As a way to fix this, I'm creating a common API for accessing thread data,
which is able to figure out how to handle the postmortem and live cases on
behalf of the caller. As a result of that, I was able to eliminate the two
decoders and unify them into a simpler one. Not only that, our TraceSave
functionality only worked for live threads, but now it can also work for
postmortem processes, which might be useful now, but it might in the future.

This common API is OnThreadBinaryDataRead. More information in the inline
documentation.

Differential Revision: https://reviews.llvm.org/D123281
2022-04-07 15:58:44 -07:00

149 lines
5.6 KiB
C++

//===-- TraceSessionSaver.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 "TraceSessionSaver.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Value.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/lldb-types.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include <fstream>
using namespace lldb;
using namespace lldb_private;
using namespace llvm;
llvm::Error TraceSessionSaver::WriteSessionToFile(
const llvm::json::Value &trace_session_description, FileSpec directory) {
FileSpec trace_path = directory;
trace_path.AppendPathComponent("trace.json");
std::ofstream os(trace_path.GetPath());
os << std::string(formatv("{0:2}", trace_session_description));
os.close();
if (!os)
return createStringError(inconvertibleErrorCode(),
formatv("couldn't write to the file {0}",
trace_path.GetPath().c_str()));
return Error::success();
}
llvm::Expected<JSONTraceSessionBase> TraceSessionSaver::BuildProcessesSection(
Process &live_process, llvm::StringRef raw_thread_trace_data_kind,
FileSpec directory) {
JSONTraceSessionBase json_session_description;
Expected<std::vector<JSONThread>> json_threads =
BuildThreadsSection(live_process, raw_thread_trace_data_kind, directory);
if (!json_threads)
return json_threads.takeError();
Expected<std::vector<JSONModule>> json_modules =
BuildModulesSection(live_process, directory);
if (!json_modules)
return json_modules.takeError();
json_session_description.processes.push_back(JSONProcess{
static_cast<int64_t>(live_process.GetID()),
live_process.GetTarget().GetArchitecture().GetTriple().getTriple(),
json_threads.get(), json_modules.get()});
return json_session_description;
}
llvm::Expected<std::vector<JSONThread>> TraceSessionSaver::BuildThreadsSection(
Process &live_process, llvm::StringRef raw_thread_trace_data_kind,
FileSpec directory) {
std::vector<JSONThread> json_threads;
for (ThreadSP thread_sp : live_process.Threads()) {
TraceSP trace_sp = live_process.GetTarget().GetTrace();
lldb::tid_t tid = thread_sp->GetID();
if (!trace_sp->IsTraced(tid))
continue;
// resolve the directory just in case
FileSystem::Instance().Resolve(directory);
FileSpec raw_trace_path = directory;
raw_trace_path.AppendPathComponent(std::to_string(tid) + ".trace");
json_threads.push_back(JSONThread{static_cast<int64_t>(tid),
raw_trace_path.GetPath().c_str()});
llvm::Error err =
live_process.GetTarget().GetTrace()->OnThreadBinaryDataRead(
tid, raw_thread_trace_data_kind,
[&](llvm::ArrayRef<uint8_t> data) -> llvm::Error {
std::basic_fstream<char> raw_trace_fs =
std::fstream(raw_trace_path.GetPath().c_str(),
std::ios::out | std::ios::binary);
raw_trace_fs.write(reinterpret_cast<const char *>(&data[0]),
data.size() * sizeof(uint8_t));
raw_trace_fs.close();
if (!raw_trace_fs)
return createStringError(
inconvertibleErrorCode(),
formatv("couldn't write to the file {0}",
raw_trace_path.GetPath().c_str()));
return Error::success();
});
if (err)
return std::move(err);
}
return json_threads;
}
llvm::Expected<std::vector<JSONModule>>
TraceSessionSaver::BuildModulesSection(Process &live_process,
FileSpec directory) {
std::vector<JSONModule> json_modules;
ModuleList module_list = live_process.GetTarget().GetImages();
for (size_t i = 0; i < module_list.GetSize(); ++i) {
ModuleSP module_sp(module_list.GetModuleAtIndex(i));
if (!module_sp)
continue;
std::string system_path = module_sp->GetPlatformFileSpec().GetPath();
// TODO: support memory-only libraries like [vdso]
if (!module_sp->GetFileSpec().IsAbsolute())
continue;
std::string file = module_sp->GetFileSpec().GetPath();
ObjectFile *objfile = module_sp->GetObjectFile();
if (objfile == nullptr)
continue;
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
Address base_addr(objfile->GetBaseAddress());
if (base_addr.IsValid() &&
!live_process.GetTarget().GetSectionLoadList().IsEmpty())
load_addr = base_addr.GetLoadAddress(&live_process.GetTarget());
if (load_addr == LLDB_INVALID_ADDRESS)
continue;
FileSystem::Instance().Resolve(directory);
FileSpec path_to_copy_module = directory;
path_to_copy_module.AppendPathComponent("modules");
path_to_copy_module.AppendPathComponent(system_path);
sys::fs::create_directories(path_to_copy_module.GetDirectory().AsCString());
if (std::error_code ec = llvm::sys::fs::copy_file(
system_path, path_to_copy_module.GetPath()))
return createStringError(
inconvertibleErrorCode(),
formatv("couldn't write to the file. {0}", ec.message()));
json_modules.push_back(
JSONModule{system_path, path_to_copy_module.GetPath(),
JSONAddress{load_addr}, module_sp->GetUUID().GetAsString()});
}
return json_modules;
}