File: | tools/lldb/source/Core/Debugger.cpp |
Location: | line 2649, column 41 |
Description: | Value stored to 'var_success' is never read |
1 | //===-- Debugger.cpp --------------------------------------------*- C++ -*-===// |
2 | // |
3 | // The LLVM Compiler Infrastructure |
4 | // |
5 | // This file is distributed under the University of Illinois Open Source |
6 | // License. See LICENSE.TXT for details. |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #include "lldb/lldb-python.h" |
11 | |
12 | #include "lldb/Core/Debugger.h" |
13 | |
14 | #include <map> |
15 | |
16 | #include "clang/AST/DeclCXX.h" |
17 | #include "clang/AST/Type.h" |
18 | #include "llvm/ADT/StringRef.h" |
19 | |
20 | #include "lldb/lldb-private.h" |
21 | #include "lldb/Core/Module.h" |
22 | #include "lldb/Core/PluginManager.h" |
23 | #include "lldb/Core/RegisterValue.h" |
24 | #include "lldb/Core/State.h" |
25 | #include "lldb/Core/StreamAsynchronousIO.h" |
26 | #include "lldb/Core/StreamCallback.h" |
27 | #include "lldb/Core/StreamFile.h" |
28 | #include "lldb/Core/StreamString.h" |
29 | #include "lldb/Core/StructuredData.h" |
30 | #include "lldb/Core/Timer.h" |
31 | #include "lldb/Core/ValueObject.h" |
32 | #include "lldb/Core/ValueObjectVariable.h" |
33 | #include "lldb/DataFormatters/DataVisualization.h" |
34 | #include "lldb/DataFormatters/FormatManager.h" |
35 | #include "lldb/DataFormatters/TypeSummary.h" |
36 | #include "lldb/Host/ConnectionFileDescriptor.h" |
37 | #include "lldb/Host/HostInfo.h" |
38 | #include "lldb/Host/Terminal.h" |
39 | #include "lldb/Host/ThreadLauncher.h" |
40 | #include "lldb/Interpreter/CommandInterpreter.h" |
41 | #include "lldb/Interpreter/OptionValueSInt64.h" |
42 | #include "lldb/Interpreter/OptionValueString.h" |
43 | #include "lldb/Symbol/ClangASTContext.h" |
44 | #include "lldb/Symbol/CompileUnit.h" |
45 | #include "lldb/Symbol/Function.h" |
46 | #include "lldb/Symbol/Symbol.h" |
47 | #include "lldb/Symbol/VariableList.h" |
48 | #include "lldb/Target/CPPLanguageRuntime.h" |
49 | #include "lldb/Target/ObjCLanguageRuntime.h" |
50 | #include "lldb/Target/TargetList.h" |
51 | #include "lldb/Target/Process.h" |
52 | #include "lldb/Target/RegisterContext.h" |
53 | #include "lldb/Target/SectionLoadList.h" |
54 | #include "lldb/Target/StopInfo.h" |
55 | #include "lldb/Target/Target.h" |
56 | #include "lldb/Target/Thread.h" |
57 | #include "lldb/Utility/AnsiTerminal.h" |
58 | |
59 | #include "llvm/Support/DynamicLibrary.h" |
60 | |
61 | using namespace lldb; |
62 | using namespace lldb_private; |
63 | |
64 | |
65 | static uint32_t g_shared_debugger_refcount = 0; |
66 | static lldb::user_id_t g_unique_id = 1; |
67 | static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024; |
68 | |
69 | #pragma mark Static Functions |
70 | |
71 | static Mutex & |
72 | GetDebuggerListMutex () |
73 | { |
74 | static Mutex g_mutex(Mutex::eMutexTypeRecursive); |
75 | return g_mutex; |
76 | } |
77 | |
78 | typedef std::vector<DebuggerSP> DebuggerList; |
79 | |
80 | static DebuggerList & |
81 | GetDebuggerList() |
82 | { |
83 | // hide the static debugger list inside a singleton accessor to avoid |
84 | // global init constructors |
85 | static DebuggerList g_list; |
86 | return g_list; |
87 | } |
88 | |
89 | OptionEnumValueElement |
90 | g_show_disassembly_enum_values[] = |
91 | { |
92 | { Debugger::eStopDisassemblyTypeNever, "never", "Never show disassembly when displaying a stop context."}, |
93 | { Debugger::eStopDisassemblyTypeNoSource, "no-source", "Show disassembly when there is no source information, or the source file is missing when displaying a stop context."}, |
94 | { Debugger::eStopDisassemblyTypeAlways, "always", "Always show disassembly when displaying a stop context."}, |
95 | { 0, NULL__null, NULL__null } |
96 | }; |
97 | |
98 | OptionEnumValueElement |
99 | g_language_enumerators[] = |
100 | { |
101 | { eScriptLanguageNone, "none", "Disable scripting languages."}, |
102 | { eScriptLanguagePython, "python", "Select python as the default scripting language."}, |
103 | { eScriptLanguageDefault, "default", "Select the lldb default as the default scripting language."}, |
104 | { 0, NULL__null, NULL__null } |
105 | }; |
106 | |
107 | #define MODULE_WITH_FUNC"{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" |
108 | #define FILE_AND_LINE"{ at ${line.file.basename}:${line.number}}" "{ at ${line.file.basename}:${line.number}}" |
109 | |
110 | #define DEFAULT_THREAD_FORMAT"thread #${thread.index}: tid = ${thread.id%tid}" "{, ${frame.pc}}" "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" "{ at ${line.file.basename}:${line.number}}" "{, name = '${thread.name}'}" "{, queue = '${thread.queue}'}" "{, activity = '${thread.info.activity.name}'}" "{, ${thread.info.trace_messages} messages}" "{, stop reason = ${thread.stop-reason}}" "{\\nReturn value: ${thread.return-value}}" "{\\nCompleted expression: ${thread.completed-expression}}" "\\n" "thread #${thread.index}: tid = ${thread.id%tid}"\ |
111 | "{, ${frame.pc}}"\ |
112 | MODULE_WITH_FUNC"{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}"\ |
113 | FILE_AND_LINE"{ at ${line.file.basename}:${line.number}}"\ |
114 | "{, name = '${thread.name}'}"\ |
115 | "{, queue = '${thread.queue}'}"\ |
116 | "{, activity = '${thread.info.activity.name}'}" \ |
117 | "{, ${thread.info.trace_messages} messages}" \ |
118 | "{, stop reason = ${thread.stop-reason}}"\ |
119 | "{\\nReturn value: ${thread.return-value}}"\ |
120 | "{\\nCompleted expression: ${thread.completed-expression}}"\ |
121 | "\\n" |
122 | |
123 | #define DEFAULT_FRAME_FORMAT"frame #${frame.index}: ${frame.pc}" "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" "{ at ${line.file.basename}:${line.number}}" "\\n" "frame #${frame.index}: ${frame.pc}"\ |
124 | MODULE_WITH_FUNC"{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}"\ |
125 | FILE_AND_LINE"{ at ${line.file.basename}:${line.number}}"\ |
126 | "\\n" |
127 | |
128 | #define DEFAULT_DISASSEMBLY_FORMAT"${current-pc-arrow}${addr-file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: " "${current-pc-arrow}${addr-file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: " |
129 | |
130 | static PropertyDefinition |
131 | g_properties[] = |
132 | { |
133 | { "auto-confirm", OptionValue::eTypeBoolean, true, false, NULL__null, NULL__null, "If true all confirmation prompts will receive their default reply." }, |
134 | { "disassembly-format", OptionValue::eTypeString , true, 0 , DEFAULT_DISASSEMBLY_FORMAT"${current-pc-arrow}${addr-file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: ", NULL__null, "The default disassembly format string to use when disassembling instruction sequences." }, |
135 | { "frame-format", OptionValue::eTypeString , true, 0 , DEFAULT_FRAME_FORMAT"frame #${frame.index}: ${frame.pc}" "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" "{ at ${line.file.basename}:${line.number}}" "\\n", NULL__null, "The default frame format string to use when displaying stack frame information for threads." }, |
136 | { "notify-void", OptionValue::eTypeBoolean, true, false, NULL__null, NULL__null, "Notify the user explicitly if an expression returns void (default: false)." }, |
137 | { "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL__null, "The debugger command line prompt displayed for the user." }, |
138 | { "script-lang", OptionValue::eTypeEnum , true, eScriptLanguagePython, NULL__null, g_language_enumerators, "The script language to be used for evaluating user-written scripts." }, |
139 | { "stop-disassembly-count", OptionValue::eTypeSInt64 , true, 4 , NULL__null, NULL__null, "The number of disassembly lines to show when displaying a stopped context." }, |
140 | { "stop-disassembly-display", OptionValue::eTypeEnum , true, Debugger::eStopDisassemblyTypeNoSource, NULL__null, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context." }, |
141 | { "stop-line-count-after", OptionValue::eTypeSInt64 , true, 3 , NULL__null, NULL__null, "The number of sources lines to display that come after the current source line when displaying a stopped context." }, |
142 | { "stop-line-count-before", OptionValue::eTypeSInt64 , true, 3 , NULL__null, NULL__null, "The number of sources lines to display that come before the current source line when displaying a stopped context." }, |
143 | { "term-width", OptionValue::eTypeSInt64 , true, 80 , NULL__null, NULL__null, "The maximum number of columns to use for displaying text." }, |
144 | { "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT"thread #${thread.index}: tid = ${thread.id%tid}" "{, ${frame.pc}}" "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" "{ at ${line.file.basename}:${line.number}}" "{, name = '${thread.name}'}" "{, queue = '${thread.queue}'}" "{, activity = '${thread.info.activity.name}'}" "{, ${thread.info.trace_messages} messages}" "{, stop reason = ${thread.stop-reason}}" "{\\nReturn value: ${thread.return-value}}" "{\\nCompleted expression: ${thread.completed-expression}}" "\\n", NULL__null, "The default thread format string to use when displaying thread information." }, |
145 | { "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL__null, NULL__null, "Whether to use an external editor or not." }, |
146 | { "use-color", OptionValue::eTypeBoolean, true, true , NULL__null, NULL__null, "Whether to use Ansi color codes or not." }, |
147 | { "auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, NULL__null, NULL__null, "If true, LLDB will automatically display small structs in one-liner format (default: true)." }, |
148 | { "escape-non-printables", OptionValue::eTypeBoolean, true, true, NULL__null, NULL__null, "If true, LLDB will automatically escape non-printable and escape characters when formatting strings." }, |
149 | |
150 | { NULL__null, OptionValue::eTypeInvalid, true, 0 , NULL__null, NULL__null, NULL__null } |
151 | }; |
152 | |
153 | enum |
154 | { |
155 | ePropertyAutoConfirm = 0, |
156 | ePropertyDisassemblyFormat, |
157 | ePropertyFrameFormat, |
158 | ePropertyNotiftVoid, |
159 | ePropertyPrompt, |
160 | ePropertyScriptLanguage, |
161 | ePropertyStopDisassemblyCount, |
162 | ePropertyStopDisassemblyDisplay, |
163 | ePropertyStopLineCountAfter, |
164 | ePropertyStopLineCountBefore, |
165 | ePropertyTerminalWidth, |
166 | ePropertyThreadFormat, |
167 | ePropertyUseExternalEditor, |
168 | ePropertyUseColor, |
169 | ePropertyAutoOneLineSummaries, |
170 | ePropertyEscapeNonPrintables |
171 | }; |
172 | |
173 | Debugger::LoadPluginCallbackType Debugger::g_load_plugin_callback = NULL__null; |
174 | |
175 | Error |
176 | Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, |
177 | VarSetOperationType op, |
178 | const char *property_path, |
179 | const char *value) |
180 | { |
181 | bool is_load_script = strcmp(property_path,"target.load-script-from-symbol-file") == 0; |
182 | bool is_escape_non_printables = strcmp(property_path, "escape-non-printables") == 0; |
183 | TargetSP target_sp; |
184 | LoadScriptFromSymFile load_script_old_value; |
185 | if (is_load_script && exe_ctx->GetTargetSP()) |
186 | { |
187 | target_sp = exe_ctx->GetTargetSP(); |
188 | load_script_old_value = target_sp->TargetProperties::GetLoadScriptFromSymbolFile(); |
189 | } |
190 | Error error (Properties::SetPropertyValue (exe_ctx, op, property_path, value)); |
191 | if (error.Success()) |
192 | { |
193 | // FIXME it would be nice to have "on-change" callbacks for properties |
194 | if (strcmp(property_path, g_properties[ePropertyPrompt].name) == 0) |
195 | { |
196 | const char *new_prompt = GetPrompt(); |
197 | std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); |
198 | if (str.length()) |
199 | new_prompt = str.c_str(); |
200 | GetCommandInterpreter().UpdatePrompt(new_prompt); |
201 | EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt))); |
202 | GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); |
203 | } |
204 | else if (strcmp(property_path, g_properties[ePropertyUseColor].name) == 0) |
205 | { |
206 | // use-color changed. Ping the prompt so it can reset the ansi terminal codes. |
207 | SetPrompt (GetPrompt()); |
208 | } |
209 | else if (is_load_script && target_sp && load_script_old_value == eLoadScriptFromSymFileWarn) |
210 | { |
211 | if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() == eLoadScriptFromSymFileTrue) |
212 | { |
213 | std::list<Error> errors; |
214 | StreamString feedback_stream; |
215 | if (!target_sp->LoadScriptingResources(errors,&feedback_stream)) |
216 | { |
217 | StreamFileSP stream_sp (GetErrorFile()); |
218 | if (stream_sp) |
219 | { |
220 | for (auto error : errors) |
221 | { |
222 | stream_sp->Printf("%s\n",error.AsCString()); |
223 | } |
224 | if (feedback_stream.GetSize()) |
225 | stream_sp->Printf("%s",feedback_stream.GetData()); |
226 | } |
227 | } |
228 | } |
229 | } |
230 | else if (is_escape_non_printables) |
231 | { |
232 | DataVisualization::ForceUpdate(); |
233 | } |
234 | } |
235 | return error; |
236 | } |
237 | |
238 | bool |
239 | Debugger::GetAutoConfirm () const |
240 | { |
241 | const uint32_t idx = ePropertyAutoConfirm; |
242 | return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL__null, idx, g_properties[idx].default_uint_value != 0); |
243 | } |
244 | |
245 | const char * |
246 | Debugger::GetDisassemblyFormat() const |
247 | { |
248 | const uint32_t idx = ePropertyDisassemblyFormat; |
249 | return m_collection_sp->GetPropertyAtIndexAsString (NULL__null, idx, g_properties[idx].default_cstr_value); |
250 | } |
251 | |
252 | const char * |
253 | Debugger::GetFrameFormat() const |
254 | { |
255 | const uint32_t idx = ePropertyFrameFormat; |
256 | return m_collection_sp->GetPropertyAtIndexAsString (NULL__null, idx, g_properties[idx].default_cstr_value); |
257 | } |
258 | |
259 | bool |
260 | Debugger::GetNotifyVoid () const |
261 | { |
262 | const uint32_t idx = ePropertyNotiftVoid; |
263 | return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL__null, idx, g_properties[idx].default_uint_value != 0); |
264 | } |
265 | |
266 | const char * |
267 | Debugger::GetPrompt() const |
268 | { |
269 | const uint32_t idx = ePropertyPrompt; |
270 | return m_collection_sp->GetPropertyAtIndexAsString (NULL__null, idx, g_properties[idx].default_cstr_value); |
271 | } |
272 | |
273 | void |
274 | Debugger::SetPrompt(const char *p) |
275 | { |
276 | const uint32_t idx = ePropertyPrompt; |
277 | m_collection_sp->SetPropertyAtIndexAsString (NULL__null, idx, p); |
278 | const char *new_prompt = GetPrompt(); |
279 | std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); |
280 | if (str.length()) |
281 | new_prompt = str.c_str(); |
282 | GetCommandInterpreter().UpdatePrompt(new_prompt); |
283 | } |
284 | |
285 | const char * |
286 | Debugger::GetThreadFormat() const |
287 | { |
288 | const uint32_t idx = ePropertyThreadFormat; |
289 | return m_collection_sp->GetPropertyAtIndexAsString (NULL__null, idx, g_properties[idx].default_cstr_value); |
290 | } |
291 | |
292 | lldb::ScriptLanguage |
293 | Debugger::GetScriptLanguage() const |
294 | { |
295 | const uint32_t idx = ePropertyScriptLanguage; |
296 | return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL__null, idx, g_properties[idx].default_uint_value); |
297 | } |
298 | |
299 | bool |
300 | Debugger::SetScriptLanguage (lldb::ScriptLanguage script_lang) |
301 | { |
302 | const uint32_t idx = ePropertyScriptLanguage; |
303 | return m_collection_sp->SetPropertyAtIndexAsEnumeration (NULL__null, idx, script_lang); |
304 | } |
305 | |
306 | uint32_t |
307 | Debugger::GetTerminalWidth () const |
308 | { |
309 | const uint32_t idx = ePropertyTerminalWidth; |
310 | return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL__null, idx, g_properties[idx].default_uint_value); |
311 | } |
312 | |
313 | bool |
314 | Debugger::SetTerminalWidth (uint32_t term_width) |
315 | { |
316 | const uint32_t idx = ePropertyTerminalWidth; |
317 | return m_collection_sp->SetPropertyAtIndexAsSInt64 (NULL__null, idx, term_width); |
318 | } |
319 | |
320 | bool |
321 | Debugger::GetUseExternalEditor () const |
322 | { |
323 | const uint32_t idx = ePropertyUseExternalEditor; |
324 | return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL__null, idx, g_properties[idx].default_uint_value != 0); |
325 | } |
326 | |
327 | bool |
328 | Debugger::SetUseExternalEditor (bool b) |
329 | { |
330 | const uint32_t idx = ePropertyUseExternalEditor; |
331 | return m_collection_sp->SetPropertyAtIndexAsBoolean (NULL__null, idx, b); |
332 | } |
333 | |
334 | bool |
335 | Debugger::GetUseColor () const |
336 | { |
337 | const uint32_t idx = ePropertyUseColor; |
338 | return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL__null, idx, g_properties[idx].default_uint_value != 0); |
339 | } |
340 | |
341 | bool |
342 | Debugger::SetUseColor (bool b) |
343 | { |
344 | const uint32_t idx = ePropertyUseColor; |
345 | bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean (NULL__null, idx, b); |
346 | SetPrompt (GetPrompt()); |
347 | return ret; |
348 | } |
349 | |
350 | uint32_t |
351 | Debugger::GetStopSourceLineCount (bool before) const |
352 | { |
353 | const uint32_t idx = before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter; |
354 | return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL__null, idx, g_properties[idx].default_uint_value); |
355 | } |
356 | |
357 | Debugger::StopDisassemblyType |
358 | Debugger::GetStopDisassemblyDisplay () const |
359 | { |
360 | const uint32_t idx = ePropertyStopDisassemblyDisplay; |
361 | return (Debugger::StopDisassemblyType)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL__null, idx, g_properties[idx].default_uint_value); |
362 | } |
363 | |
364 | uint32_t |
365 | Debugger::GetDisassemblyLineCount () const |
366 | { |
367 | const uint32_t idx = ePropertyStopDisassemblyCount; |
368 | return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL__null, idx, g_properties[idx].default_uint_value); |
369 | } |
370 | |
371 | bool |
372 | Debugger::GetAutoOneLineSummaries () const |
373 | { |
374 | const uint32_t idx = ePropertyAutoOneLineSummaries; |
375 | return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL__null, idx, true); |
376 | } |
377 | |
378 | bool |
379 | Debugger::GetEscapeNonPrintables () const |
380 | { |
381 | const uint32_t idx = ePropertyEscapeNonPrintables; |
382 | return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL__null, idx, true); |
383 | } |
384 | |
385 | #pragma mark Debugger |
386 | |
387 | //const DebuggerPropertiesSP & |
388 | //Debugger::GetSettings() const |
389 | //{ |
390 | // return m_properties_sp; |
391 | //} |
392 | // |
393 | |
394 | int |
395 | Debugger::TestDebuggerRefCount () |
396 | { |
397 | return g_shared_debugger_refcount; |
398 | } |
399 | |
400 | void |
401 | Debugger::Initialize (LoadPluginCallbackType load_plugin_callback) |
402 | { |
403 | g_load_plugin_callback = load_plugin_callback; |
404 | if (g_shared_debugger_refcount++ == 0) |
405 | lldb_private::Initialize(); |
406 | } |
407 | |
408 | void |
409 | Debugger::Terminate () |
410 | { |
411 | if (g_shared_debugger_refcount > 0) |
412 | { |
413 | g_shared_debugger_refcount--; |
414 | if (g_shared_debugger_refcount == 0) |
415 | { |
416 | lldb_private::WillTerminate(); |
417 | lldb_private::Terminate(); |
418 | |
419 | // Clear our master list of debugger objects |
420 | Mutex::Locker locker (GetDebuggerListMutex ()); |
421 | GetDebuggerList().clear(); |
422 | } |
423 | } |
424 | } |
425 | |
426 | void |
427 | Debugger::SettingsInitialize () |
428 | { |
429 | Target::SettingsInitialize (); |
430 | } |
431 | |
432 | void |
433 | Debugger::SettingsTerminate () |
434 | { |
435 | Target::SettingsTerminate (); |
436 | } |
437 | |
438 | bool |
439 | Debugger::LoadPlugin (const FileSpec& spec, Error& error) |
440 | { |
441 | if (g_load_plugin_callback) |
442 | { |
443 | llvm::sys::DynamicLibrary dynlib = g_load_plugin_callback (shared_from_this(), spec, error); |
444 | if (dynlib.isValid()) |
445 | { |
446 | m_loaded_plugins.push_back(dynlib); |
447 | return true; |
448 | } |
449 | } |
450 | else |
451 | { |
452 | // The g_load_plugin_callback is registered in SBDebugger::Initialize() |
453 | // and if the public API layer isn't available (code is linking against |
454 | // all of the internal LLDB static libraries), then we can't load plugins |
455 | error.SetErrorString("Public API layer is not available"); |
456 | } |
457 | return false; |
458 | } |
459 | |
460 | static FileSpec::EnumerateDirectoryResult |
461 | LoadPluginCallback |
462 | ( |
463 | void *baton, |
464 | FileSpec::FileType file_type, |
465 | const FileSpec &file_spec |
466 | ) |
467 | { |
468 | Error error; |
469 | |
470 | static ConstString g_dylibext("dylib"); |
471 | static ConstString g_solibext("so"); |
472 | |
473 | if (!baton) |
474 | return FileSpec::eEnumerateDirectoryResultQuit; |
475 | |
476 | Debugger *debugger = (Debugger*)baton; |
477 | |
478 | // If we have a regular file, a symbolic link or unknown file type, try |
479 | // and process the file. We must handle unknown as sometimes the directory |
480 | // enumeration might be enumerating a file system that doesn't have correct |
481 | // file type information. |
482 | if (file_type == FileSpec::eFileTypeRegular || |
483 | file_type == FileSpec::eFileTypeSymbolicLink || |
484 | file_type == FileSpec::eFileTypeUnknown ) |
485 | { |
486 | FileSpec plugin_file_spec (file_spec); |
487 | plugin_file_spec.ResolvePath (); |
488 | |
489 | if (plugin_file_spec.GetFileNameExtension() != g_dylibext && |
490 | plugin_file_spec.GetFileNameExtension() != g_solibext) |
491 | { |
492 | return FileSpec::eEnumerateDirectoryResultNext; |
493 | } |
494 | |
495 | Error plugin_load_error; |
496 | debugger->LoadPlugin (plugin_file_spec, plugin_load_error); |
497 | |
498 | return FileSpec::eEnumerateDirectoryResultNext; |
499 | } |
500 | |
501 | else if (file_type == FileSpec::eFileTypeUnknown || |
502 | file_type == FileSpec::eFileTypeDirectory || |
503 | file_type == FileSpec::eFileTypeSymbolicLink ) |
504 | { |
505 | // Try and recurse into anything that a directory or symbolic link. |
506 | // We must also do this for unknown as sometimes the directory enumeration |
507 | // might be enumerating a file system that doesn't have correct file type |
508 | // information. |
509 | return FileSpec::eEnumerateDirectoryResultEnter; |
510 | } |
511 | |
512 | return FileSpec::eEnumerateDirectoryResultNext; |
513 | } |
514 | |
515 | void |
516 | Debugger::InstanceInitialize () |
517 | { |
518 | FileSpec dir_spec; |
519 | const bool find_directories = true; |
520 | const bool find_files = true; |
521 | const bool find_other = true; |
522 | char dir_path[PATH_MAX4096]; |
523 | if (HostInfo::GetLLDBPath(ePathTypeLLDBSystemPlugins, dir_spec)) |
524 | { |
525 | if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) |
526 | { |
527 | FileSpec::EnumerateDirectory (dir_path, |
528 | find_directories, |
529 | find_files, |
530 | find_other, |
531 | LoadPluginCallback, |
532 | this); |
533 | } |
534 | } |
535 | |
536 | if (HostInfo::GetLLDBPath(ePathTypeLLDBUserPlugins, dir_spec)) |
537 | { |
538 | if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) |
539 | { |
540 | FileSpec::EnumerateDirectory (dir_path, |
541 | find_directories, |
542 | find_files, |
543 | find_other, |
544 | LoadPluginCallback, |
545 | this); |
546 | } |
547 | } |
548 | |
549 | PluginManager::DebuggerInitialize (*this); |
550 | } |
551 | |
552 | DebuggerSP |
553 | Debugger::CreateInstance (lldb::LogOutputCallback log_callback, void *baton) |
554 | { |
555 | DebuggerSP debugger_sp (new Debugger(log_callback, baton)); |
556 | if (g_shared_debugger_refcount > 0) |
557 | { |
558 | Mutex::Locker locker (GetDebuggerListMutex ()); |
559 | GetDebuggerList().push_back(debugger_sp); |
560 | } |
561 | debugger_sp->InstanceInitialize (); |
562 | return debugger_sp; |
563 | } |
564 | |
565 | void |
566 | Debugger::Destroy (DebuggerSP &debugger_sp) |
567 | { |
568 | if (debugger_sp.get() == NULL__null) |
569 | return; |
570 | |
571 | debugger_sp->Clear(); |
572 | |
573 | if (g_shared_debugger_refcount > 0) |
574 | { |
575 | Mutex::Locker locker (GetDebuggerListMutex ()); |
576 | DebuggerList &debugger_list = GetDebuggerList (); |
577 | DebuggerList::iterator pos, end = debugger_list.end(); |
578 | for (pos = debugger_list.begin (); pos != end; ++pos) |
579 | { |
580 | if ((*pos).get() == debugger_sp.get()) |
581 | { |
582 | debugger_list.erase (pos); |
583 | return; |
584 | } |
585 | } |
586 | } |
587 | } |
588 | |
589 | DebuggerSP |
590 | Debugger::FindDebuggerWithInstanceName (const ConstString &instance_name) |
591 | { |
592 | DebuggerSP debugger_sp; |
593 | if (g_shared_debugger_refcount > 0) |
594 | { |
595 | Mutex::Locker locker (GetDebuggerListMutex ()); |
596 | DebuggerList &debugger_list = GetDebuggerList(); |
597 | DebuggerList::iterator pos, end = debugger_list.end(); |
598 | |
599 | for (pos = debugger_list.begin(); pos != end; ++pos) |
600 | { |
601 | if ((*pos).get()->m_instance_name == instance_name) |
602 | { |
603 | debugger_sp = *pos; |
604 | break; |
605 | } |
606 | } |
607 | } |
608 | return debugger_sp; |
609 | } |
610 | |
611 | TargetSP |
612 | Debugger::FindTargetWithProcessID (lldb::pid_t pid) |
613 | { |
614 | TargetSP target_sp; |
615 | if (g_shared_debugger_refcount > 0) |
616 | { |
617 | Mutex::Locker locker (GetDebuggerListMutex ()); |
618 | DebuggerList &debugger_list = GetDebuggerList(); |
619 | DebuggerList::iterator pos, end = debugger_list.end(); |
620 | for (pos = debugger_list.begin(); pos != end; ++pos) |
621 | { |
622 | target_sp = (*pos)->GetTargetList().FindTargetWithProcessID (pid); |
623 | if (target_sp) |
624 | break; |
625 | } |
626 | } |
627 | return target_sp; |
628 | } |
629 | |
630 | TargetSP |
631 | Debugger::FindTargetWithProcess (Process *process) |
632 | { |
633 | TargetSP target_sp; |
634 | if (g_shared_debugger_refcount > 0) |
635 | { |
636 | Mutex::Locker locker (GetDebuggerListMutex ()); |
637 | DebuggerList &debugger_list = GetDebuggerList(); |
638 | DebuggerList::iterator pos, end = debugger_list.end(); |
639 | for (pos = debugger_list.begin(); pos != end; ++pos) |
640 | { |
641 | target_sp = (*pos)->GetTargetList().FindTargetWithProcess (process); |
642 | if (target_sp) |
643 | break; |
644 | } |
645 | } |
646 | return target_sp; |
647 | } |
648 | |
649 | Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) : |
650 | UserID(g_unique_id++), |
651 | Properties(OptionValuePropertiesSP(new OptionValueProperties())), |
652 | m_input_file_sp(new StreamFile(stdinstdin, false)), |
653 | m_output_file_sp(new StreamFile(stdoutstdout, false)), |
654 | m_error_file_sp(new StreamFile(stderrstderr, false)), |
655 | m_terminal_state(), |
656 | m_target_list(*this), |
657 | m_platform_list(), |
658 | m_listener("lldb.Debugger"), |
659 | m_source_manager_ap(), |
660 | m_source_file_cache(), |
661 | m_command_interpreter_ap(new CommandInterpreter(*this, eScriptLanguageDefault, false)), |
662 | m_input_reader_stack(), |
663 | m_instance_name(), |
664 | m_loaded_plugins(), |
665 | m_event_handler_thread (), |
666 | m_io_handler_thread (), |
667 | m_sync_broadcaster (NULL__null, "lldb.debugger.sync") |
668 | { |
669 | char instance_cstr[256]; |
670 | snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID()); |
671 | m_instance_name.SetCString(instance_cstr); |
672 | if (log_callback) |
673 | m_log_callback_stream_sp.reset (new StreamCallback (log_callback, baton)); |
674 | m_command_interpreter_ap->Initialize (); |
675 | // Always add our default platform to the platform list |
676 | PlatformSP default_platform_sp (Platform::GetHostPlatform()); |
677 | assert (default_platform_sp.get())((default_platform_sp.get()) ? static_cast<void> (0) : __assert_fail ("default_platform_sp.get()", "/tmp/buildd/llvm-toolchain-snapshot-3.7~svn227765/tools/lldb/source/Core/Debugger.cpp" , 677, __PRETTY_FUNCTION__)); |
678 | m_platform_list.Append (default_platform_sp, true); |
679 | |
680 | m_collection_sp->Initialize (g_properties); |
681 | m_collection_sp->AppendProperty (ConstString("target"), |
682 | ConstString("Settings specify to debugging targets."), |
683 | true, |
684 | Target::GetGlobalProperties()->GetValueProperties()); |
685 | if (m_command_interpreter_ap.get()) |
686 | { |
687 | m_collection_sp->AppendProperty (ConstString("interpreter"), |
688 | ConstString("Settings specify to the debugger's command interpreter."), |
689 | true, |
690 | m_command_interpreter_ap->GetValueProperties()); |
691 | } |
692 | OptionValueSInt64 *term_width = m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64 (NULL__null, ePropertyTerminalWidth); |
693 | term_width->SetMinimumValue(10); |
694 | term_width->SetMaximumValue(1024); |
695 | |
696 | // Turn off use-color if this is a dumb terminal. |
697 | const char *term = getenv ("TERM"); |
698 | if (term && !strcmp (term, "dumb")) |
699 | SetUseColor (false); |
700 | } |
701 | |
702 | Debugger::~Debugger () |
703 | { |
704 | Clear(); |
705 | } |
706 | |
707 | void |
708 | Debugger::Clear() |
709 | { |
710 | ClearIOHandlers(); |
711 | StopIOHandlerThread(); |
712 | StopEventHandlerThread(); |
713 | m_listener.Clear(); |
714 | int num_targets = m_target_list.GetNumTargets(); |
715 | for (int i = 0; i < num_targets; i++) |
716 | { |
717 | TargetSP target_sp (m_target_list.GetTargetAtIndex (i)); |
718 | if (target_sp) |
719 | { |
720 | ProcessSP process_sp (target_sp->GetProcessSP()); |
721 | if (process_sp) |
722 | process_sp->Finalize(); |
723 | target_sp->Destroy(); |
724 | } |
725 | } |
726 | BroadcasterManager::Clear (); |
727 | |
728 | // Close the input file _before_ we close the input read communications class |
729 | // as it does NOT own the input file, our m_input_file does. |
730 | m_terminal_state.Clear(); |
731 | if (m_input_file_sp) |
732 | m_input_file_sp->GetFile().Close (); |
733 | |
734 | m_command_interpreter_ap->Clear(); |
735 | } |
736 | |
737 | bool |
738 | Debugger::GetCloseInputOnEOF () const |
739 | { |
740 | // return m_input_comm.GetCloseOnEOF(); |
741 | return false; |
742 | } |
743 | |
744 | void |
745 | Debugger::SetCloseInputOnEOF (bool b) |
746 | { |
747 | // m_input_comm.SetCloseOnEOF(b); |
748 | } |
749 | |
750 | bool |
751 | Debugger::GetAsyncExecution () |
752 | { |
753 | return !m_command_interpreter_ap->GetSynchronous(); |
754 | } |
755 | |
756 | void |
757 | Debugger::SetAsyncExecution (bool async_execution) |
758 | { |
759 | m_command_interpreter_ap->SetSynchronous (!async_execution); |
760 | } |
761 | |
762 | |
763 | void |
764 | Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) |
765 | { |
766 | if (m_input_file_sp) |
767 | m_input_file_sp->GetFile().SetStream (fh, tranfer_ownership); |
768 | else |
769 | m_input_file_sp.reset (new StreamFile (fh, tranfer_ownership)); |
770 | |
771 | File &in_file = m_input_file_sp->GetFile(); |
772 | if (in_file.IsValid() == false) |
773 | in_file.SetStream (stdinstdin, true); |
774 | |
775 | // Save away the terminal state if that is relevant, so that we can restore it in RestoreInputState. |
776 | SaveInputTerminalState (); |
777 | } |
778 | |
779 | void |
780 | Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) |
781 | { |
782 | if (m_output_file_sp) |
783 | m_output_file_sp->GetFile().SetStream (fh, tranfer_ownership); |
784 | else |
785 | m_output_file_sp.reset (new StreamFile (fh, tranfer_ownership)); |
786 | |
787 | File &out_file = m_output_file_sp->GetFile(); |
788 | if (out_file.IsValid() == false) |
789 | out_file.SetStream (stdoutstdout, false); |
790 | |
791 | // do not create the ScriptInterpreter just for setting the output file handle |
792 | // as the constructor will know how to do the right thing on its own |
793 | const bool can_create = false; |
794 | ScriptInterpreter* script_interpreter = GetCommandInterpreter().GetScriptInterpreter(can_create); |
795 | if (script_interpreter) |
796 | script_interpreter->ResetOutputFileHandle (fh); |
797 | } |
798 | |
799 | void |
800 | Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) |
801 | { |
802 | if (m_error_file_sp) |
803 | m_error_file_sp->GetFile().SetStream (fh, tranfer_ownership); |
804 | else |
805 | m_error_file_sp.reset (new StreamFile (fh, tranfer_ownership)); |
806 | |
807 | File &err_file = m_error_file_sp->GetFile(); |
808 | if (err_file.IsValid() == false) |
809 | err_file.SetStream (stderrstderr, false); |
810 | } |
811 | |
812 | void |
813 | Debugger::SaveInputTerminalState () |
814 | { |
815 | if (m_input_file_sp) |
816 | { |
817 | File &in_file = m_input_file_sp->GetFile(); |
818 | if (in_file.GetDescriptor() != File::kInvalidDescriptor) |
819 | m_terminal_state.Save(in_file.GetDescriptor(), true); |
820 | } |
821 | } |
822 | |
823 | void |
824 | Debugger::RestoreInputTerminalState () |
825 | { |
826 | m_terminal_state.Restore(); |
827 | } |
828 | |
829 | ExecutionContext |
830 | Debugger::GetSelectedExecutionContext () |
831 | { |
832 | ExecutionContext exe_ctx; |
833 | TargetSP target_sp(GetSelectedTarget()); |
834 | exe_ctx.SetTargetSP (target_sp); |
835 | |
836 | if (target_sp) |
837 | { |
838 | ProcessSP process_sp (target_sp->GetProcessSP()); |
839 | exe_ctx.SetProcessSP (process_sp); |
840 | if (process_sp && process_sp->IsRunning() == false) |
841 | { |
842 | ThreadSP thread_sp (process_sp->GetThreadList().GetSelectedThread()); |
843 | if (thread_sp) |
844 | { |
845 | exe_ctx.SetThreadSP (thread_sp); |
846 | exe_ctx.SetFrameSP (thread_sp->GetSelectedFrame()); |
847 | if (exe_ctx.GetFramePtr() == NULL__null) |
848 | exe_ctx.SetFrameSP (thread_sp->GetStackFrameAtIndex (0)); |
849 | } |
850 | } |
851 | } |
852 | return exe_ctx; |
853 | } |
854 | |
855 | void |
856 | Debugger::DispatchInputInterrupt () |
857 | { |
858 | Mutex::Locker locker (m_input_reader_stack.GetMutex()); |
859 | IOHandlerSP reader_sp (m_input_reader_stack.Top()); |
860 | if (reader_sp) |
861 | reader_sp->Interrupt(); |
862 | } |
863 | |
864 | void |
865 | Debugger::DispatchInputEndOfFile () |
866 | { |
867 | Mutex::Locker locker (m_input_reader_stack.GetMutex()); |
868 | IOHandlerSP reader_sp (m_input_reader_stack.Top()); |
869 | if (reader_sp) |
870 | reader_sp->GotEOF(); |
871 | } |
872 | |
873 | void |
874 | Debugger::ClearIOHandlers () |
875 | { |
876 | // The bottom input reader should be the main debugger input reader. We do not want to close that one here. |
877 | Mutex::Locker locker (m_input_reader_stack.GetMutex()); |
878 | while (m_input_reader_stack.GetSize() > 1) |
879 | { |
880 | IOHandlerSP reader_sp (m_input_reader_stack.Top()); |
881 | if (reader_sp) |
882 | { |
883 | m_input_reader_stack.Pop(); |
884 | reader_sp->SetIsDone(true); |
885 | reader_sp->Cancel(); |
886 | } |
887 | } |
888 | } |
889 | |
890 | void |
891 | Debugger::ExecuteIOHanders() |
892 | { |
893 | |
894 | while (1) |
895 | { |
896 | IOHandlerSP reader_sp(m_input_reader_stack.Top()); |
897 | if (!reader_sp) |
898 | break; |
899 | |
900 | reader_sp->Activate(); |
901 | reader_sp->Run(); |
902 | reader_sp->Deactivate(); |
903 | |
904 | // Remove all input readers that are done from the top of the stack |
905 | while (1) |
906 | { |
907 | IOHandlerSP top_reader_sp = m_input_reader_stack.Top(); |
908 | if (top_reader_sp && top_reader_sp->GetIsDone()) |
909 | m_input_reader_stack.Pop(); |
910 | else |
911 | break; |
912 | } |
913 | } |
914 | ClearIOHandlers(); |
915 | } |
916 | |
917 | bool |
918 | Debugger::IsTopIOHandler (const lldb::IOHandlerSP& reader_sp) |
919 | { |
920 | return m_input_reader_stack.IsTop (reader_sp); |
921 | } |
922 | |
923 | |
924 | ConstString |
925 | Debugger::GetTopIOHandlerControlSequence(char ch) |
926 | { |
927 | return m_input_reader_stack.GetTopIOHandlerControlSequence (ch); |
928 | } |
929 | |
930 | const char * |
931 | Debugger::GetIOHandlerCommandPrefix() |
932 | { |
933 | return m_input_reader_stack.GetTopIOHandlerCommandPrefix(); |
934 | } |
935 | |
936 | const char * |
937 | Debugger::GetIOHandlerHelpPrologue() |
938 | { |
939 | return m_input_reader_stack.GetTopIOHandlerHelpPrologue(); |
940 | } |
941 | |
942 | void |
943 | Debugger::RunIOHandler (const IOHandlerSP& reader_sp) |
944 | { |
945 | PushIOHandler (reader_sp); |
946 | |
947 | IOHandlerSP top_reader_sp = reader_sp; |
948 | while (top_reader_sp) |
949 | { |
950 | top_reader_sp->Activate(); |
951 | top_reader_sp->Run(); |
952 | top_reader_sp->Deactivate(); |
953 | |
954 | if (top_reader_sp.get() == reader_sp.get()) |
955 | { |
956 | if (PopIOHandler (reader_sp)) |
957 | break; |
958 | } |
959 | |
960 | while (1) |
961 | { |
962 | top_reader_sp = m_input_reader_stack.Top(); |
963 | if (top_reader_sp && top_reader_sp->GetIsDone()) |
964 | m_input_reader_stack.Pop(); |
965 | else |
966 | break; |
967 | } |
968 | } |
969 | } |
970 | |
971 | void |
972 | Debugger::AdoptTopIOHandlerFilesIfInvalid (StreamFileSP &in, StreamFileSP &out, StreamFileSP &err) |
973 | { |
974 | // Before an IOHandler runs, it must have in/out/err streams. |
975 | // This function is called when one ore more of the streams |
976 | // are NULL. We use the top input reader's in/out/err streams, |
977 | // or fall back to the debugger file handles, or we fall back |
978 | // onto stdin/stdout/stderr as a last resort. |
979 | |
980 | Mutex::Locker locker (m_input_reader_stack.GetMutex()); |
981 | IOHandlerSP top_reader_sp (m_input_reader_stack.Top()); |
982 | // If no STDIN has been set, then set it appropriately |
983 | if (!in) |
984 | { |
985 | if (top_reader_sp) |
986 | in = top_reader_sp->GetInputStreamFile(); |
987 | else |
988 | in = GetInputFile(); |
989 | |
990 | // If there is nothing, use stdin |
991 | if (!in) |
992 | in = StreamFileSP(new StreamFile(stdinstdin, false)); |
993 | } |
994 | // If no STDOUT has been set, then set it appropriately |
995 | if (!out) |
996 | { |
997 | if (top_reader_sp) |
998 | out = top_reader_sp->GetOutputStreamFile(); |
999 | else |
1000 | out = GetOutputFile(); |
1001 | |
1002 | // If there is nothing, use stdout |
1003 | if (!out) |
1004 | out = StreamFileSP(new StreamFile(stdoutstdout, false)); |
1005 | } |
1006 | // If no STDERR has been set, then set it appropriately |
1007 | if (!err) |
1008 | { |
1009 | if (top_reader_sp) |
1010 | err = top_reader_sp->GetErrorStreamFile(); |
1011 | else |
1012 | err = GetErrorFile(); |
1013 | |
1014 | // If there is nothing, use stderr |
1015 | if (!err) |
1016 | err = StreamFileSP(new StreamFile(stdoutstdout, false)); |
1017 | |
1018 | } |
1019 | } |
1020 | |
1021 | void |
1022 | Debugger::PushIOHandler (const IOHandlerSP& reader_sp) |
1023 | { |
1024 | if (!reader_sp) |
1025 | return; |
1026 | |
1027 | // Got the current top input reader... |
1028 | IOHandlerSP top_reader_sp (m_input_reader_stack.Top()); |
1029 | |
1030 | // Don't push the same IO handler twice... |
1031 | if (reader_sp.get() != top_reader_sp.get()) |
1032 | { |
1033 | // Push our new input reader |
1034 | m_input_reader_stack.Push (reader_sp); |
1035 | |
1036 | // Interrupt the top input reader to it will exit its Run() function |
1037 | // and let this new input reader take over |
1038 | if (top_reader_sp) |
1039 | top_reader_sp->Deactivate(); |
1040 | } |
1041 | } |
1042 | |
1043 | bool |
1044 | Debugger::PopIOHandler (const IOHandlerSP& pop_reader_sp) |
1045 | { |
1046 | bool result = false; |
1047 | |
1048 | Mutex::Locker locker (m_input_reader_stack.GetMutex()); |
1049 | |
1050 | // The reader on the stop of the stack is done, so let the next |
1051 | // read on the stack refresh its prompt and if there is one... |
1052 | if (!m_input_reader_stack.IsEmpty()) |
1053 | { |
1054 | IOHandlerSP reader_sp(m_input_reader_stack.Top()); |
1055 | |
1056 | if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) |
1057 | { |
1058 | reader_sp->Deactivate(); |
1059 | reader_sp->Cancel(); |
1060 | m_input_reader_stack.Pop (); |
1061 | |
1062 | reader_sp = m_input_reader_stack.Top(); |
1063 | if (reader_sp) |
1064 | reader_sp->Activate(); |
1065 | |
1066 | result = true; |
1067 | } |
1068 | } |
1069 | return result; |
1070 | } |
1071 | |
1072 | bool |
1073 | Debugger::HideTopIOHandler() |
1074 | { |
1075 | Mutex::Locker locker; |
1076 | |
1077 | if (locker.TryLock(m_input_reader_stack.GetMutex())) |
1078 | { |
1079 | IOHandlerSP reader_sp(m_input_reader_stack.Top()); |
1080 | if (reader_sp) |
1081 | reader_sp->Hide(); |
1082 | return true; |
1083 | } |
1084 | return false; |
1085 | } |
1086 | |
1087 | void |
1088 | Debugger::RefreshTopIOHandler() |
1089 | { |
1090 | IOHandlerSP reader_sp(m_input_reader_stack.Top()); |
1091 | if (reader_sp) |
1092 | reader_sp->Refresh(); |
1093 | } |
1094 | |
1095 | |
1096 | StreamSP |
1097 | Debugger::GetAsyncOutputStream () |
1098 | { |
1099 | return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), |
1100 | CommandInterpreter::eBroadcastBitAsynchronousOutputData)); |
1101 | } |
1102 | |
1103 | StreamSP |
1104 | Debugger::GetAsyncErrorStream () |
1105 | { |
1106 | return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), |
1107 | CommandInterpreter::eBroadcastBitAsynchronousErrorData)); |
1108 | } |
1109 | |
1110 | size_t |
1111 | Debugger::GetNumDebuggers() |
1112 | { |
1113 | if (g_shared_debugger_refcount > 0) |
1114 | { |
1115 | Mutex::Locker locker (GetDebuggerListMutex ()); |
1116 | return GetDebuggerList().size(); |
1117 | } |
1118 | return 0; |
1119 | } |
1120 | |
1121 | lldb::DebuggerSP |
1122 | Debugger::GetDebuggerAtIndex (size_t index) |
1123 | { |
1124 | DebuggerSP debugger_sp; |
1125 | |
1126 | if (g_shared_debugger_refcount > 0) |
1127 | { |
1128 | Mutex::Locker locker (GetDebuggerListMutex ()); |
1129 | DebuggerList &debugger_list = GetDebuggerList(); |
1130 | |
1131 | if (index < debugger_list.size()) |
1132 | debugger_sp = debugger_list[index]; |
1133 | } |
1134 | |
1135 | return debugger_sp; |
1136 | } |
1137 | |
1138 | DebuggerSP |
1139 | Debugger::FindDebuggerWithID (lldb::user_id_t id) |
1140 | { |
1141 | DebuggerSP debugger_sp; |
1142 | |
1143 | if (g_shared_debugger_refcount > 0) |
1144 | { |
1145 | Mutex::Locker locker (GetDebuggerListMutex ()); |
1146 | DebuggerList &debugger_list = GetDebuggerList(); |
1147 | DebuggerList::iterator pos, end = debugger_list.end(); |
1148 | for (pos = debugger_list.begin(); pos != end; ++pos) |
1149 | { |
1150 | if ((*pos).get()->GetID() == id) |
1151 | { |
1152 | debugger_sp = *pos; |
1153 | break; |
1154 | } |
1155 | } |
1156 | } |
1157 | return debugger_sp; |
1158 | } |
1159 | |
1160 | #if 0 |
1161 | static void |
1162 | TestPromptFormats (StackFrame *frame) |
1163 | { |
1164 | if (frame == NULL__null) |
1165 | return; |
1166 | |
1167 | StreamString s; |
1168 | const char *prompt_format = |
1169 | "{addr = '${addr}'\n}" |
1170 | "{addr-file-or-load = '${addr-file-or-load}'\n}" |
1171 | "{current-pc-arrow = '${current-pc-arrow}'\n}" |
1172 | "{process.id = '${process.id}'\n}" |
1173 | "{process.name = '${process.name}'\n}" |
1174 | "{process.file.basename = '${process.file.basename}'\n}" |
1175 | "{process.file.fullpath = '${process.file.fullpath}'\n}" |
1176 | "{thread.id = '${thread.id}'\n}" |
1177 | "{thread.index = '${thread.index}'\n}" |
1178 | "{thread.name = '${thread.name}'\n}" |
1179 | "{thread.queue = '${thread.queue}'\n}" |
1180 | "{thread.stop-reason = '${thread.stop-reason}'\n}" |
1181 | "{target.arch = '${target.arch}'\n}" |
1182 | "{module.file.basename = '${module.file.basename}'\n}" |
1183 | "{module.file.fullpath = '${module.file.fullpath}'\n}" |
1184 | "{file.basename = '${file.basename}'\n}" |
1185 | "{file.fullpath = '${file.fullpath}'\n}" |
1186 | "{frame.index = '${frame.index}'\n}" |
1187 | "{frame.pc = '${frame.pc}'\n}" |
1188 | "{frame.sp = '${frame.sp}'\n}" |
1189 | "{frame.fp = '${frame.fp}'\n}" |
1190 | "{frame.flags = '${frame.flags}'\n}" |
1191 | "{frame.reg.rdi = '${frame.reg.rdi}'\n}" |
1192 | "{frame.reg.rip = '${frame.reg.rip}'\n}" |
1193 | "{frame.reg.rsp = '${frame.reg.rsp}'\n}" |
1194 | "{frame.reg.rbp = '${frame.reg.rbp}'\n}" |
1195 | "{frame.reg.rflags = '${frame.reg.rflags}'\n}" |
1196 | "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}" |
1197 | "{frame.reg.carp = '${frame.reg.carp}'\n}" |
1198 | "{function.id = '${function.id}'\n}" |
1199 | "{function.changed = '${function.changed}'\n}" |
1200 | "{function.initial-function = '${function.initial-function}'\n}" |
1201 | "{function.name = '${function.name}'\n}" |
1202 | "{function.name-without-args = '${function.name-without-args}'\n}" |
1203 | "{function.name-with-args = '${function.name-with-args}'\n}" |
1204 | "{function.addr-offset = '${function.addr-offset}'\n}" |
1205 | "{function.concrete-only-addr-offset-no-padding = '${function.concrete-only-addr-offset-no-padding}'\n}" |
1206 | "{function.line-offset = '${function.line-offset}'\n}" |
1207 | "{function.pc-offset = '${function.pc-offset}'\n}" |
1208 | "{line.file.basename = '${line.file.basename}'\n}" |
1209 | "{line.file.fullpath = '${line.file.fullpath}'\n}" |
1210 | "{line.number = '${line.number}'\n}" |
1211 | "{line.start-addr = '${line.start-addr}'\n}" |
1212 | "{line.end-addr = '${line.end-addr}'\n}" |
1213 | ; |
1214 | |
1215 | SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything)); |
1216 | ExecutionContext exe_ctx; |
1217 | frame->CalculateExecutionContext(exe_ctx); |
1218 | if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s)) |
1219 | { |
1220 | printf("%s\n", s.GetData()); |
1221 | } |
1222 | else |
1223 | { |
1224 | printf ("what we got: %s\n", s.GetData()); |
1225 | } |
1226 | } |
1227 | #endif |
1228 | |
1229 | static bool |
1230 | ScanFormatDescriptor (const char* var_name_begin, |
1231 | const char* var_name_end, |
1232 | const char** var_name_final, |
1233 | const char** percent_position, |
1234 | Format* custom_format, |
1235 | ValueObject::ValueObjectRepresentationStyle* val_obj_display) |
1236 | { |
1237 | Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES(1u << 19))); |
1238 | *percent_position = ::strchr(var_name_begin,'%'); |
1239 | if (!*percent_position || *percent_position > var_name_end) |
1240 | { |
1241 | if (log) |
1242 | log->Printf("[ScanFormatDescriptor] no format descriptor in string, skipping"); |
1243 | *var_name_final = var_name_end; |
1244 | } |
1245 | else |
1246 | { |
1247 | *var_name_final = *percent_position; |
1248 | std::string format_name(*var_name_final+1, var_name_end-*var_name_final-1); |
1249 | if (log) |
1250 | log->Printf("[ScanFormatDescriptor] parsing %s as a format descriptor", format_name.c_str()); |
1251 | if ( !FormatManager::GetFormatFromCString(format_name.c_str(), |
1252 | true, |
1253 | *custom_format) ) |
1254 | { |
1255 | if (log) |
1256 | log->Printf("[ScanFormatDescriptor] %s is an unknown format", format_name.c_str()); |
1257 | |
1258 | switch (format_name.front()) |
1259 | { |
1260 | case '@': // if this is an @ sign, print ObjC description |
1261 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleLanguageSpecific; |
1262 | break; |
1263 | case 'V': // if this is a V, print the value using the default format |
1264 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; |
1265 | break; |
1266 | case 'L': // if this is an L, print the location of the value |
1267 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleLocation; |
1268 | break; |
1269 | case 'S': // if this is an S, print the summary after all |
1270 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; |
1271 | break; |
1272 | case '#': // if this is a '#', print the number of children |
1273 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleChildrenCount; |
1274 | break; |
1275 | case 'T': // if this is a 'T', print the type |
1276 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleType; |
1277 | break; |
1278 | case 'N': // if this is a 'N', print the name |
1279 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleName; |
1280 | break; |
1281 | case '>': // if this is a '>', print the name |
1282 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleExpressionPath; |
1283 | break; |
1284 | default: |
1285 | if (log) |
1286 | log->Printf("ScanFormatDescriptor] %s is an error, leaving the previous value alone", format_name.c_str()); |
1287 | break; |
1288 | } |
1289 | } |
1290 | // a good custom format tells us to print the value using it |
1291 | else |
1292 | { |
1293 | if (log) |
1294 | log->Printf("[ScanFormatDescriptor] will display value for this VO"); |
1295 | *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; |
1296 | } |
1297 | } |
1298 | if (log) |
1299 | log->Printf("[ScanFormatDescriptor] final format description outcome: custom_format = %d, val_obj_display = %d", |
1300 | *custom_format, |
1301 | *val_obj_display); |
1302 | return true; |
1303 | } |
1304 | |
1305 | static bool |
1306 | ScanBracketedRange (const char* var_name_begin, |
1307 | const char* var_name_end, |
1308 | const char* var_name_final, |
1309 | const char** open_bracket_position, |
1310 | const char** separator_position, |
1311 | const char** close_bracket_position, |
1312 | const char** var_name_final_if_array_range, |
1313 | int64_t* index_lower, |
1314 | int64_t* index_higher) |
1315 | { |
1316 | Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES(1u << 19))); |
1317 | *open_bracket_position = ::strchr(var_name_begin,'['); |
1318 | if (*open_bracket_position && *open_bracket_position < var_name_final) |
1319 | { |
1320 | *separator_position = ::strchr(*open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield |
1321 | *close_bracket_position = ::strchr(*open_bracket_position,']'); |
1322 | // as usual, we assume that [] will come before % |
1323 | //printf("trying to expand a []\n"); |
1324 | *var_name_final_if_array_range = *open_bracket_position; |
1325 | if (*close_bracket_position - *open_bracket_position == 1) |
1326 | { |
1327 | if (log) |
1328 | log->Printf("[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); |
1329 | *index_lower = 0; |
1330 | } |
1331 | else if (*separator_position == NULL__null || *separator_position > var_name_end) |
1332 | { |
1333 | char *end = NULL__null; |
1334 | *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); |
1335 | *index_higher = *index_lower; |
1336 | if (log) |
1337 | log->Printf("[ScanBracketedRange] [%" PRId64"l" "d" "] detected, high index is same", *index_lower); |
1338 | } |
1339 | else if (*close_bracket_position && *close_bracket_position < var_name_end) |
1340 | { |
1341 | char *end = NULL__null; |
1342 | *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); |
1343 | *index_higher = ::strtoul (*separator_position+1, &end, 0); |
1344 | if (log) |
1345 | log->Printf("[ScanBracketedRange] [%" PRId64"l" "d" "-%" PRId64"l" "d" "] detected", *index_lower, *index_higher); |
1346 | } |
1347 | else |
1348 | { |
1349 | if (log) |
1350 | log->Printf("[ScanBracketedRange] expression is erroneous, cannot extract indices out of it"); |
1351 | return false; |
1352 | } |
1353 | if (*index_lower > *index_higher && *index_higher > 0) |
1354 | { |
1355 | if (log) |
1356 | log->Printf("[ScanBracketedRange] swapping indices"); |
1357 | int64_t temp = *index_lower; |
1358 | *index_lower = *index_higher; |
1359 | *index_higher = temp; |
1360 | } |
1361 | } |
1362 | else if (log) |
1363 | log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); |
1364 | return true; |
1365 | } |
1366 | |
1367 | template <typename T> |
1368 | static bool RunScriptFormatKeyword(Stream &s, ScriptInterpreter *script_interpreter, T t, const std::string& script_name) |
1369 | { |
1370 | if (script_interpreter) |
1371 | { |
1372 | Error script_error; |
1373 | std::string script_output; |
1374 | |
1375 | if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), t, script_output, script_error) && script_error.Success()) |
1376 | { |
1377 | s.Printf("%s", script_output.c_str()); |
1378 | return true; |
1379 | } |
1380 | else |
1381 | { |
1382 | s.Printf("<error: %s>",script_error.AsCString()); |
1383 | } |
1384 | } |
1385 | return false; |
1386 | } |
1387 | |
1388 | static ValueObjectSP |
1389 | ExpandIndexedExpression (ValueObject* valobj, |
1390 | size_t index, |
1391 | StackFrame* frame, |
1392 | bool deref_pointer) |
1393 | { |
1394 | Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES(1u << 19))); |
1395 | const char* ptr_deref_format = "[%d]"; |
1396 | std::string ptr_deref_buffer(10,0); |
1397 | ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index); |
1398 | if (log) |
1399 | log->Printf("[ExpandIndexedExpression] name to deref: %s",ptr_deref_buffer.c_str()); |
1400 | const char* first_unparsed; |
1401 | ValueObject::GetValueForExpressionPathOptions options; |
1402 | ValueObject::ExpressionPathEndResultType final_value_type; |
1403 | ValueObject::ExpressionPathScanEndReason reason_to_stop; |
1404 | ValueObject::ExpressionPathAftermath what_next = (deref_pointer ? ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); |
1405 | ValueObjectSP item = valobj->GetValueForExpressionPath (ptr_deref_buffer.c_str(), |
1406 | &first_unparsed, |
1407 | &reason_to_stop, |
1408 | &final_value_type, |
1409 | options, |
1410 | &what_next); |
1411 | if (!item) |
1412 | { |
1413 | if (log) |
1414 | log->Printf("[ExpandIndexedExpression] ERROR: unparsed portion = %s, why stopping = %d," |
1415 | " final_value_type %d", |
1416 | first_unparsed, reason_to_stop, final_value_type); |
1417 | } |
1418 | else |
1419 | { |
1420 | if (log) |
1421 | log->Printf("[ExpandIndexedExpression] ALL RIGHT: unparsed portion = %s, why stopping = %d," |
1422 | " final_value_type %d", |
1423 | first_unparsed, reason_to_stop, final_value_type); |
1424 | } |
1425 | return item; |
1426 | } |
1427 | |
1428 | static inline bool |
1429 | IsToken(const char *var_name_begin, const char *var) |
1430 | { |
1431 | return (::strncmp (var_name_begin, var, strlen(var)) == 0); |
1432 | } |
1433 | |
1434 | static bool |
1435 | IsTokenWithFormat(const char *var_name_begin, const char *var, std::string &format, const char *default_format, |
1436 | const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr) |
1437 | { |
1438 | int var_len = strlen(var); |
1439 | if (::strncmp (var_name_begin, var, var_len) == 0) |
1440 | { |
1441 | var_name_begin += var_len; |
1442 | if (*var_name_begin == '}') |
1443 | { |
1444 | format = default_format; |
1445 | return true; |
1446 | } |
1447 | else if (*var_name_begin == '%') |
1448 | { |
1449 | // Allow format specifiers: x|X|u with optional width specifiers. |
1450 | // ${thread.id%x} ; hex |
1451 | // ${thread.id%X} ; uppercase hex |
1452 | // ${thread.id%u} ; unsigned decimal |
1453 | // ${thread.id%8.8X} ; width.precision + specifier |
1454 | // ${thread.id%tid} ; unsigned on FreeBSD/Linux, otherwise default_format (0x%4.4x for thread.id) |
1455 | int dot_count = 0; |
1456 | const char *specifier = NULL__null; |
1457 | int width_precision_length = 0; |
1458 | const char *width_precision = ++var_name_begin; |
1459 | while (isdigit(*var_name_begin) || *var_name_begin == '.') |
1460 | { |
1461 | dot_count += (*var_name_begin == '.'); |
1462 | if (dot_count > 1) |
1463 | break; |
1464 | var_name_begin++; |
1465 | width_precision_length++; |
1466 | } |
1467 | |
1468 | if (IsToken (var_name_begin, "tid}")) |
1469 | { |
1470 | Target *target = Target::GetTargetFromContexts (exe_ctx_ptr, sc_ptr); |
1471 | if (target) |
1472 | { |
1473 | ArchSpec arch (target->GetArchitecture ()); |
1474 | llvm::Triple::OSType ostype = arch.IsValid() ? arch.GetTriple().getOS() : llvm::Triple::UnknownOS; |
1475 | if ((ostype == llvm::Triple::FreeBSD) || (ostype == llvm::Triple::Linux)) |
1476 | specifier = PRIu64"l" "u"; |
1477 | } |
1478 | if (!specifier) |
1479 | { |
1480 | format = default_format; |
1481 | return true; |
1482 | } |
1483 | } |
1484 | else if (IsToken (var_name_begin, "x}")) |
1485 | specifier = PRIx64"l" "x"; |
1486 | else if (IsToken (var_name_begin, "X}")) |
1487 | specifier = PRIX64"l" "X"; |
1488 | else if (IsToken (var_name_begin, "u}")) |
1489 | specifier = PRIu64"l" "u"; |
1490 | |
1491 | if (specifier) |
1492 | { |
1493 | format = "%"; |
1494 | if (width_precision_length) |
1495 | format += std::string(width_precision, width_precision_length); |
1496 | format += specifier; |
1497 | return true; |
1498 | } |
1499 | } |
1500 | } |
1501 | return false; |
1502 | } |
1503 | |
1504 | // Find information for the "thread.info.*" specifiers in a format string |
1505 | static bool |
1506 | FormatThreadExtendedInfoRecurse |
1507 | ( |
1508 | const char *var_name_begin, |
1509 | StructuredData::ObjectSP thread_info_dictionary, |
1510 | const SymbolContext *sc, |
1511 | const ExecutionContext *exe_ctx, |
1512 | Stream &s |
1513 | ) |
1514 | { |
1515 | bool var_success = false; |
1516 | std::string token_format; |
1517 | |
1518 | llvm::StringRef var_name(var_name_begin); |
1519 | size_t percent_idx = var_name.find('%'); |
1520 | size_t close_curly_idx = var_name.find('}'); |
1521 | llvm::StringRef path = var_name; |
1522 | llvm::StringRef formatter = var_name; |
1523 | |
1524 | // 'path' will be the dot separated list of objects to transverse up until we hit |
1525 | // a close curly brace, a percent sign, or an end of string. |
1526 | if (percent_idx != llvm::StringRef::npos || close_curly_idx != llvm::StringRef::npos) |
1527 | { |
1528 | if (percent_idx != llvm::StringRef::npos && close_curly_idx != llvm::StringRef::npos) |
1529 | { |
1530 | if (percent_idx < close_curly_idx) |
1531 | { |
1532 | path = var_name.slice(0, percent_idx); |
1533 | formatter = var_name.substr (percent_idx); |
1534 | } |
1535 | else |
1536 | { |
1537 | path = var_name.slice(0, close_curly_idx); |
1538 | formatter = var_name.substr (close_curly_idx); |
1539 | } |
1540 | } |
1541 | else if (percent_idx != llvm::StringRef::npos) |
1542 | { |
1543 | path = var_name.slice(0, percent_idx); |
1544 | formatter = var_name.substr (percent_idx); |
1545 | } |
1546 | else if (close_curly_idx != llvm::StringRef::npos) |
1547 | { |
1548 | path = var_name.slice(0, close_curly_idx); |
1549 | formatter = var_name.substr (close_curly_idx); |
1550 | } |
1551 | } |
1552 | |
1553 | StructuredData::ObjectSP value = thread_info_dictionary->GetObjectForDotSeparatedPath (path); |
1554 | |
1555 | if (value.get()) |
1556 | { |
1557 | if (value->GetType() == StructuredData::Type::eTypeInteger) |
1558 | { |
1559 | if (IsTokenWithFormat (formatter.str().c_str(), "", token_format, "0x%4.4" PRIx64"l" "x", exe_ctx, sc)) |
1560 | { |
1561 | s.Printf(token_format.c_str(), value->GetAsInteger()->GetValue()); |
1562 | var_success = true; |
1563 | } |
1564 | } |
1565 | else if (value->GetType() == StructuredData::Type::eTypeFloat) |
1566 | { |
1567 | s.Printf ("%f", value->GetAsFloat()->GetValue()); |
1568 | var_success = true; |
1569 | } |
1570 | else if (value->GetType() == StructuredData::Type::eTypeString) |
1571 | { |
1572 | s.Printf("%s", value->GetAsString()->GetValue().c_str()); |
1573 | var_success = true; |
1574 | } |
1575 | else if (value->GetType() == StructuredData::Type::eTypeArray) |
1576 | { |
1577 | if (value->GetAsArray()->GetSize() > 0) |
1578 | { |
1579 | s.Printf ("%zu", value->GetAsArray()->GetSize()); |
1580 | var_success = true; |
1581 | } |
1582 | } |
1583 | else if (value->GetType() == StructuredData::Type::eTypeDictionary) |
1584 | { |
1585 | s.Printf ("%zu", value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize()); |
1586 | var_success = true; |
1587 | } |
1588 | } |
1589 | |
1590 | return var_success; |
1591 | } |
1592 | |
1593 | |
1594 | static bool |
1595 | FormatPromptRecurse |
1596 | ( |
1597 | const char *format, |
1598 | const SymbolContext *sc, |
1599 | const ExecutionContext *exe_ctx, |
1600 | const Address *addr, |
1601 | Stream &s, |
1602 | const char **end, |
1603 | ValueObject* valobj, |
1604 | bool function_changed, |
1605 | bool initial_function |
1606 | ) |
1607 | { |
1608 | ValueObject* realvalobj = NULL__null; // makes it super-easy to parse pointers |
1609 | bool success = true; |
1610 | const char *p; |
1611 | Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES(1u << 19))); |
1612 | |
1613 | for (p = format; *p != '\0'; ++p) |
1614 | { |
1615 | if (realvalobj) |
1616 | { |
1617 | valobj = realvalobj; |
1618 | realvalobj = NULL__null; |
1619 | } |
1620 | size_t non_special_chars = ::strcspn (p, "${}\\"); |
1621 | if (non_special_chars > 0) |
1622 | { |
1623 | if (success) |
1624 | s.Write (p, non_special_chars); |
1625 | p += non_special_chars; |
1626 | } |
1627 | |
1628 | if (*p == '\0') |
1629 | { |
1630 | break; |
1631 | } |
1632 | else if (*p == '{') |
1633 | { |
1634 | // Start a new scope that must have everything it needs if it is to |
1635 | // to make it into the final output stream "s". If you want to make |
1636 | // a format that only prints out the function or symbol name if there |
1637 | // is one in the symbol context you can use: |
1638 | // "{function =${function.name}}" |
1639 | // The first '{' starts a new scope that end with the matching '}' at |
1640 | // the end of the string. The contents "function =${function.name}" |
1641 | // will then be evaluated and only be output if there is a function |
1642 | // or symbol with a valid name. |
1643 | StreamString sub_strm; |
1644 | |
1645 | ++p; // Skip the '{' |
1646 | |
1647 | if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj, function_changed, initial_function)) |
1648 | { |
1649 | // The stream had all it needed |
1650 | s.Write(sub_strm.GetData(), sub_strm.GetSize()); |
1651 | } |
1652 | if (*p != '}') |
1653 | { |
1654 | success = false; |
1655 | break; |
1656 | } |
1657 | } |
1658 | else if (*p == '}') |
1659 | { |
1660 | // End of a enclosing scope |
1661 | break; |
1662 | } |
1663 | else if (*p == '$') |
1664 | { |
1665 | // We have a prompt variable to print |
1666 | ++p; |
1667 | if (*p == '{') |
1668 | { |
1669 | ++p; |
1670 | const char *var_name_begin = p; |
1671 | const char *var_name_end = ::strchr (p, '}'); |
1672 | |
1673 | if (var_name_end && var_name_begin < var_name_end) |
1674 | { |
1675 | // if we have already failed to parse, skip this variable |
1676 | if (success) |
1677 | { |
1678 | const char *cstr = NULL__null; |
1679 | std::string token_format; |
1680 | Address format_addr; |
1681 | |
1682 | // normally "addr" means print a raw address but |
1683 | // "file-addr-or-load-addr" means print a module + file addr if there's no load addr |
1684 | bool print_file_addr_or_load_addr = false; |
1685 | bool addr_offset_concrete_func_only = false; |
1686 | bool addr_offset_print_with_no_padding = false; |
1687 | bool calculate_format_addr_function_offset = false; |
1688 | // Set reg_kind and reg_num to invalid values |
1689 | RegisterKind reg_kind = kNumRegisterKinds; |
1690 | uint32_t reg_num = LLDB_INVALID_REGNUM(4294967295U); |
1691 | FileSpec format_file_spec; |
1692 | const RegisterInfo *reg_info = NULL__null; |
1693 | RegisterContext *reg_ctx = NULL__null; |
1694 | bool do_deref_pointer = false; |
1695 | ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; |
1696 | ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::eExpressionPathEndResultTypePlain; |
1697 | |
1698 | // Each variable must set success to true below... |
1699 | bool var_success = false; |
1700 | switch (var_name_begin[0]) |
1701 | { |
1702 | case '*': |
1703 | case 'v': |
1704 | case 's': |
1705 | { |
1706 | if (!valobj) |
1707 | break; |
1708 | |
1709 | if (log) |
1710 | log->Printf("[Debugger::FormatPrompt] initial string: %s",var_name_begin); |
1711 | |
1712 | // check for *var and *svar |
1713 | if (*var_name_begin == '*') |
1714 | { |
1715 | do_deref_pointer = true; |
1716 | var_name_begin++; |
1717 | if (log) |
1718 | log->Printf("[Debugger::FormatPrompt] found a deref, new string is: %s",var_name_begin); |
1719 | } |
1720 | |
1721 | if (*var_name_begin == 's') |
1722 | { |
1723 | if (!valobj->IsSynthetic()) |
1724 | valobj = valobj->GetSyntheticValue().get(); |
1725 | if (!valobj) |
1726 | break; |
1727 | var_name_begin++; |
1728 | if (log) |
1729 | log->Printf("[Debugger::FormatPrompt] found a synthetic, new string is: %s",var_name_begin); |
1730 | } |
1731 | |
1732 | // should be a 'v' by now |
1733 | if (*var_name_begin != 'v') |
1734 | break; |
1735 | |
1736 | if (log) |
1737 | log->Printf("[Debugger::FormatPrompt] string I am working with: %s",var_name_begin); |
1738 | |
1739 | ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? |
1740 | ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); |
1741 | ValueObject::GetValueForExpressionPathOptions options; |
1742 | options.DontCheckDotVsArrowSyntax().DoAllowBitfieldSyntax().DoAllowFragileIVar().DoAllowSyntheticChildren(); |
1743 | ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; |
1744 | ValueObject* target = NULL__null; |
1745 | Format custom_format = eFormatInvalid; |
1746 | const char* var_name_final = NULL__null; |
1747 | const char* var_name_final_if_array_range = NULL__null; |
1748 | const char* close_bracket_position = NULL__null; |
1749 | int64_t index_lower = -1; |
1750 | int64_t index_higher = -1; |
1751 | bool is_array_range = false; |
1752 | const char* first_unparsed; |
1753 | bool was_plain_var = false; |
1754 | bool was_var_format = false; |
1755 | bool was_var_indexed = false; |
1756 | |
1757 | if (!valobj) break; |
1758 | // simplest case ${var}, just print valobj's value |
1759 | if (IsToken (var_name_begin, "var}")) |
1760 | { |
1761 | was_plain_var = true; |
1762 | target = valobj; |
1763 | val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; |
1764 | } |
1765 | else if (IsToken (var_name_begin, "var.script:")) |
1766 | { |
1767 | var_name_begin += ::strlen("var.script:"); |
1768 | std::string script_name(var_name_begin,var_name_end); |
1769 | ScriptInterpreter* script_interpreter = valobj->GetTargetSP()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); |
1770 | if (RunScriptFormatKeyword (s, script_interpreter, valobj, script_name)) |
1771 | var_success = true; |
1772 | break; |
1773 | } |
1774 | else if (IsToken (var_name_begin,"var%")) |
1775 | { |
1776 | was_var_format = true; |
1777 | // this is a variable with some custom format applied to it |
1778 | const char* percent_position; |
1779 | target = valobj; |
1780 | val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; |
1781 | ScanFormatDescriptor (var_name_begin, |
1782 | var_name_end, |
1783 | &var_name_final, |
1784 | &percent_position, |
1785 | &custom_format, |
1786 | &val_obj_display); |
1787 | } |
1788 | // this is ${var.something} or multiple .something nested |
1789 | else if (IsToken (var_name_begin, "var")) |
1790 | { |
1791 | if (IsToken (var_name_begin, "var[")) |
1792 | was_var_indexed = true; |
1793 | const char* percent_position; |
1794 | ScanFormatDescriptor (var_name_begin, |
1795 | var_name_end, |
1796 | &var_name_final, |
1797 | &percent_position, |
1798 | &custom_format, |
1799 | &val_obj_display); |
1800 | |
1801 | const char* open_bracket_position; |
1802 | const char* separator_position; |
1803 | ScanBracketedRange (var_name_begin, |
1804 | var_name_end, |
1805 | var_name_final, |
1806 | &open_bracket_position, |
1807 | &separator_position, |
1808 | &close_bracket_position, |
1809 | &var_name_final_if_array_range, |
1810 | &index_lower, |
1811 | &index_higher); |
1812 | |
1813 | Error error; |
1814 | |
1815 | std::string expr_path(var_name_final-var_name_begin-1,0); |
1816 | memcpy(&expr_path[0], var_name_begin+3,var_name_final-var_name_begin-3); |
1817 | |
1818 | if (log) |
1819 | log->Printf("[Debugger::FormatPrompt] symbol to expand: %s",expr_path.c_str()); |
1820 | |
1821 | target = valobj->GetValueForExpressionPath(expr_path.c_str(), |
1822 | &first_unparsed, |
1823 | &reason_to_stop, |
1824 | &final_value_type, |
1825 | options, |
1826 | &what_next).get(); |
1827 | |
1828 | if (!target) |
1829 | { |
1830 | if (log) |
1831 | log->Printf("[Debugger::FormatPrompt] ERROR: unparsed portion = %s, why stopping = %d," |
1832 | " final_value_type %d", |
1833 | first_unparsed, reason_to_stop, final_value_type); |
1834 | break; |
1835 | } |
1836 | else |
1837 | { |
1838 | if (log) |
1839 | log->Printf("[Debugger::FormatPrompt] ALL RIGHT: unparsed portion = %s, why stopping = %d," |
1840 | " final_value_type %d", |
1841 | first_unparsed, reason_to_stop, final_value_type); |
1842 | target = target->GetQualifiedRepresentationIfAvailable(target->GetDynamicValueType(), true).get(); |
1843 | } |
1844 | } |
1845 | else |
1846 | break; |
1847 | |
1848 | is_array_range = (final_value_type == ValueObject::eExpressionPathEndResultTypeBoundedRange || |
1849 | final_value_type == ValueObject::eExpressionPathEndResultTypeUnboundedRange); |
1850 | |
1851 | do_deref_pointer = (what_next == ValueObject::eExpressionPathAftermathDereference); |
1852 | |
1853 | if (do_deref_pointer && !is_array_range) |
1854 | { |
1855 | // I have not deref-ed yet, let's do it |
1856 | // this happens when we are not going through GetValueForVariableExpressionPath |
1857 | // to get to the target ValueObject |
1858 | Error error; |
1859 | target = target->Dereference(error).get(); |
1860 | if (error.Fail()) |
1861 | { |
1862 | if (log) |
1863 | log->Printf("[Debugger::FormatPrompt] ERROR: %s\n", error.AsCString("unknown")); \ |
1864 | break; |
1865 | } |
1866 | do_deref_pointer = false; |
1867 | } |
1868 | |
1869 | if (!target) |
1870 | { |
1871 | if (log) |
1872 | log->Printf("[Debugger::FormatPrompt] could not calculate target for prompt expression"); |
1873 | break; |
1874 | } |
1875 | |
1876 | // we do not want to use the summary for a bitfield of type T:n |
1877 | // if we were originally dealing with just a T - that would get |
1878 | // us into an endless recursion |
1879 | if (target->IsBitfield() && was_var_indexed) |
1880 | { |
1881 | // TODO: check for a (T:n)-specific summary - we should still obey that |
1882 | StreamString bitfield_name; |
1883 | bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(), target->GetBitfieldBitSize()); |
1884 | lldb::TypeNameSpecifierImplSP type_sp(new TypeNameSpecifierImpl(bitfield_name.GetData(),false)); |
1885 | if (!DataVisualization::GetSummaryForType(type_sp)) |
1886 | val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; |
1887 | } |
1888 | |
1889 | // TODO use flags for these |
1890 | const uint32_t type_info_flags = target->GetClangType().GetTypeInfo(NULL__null); |
1891 | bool is_array = (type_info_flags & eTypeIsArray) != 0; |
1892 | bool is_pointer = (type_info_flags & eTypeIsPointer) != 0; |
1893 | bool is_aggregate = target->GetClangType().IsAggregateType(); |
1894 | |
1895 | if ((is_array || is_pointer) && (!is_array_range) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) // this should be wrong, but there are some exceptions |
1896 | { |
1897 | StreamString str_temp; |
1898 | if (log) |
1899 | log->Printf("[Debugger::FormatPrompt] I am into array || pointer && !range"); |
1900 | |
1901 | if (target->HasSpecialPrintableRepresentation(val_obj_display, custom_format)) |
1902 | { |
1903 | // try to use the special cases |
1904 | var_success = target->DumpPrintableRepresentation(str_temp, |
1905 | val_obj_display, |
1906 | custom_format); |
1907 | if (log) |
1908 | log->Printf("[Debugger::FormatPrompt] special cases did%s match", var_success ? "" : "n't"); |
1909 | |
1910 | // should not happen |
1911 | if (var_success) |
1912 | s << str_temp.GetData(); |
1913 | var_success = true; |
1914 | break; |
1915 | } |
1916 | else |
1917 | { |
1918 | if (was_plain_var) // if ${var} |
1919 | { |
1920 | s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); |
1921 | } |
1922 | else if (is_pointer) // if pointer, value is the address stored |
1923 | { |
1924 | target->DumpPrintableRepresentation (s, |
1925 | val_obj_display, |
1926 | custom_format, |
1927 | ValueObject::ePrintableRepresentationSpecialCasesDisable); |
1928 | } |
1929 | var_success = true; |
1930 | break; |
1931 | } |
1932 | } |
1933 | |
1934 | // if directly trying to print ${var}, and this is an aggregate, display a nice |
1935 | // type @ location message |
1936 | if (is_aggregate && was_plain_var) |
1937 | { |
1938 | s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); |
1939 | var_success = true; |
1940 | break; |
1941 | } |
1942 | |
1943 | // if directly trying to print ${var%V}, and this is an aggregate, do not let the user do it |
1944 | if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue))) |
1945 | { |
1946 | s << "<invalid use of aggregate type>"; |
1947 | var_success = true; |
1948 | break; |
1949 | } |
1950 | |
1951 | if (!is_array_range) |
1952 | { |
1953 | if (log) |
1954 | log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); |
1955 | var_success = target->DumpPrintableRepresentation(s,val_obj_display, custom_format); |
1956 | } |
1957 | else |
1958 | { |
1959 | if (log) |
1960 | log->Printf("[Debugger::FormatPrompt] checking if I can handle as array"); |
1961 | if (!is_array && !is_pointer) |
1962 | break; |
1963 | if (log) |
1964 | log->Printf("[Debugger::FormatPrompt] handle as array"); |
1965 | const char* special_directions = NULL__null; |
1966 | StreamString special_directions_writer; |
1967 | if (close_bracket_position && (var_name_end-close_bracket_position > 1)) |
1968 | { |
1969 | ConstString additional_data; |
1970 | additional_data.SetCStringWithLength(close_bracket_position+1, var_name_end-close_bracket_position-1); |
1971 | special_directions_writer.Printf("${%svar%s}", |
1972 | do_deref_pointer ? "*" : "", |
1973 | additional_data.GetCString()); |
1974 | special_directions = special_directions_writer.GetData(); |
1975 | } |
1976 | |
1977 | // let us display items index_lower thru index_higher of this array |
1978 | s.PutChar('['); |
1979 | var_success = true; |
1980 | |
1981 | if (index_higher < 0) |
1982 | index_higher = valobj->GetNumChildren() - 1; |
1983 | |
1984 | uint32_t max_num_children = target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); |
1985 | |
1986 | for (;index_lower<=index_higher;index_lower++) |
1987 | { |
1988 | ValueObject* item = ExpandIndexedExpression (target, |
1989 | index_lower, |
1990 | exe_ctx->GetFramePtr(), |
1991 | false).get(); |
1992 | |
1993 | if (!item) |
1994 | { |
1995 | if (log) |
1996 | log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at index %" PRId64"l" "d", index_lower); |
1997 | } |
1998 | else |
1999 | { |
2000 | if (log) |
2001 | log->Printf("[Debugger::FormatPrompt] special_directions for child item: %s",special_directions); |
2002 | } |
2003 | |
2004 | if (!special_directions) |
2005 | var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); |
2006 | else |
2007 | var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL__null, item, function_changed, initial_function); |
2008 | |
2009 | if (--max_num_children == 0) |
2010 | { |
2011 | s.PutCString(", ..."); |
2012 | break; |
2013 | } |
2014 | |
2015 | if (index_lower < index_higher) |
2016 | s.PutChar(','); |
2017 | } |
2018 | s.PutChar(']'); |
2019 | } |
2020 | } |
2021 | break; |
2022 | case 'a': |
2023 | if (IsToken (var_name_begin, "addr-file-or-load}")) |
2024 | { |
2025 | print_file_addr_or_load_addr = true; |
2026 | } |
2027 | if (IsToken (var_name_begin, "addr}") |
2028 | || IsToken (var_name_begin, "addr-file-or-load}")) |
2029 | { |
2030 | if (addr && addr->IsValid()) |
2031 | { |
2032 | var_success = true; |
2033 | format_addr = *addr; |
2034 | } |
2035 | } |
2036 | break; |
2037 | |
2038 | case 'p': |
2039 | if (IsToken (var_name_begin, "process.")) |
2040 | { |
2041 | if (exe_ctx) |
2042 | { |
2043 | Process *process = exe_ctx->GetProcessPtr(); |
2044 | if (process) |
2045 | { |
2046 | var_name_begin += ::strlen ("process."); |
2047 | if (IsTokenWithFormat (var_name_begin, "id", token_format, "%" PRIu64"l" "u", exe_ctx, sc)) |
2048 | { |
2049 | s.Printf(token_format.c_str(), process->GetID()); |
2050 | var_success = true; |
2051 | } |
2052 | else if ((IsToken (var_name_begin, "name}")) || |
2053 | (IsToken (var_name_begin, "file.basename}")) || |
2054 | (IsToken (var_name_begin, "file.fullpath}"))) |
2055 | { |
2056 | Module *exe_module = process->GetTarget().GetExecutableModulePointer(); |
2057 | if (exe_module) |
2058 | { |
2059 | if (var_name_begin[0] == 'n' || var_name_begin[5] == 'f') |
2060 | { |
2061 | format_file_spec.GetFilename() = exe_module->GetFileSpec().GetFilename(); |
2062 | var_success = (bool)format_file_spec; |
2063 | } |
2064 | else |
2065 | { |
2066 | format_file_spec = exe_module->GetFileSpec(); |
2067 | var_success = (bool)format_file_spec; |
2068 | } |
2069 | } |
2070 | } |
2071 | else if (IsToken (var_name_begin, "script:")) |
2072 | { |
2073 | var_name_begin += ::strlen("script:"); |
2074 | std::string script_name(var_name_begin,var_name_end); |
2075 | ScriptInterpreter* script_interpreter = process->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); |
2076 | if (RunScriptFormatKeyword (s, script_interpreter, process, script_name)) |
2077 | var_success = true; |
2078 | } |
2079 | } |
2080 | } |
2081 | } |
2082 | break; |
2083 | |
2084 | case 't': |
2085 | if (IsToken (var_name_begin, "thread.")) |
2086 | { |
2087 | if (exe_ctx) |
2088 | { |
2089 | Thread *thread = exe_ctx->GetThreadPtr(); |
2090 | if (thread) |
2091 | { |
2092 | var_name_begin += ::strlen ("thread."); |
2093 | if (IsTokenWithFormat (var_name_begin, "id", token_format, "0x%4.4" PRIx64"l" "x", exe_ctx, sc)) |
2094 | { |
2095 | s.Printf(token_format.c_str(), thread->GetID()); |
2096 | var_success = true; |
2097 | } |
2098 | else if (IsTokenWithFormat (var_name_begin, "protocol_id", token_format, "0x%4.4" PRIx64"l" "x", exe_ctx, sc)) |
2099 | { |
2100 | s.Printf(token_format.c_str(), thread->GetProtocolID()); |
2101 | var_success = true; |
2102 | } |
2103 | else if (IsTokenWithFormat (var_name_begin, "index", token_format, "%" PRIu64"l" "u", exe_ctx, sc)) |
2104 | { |
2105 | s.Printf(token_format.c_str(), (uint64_t)thread->GetIndexID()); |
2106 | var_success = true; |
2107 | } |
2108 | else if (IsToken (var_name_begin, "name}")) |
2109 | { |
2110 | cstr = thread->GetName(); |
2111 | var_success = cstr && cstr[0]; |
2112 | if (var_success) |
2113 | s.PutCString(cstr); |
2114 | } |
2115 | else if (IsToken (var_name_begin, "queue}")) |
2116 | { |
2117 | cstr = thread->GetQueueName(); |
2118 | var_success = cstr && cstr[0]; |
2119 | if (var_success) |
2120 | s.PutCString(cstr); |
2121 | } |
2122 | else if (IsToken (var_name_begin, "stop-reason}")) |
2123 | { |
2124 | StopInfoSP stop_info_sp = thread->GetStopInfo (); |
2125 | if (stop_info_sp && stop_info_sp->IsValid()) |
2126 | { |
2127 | cstr = stop_info_sp->GetDescription(); |
2128 | if (cstr && cstr[0]) |
2129 | { |
2130 | s.PutCString(cstr); |
2131 | var_success = true; |
2132 | } |
2133 | } |
2134 | } |
2135 | else if (IsToken (var_name_begin, "return-value}")) |
2136 | { |
2137 | StopInfoSP stop_info_sp = thread->GetStopInfo (); |
2138 | if (stop_info_sp && stop_info_sp->IsValid()) |
2139 | { |
2140 | ValueObjectSP return_valobj_sp = StopInfo::GetReturnValueObject (stop_info_sp); |
2141 | if (return_valobj_sp) |
2142 | { |
2143 | return_valobj_sp->Dump(s); |
2144 | var_success = true; |
2145 | } |
2146 | } |
2147 | } |
2148 | else if (IsToken (var_name_begin, "completed-expression}")) |
2149 | { |
2150 | StopInfoSP stop_info_sp = thread->GetStopInfo (); |
2151 | if (stop_info_sp && stop_info_sp->IsValid()) |
2152 | { |
2153 | ClangExpressionVariableSP expression_var_sp = StopInfo::GetExpressionVariable (stop_info_sp); |
2154 | if (expression_var_sp && expression_var_sp->GetValueObject()) |
2155 | { |
2156 | expression_var_sp->GetValueObject()->Dump(s); |
2157 | var_success = true; |
2158 | } |
2159 | } |
2160 | } |
2161 | else if (IsToken (var_name_begin, "script:")) |
2162 | { |
2163 | var_name_begin += ::strlen("script:"); |
2164 | std::string script_name(var_name_begin,var_name_end); |
2165 | ScriptInterpreter* script_interpreter = thread->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); |
2166 | if (RunScriptFormatKeyword (s, script_interpreter, thread, script_name)) |
2167 | var_success = true; |
2168 | } |
2169 | else if (IsToken (var_name_begin, "info.")) |
2170 | { |
2171 | var_name_begin += ::strlen("info."); |
2172 | StructuredData::ObjectSP object_sp = thread->GetExtendedInfo(); |
2173 | if (object_sp && object_sp->GetType() == StructuredData::Type::eTypeDictionary) |
2174 | { |
2175 | var_success = FormatThreadExtendedInfoRecurse (var_name_begin, object_sp, sc, exe_ctx, s); |
2176 | } |
2177 | } |
2178 | } |
2179 | } |
2180 | } |
2181 | else if (IsToken (var_name_begin, "target.")) |
2182 | { |
2183 | // TODO: hookup properties |
2184 | // if (!target_properties_sp) |
2185 | // { |
2186 | // Target *target = Target::GetTargetFromContexts (exe_ctx, sc); |
2187 | // if (target) |
2188 | // target_properties_sp = target->GetProperties(); |
2189 | // } |
2190 | // |
2191 | // if (target_properties_sp) |
2192 | // { |
2193 | // var_name_begin += ::strlen ("target."); |
2194 | // const char *end_property = strchr(var_name_begin, '}'); |
2195 | // if (end_property) |
2196 | // { |
2197 | // ConstString property_name(var_name_begin, end_property - var_name_begin); |
2198 | // std::string property_value (target_properties_sp->GetPropertyValue(property_name)); |
2199 | // if (!property_value.empty()) |
2200 | // { |
2201 | // s.PutCString (property_value.c_str()); |
2202 | // var_success = true; |
2203 | // } |
2204 | // } |
2205 | // } |
2206 | Target *target = Target::GetTargetFromContexts (exe_ctx, sc); |
2207 | if (target) |
2208 | { |
2209 | var_name_begin += ::strlen ("target."); |
2210 | if (IsToken (var_name_begin, "arch}")) |
2211 | { |
2212 | ArchSpec arch (target->GetArchitecture ()); |
2213 | if (arch.IsValid()) |
2214 | { |
2215 | s.PutCString (arch.GetArchitectureName()); |
2216 | var_success = true; |
2217 | } |
2218 | } |
2219 | else if (IsToken (var_name_begin, "script:")) |
2220 | { |
2221 | var_name_begin += ::strlen("script:"); |
2222 | std::string script_name(var_name_begin,var_name_end); |
2223 | ScriptInterpreter* script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); |
2224 | if (RunScriptFormatKeyword (s, script_interpreter, target, script_name)) |
2225 | var_success = true; |
2226 | } |
2227 | } |
2228 | } |
2229 | break; |
2230 | |
2231 | case 'm': |
2232 | if (IsToken (var_name_begin, "module.")) |
2233 | { |
2234 | if (sc && sc->module_sp.get()) |
2235 | { |
2236 | Module *module = sc->module_sp.get(); |
2237 | var_name_begin += ::strlen ("module."); |
2238 | |
2239 | if (IsToken (var_name_begin, "file.")) |
2240 | { |
2241 | if (module->GetFileSpec()) |
2242 | { |
2243 | var_name_begin += ::strlen ("file."); |
2244 | |
2245 | if (IsToken (var_name_begin, "basename}")) |
2246 | { |
2247 | format_file_spec.GetFilename() = module->GetFileSpec().GetFilename(); |
2248 | var_success = (bool)format_file_spec; |
2249 | } |
2250 | else if (IsToken (var_name_begin, "fullpath}")) |
2251 | { |
2252 | format_file_spec = module->GetFileSpec(); |
2253 | var_success = (bool)format_file_spec; |
2254 | } |
2255 | } |
2256 | } |
2257 | } |
2258 | } |
2259 | break; |
2260 | |
2261 | |
2262 | case 'f': |
2263 | if (IsToken (var_name_begin, "file.")) |
2264 | { |
2265 | if (sc && sc->comp_unit != NULL__null) |
2266 | { |
2267 | var_name_begin += ::strlen ("file."); |
2268 | |
2269 | if (IsToken (var_name_begin, "basename}")) |
2270 | { |
2271 | format_file_spec.GetFilename() = sc->comp_unit->GetFilename(); |
2272 | var_success = (bool)format_file_spec; |
2273 | } |
2274 | else if (IsToken (var_name_begin, "fullpath}")) |
2275 | { |
2276 | format_file_spec = *sc->comp_unit; |
2277 | var_success = (bool)format_file_spec; |
2278 | } |
2279 | } |
2280 | } |
2281 | else if (IsToken (var_name_begin, "frame.")) |
2282 | { |
2283 | if (exe_ctx) |
2284 | { |
2285 | StackFrame *frame = exe_ctx->GetFramePtr(); |
2286 | if (frame) |
2287 | { |
2288 | var_name_begin += ::strlen ("frame."); |
2289 | if (IsToken (var_name_begin, "index}")) |
2290 | { |
2291 | s.Printf("%u", frame->GetFrameIndex()); |
2292 | var_success = true; |
2293 | } |
2294 | else if (IsToken (var_name_begin, "pc}")) |
2295 | { |
2296 | reg_kind = eRegisterKindGeneric; |
2297 | reg_num = LLDB_REGNUM_GENERIC_PC0; |
2298 | var_success = true; |
2299 | } |
2300 | else if (IsToken (var_name_begin, "sp}")) |
2301 | { |
2302 | reg_kind = eRegisterKindGeneric; |
2303 | reg_num = LLDB_REGNUM_GENERIC_SP1; |
2304 | var_success = true; |
2305 | } |
2306 | else if (IsToken (var_name_begin, "fp}")) |
2307 | { |
2308 | reg_kind = eRegisterKindGeneric; |
2309 | reg_num = LLDB_REGNUM_GENERIC_FP2; |
2310 | var_success = true; |
2311 | } |
2312 | else if (IsToken (var_name_begin, "flags}")) |
2313 | { |
2314 | reg_kind = eRegisterKindGeneric; |
2315 | reg_num = LLDB_REGNUM_GENERIC_FLAGS4; |
2316 | var_success = true; |
2317 | } |
2318 | else if (IsToken (var_name_begin, "reg.")) |
2319 | { |
2320 | reg_ctx = frame->GetRegisterContext().get(); |
2321 | if (reg_ctx) |
2322 | { |
2323 | var_name_begin += ::strlen ("reg."); |
2324 | if (var_name_begin < var_name_end) |
2325 | { |
2326 | std::string reg_name (var_name_begin, var_name_end); |
2327 | reg_info = reg_ctx->GetRegisterInfoByName (reg_name.c_str()); |
2328 | if (reg_info) |
2329 | var_success = true; |
2330 | } |
2331 | } |
2332 | } |
2333 | else if (IsToken (var_name_begin, "script:")) |
2334 | { |
2335 | var_name_begin += ::strlen("script:"); |
2336 | std::string script_name(var_name_begin,var_name_end); |
2337 | ScriptInterpreter* script_interpreter = frame->GetThread()->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); |
2338 | if (RunScriptFormatKeyword (s, script_interpreter, frame, script_name)) |
2339 | var_success = true; |
2340 | } |
2341 | } |
2342 | } |
2343 | } |
2344 | else if (IsToken (var_name_begin, "function.")) |
2345 | { |
2346 | if (sc && (sc->function != NULL__null || sc->symbol != NULL__null)) |
2347 | { |
2348 | var_name_begin += ::strlen ("function."); |
2349 | if (IsToken (var_name_begin, "id}")) |
2350 | { |
2351 | if (sc->function) |
2352 | s.Printf("function{0x%8.8" PRIx64"l" "x" "}", sc->function->GetID()); |
2353 | else |
2354 | s.Printf("symbol[%u]", sc->symbol->GetID()); |
2355 | |
2356 | var_success = true; |
2357 | } |
2358 | if (IsToken (var_name_begin, "changed}") && function_changed) |
2359 | { |
2360 | var_success = true; |
2361 | } |
2362 | if (IsToken (var_name_begin, "initial-function}") && initial_function) |
2363 | { |
2364 | var_success = true; |
2365 | } |
2366 | else if (IsToken (var_name_begin, "name}")) |
2367 | { |
2368 | if (sc->function) |
2369 | cstr = sc->function->GetName().AsCString (NULL__null); |
2370 | else if (sc->symbol) |
2371 | cstr = sc->symbol->GetName().AsCString (NULL__null); |
2372 | if (cstr) |
2373 | { |
2374 | s.PutCString(cstr); |
2375 | |
2376 | if (sc->block) |
2377 | { |
2378 | Block *inline_block = sc->block->GetContainingInlinedBlock (); |
2379 | if (inline_block) |
2380 | { |
2381 | const InlineFunctionInfo *inline_info = sc->block->GetInlinedFunctionInfo(); |
2382 | if (inline_info) |
2383 | { |
2384 | s.PutCString(" [inlined] "); |
2385 | inline_info->GetName().Dump(&s); |
2386 | } |
2387 | } |
2388 | } |
2389 | var_success = true; |
2390 | } |
2391 | } |
2392 | else if (IsToken (var_name_begin, "name-without-args}")) |
2393 | { |
2394 | ConstString name; |
2395 | if (sc->function) |
2396 | name = sc->function->GetMangled().GetName (Mangled::ePreferDemangledWithoutArguments); |
2397 | else if (sc->symbol) |
2398 | name = sc->symbol->GetMangled().GetName (Mangled::ePreferDemangledWithoutArguments); |
2399 | if (name) |
2400 | { |
2401 | s.PutCString(name.GetCString()); |
2402 | var_success = true; |
2403 | } |
2404 | } |
2405 | else if (IsToken (var_name_begin, "name-with-args}")) |
2406 | { |
2407 | // Print the function name with arguments in it |
2408 | |
2409 | if (sc->function) |
2410 | { |
2411 | var_success = true; |
2412 | ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL__null; |
2413 | cstr = sc->function->GetName().AsCString (NULL__null); |
2414 | if (cstr) |
2415 | { |
2416 | const InlineFunctionInfo *inline_info = NULL__null; |
2417 | VariableListSP variable_list_sp; |
2418 | bool get_function_vars = true; |
2419 | if (sc->block) |
2420 | { |
2421 | Block *inline_block = sc->block->GetContainingInlinedBlock (); |
2422 | |
2423 | if (inline_block) |
2424 | { |
2425 | get_function_vars = false; |
2426 | inline_info = sc->block->GetInlinedFunctionInfo(); |
2427 | if (inline_info) |
2428 | variable_list_sp = inline_block->GetBlockVariableList (true); |
2429 | } |
2430 | } |
2431 | |
2432 | if (get_function_vars) |
2433 | { |
2434 | variable_list_sp = sc->function->GetBlock(true).GetBlockVariableList (true); |
2435 | } |
2436 | |
2437 | if (inline_info) |
2438 | { |
2439 | s.PutCString (cstr); |
2440 | s.PutCString (" [inlined] "); |
2441 | cstr = inline_info->GetName().GetCString(); |
2442 | } |
2443 | |
2444 | VariableList args; |
2445 | if (variable_list_sp) |
2446 | variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, args); |
2447 | if (args.GetSize() > 0) |
2448 | { |
2449 | const char *open_paren = strchr (cstr, '('); |
2450 | const char *close_paren = nullptr; |
2451 | const char *generic = strchr(cstr, '<'); |
2452 | // if before the arguments list begins there is a template sign |
2453 | // then scan to the end of the generic args before you try to find |
2454 | // the arguments list |
2455 | if (generic && open_paren && generic < open_paren) |
2456 | { |
2457 | int generic_depth = 1; |
2458 | ++generic; |
2459 | for (; |
2460 | *generic && generic_depth > 0; |
2461 | generic++) |
2462 | { |
2463 | if (*generic == '<') |
2464 | generic_depth++; |
2465 | if (*generic == '>') |
2466 | generic_depth--; |
2467 | } |
2468 | if (*generic) |
2469 | open_paren = strchr(generic, '('); |
2470 | else |
2471 | open_paren = nullptr; |
2472 | } |
2473 | if (open_paren) |
2474 | { |
2475 | if (IsToken (open_paren, "(anonymous namespace)")) |
2476 | { |
2477 | open_paren = strchr (open_paren + strlen("(anonymous namespace)"), '('); |
2478 | if (open_paren) |
2479 | close_paren = strchr (open_paren, ')'); |
2480 | } |
2481 | else |
2482 | close_paren = strchr (open_paren, ')'); |
2483 | } |
2484 | |
2485 | if (open_paren) |
2486 | s.Write(cstr, open_paren - cstr + 1); |
2487 | else |
2488 | { |
2489 | s.PutCString (cstr); |
2490 | s.PutChar ('('); |
2491 | } |
2492 | const size_t num_args = args.GetSize(); |
2493 | for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) |
2494 | { |
2495 | std::string buffer; |
2496 | |
2497 | VariableSP var_sp (args.GetVariableAtIndex (arg_idx)); |
2498 | ValueObjectSP var_value_sp (ValueObjectVariable::Create (exe_scope, var_sp)); |
2499 | const char *var_representation = nullptr; |
2500 | const char *var_name = var_value_sp->GetName().GetCString(); |
2501 | if (var_value_sp->GetClangType().IsAggregateType() && |
2502 | DataVisualization::ShouldPrintAsOneLiner(*var_value_sp.get())) |
2503 | { |
2504 | static StringSummaryFormat format(TypeSummaryImpl::Flags() |
2505 | .SetHideItemNames(false) |
2506 | .SetShowMembersOneLiner(true), |
2507 | ""); |
2508 | format.FormatObject(var_value_sp.get(), buffer, TypeSummaryOptions()); |
2509 | var_representation = buffer.c_str(); |
2510 | } |
2511 | else |
2512 | var_representation = var_value_sp->GetValueAsCString(); |
2513 | if (arg_idx > 0) |
2514 | s.PutCString (", "); |
2515 | if (var_value_sp->GetError().Success()) |
2516 | { |
2517 | if (var_representation) |
2518 | s.Printf ("%s=%s", var_name, var_representation); |
2519 | else |
2520 | s.Printf ("%s=%s at %s", var_name, var_value_sp->GetTypeName().GetCString(), var_value_sp->GetLocationAsCString()); |
2521 | } |
2522 | else |
2523 | s.Printf ("%s=<unavailable>", var_name); |
2524 | } |
2525 | |
2526 | if (close_paren) |
2527 | s.PutCString (close_paren); |
2528 | else |
2529 | s.PutChar(')'); |
2530 | |
2531 | } |
2532 | else |
2533 | { |
2534 | s.PutCString(cstr); |
2535 | } |
2536 | } |
2537 | } |
2538 | else if (sc->symbol) |
2539 | { |
2540 | cstr = sc->symbol->GetName().AsCString (NULL__null); |
2541 | if (cstr) |
2542 | { |
2543 | s.PutCString(cstr); |
2544 | var_success = true; |
2545 | } |
2546 | } |
2547 | } |
2548 | else if (IsToken (var_name_begin, "addr-offset}") |
2549 | || IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) |
2550 | { |
2551 | if (IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) |
2552 | { |
2553 | addr_offset_print_with_no_padding = true; |
2554 | addr_offset_concrete_func_only = true; |
2555 | } |
2556 | var_success = addr != NULL__null; |
2557 | if (var_success) |
2558 | { |
2559 | format_addr = *addr; |
2560 | calculate_format_addr_function_offset = true; |
2561 | } |
2562 | } |
2563 | else if (IsToken (var_name_begin, "line-offset}")) |
2564 | { |
2565 | var_success = sc->line_entry.range.GetBaseAddress().IsValid(); |
2566 | if (var_success) |
2567 | { |
2568 | format_addr = sc->line_entry.range.GetBaseAddress(); |
2569 | calculate_format_addr_function_offset = true; |
2570 | } |
2571 | } |
2572 | else if (IsToken (var_name_begin, "pc-offset}")) |
2573 | { |
2574 | StackFrame *frame = exe_ctx->GetFramePtr(); |
2575 | var_success = frame != NULL__null; |
2576 | if (var_success) |
2577 | { |
2578 | format_addr = frame->GetFrameCodeAddress(); |
2579 | calculate_format_addr_function_offset = true; |
2580 | } |
2581 | } |
2582 | } |
2583 | } |
2584 | break; |
2585 | |
2586 | case 'l': |
2587 | if (IsToken (var_name_begin, "line.")) |
2588 | { |
2589 | if (sc && sc->line_entry.IsValid()) |
2590 | { |
2591 | var_name_begin += ::strlen ("line."); |
2592 | if (IsToken (var_name_begin, "file.")) |
2593 | { |
2594 | var_name_begin += ::strlen ("file."); |
2595 | |
2596 | if (IsToken (var_name_begin, "basename}")) |
2597 | { |
2598 | format_file_spec.GetFilename() = sc->line_entry.file.GetFilename(); |
2599 | var_success = (bool)format_file_spec; |
2600 | } |
2601 | else if (IsToken (var_name_begin, "fullpath}")) |
2602 | { |
2603 | format_file_spec = sc->line_entry.file; |
2604 | var_success = (bool)format_file_spec; |
2605 | } |
2606 | } |
2607 | else if (IsTokenWithFormat (var_name_begin, "number", token_format, "%" PRIu64"l" "u", exe_ctx, sc)) |
2608 | { |
2609 | var_success = true; |
2610 | s.Printf(token_format.c_str(), (uint64_t)sc->line_entry.line); |
2611 | } |
2612 | else if ((IsToken (var_name_begin, "start-addr}")) || |
2613 | (IsToken (var_name_begin, "end-addr}"))) |
2614 | { |
2615 | var_success = sc && sc->line_entry.range.GetBaseAddress().IsValid(); |
2616 | if (var_success) |
2617 | { |
2618 | format_addr = sc->line_entry.range.GetBaseAddress(); |
2619 | if (var_name_begin[0] == 'e') |
2620 | format_addr.Slide (sc->line_entry.range.GetByteSize()); |
2621 | } |
2622 | } |
2623 | } |
2624 | } |
2625 | break; |
2626 | case 'c': |
2627 | if (IsToken (var_name_begin, "current-pc-arrow")) |
2628 | { |
2629 | if (addr && exe_ctx && exe_ctx->GetFramePtr()) |
2630 | { |
2631 | RegisterContextSP reg_ctx = exe_ctx->GetFramePtr()->GetRegisterContextSP(); |
2632 | if (reg_ctx.get()) |
2633 | { |
2634 | addr_t pc_loadaddr = reg_ctx->GetPC(); |
2635 | if (pc_loadaddr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) |
2636 | { |
2637 | Address pc; |
2638 | pc.SetLoadAddress (pc_loadaddr, exe_ctx->GetTargetPtr()); |
2639 | if (pc == *addr) |
2640 | { |
2641 | s.Printf ("-> "); |
2642 | var_success = true; |
2643 | } |
2644 | } |
2645 | } |
2646 | if (var_success == false) |
2647 | { |
2648 | s.Printf(" "); |
2649 | var_success = true; |
Value stored to 'var_success' is never read | |
2650 | } |
2651 | } |
2652 | var_success = true; |
2653 | } |
2654 | break; |
2655 | } |
2656 | |
2657 | if (var_success) |
2658 | { |
2659 | // If format addr is valid, then we need to print an address |
2660 | if (reg_num != LLDB_INVALID_REGNUM(4294967295U)) |
2661 | { |
2662 | StackFrame *frame = exe_ctx->GetFramePtr(); |
2663 | // We have a register value to display... |
2664 | if (reg_num == LLDB_REGNUM_GENERIC_PC0 && reg_kind == eRegisterKindGeneric) |
2665 | { |
2666 | format_addr = frame->GetFrameCodeAddress(); |
2667 | } |
2668 | else |
2669 | { |
2670 | if (reg_ctx == NULL__null) |
2671 | reg_ctx = frame->GetRegisterContext().get(); |
2672 | |
2673 | if (reg_ctx) |
2674 | { |
2675 | if (reg_kind != kNumRegisterKinds) |
2676 | reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); |
2677 | reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num); |
2678 | var_success = reg_info != NULL__null; |
2679 | } |
2680 | } |
2681 | } |
2682 | |
2683 | if (reg_info != NULL__null) |
2684 | { |
2685 | RegisterValue reg_value; |
2686 | var_success = reg_ctx->ReadRegister (reg_info, reg_value); |
2687 | if (var_success) |
2688 | { |
2689 | reg_value.Dump(&s, reg_info, false, false, eFormatDefault); |
2690 | } |
2691 | } |
2692 | |
2693 | if (format_file_spec) |
2694 | { |
2695 | s << format_file_spec; |
2696 | } |
2697 | |
2698 | // If format addr is valid, then we need to print an address |
2699 | if (format_addr.IsValid()) |
2700 | { |
2701 | var_success = false; |
2702 | |
2703 | if (calculate_format_addr_function_offset) |
2704 | { |
2705 | Address func_addr; |
2706 | |
2707 | if (sc) |
2708 | { |
2709 | if (sc->function) |
2710 | { |
2711 | func_addr = sc->function->GetAddressRange().GetBaseAddress(); |
2712 | if (sc->block && addr_offset_concrete_func_only == false) |
2713 | { |
2714 | // Check to make sure we aren't in an inline |
2715 | // function. If we are, use the inline block |
2716 | // range that contains "format_addr" since |
2717 | // blocks can be discontiguous. |
2718 | Block *inline_block = sc->block->GetContainingInlinedBlock (); |
2719 | AddressRange inline_range; |
2720 | if (inline_block && inline_block->GetRangeContainingAddress (format_addr, inline_range)) |
2721 | func_addr = inline_range.GetBaseAddress(); |
2722 | } |
2723 | } |
2724 | else if (sc->symbol && sc->symbol->ValueIsAddress()) |
2725 | func_addr = sc->symbol->GetAddress(); |
2726 | } |
2727 | |
2728 | if (func_addr.IsValid()) |
2729 | { |
2730 | const char *addr_offset_padding = " "; |
2731 | if (addr_offset_print_with_no_padding) |
2732 | { |
2733 | addr_offset_padding = ""; |
2734 | } |
2735 | if (func_addr.GetSection() == format_addr.GetSection()) |
2736 | { |
2737 | addr_t func_file_addr = func_addr.GetFileAddress(); |
2738 | addr_t addr_file_addr = format_addr.GetFileAddress(); |
2739 | if (addr_file_addr > func_file_addr) |
2740 | s.Printf("%s+%s%" PRIu64"l" "u", addr_offset_padding, addr_offset_padding, addr_file_addr - func_file_addr); |
2741 | else if (addr_file_addr < func_file_addr) |
2742 | s.Printf("%s-%s%" PRIu64"l" "u", addr_offset_padding, addr_offset_padding, func_file_addr - addr_file_addr); |
2743 | var_success = true; |
2744 | } |
2745 | else |
2746 | { |
2747 | Target *target = Target::GetTargetFromContexts (exe_ctx, sc); |
2748 | if (target) |
2749 | { |
2750 | addr_t func_load_addr = func_addr.GetLoadAddress (target); |
2751 | addr_t addr_load_addr = format_addr.GetLoadAddress (target); |
2752 | if (addr_load_addr > func_load_addr) |
2753 | s.Printf("%s+%s%" PRIu64"l" "u", addr_offset_padding, addr_offset_padding, addr_load_addr - func_load_addr); |
2754 | else if (addr_load_addr < func_load_addr) |
2755 | s.Printf("%s-%s%" PRIu64"l" "u", addr_offset_padding, addr_offset_padding, func_load_addr - addr_load_addr); |
2756 | var_success = true; |
2757 | } |
2758 | } |
2759 | } |
2760 | } |
2761 | else |
2762 | { |
2763 | Target *target = Target::GetTargetFromContexts (exe_ctx, sc); |
2764 | addr_t vaddr = LLDB_INVALID_ADDRESS(18446744073709551615UL); |
2765 | if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) |
2766 | vaddr = format_addr.GetLoadAddress (target); |
2767 | if (vaddr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) |
2768 | vaddr = format_addr.GetFileAddress (); |
2769 | |
2770 | if (vaddr != LLDB_INVALID_ADDRESS(18446744073709551615UL)) |
2771 | { |
2772 | int addr_width = 0; |
2773 | if (exe_ctx && target) |
2774 | { |
2775 | addr_width = target->GetArchitecture().GetAddressByteSize() * 2; |
2776 | } |
2777 | if (addr_width == 0) |
2778 | addr_width = 16; |
2779 | if (print_file_addr_or_load_addr) |
2780 | { |
2781 | format_addr.Dump (&s, exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL__null, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, 0); |
2782 | } |
2783 | else |
2784 | { |
2785 | s.Printf("0x%*.*" PRIx64"l" "x", addr_width, addr_width, vaddr); |
2786 | } |
2787 | var_success = true; |
2788 | } |
2789 | } |
2790 | } |
2791 | } |
2792 | |
2793 | if (var_success == false) |
2794 | success = false; |
2795 | } |
2796 | p = var_name_end; |
2797 | } |
2798 | else |
2799 | break; |
2800 | } |
2801 | else |
2802 | { |
2803 | // We got a dollar sign with no '{' after it, it must just be a dollar sign |
2804 | s.PutChar(*p); |
2805 | } |
2806 | } |
2807 | else if (*p == '\\') |
2808 | { |
2809 | ++p; // skip the slash |
2810 | switch (*p) |
2811 | { |
2812 | case 'a': s.PutChar ('\a'); break; |
2813 | case 'b': s.PutChar ('\b'); break; |
2814 | case 'f': s.PutChar ('\f'); break; |
2815 | case 'n': s.PutChar ('\n'); break; |
2816 | case 'r': s.PutChar ('\r'); break; |
2817 | case 't': s.PutChar ('\t'); break; |
2818 | case 'v': s.PutChar ('\v'); break; |
2819 | case '\'': s.PutChar ('\''); break; |
2820 | case '\\': s.PutChar ('\\'); break; |
2821 | case '0': |
2822 | // 1 to 3 octal chars |
2823 | { |
2824 | // Make a string that can hold onto the initial zero char, |
2825 | // up to 3 octal digits, and a terminating NULL. |
2826 | char oct_str[5] = { 0, 0, 0, 0, 0 }; |
2827 | |
2828 | int i; |
2829 | for (i=0; (p[i] >= '0' && p[i] <= '7') && i<4; ++i) |
2830 | oct_str[i] = p[i]; |
2831 | |
2832 | // We don't want to consume the last octal character since |
2833 | // the main for loop will do this for us, so we advance p by |
2834 | // one less than i (even if i is zero) |
2835 | p += i - 1; |
2836 | unsigned long octal_value = ::strtoul (oct_str, NULL__null, 8); |
2837 | if (octal_value <= UINT8_MAX(255)) |
2838 | { |
2839 | s.PutChar((char)octal_value); |
2840 | } |
2841 | } |
2842 | break; |
2843 | |
2844 | case 'x': |
2845 | // hex number in the format |
2846 | if (isxdigit(p[1])) |
2847 | { |
2848 | ++p; // Skip the 'x' |
2849 | |
2850 | // Make a string that can hold onto two hex chars plus a |
2851 | // NULL terminator |
2852 | char hex_str[3] = { 0,0,0 }; |
2853 | hex_str[0] = *p; |
2854 | if (isxdigit(p[1])) |
2855 | { |
2856 | ++p; // Skip the first of the two hex chars |
2857 | hex_str[1] = *p; |
2858 | } |
2859 | |
2860 | unsigned long hex_value = strtoul (hex_str, NULL__null, 16); |
2861 | if (hex_value <= UINT8_MAX(255)) |
2862 | s.PutChar ((char)hex_value); |
2863 | } |
2864 | else |
2865 | { |
2866 | s.PutChar('x'); |
2867 | } |
2868 | break; |
2869 | |
2870 | default: |
2871 | // Just desensitize any other character by just printing what |
2872 | // came after the '\' |
2873 | s << *p; |
2874 | break; |
2875 | |
2876 | } |
2877 | |
2878 | } |
2879 | } |
2880 | if (end) |
2881 | *end = p; |
2882 | return success; |
2883 | } |
2884 | |
2885 | bool |
2886 | Debugger::FormatPrompt |
2887 | ( |
2888 | const char *format, |
2889 | const SymbolContext *sc, |
2890 | const ExecutionContext *exe_ctx, |
2891 | const Address *addr, |
2892 | Stream &s, |
2893 | ValueObject* valobj |
2894 | ) |
2895 | { |
2896 | bool use_color = exe_ctx ? exe_ctx->GetTargetRef().GetDebugger().GetUseColor() : true; |
2897 | std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); |
2898 | if (format_str.length()) |
2899 | format = format_str.c_str(); |
2900 | return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL__null, valobj, false, false); |
2901 | } |
2902 | |
2903 | bool |
2904 | Debugger::FormatDisassemblerAddress (const char *format, |
2905 | const SymbolContext *sc, |
2906 | const SymbolContext *prev_sc, |
2907 | const ExecutionContext *exe_ctx, |
2908 | const Address *addr, |
2909 | Stream &s) |
2910 | { |
2911 | if (format == NULL__null && exe_ctx != NULL__null && exe_ctx->HasTargetScope()) |
2912 | { |
2913 | format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); |
2914 | } |
2915 | bool function_changed = false; |
2916 | bool initial_function = false; |
2917 | if (prev_sc && (prev_sc->function || prev_sc->symbol)) |
2918 | { |
2919 | if (sc && (sc->function || sc->symbol)) |
2920 | { |
2921 | if (prev_sc->symbol && sc->symbol) |
2922 | { |
2923 | if (!sc->symbol->Compare (prev_sc->symbol->GetName(), prev_sc->symbol->GetType())) |
2924 | { |
2925 | function_changed = true; |
2926 | } |
2927 | } |
2928 | else if (prev_sc->function && sc->function) |
2929 | { |
2930 | if (prev_sc->function->GetMangled() != sc->function->GetMangled()) |
2931 | { |
2932 | function_changed = true; |
2933 | } |
2934 | } |
2935 | } |
2936 | } |
2937 | // The first context on a list of instructions will have a prev_sc that |
2938 | // has no Function or Symbol -- if SymbolContext had an IsValid() method, it |
2939 | // would return false. But we do get a prev_sc pointer. |
2940 | if ((sc && (sc->function || sc->symbol)) |
2941 | && prev_sc && (prev_sc->function == NULL__null && prev_sc->symbol == NULL__null)) |
2942 | { |
2943 | initial_function = true; |
2944 | } |
2945 | return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL__null, NULL__null, function_changed, initial_function); |
2946 | } |
2947 | |
2948 | |
2949 | void |
2950 | Debugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) |
2951 | { |
2952 | // For simplicity's sake, I am not going to deal with how to close down any |
2953 | // open logging streams, I just redirect everything from here on out to the |
2954 | // callback. |
2955 | m_log_callback_stream_sp.reset (new StreamCallback (log_callback, baton)); |
2956 | } |
2957 | |
2958 | bool |
2959 | Debugger::EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream) |
2960 | { |
2961 | Log::Callbacks log_callbacks; |
2962 | |
2963 | StreamSP log_stream_sp; |
2964 | if (m_log_callback_stream_sp) |
2965 | { |
2966 | log_stream_sp = m_log_callback_stream_sp; |
2967 | // For now when using the callback mode you always get thread & timestamp. |
2968 | log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP(1u << 4) | LLDB_LOG_OPTION_PREPEND_THREAD_NAME(1U << 6); |
2969 | } |
2970 | else if (log_file == NULL__null || *log_file == '\0') |
2971 | { |
2972 | log_stream_sp = GetOutputFile(); |
2973 | } |
2974 | else |
2975 | { |
2976 | LogStreamMap::iterator pos = m_log_streams.find(log_file); |
2977 | if (pos != m_log_streams.end()) |
2978 | log_stream_sp = pos->second.lock(); |
2979 | if (!log_stream_sp) |
2980 | { |
2981 | log_stream_sp.reset (new StreamFile (log_file)); |
2982 | m_log_streams[log_file] = log_stream_sp; |
2983 | } |
2984 | } |
2985 | assert (log_stream_sp.get())((log_stream_sp.get()) ? static_cast<void> (0) : __assert_fail ("log_stream_sp.get()", "/tmp/buildd/llvm-toolchain-snapshot-3.7~svn227765/tools/lldb/source/Core/Debugger.cpp" , 2985, __PRETTY_FUNCTION__)); |
2986 | |
2987 | if (log_options == 0) |
2988 | log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME(1U << 6) | LLDB_LOG_OPTION_THREADSAFE(1u << 0); |
2989 | |
2990 | if (Log::GetLogChannelCallbacks (ConstString(channel), log_callbacks)) |
2991 | { |
2992 | log_callbacks.enable (log_stream_sp, log_options, categories, &error_stream); |
2993 | return true; |
2994 | } |
2995 | else |
2996 | { |
2997 | LogChannelSP log_channel_sp (LogChannel::FindPlugin (channel)); |
2998 | if (log_channel_sp) |
2999 | { |
3000 | if (log_channel_sp->Enable (log_stream_sp, log_options, &error_stream, categories)) |
3001 | { |
3002 | return true; |
3003 | } |
3004 | else |
3005 | { |
3006 | error_stream.Printf ("Invalid log channel '%s'.\n", channel); |
3007 | return false; |
3008 | } |
3009 | } |
3010 | else |
3011 | { |
3012 | error_stream.Printf ("Invalid log channel '%s'.\n", channel); |
3013 | return false; |
3014 | } |
3015 | } |
3016 | return false; |
3017 | } |
3018 | |
3019 | SourceManager & |
3020 | Debugger::GetSourceManager () |
3021 | { |
3022 | if (m_source_manager_ap.get() == NULL__null) |
3023 | m_source_manager_ap.reset (new SourceManager (shared_from_this())); |
3024 | return *m_source_manager_ap; |
3025 | } |
3026 | |
3027 | |
3028 | |
3029 | // This function handles events that were broadcast by the process. |
3030 | void |
3031 | Debugger::HandleBreakpointEvent (const EventSP &event_sp) |
3032 | { |
3033 | using namespace lldb; |
3034 | const uint32_t event_type = Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (event_sp); |
3035 | |
3036 | // if (event_type & eBreakpointEventTypeAdded |
3037 | // || event_type & eBreakpointEventTypeRemoved |
3038 | // || event_type & eBreakpointEventTypeEnabled |
3039 | // || event_type & eBreakpointEventTypeDisabled |
3040 | // || event_type & eBreakpointEventTypeCommandChanged |
3041 | // || event_type & eBreakpointEventTypeConditionChanged |
3042 | // || event_type & eBreakpointEventTypeIgnoreChanged |
3043 | // || event_type & eBreakpointEventTypeLocationsResolved) |
3044 | // { |
3045 | // // Don't do anything about these events, since the breakpoint commands already echo these actions. |
3046 | // } |
3047 | // |
3048 | if (event_type & eBreakpointEventTypeLocationsAdded) |
3049 | { |
3050 | uint32_t num_new_locations = Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(event_sp); |
3051 | if (num_new_locations > 0) |
3052 | { |
3053 | BreakpointSP breakpoint = Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp); |
3054 | StreamFileSP output_sp (GetOutputFile()); |
3055 | if (output_sp) |
3056 | { |
3057 | output_sp->Printf("%d location%s added to breakpoint %d\n", |
3058 | num_new_locations, |
3059 | num_new_locations == 1 ? "" : "s", |
3060 | breakpoint->GetID()); |
3061 | RefreshTopIOHandler(); |
3062 | } |
3063 | } |
3064 | } |
3065 | // else if (event_type & eBreakpointEventTypeLocationsRemoved) |
3066 | // { |
3067 | // // These locations just get disabled, not sure it is worth spamming folks about this on the command line. |
3068 | // } |
3069 | // else if (event_type & eBreakpointEventTypeLocationsResolved) |
3070 | // { |
3071 | // // This might be an interesting thing to note, but I'm going to leave it quiet for now, it just looked noisy. |
3072 | // } |
3073 | } |
3074 | |
3075 | size_t |
3076 | Debugger::GetProcessSTDOUT (Process *process, Stream *stream) |
3077 | { |
3078 | size_t total_bytes = 0; |
3079 | if (stream == NULL__null) |
3080 | stream = GetOutputFile().get(); |
3081 | |
3082 | if (stream) |
3083 | { |
3084 | // The process has stuff waiting for stdout; get it and write it out to the appropriate place. |
3085 | if (process == NULL__null) |
3086 | { |
3087 | TargetSP target_sp = GetTargetList().GetSelectedTarget(); |
3088 | if (target_sp) |
3089 | process = target_sp->GetProcessSP().get(); |
3090 | } |
3091 | if (process) |
3092 | { |
3093 | Error error; |
3094 | size_t len; |
3095 | char stdio_buffer[1024]; |
3096 | while ((len = process->GetSTDOUT (stdio_buffer, sizeof (stdio_buffer), error)) > 0) |
3097 | { |
3098 | stream->Write(stdio_buffer, len); |
3099 | total_bytes += len; |
3100 | } |
3101 | } |
3102 | stream->Flush(); |
3103 | } |
3104 | return total_bytes; |
3105 | } |
3106 | |
3107 | size_t |
3108 | Debugger::GetProcessSTDERR (Process *process, Stream *stream) |
3109 | { |
3110 | size_t total_bytes = 0; |
3111 | if (stream == NULL__null) |
3112 | stream = GetOutputFile().get(); |
3113 | |
3114 | if (stream) |
3115 | { |
3116 | // The process has stuff waiting for stderr; get it and write it out to the appropriate place. |
3117 | if (process == NULL__null) |
3118 | { |
3119 | TargetSP target_sp = GetTargetList().GetSelectedTarget(); |
3120 | if (target_sp) |
3121 | process = target_sp->GetProcessSP().get(); |
3122 | } |
3123 | if (process) |
3124 | { |
3125 | Error error; |
3126 | size_t len; |
3127 | char stdio_buffer[1024]; |
3128 | while ((len = process->GetSTDERR (stdio_buffer, sizeof (stdio_buffer), error)) > 0) |
3129 | { |
3130 | stream->Write(stdio_buffer, len); |
3131 | total_bytes += len; |
3132 | } |
3133 | } |
3134 | stream->Flush(); |
3135 | } |
3136 | return total_bytes; |
3137 | } |
3138 | |
3139 | |
3140 | // This function handles events that were broadcast by the process. |
3141 | void |
3142 | Debugger::HandleProcessEvent (const EventSP &event_sp) |
3143 | { |
3144 | using namespace lldb; |
3145 | const uint32_t event_type = event_sp->GetType(); |
3146 | ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); |
3147 | |
3148 | StreamString output_stream; |
3149 | StreamString error_stream; |
3150 | const bool gui_enabled = IsForwardingEvents(); |
3151 | |
3152 | if (!gui_enabled) |
3153 | { |
3154 | bool pop_process_io_handler = false; |
3155 | assert (process_sp)((process_sp) ? static_cast<void> (0) : __assert_fail ( "process_sp", "/tmp/buildd/llvm-toolchain-snapshot-3.7~svn227765/tools/lldb/source/Core/Debugger.cpp" , 3155, __PRETTY_FUNCTION__)); |
3156 | |
3157 | if (event_type & Process::eBroadcastBitSTDOUT || event_type & Process::eBroadcastBitStateChanged) |
3158 | { |
3159 | GetProcessSTDOUT (process_sp.get(), &output_stream); |
3160 | } |
3161 | |
3162 | if (event_type & Process::eBroadcastBitSTDERR || event_type & Process::eBroadcastBitStateChanged) |
3163 | { |
3164 | GetProcessSTDERR (process_sp.get(), &error_stream); |
3165 | } |
3166 | |
3167 | if (event_type & Process::eBroadcastBitStateChanged) |
3168 | { |
3169 | Process::HandleProcessStateChangedEvent (event_sp, &output_stream, pop_process_io_handler); |
3170 | } |
3171 | |
3172 | if (output_stream.GetSize() || error_stream.GetSize()) |
3173 | { |
3174 | StreamFileSP error_stream_sp (GetOutputFile()); |
3175 | bool top_io_handler_hid = false; |
3176 | |
3177 | if (process_sp->ProcessIOHandlerIsActive() == false) |
3178 | top_io_handler_hid = HideTopIOHandler(); |
3179 | |
3180 | if (output_stream.GetSize()) |
3181 | { |
3182 | StreamFileSP output_stream_sp (GetOutputFile()); |
3183 | if (output_stream_sp) |
3184 | output_stream_sp->Write (output_stream.GetData(), output_stream.GetSize()); |
3185 | } |
3186 | |
3187 | if (error_stream.GetSize()) |
3188 | { |
3189 | StreamFileSP error_stream_sp (GetErrorFile()); |
3190 | if (error_stream_sp) |
3191 | error_stream_sp->Write (error_stream.GetData(), error_stream.GetSize()); |
3192 | } |
3193 | |
3194 | if (top_io_handler_hid) |
3195 | RefreshTopIOHandler(); |
3196 | } |
3197 | |
3198 | if (pop_process_io_handler) |
3199 | process_sp->PopProcessIOHandler(); |
3200 | } |
3201 | } |
3202 | |
3203 | void |
3204 | Debugger::HandleThreadEvent (const EventSP &event_sp) |
3205 | { |
3206 | // At present the only thread event we handle is the Frame Changed event, |
3207 | // and all we do for that is just reprint the thread status for that thread. |
3208 | using namespace lldb; |
3209 | const uint32_t event_type = event_sp->GetType(); |
3210 | if (event_type == Thread::eBroadcastBitStackChanged || |
3211 | event_type == Thread::eBroadcastBitThreadSelected ) |
3212 | { |
3213 | ThreadSP thread_sp (Thread::ThreadEventData::GetThreadFromEvent (event_sp.get())); |
3214 | if (thread_sp) |
3215 | { |
3216 | HideTopIOHandler(); |
3217 | StreamFileSP stream_sp (GetOutputFile()); |
3218 | thread_sp->GetStatus(*stream_sp, 0, 1, 1); |
3219 | RefreshTopIOHandler(); |
3220 | } |
3221 | } |
3222 | } |
3223 | |
3224 | bool |
3225 | Debugger::IsForwardingEvents () |
3226 | { |
3227 | return (bool)m_forward_listener_sp; |
3228 | } |
3229 | |
3230 | void |
3231 | Debugger::EnableForwardEvents (const ListenerSP &listener_sp) |
3232 | { |
3233 | m_forward_listener_sp = listener_sp; |
3234 | } |
3235 | |
3236 | void |
3237 | Debugger::CancelForwardEvents (const ListenerSP &listener_sp) |
3238 | { |
3239 | m_forward_listener_sp.reset(); |
3240 | } |
3241 | |
3242 | |
3243 | void |
3244 | Debugger::DefaultEventHandler() |
3245 | { |
3246 | Listener& listener(GetListener()); |
3247 | ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); |
3248 | ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); |
3249 | ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); |
3250 | BroadcastEventSpec target_event_spec (broadcaster_class_target, |
3251 | Target::eBroadcastBitBreakpointChanged); |
3252 | |
3253 | BroadcastEventSpec process_event_spec (broadcaster_class_process, |
3254 | Process::eBroadcastBitStateChanged | |
3255 | Process::eBroadcastBitSTDOUT | |
3256 | Process::eBroadcastBitSTDERR); |
3257 | |
3258 | BroadcastEventSpec thread_event_spec (broadcaster_class_thread, |
3259 | Thread::eBroadcastBitStackChanged | |
3260 | Thread::eBroadcastBitThreadSelected ); |
3261 | |
3262 | listener.StartListeningForEventSpec (*this, target_event_spec); |
3263 | listener.StartListeningForEventSpec (*this, process_event_spec); |
3264 | listener.StartListeningForEventSpec (*this, thread_event_spec); |
3265 | listener.StartListeningForEvents (m_command_interpreter_ap.get(), |
3266 | CommandInterpreter::eBroadcastBitQuitCommandReceived | |
3267 | CommandInterpreter::eBroadcastBitAsynchronousOutputData | |
3268 | CommandInterpreter::eBroadcastBitAsynchronousErrorData ); |
3269 | |
3270 | // Let the thread that spawned us know that we have started up and |
3271 | // that we are now listening to all required events so no events get missed |
3272 | m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening); |
3273 | |
3274 | bool done = false; |
3275 | while (!done) |
3276 | { |
3277 | EventSP event_sp; |
3278 | if (listener.WaitForEvent(NULL__null, event_sp)) |
3279 | { |
3280 | if (event_sp) |
3281 | { |
3282 | Broadcaster *broadcaster = event_sp->GetBroadcaster(); |
3283 | if (broadcaster) |
3284 | { |
3285 | uint32_t event_type = event_sp->GetType(); |
3286 | ConstString broadcaster_class (broadcaster->GetBroadcasterClass()); |
3287 | if (broadcaster_class == broadcaster_class_process) |
3288 | { |
3289 | HandleProcessEvent (event_sp); |
3290 | } |
3291 | else if (broadcaster_class == broadcaster_class_target) |
3292 | { |
3293 | if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(event_sp.get())) |
3294 | { |
3295 | HandleBreakpointEvent (event_sp); |
3296 | } |
3297 | } |
3298 | else if (broadcaster_class == broadcaster_class_thread) |
3299 | { |
3300 | HandleThreadEvent (event_sp); |
3301 | } |
3302 | else if (broadcaster == m_command_interpreter_ap.get()) |
3303 | { |
3304 | if (event_type & CommandInterpreter::eBroadcastBitQuitCommandReceived) |
3305 | { |
3306 | done = true; |
3307 | } |
3308 | else if (event_type & CommandInterpreter::eBroadcastBitAsynchronousErrorData) |
3309 | { |
3310 | const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get())); |
3311 | if (data && data[0]) |
3312 | { |
3313 | StreamFileSP error_sp (GetErrorFile()); |
3314 | if (error_sp) |
3315 | { |
3316 | HideTopIOHandler(); |
3317 | error_sp->PutCString(data); |
3318 | error_sp->Flush(); |
3319 | RefreshTopIOHandler(); |
3320 | } |
3321 | } |
3322 | } |
3323 | else if (event_type & CommandInterpreter::eBroadcastBitAsynchronousOutputData) |
3324 | { |
3325 | const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get())); |
3326 | if (data && data[0]) |
3327 | { |
3328 | StreamFileSP output_sp (GetOutputFile()); |
3329 | if (output_sp) |
3330 | { |
3331 | HideTopIOHandler(); |
3332 | output_sp->PutCString(data); |
3333 | output_sp->Flush(); |
3334 | RefreshTopIOHandler(); |
3335 | } |
3336 | } |
3337 | } |
3338 | } |
3339 | } |
3340 | |
3341 | if (m_forward_listener_sp) |
3342 | m_forward_listener_sp->AddEvent(event_sp); |
3343 | } |
3344 | } |
3345 | } |
3346 | } |
3347 | |
3348 | lldb::thread_result_t |
3349 | Debugger::EventHandlerThread (lldb::thread_arg_t arg) |
3350 | { |
3351 | ((Debugger *)arg)->DefaultEventHandler(); |
3352 | return NULL__null; |
3353 | } |
3354 | |
3355 | bool |
3356 | Debugger::StartEventHandlerThread() |
3357 | { |
3358 | if (!m_event_handler_thread.IsJoinable()) |
3359 | { |
3360 | // We must synchronize with the DefaultEventHandler() thread to ensure |
3361 | // it is up and running and listening to events before we return from |
3362 | // this function. We do this by listening to events for the |
3363 | // eBroadcastBitEventThreadIsListening from the m_sync_broadcaster |
3364 | Listener listener("lldb.debugger.event-handler"); |
3365 | listener.StartListeningForEvents(&m_sync_broadcaster, eBroadcastBitEventThreadIsListening); |
3366 | |
3367 | // Use larger 8MB stack for this thread |
3368 | m_event_handler_thread = ThreadLauncher::LaunchThread("lldb.debugger.event-handler", EventHandlerThread, |
3369 | this, |
3370 | NULL__null, |
3371 | g_debugger_event_thread_stack_bytes); |
3372 | |
3373 | // Make sure DefaultEventHandler() is running and listening to events before we return |
3374 | // from this function. We are only listening for events of type |
3375 | // eBroadcastBitEventThreadIsListening so we don't need to check the event, we just need |
3376 | // to wait an infinite amount of time for it (NULL timeout as the first parameter) |
3377 | lldb::EventSP event_sp; |
3378 | listener.WaitForEvent(NULL__null, event_sp); |
3379 | } |
3380 | return m_event_handler_thread.IsJoinable(); |
3381 | } |
3382 | |
3383 | void |
3384 | Debugger::StopEventHandlerThread() |
3385 | { |
3386 | if (m_event_handler_thread.IsJoinable()) |
3387 | { |
3388 | GetCommandInterpreter().BroadcastEvent(CommandInterpreter::eBroadcastBitQuitCommandReceived); |
3389 | m_event_handler_thread.Join(nullptr); |
3390 | } |
3391 | } |
3392 | |
3393 | |
3394 | lldb::thread_result_t |
3395 | Debugger::IOHandlerThread (lldb::thread_arg_t arg) |
3396 | { |
3397 | Debugger *debugger = (Debugger *)arg; |
3398 | debugger->ExecuteIOHanders(); |
3399 | debugger->StopEventHandlerThread(); |
3400 | return NULL__null; |
3401 | } |
3402 | |
3403 | bool |
3404 | Debugger::StartIOHandlerThread() |
3405 | { |
3406 | if (!m_io_handler_thread.IsJoinable()) |
3407 | m_io_handler_thread = ThreadLauncher::LaunchThread ("lldb.debugger.io-handler", |
3408 | IOHandlerThread, |
3409 | this, |
3410 | NULL__null, |
3411 | 8*1024*1024); // Use larger 8MB stack for this thread |
3412 | return m_io_handler_thread.IsJoinable(); |
3413 | } |
3414 | |
3415 | void |
3416 | Debugger::StopIOHandlerThread() |
3417 | { |
3418 | if (m_io_handler_thread.IsJoinable()) |
3419 | { |
3420 | if (m_input_file_sp) |
3421 | m_input_file_sp->GetFile().Close(); |
3422 | m_io_handler_thread.Join(nullptr); |
3423 | } |
3424 | } |
3425 | |
3426 | Target * |
3427 | Debugger::GetDummyTarget() |
3428 | { |
3429 | return m_target_list.GetDummyTarget (*this).get(); |
3430 | } |
3431 | |
3432 | Target * |
3433 | Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) |
3434 | { |
3435 | Target *target = nullptr; |
3436 | if (!prefer_dummy) |
3437 | { |
3438 | target = m_target_list.GetSelectedTarget().get(); |
3439 | if (target) |
3440 | return target; |
3441 | } |
3442 | |
3443 | return GetDummyTarget(); |
3444 | } |
3445 |