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
27namespace lldb_dap {
28
29bool 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
112std::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
125bool 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
150static uint32_t constexpr THREAD_INDEX_SHIFT = 19;
151
152uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id) {
153 return dap_frame_id >> THREAD_INDEX_SHIFT;
154}
155
156uint32_t GetLLDBFrameID(uint64_t dap_frame_id) {
157 return dap_frame_id & ((1u << THREAD_INDEX_SHIFT) - 1);
158}
159
160int64_t MakeDAPFrameID(lldb::SBFrame &frame) {
161 return ((int64_t)frame.GetThread().GetIndexID() << THREAD_INDEX_SHIFT) |
162 frame.GetFrameID();
163}
164
165lldb::SBEnvironment
166GetEnvironmentFromArguments(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
190lldb::StopDisassemblyType
191GetStopDisassemblyDisplay(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
217llvm::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
226std::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
239ScopeSyncMode::ScopeSyncMode(lldb::SBDebugger &debugger)
240 : m_debugger(debugger), m_async(m_debugger.GetAsync()) {
241 m_debugger.SetAsync(false);
242}
243
244ScopeSyncMode::~ScopeSyncMode() { m_debugger.SetAsync(m_async); }
245
246std::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
255lldb::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

source code of lldb/tools/lldb-dap/LLDBUtils.cpp