| //===-- 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/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; |
| } |