/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Core/Debugger.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- Debugger.cpp ------------------------------------------------------===// |
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 "lldb/Core/Debugger.h" |
10 | | |
11 | | #include "lldb/Breakpoint/Breakpoint.h" |
12 | | #include "lldb/Core/DebuggerEvents.h" |
13 | | #include "lldb/Core/FormatEntity.h" |
14 | | #include "lldb/Core/Mangled.h" |
15 | | #include "lldb/Core/ModuleList.h" |
16 | | #include "lldb/Core/ModuleSpec.h" |
17 | | #include "lldb/Core/PluginManager.h" |
18 | | #include "lldb/Core/StreamAsynchronousIO.h" |
19 | | #include "lldb/DataFormatters/DataVisualization.h" |
20 | | #include "lldb/Expression/REPL.h" |
21 | | #include "lldb/Host/File.h" |
22 | | #include "lldb/Host/FileSystem.h" |
23 | | #include "lldb/Host/HostInfo.h" |
24 | | #include "lldb/Host/StreamFile.h" |
25 | | #include "lldb/Host/Terminal.h" |
26 | | #include "lldb/Host/ThreadLauncher.h" |
27 | | #include "lldb/Interpreter/CommandInterpreter.h" |
28 | | #include "lldb/Interpreter/CommandReturnObject.h" |
29 | | #include "lldb/Interpreter/OptionValue.h" |
30 | | #include "lldb/Interpreter/OptionValueLanguage.h" |
31 | | #include "lldb/Interpreter/OptionValueProperties.h" |
32 | | #include "lldb/Interpreter/OptionValueSInt64.h" |
33 | | #include "lldb/Interpreter/OptionValueString.h" |
34 | | #include "lldb/Interpreter/Property.h" |
35 | | #include "lldb/Interpreter/ScriptInterpreter.h" |
36 | | #include "lldb/Symbol/Function.h" |
37 | | #include "lldb/Symbol/Symbol.h" |
38 | | #include "lldb/Symbol/SymbolContext.h" |
39 | | #include "lldb/Target/Language.h" |
40 | | #include "lldb/Target/Process.h" |
41 | | #include "lldb/Target/StructuredDataPlugin.h" |
42 | | #include "lldb/Target/Target.h" |
43 | | #include "lldb/Target/TargetList.h" |
44 | | #include "lldb/Target/Thread.h" |
45 | | #include "lldb/Target/ThreadList.h" |
46 | | #include "lldb/Utility/AnsiTerminal.h" |
47 | | #include "lldb/Utility/Event.h" |
48 | | #include "lldb/Utility/LLDBLog.h" |
49 | | #include "lldb/Utility/Listener.h" |
50 | | #include "lldb/Utility/Log.h" |
51 | | #include "lldb/Utility/State.h" |
52 | | #include "lldb/Utility/Stream.h" |
53 | | #include "lldb/Utility/StreamString.h" |
54 | | #include "lldb/lldb-enumerations.h" |
55 | | |
56 | | #if defined(_WIN32) |
57 | | #include "lldb/Host/windows/PosixApi.h" |
58 | | #include "lldb/Host/windows/windows.h" |
59 | | #endif |
60 | | |
61 | | #include "llvm/ADT/STLExtras.h" |
62 | | #include "llvm/ADT/StringRef.h" |
63 | | #include "llvm/ADT/iterator.h" |
64 | | #include "llvm/Support/DynamicLibrary.h" |
65 | | #include "llvm/Support/FileSystem.h" |
66 | | #include "llvm/Support/Process.h" |
67 | | #include "llvm/Support/ThreadPool.h" |
68 | | #include "llvm/Support/Threading.h" |
69 | | #include "llvm/Support/raw_ostream.h" |
70 | | |
71 | | #include <cstdio> |
72 | | #include <cstdlib> |
73 | | #include <cstring> |
74 | | #include <list> |
75 | | #include <memory> |
76 | | #include <mutex> |
77 | | #include <optional> |
78 | | #include <set> |
79 | | #include <string> |
80 | | #include <system_error> |
81 | | |
82 | | // Includes for pipe() |
83 | | #if defined(_WIN32) |
84 | | #include <fcntl.h> |
85 | | #include <io.h> |
86 | | #else |
87 | | #include <unistd.h> |
88 | | #endif |
89 | | |
90 | | namespace lldb_private { |
91 | | class Address; |
92 | | } |
93 | | |
94 | | using namespace lldb; |
95 | | using namespace lldb_private; |
96 | | |
97 | | static lldb::user_id_t g_unique_id = 1; |
98 | | static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024; |
99 | | |
100 | | #pragma mark Static Functions |
101 | | |
102 | | static std::recursive_mutex *g_debugger_list_mutex_ptr = |
103 | | nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain |
104 | | static Debugger::DebuggerList *g_debugger_list_ptr = |
105 | | nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain |
106 | | static llvm::ThreadPool *g_thread_pool = nullptr; |
107 | | |
108 | | static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = { |
109 | | { |
110 | | Debugger::eStopDisassemblyTypeNever, |
111 | | "never", |
112 | | "Never show disassembly when displaying a stop context.", |
113 | | }, |
114 | | { |
115 | | Debugger::eStopDisassemblyTypeNoDebugInfo, |
116 | | "no-debuginfo", |
117 | | "Show disassembly when there is no debug information.", |
118 | | }, |
119 | | { |
120 | | Debugger::eStopDisassemblyTypeNoSource, |
121 | | "no-source", |
122 | | "Show disassembly when there is no source information, or the source " |
123 | | "file " |
124 | | "is missing when displaying a stop context.", |
125 | | }, |
126 | | { |
127 | | Debugger::eStopDisassemblyTypeAlways, |
128 | | "always", |
129 | | "Always show disassembly when displaying a stop context.", |
130 | | }, |
131 | | }; |
132 | | |
133 | | static constexpr OptionEnumValueElement g_language_enumerators[] = { |
134 | | { |
135 | | eScriptLanguageNone, |
136 | | "none", |
137 | | "Disable scripting languages.", |
138 | | }, |
139 | | { |
140 | | eScriptLanguagePython, |
141 | | "python", |
142 | | "Select python as the default scripting language.", |
143 | | }, |
144 | | { |
145 | | eScriptLanguageDefault, |
146 | | "default", |
147 | | "Select the lldb default as the default scripting language.", |
148 | | }, |
149 | | }; |
150 | | |
151 | | static constexpr OptionEnumValueElement g_dwim_print_verbosities[] = { |
152 | | {eDWIMPrintVerbosityNone, "none", |
153 | | "Use no verbosity when running dwim-print."}, |
154 | | {eDWIMPrintVerbosityExpression, "expression", |
155 | | "Use partial verbosity when running dwim-print - display a message when " |
156 | | "`expression` evaluation is used."}, |
157 | | {eDWIMPrintVerbosityFull, "full", |
158 | | "Use full verbosity when running dwim-print."}, |
159 | | }; |
160 | | |
161 | | static constexpr OptionEnumValueElement s_stop_show_column_values[] = { |
162 | | { |
163 | | eStopShowColumnAnsiOrCaret, |
164 | | "ansi-or-caret", |
165 | | "Highlight the stop column with ANSI terminal codes when color/ANSI " |
166 | | "mode is enabled; otherwise, fall back to using a text-only caret (^) " |
167 | | "as if \"caret-only\" mode was selected.", |
168 | | }, |
169 | | { |
170 | | eStopShowColumnAnsi, |
171 | | "ansi", |
172 | | "Highlight the stop column with ANSI terminal codes when running LLDB " |
173 | | "with color/ANSI enabled.", |
174 | | }, |
175 | | { |
176 | | eStopShowColumnCaret, |
177 | | "caret", |
178 | | "Highlight the stop column with a caret character (^) underneath the " |
179 | | "stop column. This method introduces a new line in source listings " |
180 | | "that display thread stop locations.", |
181 | | }, |
182 | | { |
183 | | eStopShowColumnNone, |
184 | | "none", |
185 | | "Do not highlight the stop column.", |
186 | | }, |
187 | | }; |
188 | | |
189 | | #define LLDB_PROPERTIES_debugger |
190 | | #include "CoreProperties.inc" |
191 | | |
192 | | enum { |
193 | | #define LLDB_PROPERTIES_debugger |
194 | | #include "CorePropertiesEnum.inc" |
195 | | }; |
196 | | |
197 | | LoadPluginCallbackType Debugger::g_load_plugin_callback = nullptr; |
198 | | |
199 | | Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx, |
200 | | VarSetOperationType op, |
201 | | llvm::StringRef property_path, |
202 | 26.5k | llvm::StringRef value) { |
203 | 26.5k | bool is_load_script = |
204 | 26.5k | (property_path == "target.load-script-from-symbol-file"); |
205 | | // These properties might change how we visualize data. |
206 | 26.5k | bool invalidate_data_vis = (property_path == "escape-non-printables"); |
207 | 26.5k | invalidate_data_vis |= |
208 | 26.5k | (property_path == "target.max-zero-padding-in-float-format"); |
209 | 26.5k | if (invalidate_data_vis) { |
210 | 10 | DataVisualization::ForceUpdate(); |
211 | 10 | } |
212 | | |
213 | 26.5k | TargetSP target_sp; |
214 | 26.5k | LoadScriptFromSymFile load_script_old_value = eLoadScriptFromSymFileFalse; |
215 | 26.5k | if (is_load_script && exe_ctx17 && exe_ctx->GetTargetSP()16 ) { |
216 | 0 | target_sp = exe_ctx->GetTargetSP(); |
217 | 0 | load_script_old_value = |
218 | 0 | target_sp->TargetProperties::GetLoadScriptFromSymbolFile(); |
219 | 0 | } |
220 | 26.5k | Status error(Properties::SetPropertyValue(exe_ctx, op, property_path, value)); |
221 | 26.5k | if (error.Success()) { |
222 | | // FIXME it would be nice to have "on-change" callbacks for properties |
223 | 26.4k | if (property_path == g_debugger_properties[ePropertyPrompt].name) { |
224 | 5 | llvm::StringRef new_prompt = GetPrompt(); |
225 | 5 | std::string str = lldb_private::ansi::FormatAnsiTerminalCodes( |
226 | 5 | new_prompt, GetUseColor()); |
227 | 5 | if (str.length()) |
228 | 5 | new_prompt = str; |
229 | 5 | GetCommandInterpreter().UpdatePrompt(new_prompt); |
230 | 5 | auto bytes = std::make_unique<EventDataBytes>(new_prompt); |
231 | 5 | auto prompt_change_event_sp = std::make_shared<Event>( |
232 | 5 | CommandInterpreter::eBroadcastBitResetPrompt, bytes.release()); |
233 | 5 | GetCommandInterpreter().BroadcastEvent(prompt_change_event_sp); |
234 | 26.4k | } else if (property_path == g_debugger_properties[ePropertyUseColor].name) { |
235 | | // use-color changed. Ping the prompt so it can reset the ansi terminal |
236 | | // codes. |
237 | 3.16k | SetPrompt(GetPrompt()); |
238 | 23.3k | } else if (property_path == |
239 | 23.3k | g_debugger_properties[ePropertyPromptAnsiPrefix].name || |
240 | 23.3k | property_path == |
241 | 23.3k | g_debugger_properties[ePropertyPromptAnsiSuffix].name) { |
242 | | // Prompt colors changed. Ping the prompt so it can reset the ansi |
243 | | // terminal codes. |
244 | 6 | SetPrompt(GetPrompt()); |
245 | 23.3k | } else if (property_path == |
246 | 23.3k | g_debugger_properties[ePropertyUseSourceCache].name) { |
247 | | // use-source-cache changed. Wipe out the cache contents if it was |
248 | | // disabled. |
249 | 3 | if (!GetUseSourceCache()) { |
250 | 1 | m_source_file_cache.Clear(); |
251 | 1 | } |
252 | 23.3k | } else if (is_load_script && target_sp17 && |
253 | 23.3k | load_script_old_value == eLoadScriptFromSymFileWarn0 ) { |
254 | 0 | if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() == |
255 | 0 | eLoadScriptFromSymFileTrue) { |
256 | 0 | std::list<Status> errors; |
257 | 0 | StreamString feedback_stream; |
258 | 0 | if (!target_sp->LoadScriptingResources(errors, feedback_stream)) { |
259 | 0 | Stream &s = GetErrorStream(); |
260 | 0 | for (auto error : errors) { |
261 | 0 | s.Printf("%s\n", error.AsCString()); |
262 | 0 | } |
263 | 0 | if (feedback_stream.GetSize()) |
264 | 0 | s.PutCString(feedback_stream.GetString()); |
265 | 0 | } |
266 | 0 | } |
267 | 0 | } |
268 | 26.4k | } |
269 | 26.5k | return error; |
270 | 26.5k | } |
271 | | |
272 | 35 | bool Debugger::GetAutoConfirm() const { |
273 | 35 | constexpr uint32_t idx = ePropertyAutoConfirm; |
274 | 35 | return GetPropertyAtIndexAs<bool>( |
275 | 35 | idx, g_debugger_properties[idx].default_uint_value != 0); |
276 | 35 | } |
277 | | |
278 | 997k | const FormatEntity::Entry *Debugger::GetDisassemblyFormat() const { |
279 | 997k | constexpr uint32_t idx = ePropertyDisassemblyFormat; |
280 | 997k | return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx); |
281 | 997k | } |
282 | | |
283 | 4.18k | const FormatEntity::Entry *Debugger::GetFrameFormat() const { |
284 | 4.18k | constexpr uint32_t idx = ePropertyFrameFormat; |
285 | 4.18k | return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx); |
286 | 4.18k | } |
287 | | |
288 | 0 | const FormatEntity::Entry *Debugger::GetFrameFormatUnique() const { |
289 | 0 | constexpr uint32_t idx = ePropertyFrameFormatUnique; |
290 | 0 | return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx); |
291 | 0 | } |
292 | | |
293 | 46 | uint64_t Debugger::GetStopDisassemblyMaxSize() const { |
294 | 46 | constexpr uint32_t idx = ePropertyStopDisassemblyMaxSize; |
295 | 46 | return GetPropertyAtIndexAs<uint64_t>( |
296 | 46 | idx, g_debugger_properties[idx].default_uint_value); |
297 | 46 | } |
298 | | |
299 | 88 | bool Debugger::GetNotifyVoid() const { |
300 | 88 | constexpr uint32_t idx = ePropertyNotiftVoid; |
301 | 88 | return GetPropertyAtIndexAs<uint64_t>( |
302 | 88 | idx, g_debugger_properties[idx].default_uint_value != 0); |
303 | 88 | } |
304 | | |
305 | 19.9k | llvm::StringRef Debugger::GetPrompt() const { |
306 | 19.9k | constexpr uint32_t idx = ePropertyPrompt; |
307 | 19.9k | return GetPropertyAtIndexAs<llvm::StringRef>( |
308 | 19.9k | idx, g_debugger_properties[idx].default_cstr_value); |
309 | 19.9k | } |
310 | | |
311 | 7 | llvm::StringRef Debugger::GetPromptAnsiPrefix() const { |
312 | 7 | const uint32_t idx = ePropertyPromptAnsiPrefix; |
313 | 7 | return GetPropertyAtIndexAs<llvm::StringRef>( |
314 | 7 | idx, g_debugger_properties[idx].default_cstr_value); |
315 | 7 | } |
316 | | |
317 | 7 | llvm::StringRef Debugger::GetPromptAnsiSuffix() const { |
318 | 7 | const uint32_t idx = ePropertyPromptAnsiSuffix; |
319 | 7 | return GetPropertyAtIndexAs<llvm::StringRef>( |
320 | 7 | idx, g_debugger_properties[idx].default_cstr_value); |
321 | 7 | } |
322 | | |
323 | 9.26k | void Debugger::SetPrompt(llvm::StringRef p) { |
324 | 9.26k | constexpr uint32_t idx = ePropertyPrompt; |
325 | 9.26k | SetPropertyAtIndex(idx, p); |
326 | 9.26k | llvm::StringRef new_prompt = GetPrompt(); |
327 | 9.26k | std::string str = |
328 | 9.26k | lldb_private::ansi::FormatAnsiTerminalCodes(new_prompt, GetUseColor()); |
329 | 9.26k | if (str.length()) |
330 | 9.26k | new_prompt = str; |
331 | 9.26k | GetCommandInterpreter().UpdatePrompt(new_prompt); |
332 | 9.26k | } |
333 | | |
334 | 750 | const FormatEntity::Entry *Debugger::GetThreadFormat() const { |
335 | 750 | constexpr uint32_t idx = ePropertyThreadFormat; |
336 | 750 | return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx); |
337 | 750 | } |
338 | | |
339 | 1.92k | const FormatEntity::Entry *Debugger::GetThreadStopFormat() const { |
340 | 1.92k | constexpr uint32_t idx = ePropertyThreadStopFormat; |
341 | 1.92k | return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx); |
342 | 1.92k | } |
343 | | |
344 | 509k | lldb::ScriptLanguage Debugger::GetScriptLanguage() const { |
345 | 509k | const uint32_t idx = ePropertyScriptLanguage; |
346 | 509k | return GetPropertyAtIndexAs<lldb::ScriptLanguage>( |
347 | 509k | idx, static_cast<lldb::ScriptLanguage>( |
348 | 509k | g_debugger_properties[idx].default_uint_value)); |
349 | 509k | } |
350 | | |
351 | 18 | bool Debugger::SetScriptLanguage(lldb::ScriptLanguage script_lang) { |
352 | 18 | const uint32_t idx = ePropertyScriptLanguage; |
353 | 18 | return SetPropertyAtIndex(idx, script_lang); |
354 | 18 | } |
355 | | |
356 | 1 | lldb::LanguageType Debugger::GetREPLLanguage() const { |
357 | 1 | const uint32_t idx = ePropertyREPLLanguage; |
358 | 1 | return GetPropertyAtIndexAs<LanguageType>(idx, {}); |
359 | 1 | } |
360 | | |
361 | 2 | bool Debugger::SetREPLLanguage(lldb::LanguageType repl_lang) { |
362 | 2 | const uint32_t idx = ePropertyREPLLanguage; |
363 | 2 | return SetPropertyAtIndex(idx, repl_lang); |
364 | 2 | } |
365 | | |
366 | 2.67k | uint64_t Debugger::GetTerminalWidth() const { |
367 | 2.67k | const uint32_t idx = ePropertyTerminalWidth; |
368 | 2.67k | return GetPropertyAtIndexAs<int64_t>( |
369 | 2.67k | idx, g_debugger_properties[idx].default_uint_value); |
370 | 2.67k | } |
371 | | |
372 | 15 | bool Debugger::SetTerminalWidth(uint64_t term_width) { |
373 | 15 | if (auto handler_sp = m_io_handler_stack.Top()) |
374 | 1 | handler_sp->TerminalSizeChanged(); |
375 | | |
376 | 15 | const uint32_t idx = ePropertyTerminalWidth; |
377 | 15 | return SetPropertyAtIndex(idx, term_width); |
378 | 15 | } |
379 | | |
380 | 2.72k | bool Debugger::GetUseExternalEditor() const { |
381 | 2.72k | const uint32_t idx = ePropertyUseExternalEditor; |
382 | 2.72k | return GetPropertyAtIndexAs<bool>( |
383 | 2.72k | idx, g_debugger_properties[idx].default_uint_value != 0); |
384 | 2.72k | } |
385 | | |
386 | 395 | bool Debugger::SetUseExternalEditor(bool b) { |
387 | 395 | const uint32_t idx = ePropertyUseExternalEditor; |
388 | 395 | return SetPropertyAtIndex(idx, b); |
389 | 395 | } |
390 | | |
391 | 0 | llvm::StringRef Debugger::GetExternalEditor() const { |
392 | 0 | const uint32_t idx = ePropertyExternalEditor; |
393 | 0 | return GetPropertyAtIndexAs<llvm::StringRef>( |
394 | 0 | idx, g_debugger_properties[idx].default_cstr_value); |
395 | 0 | } |
396 | | |
397 | 0 | bool Debugger::SetExternalEditor(llvm::StringRef editor) { |
398 | 0 | const uint32_t idx = ePropertyExternalEditor; |
399 | 0 | return SetPropertyAtIndex(idx, editor); |
400 | 0 | } |
401 | | |
402 | 3.03M | bool Debugger::GetUseColor() const { |
403 | 3.03M | const uint32_t idx = ePropertyUseColor; |
404 | 3.03M | return GetPropertyAtIndexAs<bool>( |
405 | 3.03M | idx, g_debugger_properties[idx].default_uint_value != 0); |
406 | 3.03M | } |
407 | | |
408 | 6.09k | bool Debugger::SetUseColor(bool b) { |
409 | 6.09k | const uint32_t idx = ePropertyUseColor; |
410 | 6.09k | bool ret = SetPropertyAtIndex(idx, b); |
411 | 6.09k | SetPrompt(GetPrompt()); |
412 | 6.09k | return ret; |
413 | 6.09k | } |
414 | | |
415 | 18.2k | bool Debugger::GetShowProgress() const { |
416 | 18.2k | const uint32_t idx = ePropertyShowProgress; |
417 | 18.2k | return GetPropertyAtIndexAs<bool>( |
418 | 18.2k | idx, g_debugger_properties[idx].default_uint_value != 0); |
419 | 18.2k | } |
420 | | |
421 | 0 | bool Debugger::SetShowProgress(bool show_progress) { |
422 | 0 | const uint32_t idx = ePropertyShowProgress; |
423 | 0 | return SetPropertyAtIndex(idx, show_progress); |
424 | 0 | } |
425 | | |
426 | 1.10k | llvm::StringRef Debugger::GetShowProgressAnsiPrefix() const { |
427 | 1.10k | const uint32_t idx = ePropertyShowProgressAnsiPrefix; |
428 | 1.10k | return GetPropertyAtIndexAs<llvm::StringRef>( |
429 | 1.10k | idx, g_debugger_properties[idx].default_cstr_value); |
430 | 1.10k | } |
431 | | |
432 | 1.10k | llvm::StringRef Debugger::GetShowProgressAnsiSuffix() const { |
433 | 1.10k | const uint32_t idx = ePropertyShowProgressAnsiSuffix; |
434 | 1.10k | return GetPropertyAtIndexAs<llvm::StringRef>( |
435 | 1.10k | idx, g_debugger_properties[idx].default_cstr_value); |
436 | 1.10k | } |
437 | | |
438 | 12 | bool Debugger::GetUseAutosuggestion() const { |
439 | 12 | const uint32_t idx = ePropertyShowAutosuggestion; |
440 | 12 | return GetPropertyAtIndexAs<bool>( |
441 | 12 | idx, g_debugger_properties[idx].default_uint_value != 0); |
442 | 12 | } |
443 | | |
444 | 0 | llvm::StringRef Debugger::GetAutosuggestionAnsiPrefix() const { |
445 | 0 | const uint32_t idx = ePropertyShowAutosuggestionAnsiPrefix; |
446 | 0 | return GetPropertyAtIndexAs<llvm::StringRef>( |
447 | 0 | idx, g_debugger_properties[idx].default_cstr_value); |
448 | 0 | } |
449 | | |
450 | 0 | llvm::StringRef Debugger::GetAutosuggestionAnsiSuffix() const { |
451 | 0 | const uint32_t idx = ePropertyShowAutosuggestionAnsiSuffix; |
452 | 0 | return GetPropertyAtIndexAs<llvm::StringRef>( |
453 | 0 | idx, g_debugger_properties[idx].default_cstr_value); |
454 | 0 | } |
455 | | |
456 | 20 | bool Debugger::GetShowDontUsePoHint() const { |
457 | 20 | const uint32_t idx = ePropertyShowDontUsePoHint; |
458 | 20 | return GetPropertyAtIndexAs<bool>( |
459 | 20 | idx, g_debugger_properties[idx].default_uint_value != 0); |
460 | 20 | } |
461 | | |
462 | 13.8k | bool Debugger::GetUseSourceCache() const { |
463 | 13.8k | const uint32_t idx = ePropertyUseSourceCache; |
464 | 13.8k | return GetPropertyAtIndexAs<bool>( |
465 | 13.8k | idx, g_debugger_properties[idx].default_uint_value != 0); |
466 | 13.8k | } |
467 | | |
468 | 0 | bool Debugger::SetUseSourceCache(bool b) { |
469 | 0 | const uint32_t idx = ePropertyUseSourceCache; |
470 | 0 | bool ret = SetPropertyAtIndex(idx, b); |
471 | 0 | if (!ret) { |
472 | 0 | m_source_file_cache.Clear(); |
473 | 0 | } |
474 | 0 | return ret; |
475 | 0 | } |
476 | 31 | bool Debugger::GetHighlightSource() const { |
477 | 31 | const uint32_t idx = ePropertyHighlightSource; |
478 | 31 | return GetPropertyAtIndexAs<bool>( |
479 | 31 | idx, g_debugger_properties[idx].default_uint_value != 0); |
480 | 31 | } |
481 | | |
482 | 1.51k | StopShowColumn Debugger::GetStopShowColumn() const { |
483 | 1.51k | const uint32_t idx = ePropertyStopShowColumn; |
484 | 1.51k | return GetPropertyAtIndexAs<lldb::StopShowColumn>( |
485 | 1.51k | idx, static_cast<lldb::StopShowColumn>( |
486 | 1.51k | g_debugger_properties[idx].default_uint_value)); |
487 | 1.51k | } |
488 | | |
489 | 31 | llvm::StringRef Debugger::GetStopShowColumnAnsiPrefix() const { |
490 | 31 | const uint32_t idx = ePropertyStopShowColumnAnsiPrefix; |
491 | 31 | return GetPropertyAtIndexAs<llvm::StringRef>( |
492 | 31 | idx, g_debugger_properties[idx].default_cstr_value); |
493 | 31 | } |
494 | | |
495 | 31 | llvm::StringRef Debugger::GetStopShowColumnAnsiSuffix() const { |
496 | 31 | const uint32_t idx = ePropertyStopShowColumnAnsiSuffix; |
497 | 31 | return GetPropertyAtIndexAs<llvm::StringRef>( |
498 | 31 | idx, g_debugger_properties[idx].default_cstr_value); |
499 | 31 | } |
500 | | |
501 | 31 | llvm::StringRef Debugger::GetStopShowLineMarkerAnsiPrefix() const { |
502 | 31 | const uint32_t idx = ePropertyStopShowLineMarkerAnsiPrefix; |
503 | 31 | return GetPropertyAtIndexAs<llvm::StringRef>( |
504 | 31 | idx, g_debugger_properties[idx].default_cstr_value); |
505 | 31 | } |
506 | | |
507 | 31 | llvm::StringRef Debugger::GetStopShowLineMarkerAnsiSuffix() const { |
508 | 31 | const uint32_t idx = ePropertyStopShowLineMarkerAnsiSuffix; |
509 | 31 | return GetPropertyAtIndexAs<llvm::StringRef>( |
510 | 31 | idx, g_debugger_properties[idx].default_cstr_value); |
511 | 31 | } |
512 | | |
513 | 3.19k | uint64_t Debugger::GetStopSourceLineCount(bool before) const { |
514 | 3.19k | const uint32_t idx = |
515 | 3.19k | before ? ePropertyStopLineCountBefore1.59k : ePropertyStopLineCountAfter1.59k ; |
516 | 3.19k | return GetPropertyAtIndexAs<uint64_t>( |
517 | 3.19k | idx, g_debugger_properties[idx].default_uint_value); |
518 | 3.19k | } |
519 | | |
520 | 1.59k | Debugger::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const { |
521 | 1.59k | const uint32_t idx = ePropertyStopDisassemblyDisplay; |
522 | 1.59k | return GetPropertyAtIndexAs<Debugger::StopDisassemblyType>( |
523 | 1.59k | idx, static_cast<Debugger::StopDisassemblyType>( |
524 | 1.59k | g_debugger_properties[idx].default_uint_value)); |
525 | 1.59k | } |
526 | | |
527 | 113 | uint64_t Debugger::GetDisassemblyLineCount() const { |
528 | 113 | const uint32_t idx = ePropertyStopDisassemblyCount; |
529 | 113 | return GetPropertyAtIndexAs<uint64_t>( |
530 | 113 | idx, g_debugger_properties[idx].default_uint_value); |
531 | 113 | } |
532 | | |
533 | 35.8k | bool Debugger::GetAutoOneLineSummaries() const { |
534 | 35.8k | const uint32_t idx = ePropertyAutoOneLineSummaries; |
535 | 35.8k | return GetPropertyAtIndexAs<bool>( |
536 | 35.8k | idx, g_debugger_properties[idx].default_uint_value != 0); |
537 | 35.8k | } |
538 | | |
539 | 3.86k | bool Debugger::GetEscapeNonPrintables() const { |
540 | 3.86k | const uint32_t idx = ePropertyEscapeNonPrintables; |
541 | 3.86k | return GetPropertyAtIndexAs<bool>( |
542 | 3.86k | idx, g_debugger_properties[idx].default_uint_value != 0); |
543 | 3.86k | } |
544 | | |
545 | 0 | bool Debugger::GetAutoIndent() const { |
546 | 0 | const uint32_t idx = ePropertyAutoIndent; |
547 | 0 | return GetPropertyAtIndexAs<bool>( |
548 | 0 | idx, g_debugger_properties[idx].default_uint_value != 0); |
549 | 0 | } |
550 | | |
551 | 0 | bool Debugger::SetAutoIndent(bool b) { |
552 | 0 | const uint32_t idx = ePropertyAutoIndent; |
553 | 0 | return SetPropertyAtIndex(idx, b); |
554 | 0 | } |
555 | | |
556 | 0 | bool Debugger::GetPrintDecls() const { |
557 | 0 | const uint32_t idx = ePropertyPrintDecls; |
558 | 0 | return GetPropertyAtIndexAs<bool>( |
559 | 0 | idx, g_debugger_properties[idx].default_uint_value != 0); |
560 | 0 | } |
561 | | |
562 | 0 | bool Debugger::SetPrintDecls(bool b) { |
563 | 0 | const uint32_t idx = ePropertyPrintDecls; |
564 | 0 | return SetPropertyAtIndex(idx, b); |
565 | 0 | } |
566 | | |
567 | 0 | uint64_t Debugger::GetTabSize() const { |
568 | 0 | const uint32_t idx = ePropertyTabSize; |
569 | 0 | return GetPropertyAtIndexAs<uint64_t>( |
570 | 0 | idx, g_debugger_properties[idx].default_uint_value); |
571 | 0 | } |
572 | | |
573 | 0 | bool Debugger::SetTabSize(uint64_t tab_size) { |
574 | 0 | const uint32_t idx = ePropertyTabSize; |
575 | 0 | return SetPropertyAtIndex(idx, tab_size); |
576 | 0 | } |
577 | | |
578 | 74 | lldb::DWIMPrintVerbosity Debugger::GetDWIMPrintVerbosity() const { |
579 | 74 | const uint32_t idx = ePropertyDWIMPrintVerbosity; |
580 | 74 | return GetPropertyAtIndexAs<lldb::DWIMPrintVerbosity>( |
581 | 74 | idx, static_cast<lldb::DWIMPrintVerbosity>( |
582 | 74 | g_debugger_properties[idx].default_uint_value)); |
583 | 74 | } |
584 | | |
585 | | #pragma mark Debugger |
586 | | |
587 | | // const DebuggerPropertiesSP & |
588 | | // Debugger::GetSettings() const |
589 | | //{ |
590 | | // return m_properties_sp; |
591 | | //} |
592 | | // |
593 | | |
594 | 3.94k | void Debugger::Initialize(LoadPluginCallbackType load_plugin_callback) { |
595 | 3.94k | assert(g_debugger_list_ptr == nullptr && |
596 | 3.94k | "Debugger::Initialize called more than once!"); |
597 | 3.94k | g_debugger_list_mutex_ptr = new std::recursive_mutex(); |
598 | 3.94k | g_debugger_list_ptr = new DebuggerList(); |
599 | 3.94k | g_thread_pool = new llvm::ThreadPool(llvm::optimal_concurrency()); |
600 | 3.94k | g_load_plugin_callback = load_plugin_callback; |
601 | 3.94k | } |
602 | | |
603 | 3.92k | void Debugger::Terminate() { |
604 | 3.92k | assert(g_debugger_list_ptr && |
605 | 3.92k | "Debugger::Terminate called without a matching Debugger::Initialize!"); |
606 | | |
607 | 3.92k | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
608 | 3.92k | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
609 | 3.92k | for (const auto &debugger : *g_debugger_list_ptr) |
610 | 196 | debugger->HandleDestroyCallback(); |
611 | 3.92k | } |
612 | | |
613 | 3.92k | if (g_thread_pool) { |
614 | | // The destructor will wait for all the threads to complete. |
615 | 3.92k | delete g_thread_pool; |
616 | 3.92k | } |
617 | | |
618 | 3.92k | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
619 | | // Clear our global list of debugger objects |
620 | 3.92k | { |
621 | 3.92k | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
622 | 3.92k | for (const auto &debugger : *g_debugger_list_ptr) |
623 | 196 | debugger->Clear(); |
624 | 3.92k | g_debugger_list_ptr->clear(); |
625 | 3.92k | } |
626 | 3.92k | } |
627 | 3.92k | } |
628 | | |
629 | 3.92k | void Debugger::SettingsInitialize() { Target::SettingsInitialize(); } |
630 | | |
631 | 3.92k | void Debugger::SettingsTerminate() { Target::SettingsTerminate(); } |
632 | | |
633 | 1 | bool Debugger::LoadPlugin(const FileSpec &spec, Status &error) { |
634 | 1 | if (g_load_plugin_callback) { |
635 | 1 | llvm::sys::DynamicLibrary dynlib = |
636 | 1 | g_load_plugin_callback(shared_from_this(), spec, error); |
637 | 1 | if (dynlib.isValid()) { |
638 | 0 | m_loaded_plugins.push_back(dynlib); |
639 | 0 | return true; |
640 | 0 | } |
641 | 1 | } else { |
642 | | // The g_load_plugin_callback is registered in SBDebugger::Initialize() and |
643 | | // if the public API layer isn't available (code is linking against all of |
644 | | // the internal LLDB static libraries), then we can't load plugins |
645 | 0 | error.SetErrorString("Public API layer is not available"); |
646 | 0 | } |
647 | 1 | return false; |
648 | 1 | } |
649 | | |
650 | | static FileSystem::EnumerateDirectoryResult |
651 | | LoadPluginCallback(void *baton, llvm::sys::fs::file_type ft, |
652 | 0 | llvm::StringRef path) { |
653 | 0 | Status error; |
654 | |
|
655 | 0 | static constexpr llvm::StringLiteral g_dylibext(".dylib"); |
656 | 0 | static constexpr llvm::StringLiteral g_solibext(".so"); |
657 | |
|
658 | 0 | if (!baton) |
659 | 0 | return FileSystem::eEnumerateDirectoryResultQuit; |
660 | | |
661 | 0 | Debugger *debugger = (Debugger *)baton; |
662 | |
|
663 | 0 | namespace fs = llvm::sys::fs; |
664 | | // If we have a regular file, a symbolic link or unknown file type, try and |
665 | | // process the file. We must handle unknown as sometimes the directory |
666 | | // enumeration might be enumerating a file system that doesn't have correct |
667 | | // file type information. |
668 | 0 | if (ft == fs::file_type::regular_file || ft == fs::file_type::symlink_file || |
669 | 0 | ft == fs::file_type::type_unknown) { |
670 | 0 | FileSpec plugin_file_spec(path); |
671 | 0 | FileSystem::Instance().Resolve(plugin_file_spec); |
672 | |
|
673 | 0 | if (plugin_file_spec.GetFileNameExtension() != g_dylibext && |
674 | 0 | plugin_file_spec.GetFileNameExtension() != g_solibext) { |
675 | 0 | return FileSystem::eEnumerateDirectoryResultNext; |
676 | 0 | } |
677 | | |
678 | 0 | Status plugin_load_error; |
679 | 0 | debugger->LoadPlugin(plugin_file_spec, plugin_load_error); |
680 | |
|
681 | 0 | return FileSystem::eEnumerateDirectoryResultNext; |
682 | 0 | } else if (ft == fs::file_type::directory_file || |
683 | 0 | ft == fs::file_type::symlink_file || |
684 | 0 | ft == fs::file_type::type_unknown) { |
685 | | // Try and recurse into anything that a directory or symbolic link. We must |
686 | | // also do this for unknown as sometimes the directory enumeration might be |
687 | | // enumerating a file system that doesn't have correct file type |
688 | | // information. |
689 | 0 | return FileSystem::eEnumerateDirectoryResultEnter; |
690 | 0 | } |
691 | | |
692 | 0 | return FileSystem::eEnumerateDirectoryResultNext; |
693 | 0 | } |
694 | | |
695 | 6.09k | void Debugger::InstanceInitialize() { |
696 | 6.09k | const bool find_directories = true; |
697 | 6.09k | const bool find_files = true; |
698 | 6.09k | const bool find_other = true; |
699 | 6.09k | char dir_path[PATH_MAX]; |
700 | 6.09k | if (FileSpec dir_spec = HostInfo::GetSystemPluginDir()) { |
701 | 0 | if (FileSystem::Instance().Exists(dir_spec) && |
702 | 0 | dir_spec.GetPath(dir_path, sizeof(dir_path))) { |
703 | 0 | FileSystem::Instance().EnumerateDirectory(dir_path, find_directories, |
704 | 0 | find_files, find_other, |
705 | 0 | LoadPluginCallback, this); |
706 | 0 | } |
707 | 0 | } |
708 | | |
709 | 6.09k | if (FileSpec dir_spec = HostInfo::GetUserPluginDir()) { |
710 | 6.09k | if (FileSystem::Instance().Exists(dir_spec) && |
711 | 6.09k | dir_spec.GetPath(dir_path, sizeof(dir_path))0 ) { |
712 | 0 | FileSystem::Instance().EnumerateDirectory(dir_path, find_directories, |
713 | 0 | find_files, find_other, |
714 | 0 | LoadPluginCallback, this); |
715 | 0 | } |
716 | 6.09k | } |
717 | | |
718 | 6.09k | PluginManager::DebuggerInitialize(*this); |
719 | 6.09k | } |
720 | | |
721 | | DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback, |
722 | 6.09k | void *baton) { |
723 | 6.09k | DebuggerSP debugger_sp(new Debugger(log_callback, baton)); |
724 | 6.09k | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr6.04k ) { |
725 | 6.04k | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
726 | 6.04k | g_debugger_list_ptr->push_back(debugger_sp); |
727 | 6.04k | } |
728 | 6.09k | debugger_sp->InstanceInitialize(); |
729 | 6.09k | return debugger_sp; |
730 | 6.09k | } |
731 | | |
732 | 6.04k | void Debugger::HandleDestroyCallback() { |
733 | 6.04k | if (m_destroy_callback) { |
734 | 1 | m_destroy_callback(GetID(), m_destroy_callback_baton); |
735 | 1 | m_destroy_callback = nullptr; |
736 | 1 | } |
737 | 6.04k | } |
738 | | |
739 | 5.85k | void Debugger::Destroy(DebuggerSP &debugger_sp) { |
740 | 5.85k | if (!debugger_sp) |
741 | 5 | return; |
742 | | |
743 | 5.85k | debugger_sp->HandleDestroyCallback(); |
744 | 5.85k | CommandInterpreter &cmd_interpreter = debugger_sp->GetCommandInterpreter(); |
745 | | |
746 | 5.85k | if (cmd_interpreter.GetSaveSessionOnQuit()) { |
747 | 1 | CommandReturnObject result(debugger_sp->GetUseColor()); |
748 | 1 | cmd_interpreter.SaveTranscript(result); |
749 | 1 | if (result.Succeeded()) |
750 | 1 | (*debugger_sp->GetAsyncOutputStream()) << result.GetOutputData() << '\n'; |
751 | 0 | else |
752 | 0 | (*debugger_sp->GetAsyncErrorStream()) << result.GetErrorData() << '\n'; |
753 | 1 | } |
754 | | |
755 | 5.85k | debugger_sp->Clear(); |
756 | | |
757 | 5.85k | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
758 | 5.85k | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
759 | 5.85k | DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); |
760 | 5.87k | for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos26 ) { |
761 | 5.87k | if ((*pos).get() == debugger_sp.get()) { |
762 | 5.85k | g_debugger_list_ptr->erase(pos); |
763 | 5.85k | return; |
764 | 5.85k | } |
765 | 5.87k | } |
766 | 5.85k | } |
767 | 5.85k | } |
768 | | |
769 | | DebuggerSP |
770 | 7 | Debugger::FindDebuggerWithInstanceName(llvm::StringRef instance_name) { |
771 | 7 | if (!g_debugger_list_ptr || !g_debugger_list_mutex_ptr) |
772 | 0 | return DebuggerSP(); |
773 | | |
774 | 7 | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
775 | 7 | for (const DebuggerSP &debugger_sp : *g_debugger_list_ptr) { |
776 | 7 | if (!debugger_sp) |
777 | 0 | continue; |
778 | | |
779 | 7 | if (llvm::StringRef(debugger_sp->GetInstanceName()) == instance_name) |
780 | 5 | return debugger_sp; |
781 | 7 | } |
782 | 2 | return DebuggerSP(); |
783 | 7 | } |
784 | | |
785 | 0 | TargetSP Debugger::FindTargetWithProcessID(lldb::pid_t pid) { |
786 | 0 | TargetSP target_sp; |
787 | 0 | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
788 | 0 | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
789 | 0 | DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); |
790 | 0 | for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) { |
791 | 0 | target_sp = (*pos)->GetTargetList().FindTargetWithProcessID(pid); |
792 | 0 | if (target_sp) |
793 | 0 | break; |
794 | 0 | } |
795 | 0 | } |
796 | 0 | return target_sp; |
797 | 0 | } |
798 | | |
799 | 0 | TargetSP Debugger::FindTargetWithProcess(Process *process) { |
800 | 0 | TargetSP target_sp; |
801 | 0 | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
802 | 0 | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
803 | 0 | DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); |
804 | 0 | for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) { |
805 | 0 | target_sp = (*pos)->GetTargetList().FindTargetWithProcess(process); |
806 | 0 | if (target_sp) |
807 | 0 | break; |
808 | 0 | } |
809 | 0 | } |
810 | 0 | return target_sp; |
811 | 0 | } |
812 | | |
813 | 6.09k | ConstString Debugger::GetStaticBroadcasterClass() { |
814 | 6.09k | static ConstString class_name("lldb.debugger"); |
815 | 6.09k | return class_name; |
816 | 6.09k | } |
817 | | |
818 | | Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) |
819 | 6.09k | : UserID(g_unique_id++), |
820 | 6.09k | Properties(std::make_shared<OptionValueProperties>()), |
821 | 6.09k | m_input_file_sp(std::make_shared<NativeFile>(stdin, false)), |
822 | 6.09k | m_output_stream_sp(std::make_shared<StreamFile>(stdout, false)), |
823 | 6.09k | m_error_stream_sp(std::make_shared<StreamFile>(stderr, false)), |
824 | 6.09k | m_input_recorder(nullptr), |
825 | 6.09k | m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()), |
826 | 6.09k | m_terminal_state(), m_target_list(*this), m_platform_list(), |
827 | 6.09k | m_listener_sp(Listener::MakeListener("lldb.Debugger")), |
828 | 6.09k | m_source_manager_up(), m_source_file_cache(), |
829 | | m_command_interpreter_up( |
830 | 6.09k | std::make_unique<CommandInterpreter>(*this, false)), |
831 | 6.09k | m_io_handler_stack(), |
832 | 6.09k | m_instance_name(llvm::formatv("debugger_{0}", GetID()).str()), |
833 | 6.09k | m_loaded_plugins(), m_event_handler_thread(), m_io_handler_thread(), |
834 | 6.09k | m_sync_broadcaster(nullptr, "lldb.debugger.sync"), |
835 | 6.09k | m_broadcaster(m_broadcaster_manager_sp, |
836 | 6.09k | GetStaticBroadcasterClass().AsCString()), |
837 | 6.09k | m_forward_listener_sp(), m_clear_once() { |
838 | | // Initialize the debugger properties as early as possible as other parts of |
839 | | // LLDB will start querying them during construction. |
840 | 6.09k | m_collection_sp->Initialize(g_debugger_properties); |
841 | 6.09k | m_collection_sp->AppendProperty( |
842 | 6.09k | "target", "Settings specify to debugging targets.", true, |
843 | 6.09k | Target::GetGlobalProperties().GetValueProperties()); |
844 | 6.09k | m_collection_sp->AppendProperty( |
845 | 6.09k | "platform", "Platform settings.", true, |
846 | 6.09k | Platform::GetGlobalPlatformProperties().GetValueProperties()); |
847 | 6.09k | m_collection_sp->AppendProperty( |
848 | 6.09k | "symbols", "Symbol lookup and cache settings.", true, |
849 | 6.09k | ModuleList::GetGlobalModuleListProperties().GetValueProperties()); |
850 | 6.09k | if (m_command_interpreter_up) { |
851 | 6.09k | m_collection_sp->AppendProperty( |
852 | 6.09k | "interpreter", |
853 | 6.09k | "Settings specify to the debugger's command interpreter.", true, |
854 | 6.09k | m_command_interpreter_up->GetValueProperties()); |
855 | 6.09k | } |
856 | 6.09k | if (log_callback) |
857 | 0 | m_callback_handler_sp = |
858 | 0 | std::make_shared<CallbackLogHandler>(log_callback, baton); |
859 | 6.09k | m_command_interpreter_up->Initialize(); |
860 | | // Always add our default platform to the platform list |
861 | 6.09k | PlatformSP default_platform_sp(Platform::GetHostPlatform()); |
862 | 6.09k | assert(default_platform_sp); |
863 | 6.09k | m_platform_list.Append(default_platform_sp, true); |
864 | | |
865 | | // Create the dummy target. |
866 | 6.09k | { |
867 | 6.09k | ArchSpec arch(Target::GetDefaultArchitecture()); |
868 | 6.09k | if (!arch.IsValid()) |
869 | 6.09k | arch = HostInfo::GetArchitecture(); |
870 | 6.09k | assert(arch.IsValid() && "No valid default or host archspec"); |
871 | 6.09k | const bool is_dummy_target = true; |
872 | 6.09k | m_dummy_target_sp.reset( |
873 | 6.09k | new Target(*this, arch, default_platform_sp, is_dummy_target)); |
874 | 6.09k | } |
875 | 0 | assert(m_dummy_target_sp.get() && "Couldn't construct dummy target?"); |
876 | | |
877 | 6.09k | OptionValueSInt64 *term_width = |
878 | 6.09k | m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64( |
879 | 6.09k | ePropertyTerminalWidth); |
880 | 6.09k | term_width->SetMinimumValue(10); |
881 | 6.09k | term_width->SetMaximumValue(1024); |
882 | | |
883 | | // Turn off use-color if this is a dumb terminal. |
884 | 6.09k | const char *term = getenv("TERM"); |
885 | 6.09k | if (term && !strcmp(term, "dumb")6.07k ) |
886 | 0 | SetUseColor(false); |
887 | | // Turn off use-color if we don't write to a terminal with color support. |
888 | 6.09k | if (!GetOutputFile().GetIsTerminalWithColors()) |
889 | 6.08k | SetUseColor(false); |
890 | | |
891 | 6.09k | if (Diagnostics::Enabled()) { |
892 | 6.04k | m_diagnostics_callback_id = Diagnostics::Instance().AddCallback( |
893 | 6.04k | [this](const FileSpec &dir) -> llvm::Error { |
894 | 4 | for (auto &entry : m_stream_handlers) { |
895 | 1 | llvm::StringRef log_path = entry.first(); |
896 | 1 | llvm::StringRef file_name = llvm::sys::path::filename(log_path); |
897 | 1 | FileSpec destination = dir.CopyByAppendingPathComponent(file_name); |
898 | 1 | std::error_code ec = |
899 | 1 | llvm::sys::fs::copy_file(log_path, destination.GetPath()); |
900 | 1 | if (ec) |
901 | 0 | return llvm::errorCodeToError(ec); |
902 | 1 | } |
903 | 4 | return llvm::Error::success(); |
904 | 4 | }); |
905 | 6.04k | } |
906 | | |
907 | | #if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) |
908 | | // Enabling use of ANSI color codes because LLDB is using them to highlight |
909 | | // text. |
910 | | llvm::sys::Process::UseANSIEscapeCodes(true); |
911 | | #endif |
912 | 6.09k | } |
913 | | |
914 | 6.08k | Debugger::~Debugger() { Clear(); } |
915 | | |
916 | 12.1k | void Debugger::Clear() { |
917 | | // Make sure we call this function only once. With the C++ global destructor |
918 | | // chain having a list of debuggers and with code that can be running on |
919 | | // other threads, we need to ensure this doesn't happen multiple times. |
920 | | // |
921 | | // The following functions call Debugger::Clear(): |
922 | | // Debugger::~Debugger(); |
923 | | // static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp); |
924 | | // static void Debugger::Terminate(); |
925 | 12.1k | llvm::call_once(m_clear_once, [this]() { |
926 | 6.09k | ClearIOHandlers(); |
927 | 6.09k | StopIOHandlerThread(); |
928 | 6.09k | StopEventHandlerThread(); |
929 | 6.09k | m_listener_sp->Clear(); |
930 | 6.09k | for (TargetSP target_sp : m_target_list.Targets()) { |
931 | 369 | if (target_sp) { |
932 | 369 | if (ProcessSP process_sp = target_sp->GetProcessSP()) |
933 | 193 | process_sp->Finalize(); |
934 | 369 | target_sp->Destroy(); |
935 | 369 | } |
936 | 369 | } |
937 | 6.09k | m_broadcaster_manager_sp->Clear(); |
938 | | |
939 | | // Close the input file _before_ we close the input read communications |
940 | | // class as it does NOT own the input file, our m_input_file does. |
941 | 6.09k | m_terminal_state.Clear(); |
942 | 6.09k | GetInputFile().Close(); |
943 | | |
944 | 6.09k | m_command_interpreter_up->Clear(); |
945 | | |
946 | 6.09k | if (Diagnostics::Enabled()) |
947 | 6.04k | Diagnostics::Instance().RemoveCallback(m_diagnostics_callback_id); |
948 | 6.09k | }); |
949 | 12.1k | } |
950 | | |
951 | 6.31k | bool Debugger::GetAsyncExecution() { |
952 | 6.31k | return !m_command_interpreter_up->GetSynchronous(); |
953 | 6.31k | } |
954 | | |
955 | 11.1k | void Debugger::SetAsyncExecution(bool async_execution) { |
956 | 11.1k | m_command_interpreter_up->SetSynchronous(!async_execution); |
957 | 11.1k | } |
958 | | |
959 | 0 | repro::DataRecorder *Debugger::GetInputRecorder() { return m_input_recorder; } |
960 | | |
961 | 397 | static inline int OpenPipe(int fds[2], std::size_t size) { |
962 | | #ifdef _WIN32 |
963 | | return _pipe(fds, size, O_BINARY); |
964 | | #else |
965 | 397 | (void)size; |
966 | 397 | return pipe(fds); |
967 | 397 | #endif |
968 | 397 | } |
969 | | |
970 | 397 | Status Debugger::SetInputString(const char *data) { |
971 | 397 | Status result; |
972 | 397 | enum PIPES { READ, WRITE }; // Indexes for the read and write fds |
973 | 397 | int fds[2] = {-1, -1}; |
974 | | |
975 | 397 | if (data == nullptr) { |
976 | 0 | result.SetErrorString("String data is null"); |
977 | 0 | return result; |
978 | 0 | } |
979 | | |
980 | 397 | size_t size = strlen(data); |
981 | 397 | if (size == 0) { |
982 | 0 | result.SetErrorString("String data is empty"); |
983 | 0 | return result; |
984 | 0 | } |
985 | | |
986 | 397 | if (OpenPipe(fds, size) != 0) { |
987 | 0 | result.SetErrorString( |
988 | 0 | "can't create pipe file descriptors for LLDB commands"); |
989 | 0 | return result; |
990 | 0 | } |
991 | | |
992 | 397 | int r = write(fds[WRITE], data, size); |
993 | 397 | (void)r; |
994 | | // Close the write end of the pipe, so that the command interpreter will exit |
995 | | // when it consumes all the data. |
996 | 397 | llvm::sys::Process::SafelyCloseFileDescriptor(fds[WRITE]); |
997 | | |
998 | | // Open the read file descriptor as a FILE * that we can return as an input |
999 | | // handle. |
1000 | 397 | FILE *commands_file = fdopen(fds[READ], "rb"); |
1001 | 397 | if (commands_file == nullptr) { |
1002 | 0 | result.SetErrorStringWithFormat("fdopen(%i, \"rb\") failed (errno = %i) " |
1003 | 0 | "when trying to open LLDB commands pipe", |
1004 | 0 | fds[READ], errno); |
1005 | 0 | llvm::sys::Process::SafelyCloseFileDescriptor(fds[READ]); |
1006 | 0 | return result; |
1007 | 0 | } |
1008 | | |
1009 | 397 | SetInputFile((FileSP)std::make_shared<NativeFile>(commands_file, true)); |
1010 | 397 | return result; |
1011 | 397 | } |
1012 | | |
1013 | 946 | void Debugger::SetInputFile(FileSP file_sp) { |
1014 | 946 | assert(file_sp && file_sp->IsValid()); |
1015 | 946 | m_input_file_sp = std::move(file_sp); |
1016 | | // Save away the terminal state if that is relevant, so that we can restore |
1017 | | // it in RestoreInputState. |
1018 | 946 | SaveInputTerminalState(); |
1019 | 946 | } |
1020 | | |
1021 | 480 | void Debugger::SetOutputFile(FileSP file_sp) { |
1022 | 480 | assert(file_sp && file_sp->IsValid()); |
1023 | 480 | m_output_stream_sp = std::make_shared<StreamFile>(file_sp); |
1024 | 480 | } |
1025 | | |
1026 | 461 | void Debugger::SetErrorFile(FileSP file_sp) { |
1027 | 461 | assert(file_sp && file_sp->IsValid()); |
1028 | 461 | m_error_stream_sp = std::make_shared<StreamFile>(file_sp); |
1029 | 461 | } |
1030 | | |
1031 | 947 | void Debugger::SaveInputTerminalState() { |
1032 | 947 | int fd = GetInputFile().GetDescriptor(); |
1033 | 947 | if (fd != File::kInvalidDescriptor) |
1034 | 945 | m_terminal_state.Save(fd, true); |
1035 | 947 | } |
1036 | | |
1037 | 1 | void Debugger::RestoreInputTerminalState() { m_terminal_state.Restore(); } |
1038 | | |
1039 | 362k | ExecutionContext Debugger::GetSelectedExecutionContext() { |
1040 | 362k | bool adopt_selected = true; |
1041 | 362k | ExecutionContextRef exe_ctx_ref(GetSelectedTarget().get(), adopt_selected); |
1042 | 362k | return ExecutionContext(exe_ctx_ref); |
1043 | 362k | } |
1044 | | |
1045 | 2 | void Debugger::DispatchInputInterrupt() { |
1046 | 2 | std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); |
1047 | 2 | IOHandlerSP reader_sp(m_io_handler_stack.Top()); |
1048 | 2 | if (reader_sp) |
1049 | 2 | reader_sp->Interrupt(); |
1050 | 2 | } |
1051 | | |
1052 | 0 | void Debugger::DispatchInputEndOfFile() { |
1053 | 0 | std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); |
1054 | 0 | IOHandlerSP reader_sp(m_io_handler_stack.Top()); |
1055 | 0 | if (reader_sp) |
1056 | 0 | reader_sp->GotEOF(); |
1057 | 0 | } |
1058 | | |
1059 | 6.63k | void Debugger::ClearIOHandlers() { |
1060 | | // The bottom input reader should be the main debugger input reader. We do |
1061 | | // not want to close that one here. |
1062 | 6.63k | std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); |
1063 | 6.63k | while (m_io_handler_stack.GetSize() > 1) { |
1064 | 0 | IOHandlerSP reader_sp(m_io_handler_stack.Top()); |
1065 | 0 | if (reader_sp) |
1066 | 0 | PopIOHandler(reader_sp); |
1067 | 0 | } |
1068 | 6.63k | } |
1069 | | |
1070 | 540 | void Debugger::RunIOHandlers() { |
1071 | 540 | IOHandlerSP reader_sp = m_io_handler_stack.Top(); |
1072 | 1.11k | while (true) { |
1073 | 1.11k | if (!reader_sp) |
1074 | 540 | break; |
1075 | | |
1076 | 570 | reader_sp->Run(); |
1077 | 570 | { |
1078 | 570 | std::lock_guard<std::recursive_mutex> guard( |
1079 | 570 | m_io_handler_synchronous_mutex); |
1080 | | |
1081 | | // Remove all input readers that are done from the top of the stack |
1082 | 1.12k | while (true) { |
1083 | 1.12k | IOHandlerSP top_reader_sp = m_io_handler_stack.Top(); |
1084 | 1.12k | if (top_reader_sp && top_reader_sp->GetIsDone()582 ) |
1085 | 552 | PopIOHandler(top_reader_sp); |
1086 | 570 | else |
1087 | 570 | break; |
1088 | 1.12k | } |
1089 | 570 | reader_sp = m_io_handler_stack.Top(); |
1090 | 570 | } |
1091 | 570 | } |
1092 | 540 | ClearIOHandlers(); |
1093 | 540 | } |
1094 | | |
1095 | 916 | void Debugger::RunIOHandlerSync(const IOHandlerSP &reader_sp) { |
1096 | 916 | std::lock_guard<std::recursive_mutex> guard(m_io_handler_synchronous_mutex); |
1097 | | |
1098 | 916 | PushIOHandler(reader_sp); |
1099 | 916 | IOHandlerSP top_reader_sp = reader_sp; |
1100 | | |
1101 | 924 | while (top_reader_sp) { |
1102 | 924 | if (!top_reader_sp) |
1103 | 0 | break; |
1104 | | |
1105 | 924 | top_reader_sp->Run(); |
1106 | | |
1107 | | // Don't unwind past the starting point. |
1108 | 924 | if (top_reader_sp.get() == reader_sp.get()) { |
1109 | 920 | if (PopIOHandler(reader_sp)) |
1110 | 916 | break; |
1111 | 920 | } |
1112 | | |
1113 | | // If we pushed new IO handlers, pop them if they're done or restart the |
1114 | | // loop to run them if they're not. |
1115 | 12 | while (8 true) { |
1116 | 12 | top_reader_sp = m_io_handler_stack.Top(); |
1117 | 12 | if (top_reader_sp && top_reader_sp->GetIsDone()) { |
1118 | 4 | PopIOHandler(top_reader_sp); |
1119 | | // Don't unwind past the starting point. |
1120 | 4 | if (top_reader_sp.get() == reader_sp.get()) |
1121 | 0 | return; |
1122 | 8 | } else { |
1123 | 8 | break; |
1124 | 8 | } |
1125 | 12 | } |
1126 | 8 | } |
1127 | 916 | } |
1128 | | |
1129 | 0 | bool Debugger::IsTopIOHandler(const lldb::IOHandlerSP &reader_sp) { |
1130 | 0 | return m_io_handler_stack.IsTop(reader_sp); |
1131 | 0 | } |
1132 | | |
1133 | | bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type, |
1134 | 0 | IOHandler::Type second_top_type) { |
1135 | 0 | return m_io_handler_stack.CheckTopIOHandlerTypes(top_type, second_top_type); |
1136 | 0 | } |
1137 | | |
1138 | 2.47k | void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) { |
1139 | 2.47k | bool printed = m_io_handler_stack.PrintAsync(s, len, is_stdout); |
1140 | 2.47k | if (!printed) { |
1141 | 177 | lldb::StreamFileSP stream = |
1142 | 177 | is_stdout ? m_output_stream_sp100 : m_error_stream_sp77 ; |
1143 | 177 | stream->Write(s, len); |
1144 | 177 | } |
1145 | 2.47k | } |
1146 | | |
1147 | 0 | llvm::StringRef Debugger::GetTopIOHandlerControlSequence(char ch) { |
1148 | 0 | return m_io_handler_stack.GetTopIOHandlerControlSequence(ch); |
1149 | 0 | } |
1150 | | |
1151 | 25 | const char *Debugger::GetIOHandlerCommandPrefix() { |
1152 | 25 | return m_io_handler_stack.GetTopIOHandlerCommandPrefix(); |
1153 | 25 | } |
1154 | | |
1155 | 13 | const char *Debugger::GetIOHandlerHelpPrologue() { |
1156 | 13 | return m_io_handler_stack.GetTopIOHandlerHelpPrologue(); |
1157 | 13 | } |
1158 | | |
1159 | 17.2k | bool Debugger::RemoveIOHandler(const IOHandlerSP &reader_sp) { |
1160 | 17.2k | return PopIOHandler(reader_sp); |
1161 | 17.2k | } |
1162 | | |
1163 | | void Debugger::RunIOHandlerAsync(const IOHandlerSP &reader_sp, |
1164 | 8.10k | bool cancel_top_handler) { |
1165 | 8.10k | PushIOHandler(reader_sp, cancel_top_handler); |
1166 | 8.10k | } |
1167 | | |
1168 | | void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out, |
1169 | 7.08k | StreamFileSP &err) { |
1170 | | // Before an IOHandler runs, it must have in/out/err streams. This function |
1171 | | // is called when one ore more of the streams are nullptr. We use the top |
1172 | | // input reader's in/out/err streams, or fall back to the debugger file |
1173 | | // handles, or we fall back onto stdin/stdout/stderr as a last resort. |
1174 | | |
1175 | 7.08k | std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); |
1176 | 7.08k | IOHandlerSP top_reader_sp(m_io_handler_stack.Top()); |
1177 | | // If no STDIN has been set, then set it appropriately |
1178 | 7.08k | if (!in || !in->IsValid()901 ) { |
1179 | 6.18k | if (top_reader_sp) |
1180 | 778 | in = top_reader_sp->GetInputFileSP(); |
1181 | 5.41k | else |
1182 | 5.41k | in = GetInputFileSP(); |
1183 | | // If there is nothing, use stdin |
1184 | 6.18k | if (!in) |
1185 | 0 | in = std::make_shared<NativeFile>(stdin, false); |
1186 | 6.18k | } |
1187 | | // If no STDOUT has been set, then set it appropriately |
1188 | 7.08k | if (!out || !out->GetFile().IsValid()0 ) { |
1189 | 7.08k | if (top_reader_sp) |
1190 | 1.67k | out = top_reader_sp->GetOutputStreamFileSP(); |
1191 | 5.41k | else |
1192 | 5.41k | out = GetOutputStreamSP(); |
1193 | | // If there is nothing, use stdout |
1194 | 7.08k | if (!out) |
1195 | 0 | out = std::make_shared<StreamFile>(stdout, false); |
1196 | 7.08k | } |
1197 | | // If no STDERR has been set, then set it appropriately |
1198 | 7.08k | if (!err || !err->GetFile().IsValid()0 ) { |
1199 | 7.08k | if (top_reader_sp) |
1200 | 1.67k | err = top_reader_sp->GetErrorStreamFileSP(); |
1201 | 5.41k | else |
1202 | 5.41k | err = GetErrorStreamSP(); |
1203 | | // If there is nothing, use stderr |
1204 | 7.08k | if (!err) |
1205 | 0 | err = std::make_shared<StreamFile>(stderr, false); |
1206 | 7.08k | } |
1207 | 7.08k | } |
1208 | | |
1209 | | void Debugger::PushIOHandler(const IOHandlerSP &reader_sp, |
1210 | 9.02k | bool cancel_top_handler) { |
1211 | 9.02k | if (!reader_sp) |
1212 | 0 | return; |
1213 | | |
1214 | 9.02k | std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); |
1215 | | |
1216 | | // Get the current top input reader... |
1217 | 9.02k | IOHandlerSP top_reader_sp(m_io_handler_stack.Top()); |
1218 | | |
1219 | | // Don't push the same IO handler twice... |
1220 | 9.02k | if (reader_sp == top_reader_sp) |
1221 | 63 | return; |
1222 | | |
1223 | | // Push our new input reader |
1224 | 8.95k | m_io_handler_stack.Push(reader_sp); |
1225 | 8.95k | reader_sp->Activate(); |
1226 | | |
1227 | | // Interrupt the top input reader to it will exit its Run() function and let |
1228 | | // this new input reader take over |
1229 | 8.95k | if (top_reader_sp) { |
1230 | 1.04k | top_reader_sp->Deactivate(); |
1231 | 1.04k | if (cancel_top_handler) |
1232 | 1.03k | top_reader_sp->Cancel(); |
1233 | 1.04k | } |
1234 | 8.95k | } |
1235 | | |
1236 | 18.7k | bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) { |
1237 | 18.7k | if (!pop_reader_sp) |
1238 | 0 | return false; |
1239 | | |
1240 | 18.7k | std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex()); |
1241 | | |
1242 | | // The reader on the stop of the stack is done, so let the next read on the |
1243 | | // stack refresh its prompt and if there is one... |
1244 | 18.7k | if (m_io_handler_stack.IsEmpty()) |
1245 | 9.58k | return false; |
1246 | | |
1247 | 9.18k | IOHandlerSP reader_sp(m_io_handler_stack.Top()); |
1248 | | |
1249 | 9.18k | if (pop_reader_sp != reader_sp) |
1250 | 227 | return false; |
1251 | | |
1252 | 8.95k | reader_sp->Deactivate(); |
1253 | 8.95k | reader_sp->Cancel(); |
1254 | 8.95k | m_io_handler_stack.Pop(); |
1255 | | |
1256 | 8.95k | reader_sp = m_io_handler_stack.Top(); |
1257 | 8.95k | if (reader_sp) |
1258 | 1.04k | reader_sp->Activate(); |
1259 | | |
1260 | 8.95k | return true; |
1261 | 9.18k | } |
1262 | | |
1263 | 3.76k | StreamSP Debugger::GetAsyncOutputStream() { |
1264 | 3.76k | return std::make_shared<StreamAsynchronousIO>(*this, true, GetUseColor()); |
1265 | 3.76k | } |
1266 | | |
1267 | 3.70k | StreamSP Debugger::GetAsyncErrorStream() { |
1268 | 3.70k | return std::make_shared<StreamAsynchronousIO>(*this, false, GetUseColor()); |
1269 | 3.70k | } |
1270 | | |
1271 | 7 | void Debugger::RequestInterrupt() { |
1272 | 7 | std::lock_guard<std::mutex> guard(m_interrupt_mutex); |
1273 | 7 | m_interrupt_requested++; |
1274 | 7 | } |
1275 | | |
1276 | 9 | void Debugger::CancelInterruptRequest() { |
1277 | 9 | std::lock_guard<std::mutex> guard(m_interrupt_mutex); |
1278 | 9 | if (m_interrupt_requested > 0) |
1279 | 7 | m_interrupt_requested--; |
1280 | 9 | } |
1281 | | |
1282 | 1.47M | bool Debugger::InterruptRequested() { |
1283 | | // This is the one we should call internally. This will return true either |
1284 | | // if there's a debugger interrupt and we aren't on the IOHandler thread, |
1285 | | // or if we are on the IOHandler thread and there's a CommandInterpreter |
1286 | | // interrupt. |
1287 | 1.47M | if (!IsIOHandlerThreadCurrentThread()) { |
1288 | 1.45M | std::lock_guard<std::mutex> guard(m_interrupt_mutex); |
1289 | 1.45M | return m_interrupt_requested != 0; |
1290 | 1.45M | } |
1291 | 18.3k | return GetCommandInterpreter().WasInterrupted(); |
1292 | 1.47M | } |
1293 | | |
1294 | | Debugger::InterruptionReport::InterruptionReport( |
1295 | | std::string function_name, const llvm::formatv_object_base &payload) |
1296 | 7 | : m_function_name(std::move(function_name)), |
1297 | 7 | m_interrupt_time(std::chrono::system_clock::now()), |
1298 | 7 | m_thread_id(llvm::get_threadid()) { |
1299 | 7 | llvm::raw_string_ostream desc(m_description); |
1300 | 7 | desc << payload << "\n"; |
1301 | 7 | } |
1302 | | |
1303 | 7 | void Debugger::ReportInterruption(const InterruptionReport &report) { |
1304 | | // For now, just log the description: |
1305 | 7 | Log *log = GetLog(LLDBLog::Host); |
1306 | 7 | LLDB_LOG(log, "Interruption: {0}", report.m_description); |
1307 | 7 | } |
1308 | | |
1309 | 117k | Debugger::DebuggerList Debugger::DebuggersRequestingInterruption() { |
1310 | 117k | DebuggerList result; |
1311 | 117k | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr117k ) { |
1312 | 117k | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
1313 | 119k | for (auto debugger_sp : *g_debugger_list_ptr) { |
1314 | 119k | if (debugger_sp->InterruptRequested()) |
1315 | 0 | result.push_back(debugger_sp); |
1316 | 119k | } |
1317 | 117k | } |
1318 | 117k | return result; |
1319 | 117k | } |
1320 | | |
1321 | 207 | size_t Debugger::GetNumDebuggers() { |
1322 | 207 | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
1323 | 207 | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
1324 | 207 | return g_debugger_list_ptr->size(); |
1325 | 207 | } |
1326 | 0 | return 0; |
1327 | 207 | } |
1328 | | |
1329 | 112 | lldb::DebuggerSP Debugger::GetDebuggerAtIndex(size_t index) { |
1330 | 112 | DebuggerSP debugger_sp; |
1331 | | |
1332 | 112 | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
1333 | 112 | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
1334 | 112 | if (index < g_debugger_list_ptr->size()) |
1335 | 112 | debugger_sp = g_debugger_list_ptr->at(index); |
1336 | 112 | } |
1337 | | |
1338 | 112 | return debugger_sp; |
1339 | 112 | } |
1340 | | |
1341 | 9.60k | DebuggerSP Debugger::FindDebuggerWithID(lldb::user_id_t id) { |
1342 | 9.60k | DebuggerSP debugger_sp; |
1343 | | |
1344 | 9.60k | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
1345 | 9.60k | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
1346 | 9.60k | DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); |
1347 | 9.64k | for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos38 ) { |
1348 | 9.64k | if ((*pos)->GetID() == id) { |
1349 | 9.60k | debugger_sp = *pos; |
1350 | 9.60k | break; |
1351 | 9.60k | } |
1352 | 9.64k | } |
1353 | 9.60k | } |
1354 | 9.60k | return debugger_sp; |
1355 | 9.60k | } |
1356 | | |
1357 | | bool Debugger::FormatDisassemblerAddress(const FormatEntity::Entry *format, |
1358 | | const SymbolContext *sc, |
1359 | | const SymbolContext *prev_sc, |
1360 | | const ExecutionContext *exe_ctx, |
1361 | 1.94M | const Address *addr, Stream &s) { |
1362 | 1.94M | FormatEntity::Entry format_entry; |
1363 | | |
1364 | 1.94M | if (format == nullptr) { |
1365 | 969k | if (exe_ctx != nullptr && exe_ctx->HasTargetScope()) |
1366 | 969k | format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); |
1367 | 969k | if (format == nullptr) { |
1368 | 0 | FormatEntity::Parse("${addr}: ", format_entry); |
1369 | 0 | format = &format_entry; |
1370 | 0 | } |
1371 | 969k | } |
1372 | 1.94M | bool function_changed = false; |
1373 | 1.94M | bool initial_function = false; |
1374 | 1.94M | if (prev_sc && (969k prev_sc->function969k || prev_sc->symbol968k )) { |
1375 | 941k | if (sc && (sc->function || sc->symbol940k )) { |
1376 | 941k | if (prev_sc->symbol && sc->symbol) { |
1377 | 941k | if (!sc->symbol->Compare(prev_sc->symbol->GetName(), |
1378 | 941k | prev_sc->symbol->GetType())) { |
1379 | 7 | function_changed = true; |
1380 | 7 | } |
1381 | 941k | } else if (0 prev_sc->function0 && sc->function0 ) { |
1382 | 0 | if (prev_sc->function->GetMangled() != sc->function->GetMangled()) { |
1383 | 0 | function_changed = true; |
1384 | 0 | } |
1385 | 0 | } |
1386 | 941k | } |
1387 | 941k | } |
1388 | | // The first context on a list of instructions will have a prev_sc that has |
1389 | | // no Function or Symbol -- if SymbolContext had an IsValid() method, it |
1390 | | // would return false. But we do get a prev_sc pointer. |
1391 | 1.94M | if ((sc && (1.93M sc->function1.93M || sc->symbol1.93M )) && prev_sc1.93M && |
1392 | 1.94M | (969k prev_sc->function == nullptr969k && prev_sc->symbol == nullptr968k )) { |
1393 | 28.3k | initial_function = true; |
1394 | 28.3k | } |
1395 | 1.94M | return FormatEntity::Format(*format, s, sc, exe_ctx, addr, nullptr, |
1396 | 1.94M | function_changed, initial_function); |
1397 | 1.94M | } |
1398 | | |
1399 | | void Debugger::AssertCallback(llvm::StringRef message, |
1400 | | llvm::StringRef backtrace, |
1401 | 0 | llvm::StringRef prompt) { |
1402 | 0 | Debugger::ReportError( |
1403 | 0 | llvm::formatv("{0}\n{1}{2}", message, backtrace, prompt).str()); |
1404 | 0 | } |
1405 | | |
1406 | | void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback, |
1407 | 1 | void *baton) { |
1408 | | // For simplicity's sake, I am not going to deal with how to close down any |
1409 | | // open logging streams, I just redirect everything from here on out to the |
1410 | | // callback. |
1411 | 1 | m_callback_handler_sp = |
1412 | 1 | std::make_shared<CallbackLogHandler>(log_callback, baton); |
1413 | 1 | } |
1414 | | |
1415 | | void Debugger::SetDestroyCallback( |
1416 | 1 | lldb_private::DebuggerDestroyCallback destroy_callback, void *baton) { |
1417 | 1 | m_destroy_callback = destroy_callback; |
1418 | 1 | m_destroy_callback_baton = baton; |
1419 | 1 | } |
1420 | | |
1421 | | static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id, |
1422 | | std::string title, std::string details, |
1423 | | uint64_t completed, uint64_t total, |
1424 | 519k | bool is_debugger_specific) { |
1425 | | // Only deliver progress events if we have any progress listeners. |
1426 | 519k | const uint32_t event_type = Debugger::eBroadcastBitProgress; |
1427 | 519k | if (!debugger.GetBroadcaster().EventTypeHasListeners(event_type)) |
1428 | 500k | return; |
1429 | 18.9k | EventSP event_sp(new Event( |
1430 | 18.9k | event_type, |
1431 | 18.9k | new ProgressEventData(progress_id, std::move(title), std::move(details), |
1432 | 18.9k | completed, total, is_debugger_specific))); |
1433 | 18.9k | debugger.GetBroadcaster().BroadcastEvent(event_sp); |
1434 | 18.9k | } |
1435 | | |
1436 | | void Debugger::ReportProgress(uint64_t progress_id, std::string title, |
1437 | | std::string details, uint64_t completed, |
1438 | | uint64_t total, |
1439 | 510k | std::optional<lldb::user_id_t> debugger_id) { |
1440 | | // Check if this progress is for a specific debugger. |
1441 | 510k | if (debugger_id) { |
1442 | | // It is debugger specific, grab it and deliver the event if the debugger |
1443 | | // still exists. |
1444 | 0 | DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id); |
1445 | 0 | if (debugger_sp) |
1446 | 0 | PrivateReportProgress(*debugger_sp, progress_id, std::move(title), |
1447 | 0 | std::move(details), completed, total, |
1448 | 0 | /*is_debugger_specific*/ true); |
1449 | 0 | return; |
1450 | 0 | } |
1451 | | // The progress event is not debugger specific, iterate over all debuggers |
1452 | | // and deliver a progress event to each one. |
1453 | 510k | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr510k ) { |
1454 | 510k | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
1455 | 510k | DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); |
1456 | 1.03M | for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos519k ) |
1457 | 519k | PrivateReportProgress(*(*pos), progress_id, title, details, completed, |
1458 | 519k | total, /*is_debugger_specific*/ false); |
1459 | 510k | } |
1460 | 510k | } |
1461 | | |
1462 | | static void PrivateReportDiagnostic(Debugger &debugger, |
1463 | | DiagnosticEventData::Type type, |
1464 | | std::string message, |
1465 | 107 | bool debugger_specific) { |
1466 | 107 | uint32_t event_type = 0; |
1467 | 107 | switch (type) { |
1468 | 0 | case DiagnosticEventData::Type::Info: |
1469 | 0 | assert(false && "DiagnosticEventData::Type::Info should not be broadcast"); |
1470 | 0 | return; |
1471 | 78 | case DiagnosticEventData::Type::Warning: |
1472 | 78 | event_type = Debugger::eBroadcastBitWarning; |
1473 | 78 | break; |
1474 | 29 | case DiagnosticEventData::Type::Error: |
1475 | 29 | event_type = Debugger::eBroadcastBitError; |
1476 | 29 | break; |
1477 | 107 | } |
1478 | | |
1479 | 107 | Broadcaster &broadcaster = debugger.GetBroadcaster(); |
1480 | 107 | if (!broadcaster.EventTypeHasListeners(event_type)) { |
1481 | | // Diagnostics are too important to drop. If nobody is listening, print the |
1482 | | // diagnostic directly to the debugger's error stream. |
1483 | 74 | DiagnosticEventData event_data(type, std::move(message), debugger_specific); |
1484 | 74 | StreamSP stream = debugger.GetAsyncErrorStream(); |
1485 | 74 | event_data.Dump(stream.get()); |
1486 | 74 | return; |
1487 | 74 | } |
1488 | 33 | EventSP event_sp = std::make_shared<Event>( |
1489 | 33 | event_type, |
1490 | 33 | new DiagnosticEventData(type, std::move(message), debugger_specific)); |
1491 | 33 | broadcaster.BroadcastEvent(event_sp); |
1492 | 33 | } |
1493 | | |
1494 | | void Debugger::ReportDiagnosticImpl(DiagnosticEventData::Type type, |
1495 | | std::string message, |
1496 | | std::optional<lldb::user_id_t> debugger_id, |
1497 | 725 | std::once_flag *once) { |
1498 | 725 | auto ReportDiagnosticLambda = [&]() { |
1499 | | // The diagnostic subsystem is optional but we still want to broadcast |
1500 | | // events when it's disabled. |
1501 | 108 | if (Diagnostics::Enabled()) |
1502 | 100 | Diagnostics::Instance().Report(message); |
1503 | | |
1504 | | // We don't broadcast info events. |
1505 | 108 | if (type == DiagnosticEventData::Type::Info) |
1506 | 0 | return; |
1507 | | |
1508 | | // Check if this diagnostic is for a specific debugger. |
1509 | 108 | if (debugger_id) { |
1510 | | // It is debugger specific, grab it and deliver the event if the debugger |
1511 | | // still exists. |
1512 | 70 | DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id); |
1513 | 70 | if (debugger_sp) |
1514 | 70 | PrivateReportDiagnostic(*debugger_sp, type, std::move(message), true); |
1515 | 70 | return; |
1516 | 70 | } |
1517 | | // The diagnostic event is not debugger specific, iterate over all debuggers |
1518 | | // and deliver a diagnostic event to each one. |
1519 | 38 | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr35 ) { |
1520 | 35 | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
1521 | 35 | for (const auto &debugger : *g_debugger_list_ptr) |
1522 | 37 | PrivateReportDiagnostic(*debugger, type, message, false); |
1523 | 35 | } |
1524 | 38 | }; |
1525 | | |
1526 | 725 | if (once) |
1527 | 652 | std::call_once(*once, ReportDiagnosticLambda); |
1528 | 73 | else |
1529 | 73 | ReportDiagnosticLambda(); |
1530 | 725 | } |
1531 | | |
1532 | | void Debugger::ReportWarning(std::string message, |
1533 | | std::optional<lldb::user_id_t> debugger_id, |
1534 | 695 | std::once_flag *once) { |
1535 | 695 | ReportDiagnosticImpl(DiagnosticEventData::Type::Warning, std::move(message), |
1536 | 695 | debugger_id, once); |
1537 | 695 | } |
1538 | | |
1539 | | void Debugger::ReportError(std::string message, |
1540 | | std::optional<lldb::user_id_t> debugger_id, |
1541 | 30 | std::once_flag *once) { |
1542 | 30 | ReportDiagnosticImpl(DiagnosticEventData::Type::Error, std::move(message), |
1543 | 30 | debugger_id, once); |
1544 | 30 | } |
1545 | | |
1546 | | void Debugger::ReportInfo(std::string message, |
1547 | | std::optional<lldb::user_id_t> debugger_id, |
1548 | 0 | std::once_flag *once) { |
1549 | 0 | ReportDiagnosticImpl(DiagnosticEventData::Type::Info, std::move(message), |
1550 | 0 | debugger_id, once); |
1551 | 0 | } |
1552 | | |
1553 | 0 | void Debugger::ReportSymbolChange(const ModuleSpec &module_spec) { |
1554 | 0 | if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { |
1555 | 0 | std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr); |
1556 | 0 | for (DebuggerSP debugger_sp : *g_debugger_list_ptr) { |
1557 | 0 | EventSP event_sp = std::make_shared<Event>( |
1558 | 0 | Debugger::eBroadcastSymbolChange, |
1559 | 0 | new SymbolChangeEventData(debugger_sp, module_spec)); |
1560 | 0 | debugger_sp->GetBroadcaster().BroadcastEvent(event_sp); |
1561 | 0 | } |
1562 | 0 | } |
1563 | 0 | } |
1564 | | |
1565 | | static std::shared_ptr<LogHandler> |
1566 | | CreateLogHandler(LogHandlerKind log_handler_kind, int fd, bool should_close, |
1567 | 93 | size_t buffer_size) { |
1568 | 93 | switch (log_handler_kind) { |
1569 | 92 | case eLogHandlerStream: |
1570 | 92 | return std::make_shared<StreamLogHandler>(fd, should_close, buffer_size); |
1571 | 1 | case eLogHandlerCircular: |
1572 | 1 | return std::make_shared<RotatingLogHandler>(buffer_size); |
1573 | 0 | case eLogHandlerSystem: |
1574 | 0 | return std::make_shared<SystemLogHandler>(); |
1575 | 0 | case eLogHandlerCallback: |
1576 | 0 | return {}; |
1577 | 93 | } |
1578 | 0 | return {}; |
1579 | 93 | } |
1580 | | |
1581 | | bool Debugger::EnableLog(llvm::StringRef channel, |
1582 | | llvm::ArrayRef<const char *> categories, |
1583 | | llvm::StringRef log_file, uint32_t log_options, |
1584 | | size_t buffer_size, LogHandlerKind log_handler_kind, |
1585 | 95 | llvm::raw_ostream &error_stream) { |
1586 | | |
1587 | 95 | std::shared_ptr<LogHandler> log_handler_sp; |
1588 | 95 | if (m_callback_handler_sp) { |
1589 | 1 | log_handler_sp = m_callback_handler_sp; |
1590 | | // For now when using the callback mode you always get thread & timestamp. |
1591 | 1 | log_options |= |
1592 | 1 | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME; |
1593 | 94 | } else if (log_file.empty()) { |
1594 | 61 | log_handler_sp = |
1595 | 61 | CreateLogHandler(log_handler_kind, GetOutputFile().GetDescriptor(), |
1596 | 61 | /*should_close=*/false, buffer_size); |
1597 | 61 | } else { |
1598 | 33 | auto pos = m_stream_handlers.find(log_file); |
1599 | 33 | if (pos != m_stream_handlers.end()) |
1600 | 6 | log_handler_sp = pos->second.lock(); |
1601 | 33 | if (!log_handler_sp) { |
1602 | 33 | File::OpenOptions flags = |
1603 | 33 | File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate; |
1604 | 33 | if (log_options & LLDB_LOG_OPTION_APPEND) |
1605 | 1 | flags |= File::eOpenOptionAppend; |
1606 | 32 | else |
1607 | 32 | flags |= File::eOpenOptionTruncate; |
1608 | 33 | llvm::Expected<FileUP> file = FileSystem::Instance().Open( |
1609 | 33 | FileSpec(log_file), flags, lldb::eFilePermissionsFileDefault, false); |
1610 | 33 | if (!file) { |
1611 | 1 | error_stream << "Unable to open log file '" << log_file |
1612 | 1 | << "': " << llvm::toString(file.takeError()) << "\n"; |
1613 | 1 | return false; |
1614 | 1 | } |
1615 | | |
1616 | 32 | log_handler_sp = |
1617 | 32 | CreateLogHandler(log_handler_kind, (*file)->GetDescriptor(), |
1618 | 32 | /*should_close=*/true, buffer_size); |
1619 | 32 | m_stream_handlers[log_file] = log_handler_sp; |
1620 | 32 | } |
1621 | 33 | } |
1622 | 94 | assert(log_handler_sp); |
1623 | | |
1624 | 94 | if (log_options == 0) |
1625 | 91 | log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME; |
1626 | | |
1627 | 94 | return Log::EnableLogChannel(log_handler_sp, log_options, channel, categories, |
1628 | 94 | error_stream); |
1629 | 94 | } |
1630 | | |
1631 | | ScriptInterpreter * |
1632 | | Debugger::GetScriptInterpreter(bool can_create, |
1633 | 45.0k | std::optional<lldb::ScriptLanguage> language) { |
1634 | 45.0k | std::lock_guard<std::recursive_mutex> locker(m_script_interpreter_mutex); |
1635 | 45.0k | lldb::ScriptLanguage script_language = |
1636 | 45.0k | language ? *language297 : GetScriptLanguage()44.7k ; |
1637 | | |
1638 | 45.0k | if (!m_script_interpreters[script_language]) { |
1639 | 1.81k | if (!can_create) |
1640 | 0 | return nullptr; |
1641 | 1.81k | m_script_interpreters[script_language] = |
1642 | 1.81k | PluginManager::GetScriptInterpreterForLanguage(script_language, *this); |
1643 | 1.81k | } |
1644 | | |
1645 | 45.0k | return m_script_interpreters[script_language].get(); |
1646 | 45.0k | } |
1647 | | |
1648 | 21 | SourceManager &Debugger::GetSourceManager() { |
1649 | 21 | if (!m_source_manager_up) |
1650 | 8 | m_source_manager_up = std::make_unique<SourceManager>(shared_from_this()); |
1651 | 21 | return *m_source_manager_up; |
1652 | 21 | } |
1653 | | |
1654 | | // This function handles events that were broadcast by the process. |
1655 | 196 | void Debugger::HandleBreakpointEvent(const EventSP &event_sp) { |
1656 | 196 | using namespace lldb; |
1657 | 196 | const uint32_t event_type = |
1658 | 196 | Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent( |
1659 | 196 | event_sp); |
1660 | | |
1661 | | // if (event_type & eBreakpointEventTypeAdded |
1662 | | // || event_type & eBreakpointEventTypeRemoved |
1663 | | // || event_type & eBreakpointEventTypeEnabled |
1664 | | // || event_type & eBreakpointEventTypeDisabled |
1665 | | // || event_type & eBreakpointEventTypeCommandChanged |
1666 | | // || event_type & eBreakpointEventTypeConditionChanged |
1667 | | // || event_type & eBreakpointEventTypeIgnoreChanged |
1668 | | // || event_type & eBreakpointEventTypeLocationsResolved) |
1669 | | // { |
1670 | | // // Don't do anything about these events, since the breakpoint |
1671 | | // commands already echo these actions. |
1672 | | // } |
1673 | | // |
1674 | 196 | if (event_type & eBreakpointEventTypeLocationsAdded) { |
1675 | 3 | uint32_t num_new_locations = |
1676 | 3 | Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent( |
1677 | 3 | event_sp); |
1678 | 3 | if (num_new_locations > 0) { |
1679 | 3 | BreakpointSP breakpoint = |
1680 | 3 | Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp); |
1681 | 3 | StreamSP output_sp(GetAsyncOutputStream()); |
1682 | 3 | if (output_sp) { |
1683 | 3 | output_sp->Printf("%d location%s added to breakpoint %d\n", |
1684 | 3 | num_new_locations, num_new_locations == 1 ? "" : "s"0 , |
1685 | 3 | breakpoint->GetID()); |
1686 | 3 | output_sp->Flush(); |
1687 | 3 | } |
1688 | 3 | } |
1689 | 3 | } |
1690 | | // else if (event_type & eBreakpointEventTypeLocationsRemoved) |
1691 | | // { |
1692 | | // // These locations just get disabled, not sure it is worth spamming |
1693 | | // folks about this on the command line. |
1694 | | // } |
1695 | | // else if (event_type & eBreakpointEventTypeLocationsResolved) |
1696 | | // { |
1697 | | // // This might be an interesting thing to note, but I'm going to |
1698 | | // leave it quiet for now, it just looked noisy. |
1699 | | // } |
1700 | 196 | } |
1701 | | |
1702 | | void Debugger::FlushProcessOutput(Process &process, bool flush_stdout, |
1703 | 1.34k | bool flush_stderr) { |
1704 | 1.34k | const auto &flush = [&](Stream &stream, |
1705 | 2.61k | size_t (Process::*get)(char *, size_t, Status &)) { |
1706 | 2.61k | Status error; |
1707 | 2.61k | size_t len; |
1708 | 2.61k | char buffer[1024]; |
1709 | 2.65k | while ((len = (process.*get)(buffer, sizeof(buffer), error)) > 0) |
1710 | 42 | stream.Write(buffer, len); |
1711 | 2.61k | stream.Flush(); |
1712 | 2.61k | }; |
1713 | | |
1714 | 1.34k | std::lock_guard<std::mutex> guard(m_output_flush_mutex); |
1715 | 1.34k | if (flush_stdout) |
1716 | 1.34k | flush(*GetAsyncOutputStream(), &Process::GetSTDOUT); |
1717 | 1.34k | if (flush_stderr) |
1718 | 1.27k | flush(*GetAsyncErrorStream(), &Process::GetSTDERR); |
1719 | 1.34k | } |
1720 | | |
1721 | | // This function handles events that were broadcast by the process. |
1722 | 78 | void Debugger::HandleProcessEvent(const EventSP &event_sp) { |
1723 | 78 | using namespace lldb; |
1724 | 78 | const uint32_t event_type = event_sp->GetType(); |
1725 | 78 | ProcessSP process_sp = |
1726 | 78 | (event_type == Process::eBroadcastBitStructuredData) |
1727 | 78 | ? EventDataStructuredData::GetProcessFromEvent(event_sp.get())0 |
1728 | 78 | : Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); |
1729 | | |
1730 | 78 | StreamSP output_stream_sp = GetAsyncOutputStream(); |
1731 | 78 | StreamSP error_stream_sp = GetAsyncErrorStream(); |
1732 | 78 | const bool gui_enabled = IsForwardingEvents(); |
1733 | | |
1734 | 78 | if (!gui_enabled) { |
1735 | 78 | bool pop_process_io_handler = false; |
1736 | 78 | assert(process_sp); |
1737 | | |
1738 | 78 | bool state_is_stopped = false; |
1739 | 78 | const bool got_state_changed = |
1740 | 78 | (event_type & Process::eBroadcastBitStateChanged) != 0; |
1741 | 78 | const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0; |
1742 | 78 | const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0; |
1743 | 78 | const bool got_structured_data = |
1744 | 78 | (event_type & Process::eBroadcastBitStructuredData) != 0; |
1745 | | |
1746 | 78 | if (got_state_changed) { |
1747 | 10 | StateType event_state = |
1748 | 10 | Process::ProcessEventData::GetStateFromEvent(event_sp.get()); |
1749 | 10 | state_is_stopped = StateIsStoppedState(event_state, false); |
1750 | 10 | } |
1751 | | |
1752 | | // Display running state changes first before any STDIO |
1753 | 78 | if (got_state_changed && !state_is_stopped10 ) { |
1754 | | // This is a public stop which we are going to announce to the user, so |
1755 | | // we should force the most relevant frame selection here. |
1756 | 5 | Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(), |
1757 | 5 | SelectMostRelevantFrame, |
1758 | 5 | pop_process_io_handler); |
1759 | 5 | } |
1760 | | |
1761 | | // Now display STDOUT and STDERR |
1762 | 78 | FlushProcessOutput(*process_sp, got_stdout || got_state_changed10 , |
1763 | 78 | got_stderr || got_state_changed); |
1764 | | |
1765 | | // Give structured data events an opportunity to display. |
1766 | 78 | if (got_structured_data) { |
1767 | 0 | StructuredDataPluginSP plugin_sp = |
1768 | 0 | EventDataStructuredData::GetPluginFromEvent(event_sp.get()); |
1769 | 0 | if (plugin_sp) { |
1770 | 0 | auto structured_data_sp = |
1771 | 0 | EventDataStructuredData::GetObjectFromEvent(event_sp.get()); |
1772 | 0 | if (output_stream_sp) { |
1773 | 0 | StreamString content_stream; |
1774 | 0 | Status error = |
1775 | 0 | plugin_sp->GetDescription(structured_data_sp, content_stream); |
1776 | 0 | if (error.Success()) { |
1777 | 0 | if (!content_stream.GetString().empty()) { |
1778 | | // Add newline. |
1779 | 0 | content_stream.PutChar('\n'); |
1780 | 0 | content_stream.Flush(); |
1781 | | |
1782 | | // Print it. |
1783 | 0 | output_stream_sp->PutCString(content_stream.GetString()); |
1784 | 0 | } |
1785 | 0 | } else { |
1786 | 0 | error_stream_sp->Format("Failed to print structured " |
1787 | 0 | "data with plugin {0}: {1}", |
1788 | 0 | plugin_sp->GetPluginName(), error); |
1789 | 0 | } |
1790 | 0 | } |
1791 | 0 | } |
1792 | 0 | } |
1793 | | |
1794 | | // Now display any stopped state changes after any STDIO |
1795 | 78 | if (got_state_changed && state_is_stopped10 ) { |
1796 | 5 | Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(), |
1797 | 5 | SelectMostRelevantFrame, |
1798 | 5 | pop_process_io_handler); |
1799 | 5 | } |
1800 | | |
1801 | 78 | output_stream_sp->Flush(); |
1802 | 78 | error_stream_sp->Flush(); |
1803 | | |
1804 | 78 | if (pop_process_io_handler) |
1805 | 5 | process_sp->PopProcessIOHandler(); |
1806 | 78 | } |
1807 | 78 | } |
1808 | | |
1809 | 15 | void Debugger::HandleThreadEvent(const EventSP &event_sp) { |
1810 | | // At present the only thread event we handle is the Frame Changed event, and |
1811 | | // all we do for that is just reprint the thread status for that thread. |
1812 | 15 | using namespace lldb; |
1813 | 15 | const uint32_t event_type = event_sp->GetType(); |
1814 | 15 | const bool stop_format = true; |
1815 | 15 | if (event_type == Thread::eBroadcastBitStackChanged || |
1816 | 15 | event_type == Thread::eBroadcastBitThreadSelected) { |
1817 | 15 | ThreadSP thread_sp( |
1818 | 15 | Thread::ThreadEventData::GetThreadFromEvent(event_sp.get())); |
1819 | 15 | if (thread_sp) { |
1820 | 15 | thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format); |
1821 | 15 | } |
1822 | 15 | } |
1823 | 15 | } |
1824 | | |
1825 | 7.72k | bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; } |
1826 | | |
1827 | 0 | void Debugger::EnableForwardEvents(const ListenerSP &listener_sp) { |
1828 | 0 | m_forward_listener_sp = listener_sp; |
1829 | 0 | } |
1830 | | |
1831 | 0 | void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) { |
1832 | 0 | m_forward_listener_sp.reset(); |
1833 | 0 | } |
1834 | | |
1835 | 539 | lldb::thread_result_t Debugger::DefaultEventHandler() { |
1836 | 539 | ListenerSP listener_sp(GetListener()); |
1837 | 539 | ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); |
1838 | 539 | ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); |
1839 | 539 | ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); |
1840 | 539 | BroadcastEventSpec target_event_spec(broadcaster_class_target, |
1841 | 539 | Target::eBroadcastBitBreakpointChanged); |
1842 | | |
1843 | 539 | BroadcastEventSpec process_event_spec( |
1844 | 539 | broadcaster_class_process, |
1845 | 539 | Process::eBroadcastBitStateChanged | Process::eBroadcastBitSTDOUT | |
1846 | 539 | Process::eBroadcastBitSTDERR | Process::eBroadcastBitStructuredData); |
1847 | | |
1848 | 539 | BroadcastEventSpec thread_event_spec(broadcaster_class_thread, |
1849 | 539 | Thread::eBroadcastBitStackChanged | |
1850 | 539 | Thread::eBroadcastBitThreadSelected); |
1851 | | |
1852 | 539 | listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp, |
1853 | 539 | target_event_spec); |
1854 | 539 | listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp, |
1855 | 539 | process_event_spec); |
1856 | 539 | listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp, |
1857 | 539 | thread_event_spec); |
1858 | 539 | listener_sp->StartListeningForEvents( |
1859 | 539 | m_command_interpreter_up.get(), |
1860 | 539 | CommandInterpreter::eBroadcastBitQuitCommandReceived | |
1861 | 539 | CommandInterpreter::eBroadcastBitAsynchronousOutputData | |
1862 | 539 | CommandInterpreter::eBroadcastBitAsynchronousErrorData); |
1863 | | |
1864 | 539 | listener_sp->StartListeningForEvents( |
1865 | 539 | &m_broadcaster, eBroadcastBitProgress | eBroadcastBitWarning | |
1866 | 539 | eBroadcastBitError | eBroadcastSymbolChange); |
1867 | | |
1868 | | // Let the thread that spawned us know that we have started up and that we |
1869 | | // are now listening to all required events so no events get missed |
1870 | 539 | m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening); |
1871 | | |
1872 | 539 | bool done = false; |
1873 | 19.6k | while (!done) { |
1874 | 19.0k | EventSP event_sp; |
1875 | 19.0k | if (listener_sp->GetEvent(event_sp, std::nullopt)) { |
1876 | 19.0k | if (event_sp) { |
1877 | 19.0k | Broadcaster *broadcaster = event_sp->GetBroadcaster(); |
1878 | 19.0k | if (broadcaster) { |
1879 | 19.0k | uint32_t event_type = event_sp->GetType(); |
1880 | 19.0k | ConstString broadcaster_class(broadcaster->GetBroadcasterClass()); |
1881 | 19.0k | if (broadcaster_class == broadcaster_class_process) { |
1882 | 78 | HandleProcessEvent(event_sp); |
1883 | 19.0k | } else if (broadcaster_class == broadcaster_class_target) { |
1884 | 196 | if (Breakpoint::BreakpointEventData::GetEventDataFromEvent( |
1885 | 196 | event_sp.get())) { |
1886 | 196 | HandleBreakpointEvent(event_sp); |
1887 | 196 | } |
1888 | 18.8k | } else if (broadcaster_class == broadcaster_class_thread) { |
1889 | 15 | HandleThreadEvent(event_sp); |
1890 | 18.8k | } else if (broadcaster == m_command_interpreter_up.get()) { |
1891 | 539 | if (event_type & |
1892 | 539 | CommandInterpreter::eBroadcastBitQuitCommandReceived) { |
1893 | 539 | done = true; |
1894 | 539 | } else if (0 event_type & |
1895 | 0 | CommandInterpreter::eBroadcastBitAsynchronousErrorData) { |
1896 | 0 | const char *data = static_cast<const char *>( |
1897 | 0 | EventDataBytes::GetBytesFromEvent(event_sp.get())); |
1898 | 0 | if (data && data[0]) { |
1899 | 0 | StreamSP error_sp(GetAsyncErrorStream()); |
1900 | 0 | if (error_sp) { |
1901 | 0 | error_sp->PutCString(data); |
1902 | 0 | error_sp->Flush(); |
1903 | 0 | } |
1904 | 0 | } |
1905 | 0 | } else if (event_type & CommandInterpreter:: |
1906 | 0 | eBroadcastBitAsynchronousOutputData) { |
1907 | 0 | const char *data = static_cast<const char *>( |
1908 | 0 | EventDataBytes::GetBytesFromEvent(event_sp.get())); |
1909 | 0 | if (data && data[0]) { |
1910 | 0 | StreamSP output_sp(GetAsyncOutputStream()); |
1911 | 0 | if (output_sp) { |
1912 | 0 | output_sp->PutCString(data); |
1913 | 0 | output_sp->Flush(); |
1914 | 0 | } |
1915 | 0 | } |
1916 | 0 | } |
1917 | 18.2k | } else if (broadcaster == &m_broadcaster) { |
1918 | 18.2k | if (event_type & Debugger::eBroadcastBitProgress) |
1919 | 18.2k | HandleProgressEvent(event_sp); |
1920 | 24 | else if (event_type & Debugger::eBroadcastBitWarning) |
1921 | 10 | HandleDiagnosticEvent(event_sp); |
1922 | 14 | else if (event_type & Debugger::eBroadcastBitError) |
1923 | 14 | HandleDiagnosticEvent(event_sp); |
1924 | 18.2k | } |
1925 | 19.0k | } |
1926 | | |
1927 | 19.0k | if (m_forward_listener_sp) |
1928 | 0 | m_forward_listener_sp->AddEvent(event_sp); |
1929 | 19.0k | } |
1930 | 19.0k | } |
1931 | 19.0k | } |
1932 | 539 | return {}; |
1933 | 539 | } |
1934 | | |
1935 | 539 | bool Debugger::StartEventHandlerThread() { |
1936 | 539 | if (!m_event_handler_thread.IsJoinable()) { |
1937 | | // We must synchronize with the DefaultEventHandler() thread to ensure it |
1938 | | // is up and running and listening to events before we return from this |
1939 | | // function. We do this by listening to events for the |
1940 | | // eBroadcastBitEventThreadIsListening from the m_sync_broadcaster |
1941 | 539 | ConstString full_name("lldb.debugger.event-handler"); |
1942 | 539 | ListenerSP listener_sp(Listener::MakeListener(full_name.AsCString())); |
1943 | 539 | listener_sp->StartListeningForEvents(&m_sync_broadcaster, |
1944 | 539 | eBroadcastBitEventThreadIsListening); |
1945 | | |
1946 | 539 | llvm::StringRef thread_name = |
1947 | 539 | full_name.GetLength() < llvm::get_max_thread_name_length() |
1948 | 539 | ? full_name.GetStringRef() |
1949 | 539 | : "dbg.evt-handler"0 ; |
1950 | | |
1951 | | // Use larger 8MB stack for this thread |
1952 | 539 | llvm::Expected<HostThread> event_handler_thread = |
1953 | 539 | ThreadLauncher::LaunchThread( |
1954 | 539 | thread_name, [this] { return DefaultEventHandler(); }, |
1955 | 539 | g_debugger_event_thread_stack_bytes); |
1956 | | |
1957 | 539 | if (event_handler_thread) { |
1958 | 539 | m_event_handler_thread = *event_handler_thread; |
1959 | 539 | } else { |
1960 | 0 | LLDB_LOG_ERROR(GetLog(LLDBLog::Host), event_handler_thread.takeError(), |
1961 | 0 | "failed to launch host thread: {0}"); |
1962 | 0 | } |
1963 | | |
1964 | | // Make sure DefaultEventHandler() is running and listening to events |
1965 | | // before we return from this function. We are only listening for events of |
1966 | | // type eBroadcastBitEventThreadIsListening so we don't need to check the |
1967 | | // event, we just need to wait an infinite amount of time for it (nullptr |
1968 | | // timeout as the first parameter) |
1969 | 539 | lldb::EventSP event_sp; |
1970 | 539 | listener_sp->GetEvent(event_sp, std::nullopt); |
1971 | 539 | } |
1972 | 539 | return m_event_handler_thread.IsJoinable(); |
1973 | 539 | } |
1974 | | |
1975 | 6.63k | void Debugger::StopEventHandlerThread() { |
1976 | 6.63k | if (m_event_handler_thread.IsJoinable()) { |
1977 | 539 | GetCommandInterpreter().BroadcastEvent( |
1978 | 539 | CommandInterpreter::eBroadcastBitQuitCommandReceived); |
1979 | 539 | m_event_handler_thread.Join(nullptr); |
1980 | 539 | } |
1981 | 6.63k | } |
1982 | | |
1983 | 3 | lldb::thread_result_t Debugger::IOHandlerThread() { |
1984 | 3 | RunIOHandlers(); |
1985 | 3 | StopEventHandlerThread(); |
1986 | 3 | return {}; |
1987 | 3 | } |
1988 | | |
1989 | 18.2k | void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { |
1990 | 18.2k | auto *data = ProgressEventData::GetEventDataFromEvent(event_sp.get()); |
1991 | 18.2k | if (!data) |
1992 | 0 | return; |
1993 | | |
1994 | | // Do some bookkeeping for the current event, regardless of whether we're |
1995 | | // going to show the progress. |
1996 | 18.2k | const uint64_t id = data->GetID(); |
1997 | 18.2k | if (m_current_event_id) { |
1998 | 9.84k | Log *log = GetLog(LLDBLog::Events); |
1999 | 9.84k | if (log && log->GetVerbose()0 ) { |
2000 | 0 | StreamString log_stream; |
2001 | 0 | log_stream.AsRawOstream() |
2002 | 0 | << static_cast<void *>(this) << " Debugger(" << GetID() |
2003 | 0 | << ")::HandleProgressEvent( m_current_event_id = " |
2004 | 0 | << *m_current_event_id << ", data = { "; |
2005 | 0 | data->Dump(&log_stream); |
2006 | 0 | log_stream << " } )"; |
2007 | 0 | log->PutString(log_stream.GetString()); |
2008 | 0 | } |
2009 | 9.84k | if (id != *m_current_event_id) |
2010 | 10 | return; |
2011 | 9.83k | if (data->GetCompleted() == data->GetTotal()) |
2012 | 8.39k | m_current_event_id.reset(); |
2013 | 9.83k | } else { |
2014 | 8.39k | m_current_event_id = id; |
2015 | 8.39k | } |
2016 | | |
2017 | | // Decide whether we actually are going to show the progress. This decision |
2018 | | // can change between iterations so check it inside the loop. |
2019 | 18.2k | if (!GetShowProgress()) |
2020 | 0 | return; |
2021 | | |
2022 | | // Determine whether the current output file is an interactive terminal with |
2023 | | // color support. We assume that if we support ANSI escape codes we support |
2024 | | // vt100 escape codes. |
2025 | 18.2k | File &file = GetOutputFile(); |
2026 | 18.2k | if (!file.GetIsInteractive() || !file.GetIsTerminalWithColors()2.19k ) |
2027 | 16.0k | return; |
2028 | | |
2029 | 2.19k | StreamSP output = GetAsyncOutputStream(); |
2030 | | |
2031 | | // Print over previous line, if any. |
2032 | 2.19k | output->Printf("\r"); |
2033 | | |
2034 | 2.19k | if (data->GetCompleted() == data->GetTotal()) { |
2035 | | // Clear the current line. |
2036 | 1.08k | output->Printf("\x1B[2K"); |
2037 | 1.08k | output->Flush(); |
2038 | 1.08k | return; |
2039 | 1.08k | } |
2040 | | |
2041 | | // Trim the progress message if it exceeds the window's width and print it. |
2042 | 1.10k | std::string message = data->GetMessage(); |
2043 | 1.10k | if (data->IsFinite()) |
2044 | 20 | message = llvm::formatv("[{0}/{1}] {2}", data->GetCompleted(), |
2045 | 20 | data->GetTotal(), message) |
2046 | 20 | .str(); |
2047 | | |
2048 | | // Trim the progress message if it exceeds the window's width and print it. |
2049 | 1.10k | const uint32_t term_width = GetTerminalWidth(); |
2050 | 1.10k | const uint32_t ellipsis = 3; |
2051 | 1.10k | if (message.size() + ellipsis >= term_width) |
2052 | 95 | message = message.substr(0, term_width - ellipsis); |
2053 | | |
2054 | 1.10k | const bool use_color = GetUseColor(); |
2055 | 1.10k | llvm::StringRef ansi_prefix = GetShowProgressAnsiPrefix(); |
2056 | 1.10k | if (!ansi_prefix.empty()) |
2057 | 1.10k | output->Printf( |
2058 | 1.10k | "%s", ansi::FormatAnsiTerminalCodes(ansi_prefix, use_color).c_str()); |
2059 | | |
2060 | 1.10k | output->Printf("%s...", message.c_str()); |
2061 | | |
2062 | 1.10k | llvm::StringRef ansi_suffix = GetShowProgressAnsiSuffix(); |
2063 | 1.10k | if (!ansi_suffix.empty()) |
2064 | 1.10k | output->Printf( |
2065 | 1.10k | "%s", ansi::FormatAnsiTerminalCodes(ansi_suffix, use_color).c_str()); |
2066 | | |
2067 | | // Clear until the end of the line. |
2068 | 1.10k | output->Printf("\x1B[K\r"); |
2069 | | |
2070 | | // Flush the output. |
2071 | 1.10k | output->Flush(); |
2072 | 1.10k | } |
2073 | | |
2074 | 24 | void Debugger::HandleDiagnosticEvent(const lldb::EventSP &event_sp) { |
2075 | 24 | auto *data = DiagnosticEventData::GetEventDataFromEvent(event_sp.get()); |
2076 | 24 | if (!data) |
2077 | 0 | return; |
2078 | | |
2079 | 24 | StreamSP stream = GetAsyncErrorStream(); |
2080 | 24 | data->Dump(stream.get()); |
2081 | 24 | } |
2082 | | |
2083 | 1.49M | bool Debugger::HasIOHandlerThread() const { |
2084 | 1.49M | return m_io_handler_thread.IsJoinable(); |
2085 | 1.49M | } |
2086 | | |
2087 | 1.07k | HostThread Debugger::SetIOHandlerThread(HostThread &new_thread) { |
2088 | 1.07k | HostThread old_host = m_io_handler_thread; |
2089 | 1.07k | m_io_handler_thread = new_thread; |
2090 | 1.07k | return old_host; |
2091 | 1.07k | } |
2092 | | |
2093 | 3 | bool Debugger::StartIOHandlerThread() { |
2094 | 3 | if (!m_io_handler_thread.IsJoinable()) { |
2095 | 3 | llvm::Expected<HostThread> io_handler_thread = ThreadLauncher::LaunchThread( |
2096 | 3 | "lldb.debugger.io-handler", [this] { return IOHandlerThread(); }, |
2097 | 3 | 8 * 1024 * 1024); // Use larger 8MB stack for this thread |
2098 | 3 | if (io_handler_thread) { |
2099 | 3 | m_io_handler_thread = *io_handler_thread; |
2100 | 3 | } else { |
2101 | 0 | LLDB_LOG_ERROR(GetLog(LLDBLog::Host), io_handler_thread.takeError(), |
2102 | 0 | "failed to launch host thread: {0}"); |
2103 | 0 | } |
2104 | 3 | } |
2105 | 3 | return m_io_handler_thread.IsJoinable(); |
2106 | 3 | } |
2107 | | |
2108 | 6.09k | void Debugger::StopIOHandlerThread() { |
2109 | 6.09k | if (m_io_handler_thread.IsJoinable()) { |
2110 | 3 | GetInputFile().Close(); |
2111 | 3 | m_io_handler_thread.Join(nullptr); |
2112 | 3 | } |
2113 | 6.09k | } |
2114 | | |
2115 | 0 | void Debugger::JoinIOHandlerThread() { |
2116 | 0 | if (HasIOHandlerThread()) { |
2117 | 0 | thread_result_t result; |
2118 | 0 | m_io_handler_thread.Join(&result); |
2119 | 0 | m_io_handler_thread = LLDB_INVALID_HOST_THREAD; |
2120 | 0 | } |
2121 | 0 | } |
2122 | | |
2123 | 1.49M | bool Debugger::IsIOHandlerThreadCurrentThread() const { |
2124 | 1.49M | if (!HasIOHandlerThread()) |
2125 | 1.45M | return false; |
2126 | 47.0k | return m_io_handler_thread.EqualsThread(Host::GetCurrentThread()); |
2127 | 1.49M | } |
2128 | | |
2129 | 8.99k | Target &Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) { |
2130 | 8.99k | if (!prefer_dummy) { |
2131 | 8.99k | if (TargetSP target = m_target_list.GetSelectedTarget()) |
2132 | 8.86k | return *target; |
2133 | 8.99k | } |
2134 | 137 | return GetDummyTarget(); |
2135 | 8.99k | } |
2136 | | |
2137 | 3 | Status Debugger::RunREPL(LanguageType language, const char *repl_options) { |
2138 | 3 | Status err; |
2139 | 3 | FileSpec repl_executable; |
2140 | | |
2141 | 3 | if (language == eLanguageTypeUnknown) |
2142 | 1 | language = GetREPLLanguage(); |
2143 | | |
2144 | 3 | if (language == eLanguageTypeUnknown) { |
2145 | 1 | LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs(); |
2146 | | |
2147 | 1 | if (auto single_lang = repl_languages.GetSingularLanguage()) { |
2148 | 0 | language = *single_lang; |
2149 | 1 | } else if (repl_languages.Empty()) { |
2150 | 0 | err.SetErrorString( |
2151 | 0 | "LLDB isn't configured with REPL support for any languages."); |
2152 | 0 | return err; |
2153 | 1 | } else { |
2154 | 1 | err.SetErrorString( |
2155 | 1 | "Multiple possible REPL languages. Please specify a language."); |
2156 | 1 | return err; |
2157 | 1 | } |
2158 | 1 | } |
2159 | | |
2160 | 2 | Target *const target = |
2161 | 2 | nullptr; // passing in an empty target means the REPL must create one |
2162 | | |
2163 | 2 | REPLSP repl_sp(REPL::Create(err, language, this, target, repl_options)); |
2164 | | |
2165 | 2 | if (!err.Success()) { |
2166 | 1 | return err; |
2167 | 1 | } |
2168 | | |
2169 | 1 | if (!repl_sp) { |
2170 | 1 | err.SetErrorStringWithFormat("couldn't find a REPL for %s", |
2171 | 1 | Language::GetNameForLanguageType(language)); |
2172 | 1 | return err; |
2173 | 1 | } |
2174 | | |
2175 | 0 | repl_sp->SetCompilerOptions(repl_options); |
2176 | 0 | repl_sp->RunLoop(); |
2177 | |
|
2178 | 0 | return err; |
2179 | 1 | } |
2180 | | |
2181 | 4.28k | llvm::ThreadPool &Debugger::GetThreadPool() { |
2182 | 4.28k | assert(g_thread_pool && |
2183 | 4.28k | "Debugger::GetThreadPool called before Debugger::Initialize"); |
2184 | 4.28k | return *g_thread_pool; |
2185 | 4.28k | } |