//===-- LLDBUtils.cpp -------------------------------------------*- C++ -*-===// // // 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 "LLDBUtils.h" #include "JSONUtils.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBThread.h" #include "lldb/lldb-enumerations.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" #include #include #include namespace lldb_dap { bool RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, const llvm::ArrayRef &commands, llvm::raw_ostream &strm, bool parse_command_directives, bool echo_commands) { if (commands.empty()) return true; bool did_print_prefix = false; // We only need the prompt when echoing commands. std::string prompt_string; if (echo_commands) { prompt_string = "(lldb) "; // Get the current prompt from settings. if (const lldb::SBStructuredData prompt = debugger.GetSetting("prompt")) { const size_t prompt_length = prompt.GetStringValue(nullptr, 0); if (prompt_length != 0) { prompt_string.resize(prompt_length + 1); prompt.GetStringValue(prompt_string.data(), prompt_string.length()); } } } lldb::SBCommandInterpreter interp = debugger.GetCommandInterpreter(); for (llvm::StringRef command : commands) { lldb::SBCommandReturnObject result; bool quiet_on_success = false; bool check_error = false; while (parse_command_directives) { if (command.starts_with("?")) { command = command.drop_front(); quiet_on_success = true; } else if (command.starts_with("!")) { command = command.drop_front(); check_error = true; } else { break; } } { // Prevent simultaneous calls to HandleCommand, e.g. EventThreadFunction // may asynchronously call RunExitCommands when we are already calling // RunTerminateCommands. static std::mutex handle_command_mutex; std::lock_guard locker(handle_command_mutex); interp.HandleCommand(command.str().c_str(), result, /*add_to_history=*/true); } const bool got_error = !result.Succeeded(); // The if statement below is assuming we always print out `!` prefixed // lines. The only time we don't print is when we have `quiet_on_success == // true` and we don't have an error. if (quiet_on_success ? got_error : true) { if (!did_print_prefix && !prefix.empty()) { strm << prefix << "\n"; did_print_prefix = true; } if (echo_commands) strm << prompt_string.c_str() << command << '\n'; auto output_len = result.GetOutputSize(); if (output_len) { const char *output = result.GetOutput(); strm << output; } auto error_len = result.GetErrorSize(); if (error_len) { const char *error = result.GetError(); strm << error; } } if (check_error && got_error) return false; // Stop running commands. } return true; } std::string RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, const llvm::ArrayRef &commands, bool &required_command_failed, bool parse_command_directives, bool echo_commands) { required_command_failed = false; std::string s; llvm::raw_string_ostream strm(s); required_command_failed = !RunLLDBCommands(debugger, prefix, commands, strm, parse_command_directives, echo_commands); return s; } bool ThreadHasStopReason(lldb::SBThread &thread) { switch (thread.GetStopReason()) { case lldb::eStopReasonTrace: case lldb::eStopReasonPlanComplete: case lldb::eStopReasonBreakpoint: case lldb::eStopReasonWatchpoint: case lldb::eStopReasonInstrumentation: case lldb::eStopReasonSignal: case lldb::eStopReasonException: case lldb::eStopReasonExec: case lldb::eStopReasonProcessorTrace: case lldb::eStopReasonFork: case lldb::eStopReasonVFork: case lldb::eStopReasonVForkDone: case lldb::eStopReasonInterrupt: case lldb::eStopReasonHistoryBoundary: return true; case lldb::eStopReasonThreadExiting: case lldb::eStopReasonInvalid: case lldb::eStopReasonNone: break; } return false; } static uint32_t constexpr THREAD_INDEX_SHIFT = 19; uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id) { return dap_frame_id >> THREAD_INDEX_SHIFT; } uint32_t GetLLDBFrameID(uint64_t dap_frame_id) { return dap_frame_id & ((1u << THREAD_INDEX_SHIFT) - 1); } int64_t MakeDAPFrameID(lldb::SBFrame &frame) { return ((int64_t)frame.GetThread().GetIndexID() << THREAD_INDEX_SHIFT) | frame.GetFrameID(); } lldb::SBEnvironment GetEnvironmentFromArguments(const llvm::json::Object &arguments) { lldb::SBEnvironment envs{}; constexpr llvm::StringRef env_key = "env"; const llvm::json::Value *raw_json_env = arguments.get(env_key); if (!raw_json_env) return envs; if (raw_json_env->kind() == llvm::json::Value::Object) { auto env_map = GetStringMap(arguments, env_key); for (const auto &[key, value] : env_map) envs.Set(key.c_str(), value.c_str(), true); } else if (raw_json_env->kind() == llvm::json::Value::Array) { const auto envs_strings = GetStrings(&arguments, env_key); lldb::SBStringList entries{}; for (const auto &env : envs_strings) entries.AppendString(env.c_str()); envs.SetEntries(entries, true); } return envs; } lldb::StopDisassemblyType GetStopDisassemblyDisplay(lldb::SBDebugger &debugger) { lldb::StopDisassemblyType result = lldb::StopDisassemblyType::eStopDisassemblyTypeNoDebugInfo; lldb::SBStructuredData string_result = debugger.GetSetting("stop-disassembly-display"); const size_t result_length = string_result.GetStringValue(nullptr, 0); if (result_length > 0) { std::string result_string(result_length, '\0'); string_result.GetStringValue(result_string.data(), result_length + 1); result = llvm::StringSwitch(result_string) .Case("never", lldb::StopDisassemblyType::eStopDisassemblyTypeNever) .Case("always", lldb::StopDisassemblyType::eStopDisassemblyTypeAlways) .Case("no-source", lldb::StopDisassemblyType::eStopDisassemblyTypeNoSource) .Case("no-debuginfo", lldb::StopDisassemblyType::eStopDisassemblyTypeNoDebugInfo) .Default( lldb::StopDisassemblyType::eStopDisassemblyTypeNoDebugInfo); } return result; } llvm::Error ToError(const lldb::SBError &error) { if (error.Success()) return llvm::Error::success(); return llvm::createStringError( std::error_code(error.GetError(), std::generic_category()), error.GetCString()); } std::string GetStringValue(const lldb::SBStructuredData &data) { if (!data.IsValid()) return ""; const size_t str_length = data.GetStringValue(nullptr, 0); if (!str_length) return ""; std::string str(str_length, 0); data.GetStringValue(str.data(), str_length + 1); return str; } ScopeSyncMode::ScopeSyncMode(lldb::SBDebugger &debugger) : m_debugger(debugger), m_async(m_debugger.GetAsync()) { m_debugger.SetAsync(false); } ScopeSyncMode::~ScopeSyncMode() { m_debugger.SetAsync(m_async); } std::string GetSBFileSpecPath(const lldb::SBFileSpec &file_spec) { const auto directory_length = ::strlen(file_spec.GetDirectory()); const auto file_name_length = ::strlen(file_spec.GetFilename()); std::string path(directory_length + file_name_length + 1, '\0'); file_spec.GetPath(path.data(), path.length() + 1); return path; } lldb::SBLineEntry GetLineEntryForAddress(lldb::SBTarget &target, const lldb::SBAddress &address) { lldb::SBSymbolContext sc = target.ResolveSymbolContextForAddress( address, lldb::eSymbolContextLineEntry); return sc.GetLineEntry(); } } // namespace lldb_dap