| 1 | //===-- LLDBUtils.cpp -------------------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "LLDBUtils.h" |
| 10 | #include "JSONUtils.h" |
| 11 | #include "lldb/API/SBCommandInterpreter.h" |
| 12 | #include "lldb/API/SBCommandReturnObject.h" |
| 13 | #include "lldb/API/SBDebugger.h" |
| 14 | #include "lldb/API/SBFrame.h" |
| 15 | #include "lldb/API/SBStringList.h" |
| 16 | #include "lldb/API/SBStructuredData.h" |
| 17 | #include "lldb/API/SBThread.h" |
| 18 | #include "lldb/lldb-enumerations.h" |
| 19 | #include "llvm/ADT/ArrayRef.h" |
| 20 | #include "llvm/Support/JSON.h" |
| 21 | #include "llvm/Support/raw_ostream.h" |
| 22 | |
| 23 | #include <cstring> |
| 24 | #include <mutex> |
| 25 | #include <system_error> |
| 26 | |
| 27 | namespace lldb_dap { |
| 28 | |
| 29 | bool RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, |
| 30 | const llvm::ArrayRef<std::string> &commands, |
| 31 | llvm::raw_ostream &strm, bool parse_command_directives, |
| 32 | bool echo_commands) { |
| 33 | if (commands.empty()) |
| 34 | return true; |
| 35 | |
| 36 | bool did_print_prefix = false; |
| 37 | |
| 38 | // We only need the prompt when echoing commands. |
| 39 | std::string prompt_string; |
| 40 | if (echo_commands) { |
| 41 | prompt_string = "(lldb) " ; |
| 42 | |
| 43 | // Get the current prompt from settings. |
| 44 | if (const lldb::SBStructuredData prompt = debugger.GetSetting(setting: "prompt" )) { |
| 45 | const size_t prompt_length = prompt.GetStringValue(dst: nullptr, dst_len: 0); |
| 46 | |
| 47 | if (prompt_length != 0) { |
| 48 | prompt_string.resize(n: prompt_length + 1); |
| 49 | prompt.GetStringValue(dst: prompt_string.data(), dst_len: prompt_string.length()); |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | lldb::SBCommandInterpreter interp = debugger.GetCommandInterpreter(); |
| 55 | for (llvm::StringRef command : commands) { |
| 56 | lldb::SBCommandReturnObject result; |
| 57 | bool quiet_on_success = false; |
| 58 | bool check_error = false; |
| 59 | |
| 60 | while (parse_command_directives) { |
| 61 | if (command.starts_with(Prefix: "?" )) { |
| 62 | command = command.drop_front(); |
| 63 | quiet_on_success = true; |
| 64 | } else if (command.starts_with(Prefix: "!" )) { |
| 65 | command = command.drop_front(); |
| 66 | check_error = true; |
| 67 | } else { |
| 68 | break; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | { |
| 73 | // Prevent simultaneous calls to HandleCommand, e.g. EventThreadFunction |
| 74 | // may asynchronously call RunExitCommands when we are already calling |
| 75 | // RunTerminateCommands. |
| 76 | static std::mutex handle_command_mutex; |
| 77 | std::lock_guard<std::mutex> locker(handle_command_mutex); |
| 78 | interp.HandleCommand(command_line: command.str().c_str(), result, |
| 79 | /*add_to_history=*/true); |
| 80 | } |
| 81 | |
| 82 | const bool got_error = !result.Succeeded(); |
| 83 | // The if statement below is assuming we always print out `!` prefixed |
| 84 | // lines. The only time we don't print is when we have `quiet_on_success == |
| 85 | // true` and we don't have an error. |
| 86 | if (quiet_on_success ? got_error : true) { |
| 87 | if (!did_print_prefix && !prefix.empty()) { |
| 88 | strm << prefix << "\n" ; |
| 89 | did_print_prefix = true; |
| 90 | } |
| 91 | |
| 92 | if (echo_commands) |
| 93 | strm << prompt_string.c_str() << command << '\n'; |
| 94 | |
| 95 | auto output_len = result.GetOutputSize(); |
| 96 | if (output_len) { |
| 97 | const char *output = result.GetOutput(); |
| 98 | strm << output; |
| 99 | } |
| 100 | auto error_len = result.GetErrorSize(); |
| 101 | if (error_len) { |
| 102 | const char *error = result.GetError(); |
| 103 | strm << error; |
| 104 | } |
| 105 | } |
| 106 | if (check_error && got_error) |
| 107 | return false; // Stop running commands. |
| 108 | } |
| 109 | return true; |
| 110 | } |
| 111 | |
| 112 | std::string RunLLDBCommands(lldb::SBDebugger &debugger, llvm::StringRef prefix, |
| 113 | const llvm::ArrayRef<std::string> &commands, |
| 114 | bool &required_command_failed, |
| 115 | bool parse_command_directives, bool echo_commands) { |
| 116 | required_command_failed = false; |
| 117 | std::string s; |
| 118 | llvm::raw_string_ostream strm(s); |
| 119 | required_command_failed = |
| 120 | !RunLLDBCommands(debugger, prefix, commands, strm, |
| 121 | parse_command_directives, echo_commands); |
| 122 | return s; |
| 123 | } |
| 124 | |
| 125 | bool ThreadHasStopReason(lldb::SBThread &thread) { |
| 126 | switch (thread.GetStopReason()) { |
| 127 | case lldb::eStopReasonTrace: |
| 128 | case lldb::eStopReasonPlanComplete: |
| 129 | case lldb::eStopReasonBreakpoint: |
| 130 | case lldb::eStopReasonWatchpoint: |
| 131 | case lldb::eStopReasonInstrumentation: |
| 132 | case lldb::eStopReasonSignal: |
| 133 | case lldb::eStopReasonException: |
| 134 | case lldb::eStopReasonExec: |
| 135 | case lldb::eStopReasonProcessorTrace: |
| 136 | case lldb::eStopReasonFork: |
| 137 | case lldb::eStopReasonVFork: |
| 138 | case lldb::eStopReasonVForkDone: |
| 139 | case lldb::eStopReasonInterrupt: |
| 140 | case lldb::eStopReasonHistoryBoundary: |
| 141 | return true; |
| 142 | case lldb::eStopReasonThreadExiting: |
| 143 | case lldb::eStopReasonInvalid: |
| 144 | case lldb::eStopReasonNone: |
| 145 | break; |
| 146 | } |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | static uint32_t constexpr THREAD_INDEX_SHIFT = 19; |
| 151 | |
| 152 | uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id) { |
| 153 | return dap_frame_id >> THREAD_INDEX_SHIFT; |
| 154 | } |
| 155 | |
| 156 | uint32_t GetLLDBFrameID(uint64_t dap_frame_id) { |
| 157 | return dap_frame_id & ((1u << THREAD_INDEX_SHIFT) - 1); |
| 158 | } |
| 159 | |
| 160 | int64_t MakeDAPFrameID(lldb::SBFrame &frame) { |
| 161 | return ((int64_t)frame.GetThread().GetIndexID() << THREAD_INDEX_SHIFT) | |
| 162 | frame.GetFrameID(); |
| 163 | } |
| 164 | |
| 165 | lldb::SBEnvironment |
| 166 | GetEnvironmentFromArguments(const llvm::json::Object &arguments) { |
| 167 | lldb::SBEnvironment envs{}; |
| 168 | constexpr llvm::StringRef env_key = "env" ; |
| 169 | const llvm::json::Value *raw_json_env = arguments.get(K: env_key); |
| 170 | |
| 171 | if (!raw_json_env) |
| 172 | return envs; |
| 173 | |
| 174 | if (raw_json_env->kind() == llvm::json::Value::Object) { |
| 175 | auto env_map = GetStringMap(obj: arguments, key: env_key); |
| 176 | for (const auto &[key, value] : env_map) |
| 177 | envs.Set(name: key.c_str(), value: value.c_str(), overwrite: true); |
| 178 | |
| 179 | } else if (raw_json_env->kind() == llvm::json::Value::Array) { |
| 180 | const auto envs_strings = GetStrings(obj: &arguments, key: env_key); |
| 181 | lldb::SBStringList entries{}; |
| 182 | for (const auto &env : envs_strings) |
| 183 | entries.AppendString(str: env.c_str()); |
| 184 | |
| 185 | envs.SetEntries(entries, append: true); |
| 186 | } |
| 187 | return envs; |
| 188 | } |
| 189 | |
| 190 | lldb::StopDisassemblyType |
| 191 | GetStopDisassemblyDisplay(lldb::SBDebugger &debugger) { |
| 192 | lldb::StopDisassemblyType result = |
| 193 | lldb::StopDisassemblyType::eStopDisassemblyTypeNoDebugInfo; |
| 194 | lldb::SBStructuredData string_result = |
| 195 | debugger.GetSetting(setting: "stop-disassembly-display" ); |
| 196 | const size_t result_length = string_result.GetStringValue(dst: nullptr, dst_len: 0); |
| 197 | if (result_length > 0) { |
| 198 | std::string result_string(result_length, '\0'); |
| 199 | string_result.GetStringValue(dst: result_string.data(), dst_len: result_length + 1); |
| 200 | |
| 201 | result = |
| 202 | llvm::StringSwitch<lldb::StopDisassemblyType>(result_string) |
| 203 | .Case(S: "never" , Value: lldb::StopDisassemblyType::eStopDisassemblyTypeNever) |
| 204 | .Case(S: "always" , |
| 205 | Value: lldb::StopDisassemblyType::eStopDisassemblyTypeAlways) |
| 206 | .Case(S: "no-source" , |
| 207 | Value: lldb::StopDisassemblyType::eStopDisassemblyTypeNoSource) |
| 208 | .Case(S: "no-debuginfo" , |
| 209 | Value: lldb::StopDisassemblyType::eStopDisassemblyTypeNoDebugInfo) |
| 210 | .Default( |
| 211 | Value: lldb::StopDisassemblyType::eStopDisassemblyTypeNoDebugInfo); |
| 212 | } |
| 213 | |
| 214 | return result; |
| 215 | } |
| 216 | |
| 217 | llvm::Error ToError(const lldb::SBError &error) { |
| 218 | if (error.Success()) |
| 219 | return llvm::Error::success(); |
| 220 | |
| 221 | return llvm::createStringError( |
| 222 | EC: std::error_code(error.GetError(), std::generic_category()), |
| 223 | S: error.GetCString()); |
| 224 | } |
| 225 | |
| 226 | std::string GetStringValue(const lldb::SBStructuredData &data) { |
| 227 | if (!data.IsValid()) |
| 228 | return "" ; |
| 229 | |
| 230 | const size_t str_length = data.GetStringValue(dst: nullptr, dst_len: 0); |
| 231 | if (!str_length) |
| 232 | return "" ; |
| 233 | |
| 234 | std::string str(str_length, 0); |
| 235 | data.GetStringValue(dst: str.data(), dst_len: str_length + 1); |
| 236 | return str; |
| 237 | } |
| 238 | |
| 239 | ScopeSyncMode::ScopeSyncMode(lldb::SBDebugger &debugger) |
| 240 | : m_debugger(debugger), m_async(m_debugger.GetAsync()) { |
| 241 | m_debugger.SetAsync(false); |
| 242 | } |
| 243 | |
| 244 | ScopeSyncMode::~ScopeSyncMode() { m_debugger.SetAsync(m_async); } |
| 245 | |
| 246 | std::string GetSBFileSpecPath(const lldb::SBFileSpec &file_spec) { |
| 247 | const auto directory_length = ::strlen(s: file_spec.GetDirectory()); |
| 248 | const auto file_name_length = ::strlen(s: file_spec.GetFilename()); |
| 249 | |
| 250 | std::string path(directory_length + file_name_length + 1, '\0'); |
| 251 | file_spec.GetPath(dst_path: path.data(), dst_len: path.length() + 1); |
| 252 | return path; |
| 253 | } |
| 254 | |
| 255 | lldb::SBLineEntry GetLineEntryForAddress(lldb::SBTarget &target, |
| 256 | const lldb::SBAddress &address) { |
| 257 | lldb::SBSymbolContext sc = target.ResolveSymbolContextForAddress( |
| 258 | addr: address, resolve_scope: lldb::eSymbolContextLineEntry); |
| 259 | return sc.GetLineEntry(); |
| 260 | } |
| 261 | |
| 262 | } // namespace lldb_dap |
| 263 | |