1 | //===-- CommandObjectBreakpoint.cpp ---------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "CommandObjectBreakpoint.h" |
10 | #include "CommandObjectBreakpointCommand.h" |
11 | #include "lldb/Breakpoint/Breakpoint.h" |
12 | #include "lldb/Breakpoint/BreakpointIDList.h" |
13 | #include "lldb/Breakpoint/BreakpointLocation.h" |
14 | #include "lldb/Host/OptionParser.h" |
15 | #include "lldb/Interpreter/CommandInterpreter.h" |
16 | #include "lldb/Interpreter/CommandOptionArgumentTable.h" |
17 | #include "lldb/Interpreter/CommandReturnObject.h" |
18 | #include "lldb/Interpreter/OptionArgParser.h" |
19 | #include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" |
20 | #include "lldb/Interpreter/OptionValueBoolean.h" |
21 | #include "lldb/Interpreter/OptionValueFileColonLine.h" |
22 | #include "lldb/Interpreter/OptionValueString.h" |
23 | #include "lldb/Interpreter/OptionValueUInt64.h" |
24 | #include "lldb/Interpreter/Options.h" |
25 | #include "lldb/Target/Language.h" |
26 | #include "lldb/Target/StackFrame.h" |
27 | #include "lldb/Target/Target.h" |
28 | #include "lldb/Target/ThreadSpec.h" |
29 | #include "lldb/Utility/RegularExpression.h" |
30 | #include "lldb/Utility/StreamString.h" |
31 | |
32 | #include <memory> |
33 | #include <optional> |
34 | #include <vector> |
35 | |
36 | using namespace lldb; |
37 | using namespace lldb_private; |
38 | |
39 | static void AddBreakpointDescription(Stream *s, Breakpoint *bp, |
40 | lldb::DescriptionLevel level) { |
41 | s->IndentMore(); |
42 | bp->GetDescription(s, level, show_locations: true); |
43 | s->IndentLess(); |
44 | s->EOL(); |
45 | } |
46 | |
47 | // Modifiable Breakpoint Options |
48 | #pragma mark Modify::CommandOptions |
49 | #define LLDB_OPTIONS_breakpoint_modify |
50 | #include "CommandOptions.inc" |
51 | |
52 | class lldb_private::BreakpointOptionGroup : public OptionGroup { |
53 | public: |
54 | BreakpointOptionGroup() : m_bp_opts(false) {} |
55 | |
56 | ~BreakpointOptionGroup() override = default; |
57 | |
58 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
59 | return llvm::ArrayRef(g_breakpoint_modify_options); |
60 | } |
61 | |
62 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
63 | ExecutionContext *execution_context) override { |
64 | Status error; |
65 | const int short_option = |
66 | g_breakpoint_modify_options[option_idx].short_option; |
67 | |
68 | switch (short_option) { |
69 | case 'c': |
70 | // Normally an empty breakpoint condition marks is as unset. But we need |
71 | // to say it was passed in. |
72 | m_bp_opts.SetCondition(option_arg.str().c_str()); |
73 | m_bp_opts.m_set_flags.Set(BreakpointOptions::eCondition); |
74 | break; |
75 | case 'C': |
76 | m_commands.push_back(std::string(option_arg)); |
77 | break; |
78 | case 'd': |
79 | m_bp_opts.SetEnabled(false); |
80 | break; |
81 | case 'e': |
82 | m_bp_opts.SetEnabled(true); |
83 | break; |
84 | case 'G': { |
85 | bool value, success; |
86 | value = OptionArgParser::ToBoolean(s: option_arg, fail_value: false, success_ptr: &success); |
87 | if (success) { |
88 | m_bp_opts.SetAutoContinue(value); |
89 | } else |
90 | error.SetErrorStringWithFormat( |
91 | "invalid boolean value '%s' passed for -G option" , |
92 | option_arg.str().c_str()); |
93 | } break; |
94 | case 'i': { |
95 | uint32_t ignore_count; |
96 | if (option_arg.getAsInteger(0, ignore_count)) |
97 | error.SetErrorStringWithFormat("invalid ignore count '%s'" , |
98 | option_arg.str().c_str()); |
99 | else |
100 | m_bp_opts.SetIgnoreCount(ignore_count); |
101 | } break; |
102 | case 'o': { |
103 | bool value, success; |
104 | value = OptionArgParser::ToBoolean(s: option_arg, fail_value: false, success_ptr: &success); |
105 | if (success) { |
106 | m_bp_opts.SetOneShot(value); |
107 | } else |
108 | error.SetErrorStringWithFormat( |
109 | "invalid boolean value '%s' passed for -o option" , |
110 | option_arg.str().c_str()); |
111 | } break; |
112 | case 't': { |
113 | lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID; |
114 | if (option_arg == "current" ) { |
115 | if (!execution_context) { |
116 | error.SetErrorStringWithFormat("No context to determine current " |
117 | "thread" ); |
118 | } else { |
119 | ThreadSP ctx_thread_sp = execution_context->GetThreadSP(); |
120 | if (!ctx_thread_sp || !ctx_thread_sp->IsValid()) { |
121 | error.SetErrorStringWithFormat("No currently selected thread" ); |
122 | } else { |
123 | thread_id = ctx_thread_sp->GetID(); |
124 | } |
125 | } |
126 | } else if (option_arg.getAsInteger(0, thread_id)) { |
127 | error.SetErrorStringWithFormat("invalid thread id string '%s'" , |
128 | option_arg.str().c_str()); |
129 | } |
130 | if (thread_id != LLDB_INVALID_THREAD_ID) |
131 | m_bp_opts.SetThreadID(thread_id); |
132 | } break; |
133 | case 'T': |
134 | m_bp_opts.GetThreadSpec()->SetName(option_arg.str().c_str()); |
135 | break; |
136 | case 'q': |
137 | m_bp_opts.GetThreadSpec()->SetQueueName(option_arg.str().c_str()); |
138 | break; |
139 | case 'x': { |
140 | uint32_t thread_index = UINT32_MAX; |
141 | if (option_arg.getAsInteger(0, thread_index)) { |
142 | error.SetErrorStringWithFormat("invalid thread index string '%s'" , |
143 | option_arg.str().c_str()); |
144 | } else { |
145 | m_bp_opts.GetThreadSpec()->SetIndex(thread_index); |
146 | } |
147 | } break; |
148 | default: |
149 | llvm_unreachable("Unimplemented option" ); |
150 | } |
151 | |
152 | return error; |
153 | } |
154 | |
155 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
156 | m_bp_opts.Clear(); |
157 | m_commands.clear(); |
158 | } |
159 | |
160 | Status OptionParsingFinished(ExecutionContext *execution_context) override { |
161 | if (!m_commands.empty()) { |
162 | auto cmd_data = std::make_unique<BreakpointOptions::CommandData>(); |
163 | |
164 | for (std::string &str : m_commands) |
165 | cmd_data->user_source.AppendString(str); |
166 | |
167 | cmd_data->stop_on_error = true; |
168 | m_bp_opts.SetCommandDataCallback(cmd_data); |
169 | } |
170 | return Status(); |
171 | } |
172 | |
173 | const BreakpointOptions &GetBreakpointOptions() { return m_bp_opts; } |
174 | |
175 | std::vector<std::string> m_commands; |
176 | BreakpointOptions m_bp_opts; |
177 | }; |
178 | |
179 | #define LLDB_OPTIONS_breakpoint_dummy |
180 | #include "CommandOptions.inc" |
181 | |
182 | class BreakpointDummyOptionGroup : public OptionGroup { |
183 | public: |
184 | BreakpointDummyOptionGroup() = default; |
185 | |
186 | ~BreakpointDummyOptionGroup() override = default; |
187 | |
188 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
189 | return llvm::ArrayRef(g_breakpoint_dummy_options); |
190 | } |
191 | |
192 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
193 | ExecutionContext *execution_context) override { |
194 | Status error; |
195 | const int short_option = |
196 | g_breakpoint_dummy_options[option_idx].short_option; |
197 | |
198 | switch (short_option) { |
199 | case 'D': |
200 | m_use_dummy = true; |
201 | break; |
202 | default: |
203 | llvm_unreachable("Unimplemented option" ); |
204 | } |
205 | |
206 | return error; |
207 | } |
208 | |
209 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
210 | m_use_dummy = false; |
211 | } |
212 | |
213 | bool m_use_dummy; |
214 | }; |
215 | |
216 | #define LLDB_OPTIONS_breakpoint_set |
217 | #include "CommandOptions.inc" |
218 | |
219 | // CommandObjectBreakpointSet |
220 | |
221 | class CommandObjectBreakpointSet : public CommandObjectParsed { |
222 | public: |
223 | enum BreakpointSetType { |
224 | eSetTypeInvalid, |
225 | eSetTypeFileAndLine, |
226 | eSetTypeAddress, |
227 | eSetTypeFunctionName, |
228 | eSetTypeFunctionRegexp, |
229 | eSetTypeSourceRegexp, |
230 | eSetTypeException, |
231 | eSetTypeScripted, |
232 | }; |
233 | |
234 | CommandObjectBreakpointSet(CommandInterpreter &interpreter) |
235 | : CommandObjectParsed( |
236 | interpreter, "breakpoint set" , |
237 | "Sets a breakpoint or set of breakpoints in the executable." , |
238 | "breakpoint set <cmd-options>" ), |
239 | m_python_class_options("scripted breakpoint" , true, 'P') { |
240 | // We're picking up all the normal options, commands and disable. |
241 | m_all_options.Append(group: &m_python_class_options, |
242 | LLDB_OPT_SET_1 | LLDB_OPT_SET_2, LLDB_OPT_SET_11); |
243 | m_all_options.Append(&m_bp_opts, |
244 | LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4, |
245 | LLDB_OPT_SET_ALL); |
246 | m_all_options.Append(group: &m_dummy_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); |
247 | m_all_options.Append(&m_options); |
248 | m_all_options.Finalize(); |
249 | } |
250 | |
251 | ~CommandObjectBreakpointSet() override = default; |
252 | |
253 | Options *GetOptions() override { return &m_all_options; } |
254 | |
255 | class CommandOptions : public OptionGroup { |
256 | public: |
257 | CommandOptions() = default; |
258 | |
259 | ~CommandOptions() override = default; |
260 | |
261 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
262 | ExecutionContext *execution_context) override { |
263 | Status error; |
264 | const int short_option = |
265 | g_breakpoint_set_options[option_idx].short_option; |
266 | |
267 | switch (short_option) { |
268 | case 'a': { |
269 | m_load_addr = OptionArgParser::ToAddress(exe_ctx: execution_context, s: option_arg, |
270 | LLDB_INVALID_ADDRESS, error_ptr: &error); |
271 | } break; |
272 | |
273 | case 'A': |
274 | m_all_files = true; |
275 | break; |
276 | |
277 | case 'b': |
278 | m_func_names.push_back(std::string(option_arg)); |
279 | m_func_name_type_mask |= eFunctionNameTypeBase; |
280 | break; |
281 | |
282 | case 'u': |
283 | if (option_arg.getAsInteger(0, m_column)) |
284 | error.SetErrorStringWithFormat("invalid column number: %s" , |
285 | option_arg.str().c_str()); |
286 | break; |
287 | |
288 | case 'E': { |
289 | LanguageType language = Language::GetLanguageTypeFromString(string: option_arg); |
290 | |
291 | switch (language) { |
292 | case eLanguageTypeC89: |
293 | case eLanguageTypeC: |
294 | case eLanguageTypeC99: |
295 | case eLanguageTypeC11: |
296 | m_exception_language = eLanguageTypeC; |
297 | break; |
298 | case eLanguageTypeC_plus_plus: |
299 | case eLanguageTypeC_plus_plus_03: |
300 | case eLanguageTypeC_plus_plus_11: |
301 | case eLanguageTypeC_plus_plus_14: |
302 | m_exception_language = eLanguageTypeC_plus_plus; |
303 | break; |
304 | case eLanguageTypeObjC: |
305 | m_exception_language = eLanguageTypeObjC; |
306 | break; |
307 | case eLanguageTypeObjC_plus_plus: |
308 | error.SetErrorStringWithFormat( |
309 | "Set exception breakpoints separately for c++ and objective-c" ); |
310 | break; |
311 | case eLanguageTypeUnknown: |
312 | error.SetErrorStringWithFormat( |
313 | "Unknown language type: '%s' for exception breakpoint" , |
314 | option_arg.str().c_str()); |
315 | break; |
316 | default: |
317 | error.SetErrorStringWithFormat( |
318 | "Unsupported language type: '%s' for exception breakpoint" , |
319 | option_arg.str().c_str()); |
320 | } |
321 | } break; |
322 | |
323 | case 'f': |
324 | m_filenames.AppendIfUnique(file: FileSpec(option_arg)); |
325 | break; |
326 | |
327 | case 'F': |
328 | m_func_names.push_back(std::string(option_arg)); |
329 | m_func_name_type_mask |= eFunctionNameTypeFull; |
330 | break; |
331 | |
332 | case 'h': { |
333 | bool success; |
334 | m_catch_bp = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
335 | if (!success) |
336 | error.SetErrorStringWithFormat( |
337 | "Invalid boolean value for on-catch option: '%s'" , |
338 | option_arg.str().c_str()); |
339 | } break; |
340 | |
341 | case 'H': |
342 | m_hardware = true; |
343 | break; |
344 | |
345 | case 'K': { |
346 | bool success; |
347 | bool value; |
348 | value = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
349 | if (value) |
350 | m_skip_prologue = eLazyBoolYes; |
351 | else |
352 | m_skip_prologue = eLazyBoolNo; |
353 | |
354 | if (!success) |
355 | error.SetErrorStringWithFormat( |
356 | "Invalid boolean value for skip prologue option: '%s'" , |
357 | option_arg.str().c_str()); |
358 | } break; |
359 | |
360 | case 'l': |
361 | if (option_arg.getAsInteger(0, m_line_num)) |
362 | error.SetErrorStringWithFormat("invalid line number: %s." , |
363 | option_arg.str().c_str()); |
364 | break; |
365 | |
366 | case 'L': |
367 | m_language = Language::GetLanguageTypeFromString(string: option_arg); |
368 | if (m_language == eLanguageTypeUnknown) |
369 | error.SetErrorStringWithFormat( |
370 | "Unknown language type: '%s' for breakpoint" , |
371 | option_arg.str().c_str()); |
372 | break; |
373 | |
374 | case 'm': { |
375 | bool success; |
376 | bool value; |
377 | value = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
378 | if (value) |
379 | m_move_to_nearest_code = eLazyBoolYes; |
380 | else |
381 | m_move_to_nearest_code = eLazyBoolNo; |
382 | |
383 | if (!success) |
384 | error.SetErrorStringWithFormat( |
385 | "Invalid boolean value for move-to-nearest-code option: '%s'" , |
386 | option_arg.str().c_str()); |
387 | break; |
388 | } |
389 | |
390 | case 'M': |
391 | m_func_names.push_back(std::string(option_arg)); |
392 | m_func_name_type_mask |= eFunctionNameTypeMethod; |
393 | break; |
394 | |
395 | case 'n': |
396 | m_func_names.push_back(std::string(option_arg)); |
397 | m_func_name_type_mask |= eFunctionNameTypeAuto; |
398 | break; |
399 | |
400 | case 'N': { |
401 | if (BreakpointID::StringIsBreakpointName(str: option_arg, error)) |
402 | m_breakpoint_names.push_back(std::string(option_arg)); |
403 | else |
404 | error.SetErrorStringWithFormat("Invalid breakpoint name: %s" , |
405 | option_arg.str().c_str()); |
406 | break; |
407 | } |
408 | |
409 | case 'R': { |
410 | lldb::addr_t tmp_offset_addr; |
411 | tmp_offset_addr = OptionArgParser::ToAddress(exe_ctx: execution_context, |
412 | s: option_arg, fail_value: 0, error_ptr: &error); |
413 | if (error.Success()) |
414 | m_offset_addr = tmp_offset_addr; |
415 | } break; |
416 | |
417 | case 'O': |
418 | m_exception_extra_args.AppendArgument(arg_str: "-O" ); |
419 | m_exception_extra_args.AppendArgument(arg_str: option_arg); |
420 | break; |
421 | |
422 | case 'p': |
423 | m_source_text_regexp.assign(str: std::string(option_arg)); |
424 | break; |
425 | |
426 | case 'r': |
427 | m_func_regexp.assign(str: std::string(option_arg)); |
428 | break; |
429 | |
430 | case 's': |
431 | m_modules.AppendIfUnique(file: FileSpec(option_arg)); |
432 | break; |
433 | |
434 | case 'S': |
435 | m_func_names.push_back(std::string(option_arg)); |
436 | m_func_name_type_mask |= eFunctionNameTypeSelector; |
437 | break; |
438 | |
439 | case 'w': { |
440 | bool success; |
441 | m_throw_bp = OptionArgParser::ToBoolean(s: option_arg, fail_value: true, success_ptr: &success); |
442 | if (!success) |
443 | error.SetErrorStringWithFormat( |
444 | "Invalid boolean value for on-throw option: '%s'" , |
445 | option_arg.str().c_str()); |
446 | } break; |
447 | |
448 | case 'X': |
449 | m_source_regex_func_names.insert(std::string(option_arg)); |
450 | break; |
451 | |
452 | case 'y': |
453 | { |
454 | OptionValueFileColonLine value; |
455 | Status fcl_err = value.SetValueFromString(value: option_arg); |
456 | if (!fcl_err.Success()) { |
457 | error.SetErrorStringWithFormat( |
458 | "Invalid value for file:line specifier: %s" , |
459 | fcl_err.AsCString()); |
460 | } else { |
461 | m_filenames.AppendIfUnique(file: value.GetFileSpec()); |
462 | m_line_num = value.GetLineNumber(); |
463 | m_column = value.GetColumnNumber(); |
464 | } |
465 | } break; |
466 | |
467 | default: |
468 | llvm_unreachable("Unimplemented option" ); |
469 | } |
470 | |
471 | return error; |
472 | } |
473 | |
474 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
475 | m_filenames.Clear(); |
476 | m_line_num = 0; |
477 | m_column = 0; |
478 | m_func_names.clear(); |
479 | m_func_name_type_mask = eFunctionNameTypeNone; |
480 | m_func_regexp.clear(); |
481 | m_source_text_regexp.clear(); |
482 | m_modules.Clear(); |
483 | m_load_addr = LLDB_INVALID_ADDRESS; |
484 | m_offset_addr = 0; |
485 | m_catch_bp = false; |
486 | m_throw_bp = true; |
487 | m_hardware = false; |
488 | m_exception_language = eLanguageTypeUnknown; |
489 | m_language = lldb::eLanguageTypeUnknown; |
490 | m_skip_prologue = eLazyBoolCalculate; |
491 | m_breakpoint_names.clear(); |
492 | m_all_files = false; |
493 | m_exception_extra_args.Clear(); |
494 | m_move_to_nearest_code = eLazyBoolCalculate; |
495 | m_source_regex_func_names.clear(); |
496 | m_current_key.clear(); |
497 | } |
498 | |
499 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
500 | return llvm::ArrayRef(g_breakpoint_set_options); |
501 | } |
502 | |
503 | // Instance variables to hold the values for command options. |
504 | |
505 | std::string m_condition; |
506 | FileSpecList m_filenames; |
507 | uint32_t m_line_num = 0; |
508 | uint32_t m_column = 0; |
509 | std::vector<std::string> m_func_names; |
510 | std::vector<std::string> m_breakpoint_names; |
511 | lldb::FunctionNameType m_func_name_type_mask = eFunctionNameTypeNone; |
512 | std::string m_func_regexp; |
513 | std::string m_source_text_regexp; |
514 | FileSpecList m_modules; |
515 | lldb::addr_t m_load_addr = 0; |
516 | lldb::addr_t m_offset_addr; |
517 | bool m_catch_bp = false; |
518 | bool m_throw_bp = true; |
519 | bool m_hardware = false; // Request to use hardware breakpoints |
520 | lldb::LanguageType m_exception_language = eLanguageTypeUnknown; |
521 | lldb::LanguageType m_language = lldb::eLanguageTypeUnknown; |
522 | LazyBool m_skip_prologue = eLazyBoolCalculate; |
523 | bool m_all_files = false; |
524 | Args m_exception_extra_args; |
525 | LazyBool m_move_to_nearest_code = eLazyBoolCalculate; |
526 | std::unordered_set<std::string> m_source_regex_func_names; |
527 | std::string m_current_key; |
528 | }; |
529 | |
530 | protected: |
531 | void DoExecute(Args &command, CommandReturnObject &result) override { |
532 | Target &target = GetSelectedOrDummyTarget(m_dummy_options.m_use_dummy); |
533 | |
534 | // The following are the various types of breakpoints that could be set: |
535 | // 1). -f -l -p [-s -g] (setting breakpoint by source location) |
536 | // 2). -a [-s -g] (setting breakpoint by address) |
537 | // 3). -n [-s -g] (setting breakpoint by function name) |
538 | // 4). -r [-s -g] (setting breakpoint by function name regular |
539 | // expression) |
540 | // 5). -p -f (setting a breakpoint by comparing a reg-exp |
541 | // to source text) |
542 | // 6). -E [-w -h] (setting a breakpoint for exceptions for a |
543 | // given language.) |
544 | |
545 | BreakpointSetType break_type = eSetTypeInvalid; |
546 | |
547 | if (!m_python_class_options.GetName().empty()) |
548 | break_type = eSetTypeScripted; |
549 | else if (m_options.m_line_num != 0) |
550 | break_type = eSetTypeFileAndLine; |
551 | else if (m_options.m_load_addr != LLDB_INVALID_ADDRESS) |
552 | break_type = eSetTypeAddress; |
553 | else if (!m_options.m_func_names.empty()) |
554 | break_type = eSetTypeFunctionName; |
555 | else if (!m_options.m_func_regexp.empty()) |
556 | break_type = eSetTypeFunctionRegexp; |
557 | else if (!m_options.m_source_text_regexp.empty()) |
558 | break_type = eSetTypeSourceRegexp; |
559 | else if (m_options.m_exception_language != eLanguageTypeUnknown) |
560 | break_type = eSetTypeException; |
561 | |
562 | BreakpointSP bp_sp = nullptr; |
563 | FileSpec module_spec; |
564 | const bool internal = false; |
565 | |
566 | // If the user didn't specify skip-prologue, having an offset should turn |
567 | // that off. |
568 | if (m_options.m_offset_addr != 0 && |
569 | m_options.m_skip_prologue == eLazyBoolCalculate) |
570 | m_options.m_skip_prologue = eLazyBoolNo; |
571 | |
572 | switch (break_type) { |
573 | case eSetTypeFileAndLine: // Breakpoint by source position |
574 | { |
575 | FileSpec file; |
576 | const size_t num_files = m_options.m_filenames.GetSize(); |
577 | if (num_files == 0) { |
578 | if (!GetDefaultFile(target, file, result)) { |
579 | result.AppendError(in_string: "No file supplied and no default file available." ); |
580 | return; |
581 | } |
582 | } else if (num_files > 1) { |
583 | result.AppendError(in_string: "Only one file at a time is allowed for file and " |
584 | "line breakpoints." ); |
585 | return; |
586 | } else |
587 | file = m_options.m_filenames.GetFileSpecAtIndex(0); |
588 | |
589 | // Only check for inline functions if |
590 | LazyBool check_inlines = eLazyBoolCalculate; |
591 | |
592 | bp_sp = target.CreateBreakpoint( |
593 | &(m_options.m_modules), file, m_options.m_line_num, |
594 | m_options.m_column, m_options.m_offset_addr, check_inlines, |
595 | m_options.m_skip_prologue, internal, m_options.m_hardware, |
596 | m_options.m_move_to_nearest_code); |
597 | } break; |
598 | |
599 | case eSetTypeAddress: // Breakpoint by address |
600 | { |
601 | // If a shared library has been specified, make an lldb_private::Address |
602 | // with the library, and use that. That way the address breakpoint |
603 | // will track the load location of the library. |
604 | size_t num_modules_specified = m_options.m_modules.GetSize(); |
605 | if (num_modules_specified == 1) { |
606 | const FileSpec &file_spec = |
607 | m_options.m_modules.GetFileSpecAtIndex(0); |
608 | bp_sp = target.CreateAddressInModuleBreakpoint( |
609 | m_options.m_load_addr, internal, file_spec, m_options.m_hardware); |
610 | } else if (num_modules_specified == 0) { |
611 | bp_sp = target.CreateBreakpoint(m_options.m_load_addr, internal, |
612 | m_options.m_hardware); |
613 | } else { |
614 | result.AppendError(in_string: "Only one shared library can be specified for " |
615 | "address breakpoints." ); |
616 | return; |
617 | } |
618 | break; |
619 | } |
620 | case eSetTypeFunctionName: // Breakpoint by function name |
621 | { |
622 | FunctionNameType name_type_mask = m_options.m_func_name_type_mask; |
623 | |
624 | if (name_type_mask == 0) |
625 | name_type_mask = eFunctionNameTypeAuto; |
626 | |
627 | bp_sp = target.CreateBreakpoint( |
628 | &(m_options.m_modules), &(m_options.m_filenames), |
629 | m_options.m_func_names, name_type_mask, m_options.m_language, |
630 | m_options.m_offset_addr, m_options.m_skip_prologue, internal, |
631 | m_options.m_hardware); |
632 | } break; |
633 | |
634 | case eSetTypeFunctionRegexp: // Breakpoint by regular expression function |
635 | // name |
636 | { |
637 | RegularExpression regexp(m_options.m_func_regexp); |
638 | if (llvm::Error err = regexp.GetError()) { |
639 | result.AppendErrorWithFormat( |
640 | format: "Function name regular expression could not be compiled: %s" , |
641 | llvm::toString(std::move(err)).c_str()); |
642 | // Check if the incorrect regex looks like a globbing expression and |
643 | // warn the user about it. |
644 | if (!m_options.m_func_regexp.empty()) { |
645 | if (m_options.m_func_regexp[0] == '*' || |
646 | m_options.m_func_regexp[0] == '?') |
647 | result.AppendWarning( |
648 | in_string: "Function name regex does not accept glob patterns." ); |
649 | } |
650 | return; |
651 | } |
652 | |
653 | bp_sp = target.CreateFuncRegexBreakpoint( |
654 | &(m_options.m_modules), &(m_options.m_filenames), std::move(regexp), |
655 | m_options.m_language, m_options.m_skip_prologue, internal, |
656 | m_options.m_hardware); |
657 | } break; |
658 | case eSetTypeSourceRegexp: // Breakpoint by regexp on source text. |
659 | { |
660 | const size_t num_files = m_options.m_filenames.GetSize(); |
661 | |
662 | if (num_files == 0 && !m_options.m_all_files) { |
663 | FileSpec file; |
664 | if (!GetDefaultFile(target, file, result)) { |
665 | result.AppendError( |
666 | in_string: "No files provided and could not find default file." ); |
667 | return; |
668 | } else { |
669 | m_options.m_filenames.Append(file); |
670 | } |
671 | } |
672 | |
673 | RegularExpression regexp(m_options.m_source_text_regexp); |
674 | if (llvm::Error err = regexp.GetError()) { |
675 | result.AppendErrorWithFormat( |
676 | format: "Source text regular expression could not be compiled: \"%s\"" , |
677 | llvm::toString(std::move(err)).c_str()); |
678 | return; |
679 | } |
680 | bp_sp = target.CreateSourceRegexBreakpoint( |
681 | &(m_options.m_modules), &(m_options.m_filenames), |
682 | m_options.m_source_regex_func_names, std::move(regexp), internal, |
683 | m_options.m_hardware, m_options.m_move_to_nearest_code); |
684 | } break; |
685 | case eSetTypeException: { |
686 | Status precond_error; |
687 | bp_sp = target.CreateExceptionBreakpoint( |
688 | m_options.m_exception_language, m_options.m_catch_bp, |
689 | m_options.m_throw_bp, internal, &m_options.m_exception_extra_args, |
690 | &precond_error); |
691 | if (precond_error.Fail()) { |
692 | result.AppendErrorWithFormat( |
693 | format: "Error setting extra exception arguments: %s" , |
694 | precond_error.AsCString()); |
695 | target.RemoveBreakpointByID(break_id: bp_sp->GetID()); |
696 | return; |
697 | } |
698 | } break; |
699 | case eSetTypeScripted: { |
700 | |
701 | Status error; |
702 | bp_sp = target.CreateScriptedBreakpoint( |
703 | m_python_class_options.GetName().c_str(), &(m_options.m_modules), |
704 | &(m_options.m_filenames), false, m_options.m_hardware, |
705 | m_python_class_options.GetStructuredData(), &error); |
706 | if (error.Fail()) { |
707 | result.AppendErrorWithFormat( |
708 | format: "Error setting extra exception arguments: %s" , error.AsCString()); |
709 | target.RemoveBreakpointByID(break_id: bp_sp->GetID()); |
710 | return; |
711 | } |
712 | } break; |
713 | default: |
714 | break; |
715 | } |
716 | |
717 | // Now set the various options that were passed in: |
718 | if (bp_sp) { |
719 | bp_sp->GetOptions().CopyOverSetOptions(m_bp_opts.GetBreakpointOptions()); |
720 | |
721 | if (!m_options.m_breakpoint_names.empty()) { |
722 | Status name_error; |
723 | for (auto name : m_options.m_breakpoint_names) { |
724 | target.AddNameToBreakpoint(bp_sp, name.c_str(), name_error); |
725 | if (name_error.Fail()) { |
726 | result.AppendErrorWithFormat("Invalid breakpoint name: %s" , |
727 | name.c_str()); |
728 | target.RemoveBreakpointByID(bp_sp->GetID()); |
729 | return; |
730 | } |
731 | } |
732 | } |
733 | } |
734 | |
735 | if (bp_sp) { |
736 | Stream &output_stream = result.GetOutputStream(); |
737 | const bool show_locations = false; |
738 | bp_sp->GetDescription(s: &output_stream, level: lldb::eDescriptionLevelInitial, |
739 | show_locations); |
740 | if (&target == &GetDummyTarget()) |
741 | output_stream.Printf(format: "Breakpoint set in dummy target, will get copied " |
742 | "into future targets.\n" ); |
743 | else { |
744 | // Don't print out this warning for exception breakpoints. They can |
745 | // get set before the target is set, but we won't know how to actually |
746 | // set the breakpoint till we run. |
747 | if (bp_sp->GetNumLocations() == 0 && break_type != eSetTypeException) { |
748 | output_stream.Printf(format: "WARNING: Unable to resolve breakpoint to any " |
749 | "actual locations.\n" ); |
750 | } |
751 | } |
752 | result.SetStatus(eReturnStatusSuccessFinishResult); |
753 | } else if (!bp_sp) { |
754 | result.AppendError(in_string: "Breakpoint creation failed: No breakpoint created." ); |
755 | } |
756 | } |
757 | |
758 | private: |
759 | bool GetDefaultFile(Target &target, FileSpec &file, |
760 | CommandReturnObject &result) { |
761 | uint32_t default_line; |
762 | // First use the Source Manager's default file. Then use the current stack |
763 | // frame's file. |
764 | if (!target.GetSourceManager().GetDefaultFileAndLine(file_spec&: file, line&: default_line)) { |
765 | StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); |
766 | if (cur_frame == nullptr) { |
767 | result.AppendError( |
768 | in_string: "No selected frame to use to find the default file." ); |
769 | return false; |
770 | } else if (!cur_frame->HasDebugInformation()) { |
771 | result.AppendError(in_string: "Cannot use the selected frame to find the default " |
772 | "file, it has no debug info." ); |
773 | return false; |
774 | } else { |
775 | const SymbolContext &sc = |
776 | cur_frame->GetSymbolContext(resolve_scope: eSymbolContextLineEntry); |
777 | if (sc.line_entry.file) { |
778 | file = sc.line_entry.file; |
779 | } else { |
780 | result.AppendError(in_string: "Can't find the file for the selected frame to " |
781 | "use as the default file." ); |
782 | return false; |
783 | } |
784 | } |
785 | } |
786 | return true; |
787 | } |
788 | |
789 | BreakpointOptionGroup m_bp_opts; |
790 | BreakpointDummyOptionGroup m_dummy_options; |
791 | OptionGroupPythonClassWithDict m_python_class_options; |
792 | CommandOptions m_options; |
793 | OptionGroupOptions m_all_options; |
794 | }; |
795 | |
796 | // CommandObjectBreakpointModify |
797 | #pragma mark Modify |
798 | |
799 | class CommandObjectBreakpointModify : public CommandObjectParsed { |
800 | public: |
801 | CommandObjectBreakpointModify(CommandInterpreter &interpreter) |
802 | : CommandObjectParsed(interpreter, "breakpoint modify" , |
803 | "Modify the options on a breakpoint or set of " |
804 | "breakpoints in the executable. " |
805 | "If no breakpoint is specified, acts on the last " |
806 | "created breakpoint. " |
807 | "With the exception of -e, -d and -i, passing an " |
808 | "empty argument clears the modification." , |
809 | nullptr) { |
810 | CommandArgumentEntry arg; |
811 | CommandObject::AddIDsArgumentData(arg, ID: eArgTypeBreakpointID, |
812 | IDRange: eArgTypeBreakpointIDRange); |
813 | // Add the entry for the first argument for this command to the object's |
814 | // arguments vector. |
815 | m_arguments.push_back(x: arg); |
816 | |
817 | m_options.Append(&m_bp_opts, |
818 | LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, |
819 | LLDB_OPT_SET_ALL); |
820 | m_options.Append(group: &m_dummy_opts, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); |
821 | m_options.Finalize(); |
822 | } |
823 | |
824 | ~CommandObjectBreakpointModify() override = default; |
825 | |
826 | void |
827 | HandleArgumentCompletion(CompletionRequest &request, |
828 | OptionElementVector &opt_element_vector) override { |
829 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
830 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eBreakpointCompletion, request, searcher: nullptr); |
831 | } |
832 | |
833 | Options *GetOptions() override { return &m_options; } |
834 | |
835 | protected: |
836 | void DoExecute(Args &command, CommandReturnObject &result) override { |
837 | Target &target = GetSelectedOrDummyTarget(m_dummy_opts.m_use_dummy); |
838 | |
839 | std::unique_lock<std::recursive_mutex> lock; |
840 | target.GetBreakpointList().GetListMutex(lock); |
841 | |
842 | BreakpointIDList valid_bp_ids; |
843 | |
844 | CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( |
845 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
846 | purpose: BreakpointName::Permissions::PermissionKinds::disablePerm); |
847 | |
848 | if (result.Succeeded()) { |
849 | const size_t count = valid_bp_ids.GetSize(); |
850 | for (size_t i = 0; i < count; ++i) { |
851 | BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index: i); |
852 | |
853 | if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) { |
854 | Breakpoint *bp = |
855 | target.GetBreakpointByID(break_id: cur_bp_id.GetBreakpointID()).get(); |
856 | if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) { |
857 | BreakpointLocation *location = |
858 | bp->FindLocationByID(bp_loc_id: cur_bp_id.GetLocationID()).get(); |
859 | if (location) |
860 | location->GetLocationOptions().CopyOverSetOptions( |
861 | m_bp_opts.GetBreakpointOptions()); |
862 | } else { |
863 | bp->GetOptions().CopyOverSetOptions( |
864 | m_bp_opts.GetBreakpointOptions()); |
865 | } |
866 | } |
867 | } |
868 | } |
869 | } |
870 | |
871 | private: |
872 | BreakpointOptionGroup m_bp_opts; |
873 | BreakpointDummyOptionGroup m_dummy_opts; |
874 | OptionGroupOptions m_options; |
875 | }; |
876 | |
877 | // CommandObjectBreakpointEnable |
878 | #pragma mark Enable |
879 | |
880 | class CommandObjectBreakpointEnable : public CommandObjectParsed { |
881 | public: |
882 | CommandObjectBreakpointEnable(CommandInterpreter &interpreter) |
883 | : CommandObjectParsed(interpreter, "enable" , |
884 | "Enable the specified disabled breakpoint(s). If " |
885 | "no breakpoints are specified, enable all of them." , |
886 | nullptr) { |
887 | CommandArgumentEntry arg; |
888 | CommandObject::AddIDsArgumentData(arg, ID: eArgTypeBreakpointID, |
889 | IDRange: eArgTypeBreakpointIDRange); |
890 | // Add the entry for the first argument for this command to the object's |
891 | // arguments vector. |
892 | m_arguments.push_back(x: arg); |
893 | } |
894 | |
895 | ~CommandObjectBreakpointEnable() override = default; |
896 | |
897 | void |
898 | HandleArgumentCompletion(CompletionRequest &request, |
899 | OptionElementVector &opt_element_vector) override { |
900 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
901 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eBreakpointCompletion, request, searcher: nullptr); |
902 | } |
903 | |
904 | protected: |
905 | void DoExecute(Args &command, CommandReturnObject &result) override { |
906 | Target &target = GetSelectedOrDummyTarget(); |
907 | |
908 | std::unique_lock<std::recursive_mutex> lock; |
909 | target.GetBreakpointList().GetListMutex(lock); |
910 | |
911 | const BreakpointList &breakpoints = target.GetBreakpointList(); |
912 | |
913 | size_t num_breakpoints = breakpoints.GetSize(); |
914 | |
915 | if (num_breakpoints == 0) { |
916 | result.AppendError(in_string: "No breakpoints exist to be enabled." ); |
917 | return; |
918 | } |
919 | |
920 | if (command.empty()) { |
921 | // No breakpoint selected; enable all currently set breakpoints. |
922 | target.EnableAllowedBreakpoints(); |
923 | result.AppendMessageWithFormat(format: "All breakpoints enabled. (%" PRIu64 |
924 | " breakpoints)\n" , |
925 | (uint64_t)num_breakpoints); |
926 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
927 | } else { |
928 | // Particular breakpoint selected; enable that breakpoint. |
929 | BreakpointIDList valid_bp_ids; |
930 | CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( |
931 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
932 | purpose: BreakpointName::Permissions::PermissionKinds::disablePerm); |
933 | |
934 | if (result.Succeeded()) { |
935 | int enable_count = 0; |
936 | int loc_count = 0; |
937 | const size_t count = valid_bp_ids.GetSize(); |
938 | for (size_t i = 0; i < count; ++i) { |
939 | BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index: i); |
940 | |
941 | if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) { |
942 | Breakpoint *breakpoint = |
943 | target.GetBreakpointByID(break_id: cur_bp_id.GetBreakpointID()).get(); |
944 | if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) { |
945 | BreakpointLocation *location = |
946 | breakpoint->FindLocationByID(bp_loc_id: cur_bp_id.GetLocationID()).get(); |
947 | if (location) { |
948 | location->SetEnabled(true); |
949 | ++loc_count; |
950 | } |
951 | } else { |
952 | breakpoint->SetEnabled(true); |
953 | ++enable_count; |
954 | } |
955 | } |
956 | } |
957 | result.AppendMessageWithFormat(format: "%d breakpoints enabled.\n" , |
958 | enable_count + loc_count); |
959 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
960 | } |
961 | } |
962 | } |
963 | }; |
964 | |
965 | // CommandObjectBreakpointDisable |
966 | #pragma mark Disable |
967 | |
968 | class CommandObjectBreakpointDisable : public CommandObjectParsed { |
969 | public: |
970 | CommandObjectBreakpointDisable(CommandInterpreter &interpreter) |
971 | : CommandObjectParsed( |
972 | interpreter, "breakpoint disable" , |
973 | "Disable the specified breakpoint(s) without deleting " |
974 | "them. If none are specified, disable all " |
975 | "breakpoints." , |
976 | nullptr) { |
977 | SetHelpLong( |
978 | "Disable the specified breakpoint(s) without deleting them. \ |
979 | If none are specified, disable all breakpoints." |
980 | R"( |
981 | |
982 | )" |
983 | "Note: disabling a breakpoint will cause none of its locations to be hit \ |
984 | regardless of whether individual locations are enabled or disabled. After the sequence:" |
985 | R"( |
986 | |
987 | (lldb) break disable 1 |
988 | (lldb) break enable 1.1 |
989 | |
990 | execution will NOT stop at location 1.1. To achieve that, type: |
991 | |
992 | (lldb) break disable 1.* |
993 | (lldb) break enable 1.1 |
994 | |
995 | )" |
996 | "The first command disables all locations for breakpoint 1, \ |
997 | the second re-enables the first location." ); |
998 | |
999 | CommandArgumentEntry arg; |
1000 | CommandObject::AddIDsArgumentData(arg, ID: eArgTypeBreakpointID, |
1001 | IDRange: eArgTypeBreakpointIDRange); |
1002 | // Add the entry for the first argument for this command to the object's |
1003 | // arguments vector. |
1004 | m_arguments.push_back(x: arg); |
1005 | } |
1006 | |
1007 | ~CommandObjectBreakpointDisable() override = default; |
1008 | |
1009 | void |
1010 | HandleArgumentCompletion(CompletionRequest &request, |
1011 | OptionElementVector &opt_element_vector) override { |
1012 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
1013 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eBreakpointCompletion, request, searcher: nullptr); |
1014 | } |
1015 | |
1016 | protected: |
1017 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1018 | Target &target = GetSelectedOrDummyTarget(); |
1019 | std::unique_lock<std::recursive_mutex> lock; |
1020 | target.GetBreakpointList().GetListMutex(lock); |
1021 | |
1022 | const BreakpointList &breakpoints = target.GetBreakpointList(); |
1023 | size_t num_breakpoints = breakpoints.GetSize(); |
1024 | |
1025 | if (num_breakpoints == 0) { |
1026 | result.AppendError(in_string: "No breakpoints exist to be disabled." ); |
1027 | return; |
1028 | } |
1029 | |
1030 | if (command.empty()) { |
1031 | // No breakpoint selected; disable all currently set breakpoints. |
1032 | target.DisableAllowedBreakpoints(); |
1033 | result.AppendMessageWithFormat(format: "All breakpoints disabled. (%" PRIu64 |
1034 | " breakpoints)\n" , |
1035 | (uint64_t)num_breakpoints); |
1036 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1037 | } else { |
1038 | // Particular breakpoint selected; disable that breakpoint. |
1039 | BreakpointIDList valid_bp_ids; |
1040 | |
1041 | CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( |
1042 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
1043 | purpose: BreakpointName::Permissions::PermissionKinds::disablePerm); |
1044 | |
1045 | if (result.Succeeded()) { |
1046 | int disable_count = 0; |
1047 | int loc_count = 0; |
1048 | const size_t count = valid_bp_ids.GetSize(); |
1049 | for (size_t i = 0; i < count; ++i) { |
1050 | BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index: i); |
1051 | |
1052 | if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) { |
1053 | Breakpoint *breakpoint = |
1054 | target.GetBreakpointByID(break_id: cur_bp_id.GetBreakpointID()).get(); |
1055 | if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) { |
1056 | BreakpointLocation *location = |
1057 | breakpoint->FindLocationByID(bp_loc_id: cur_bp_id.GetLocationID()).get(); |
1058 | if (location) { |
1059 | location->SetEnabled(false); |
1060 | ++loc_count; |
1061 | } |
1062 | } else { |
1063 | breakpoint->SetEnabled(false); |
1064 | ++disable_count; |
1065 | } |
1066 | } |
1067 | } |
1068 | result.AppendMessageWithFormat(format: "%d breakpoints disabled.\n" , |
1069 | disable_count + loc_count); |
1070 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1071 | } |
1072 | } |
1073 | } |
1074 | }; |
1075 | |
1076 | // CommandObjectBreakpointList |
1077 | |
1078 | #pragma mark List::CommandOptions |
1079 | #define LLDB_OPTIONS_breakpoint_list |
1080 | #include "CommandOptions.inc" |
1081 | |
1082 | #pragma mark List |
1083 | |
1084 | class CommandObjectBreakpointList : public CommandObjectParsed { |
1085 | public: |
1086 | CommandObjectBreakpointList(CommandInterpreter &interpreter) |
1087 | : CommandObjectParsed( |
1088 | interpreter, "breakpoint list" , |
1089 | "List some or all breakpoints at configurable levels of detail." , |
1090 | nullptr) { |
1091 | CommandArgumentEntry arg; |
1092 | CommandArgumentData bp_id_arg; |
1093 | |
1094 | // Define the first (and only) variant of this arg. |
1095 | bp_id_arg.arg_type = eArgTypeBreakpointID; |
1096 | bp_id_arg.arg_repetition = eArgRepeatOptional; |
1097 | |
1098 | // There is only one variant this argument could be; put it into the |
1099 | // argument entry. |
1100 | arg.push_back(x: bp_id_arg); |
1101 | |
1102 | // Push the data for the first argument into the m_arguments vector. |
1103 | m_arguments.push_back(x: arg); |
1104 | } |
1105 | |
1106 | ~CommandObjectBreakpointList() override = default; |
1107 | |
1108 | Options *GetOptions() override { return &m_options; } |
1109 | |
1110 | class CommandOptions : public Options { |
1111 | public: |
1112 | CommandOptions() = default; |
1113 | |
1114 | ~CommandOptions() override = default; |
1115 | |
1116 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1117 | ExecutionContext *execution_context) override { |
1118 | Status error; |
1119 | const int short_option = m_getopt_table[option_idx].val; |
1120 | |
1121 | switch (short_option) { |
1122 | case 'b': |
1123 | m_level = lldb::eDescriptionLevelBrief; |
1124 | break; |
1125 | case 'D': |
1126 | m_use_dummy = true; |
1127 | break; |
1128 | case 'f': |
1129 | m_level = lldb::eDescriptionLevelFull; |
1130 | break; |
1131 | case 'v': |
1132 | m_level = lldb::eDescriptionLevelVerbose; |
1133 | break; |
1134 | case 'i': |
1135 | m_internal = true; |
1136 | break; |
1137 | default: |
1138 | llvm_unreachable("Unimplemented option" ); |
1139 | } |
1140 | |
1141 | return error; |
1142 | } |
1143 | |
1144 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1145 | m_level = lldb::eDescriptionLevelFull; |
1146 | m_internal = false; |
1147 | m_use_dummy = false; |
1148 | } |
1149 | |
1150 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1151 | return llvm::ArrayRef(g_breakpoint_list_options); |
1152 | } |
1153 | |
1154 | // Instance variables to hold the values for command options. |
1155 | |
1156 | lldb::DescriptionLevel m_level = lldb::eDescriptionLevelBrief; |
1157 | |
1158 | bool m_internal; |
1159 | bool m_use_dummy = false; |
1160 | }; |
1161 | |
1162 | protected: |
1163 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1164 | Target &target = GetSelectedOrDummyTarget(prefer_dummy: m_options.m_use_dummy); |
1165 | |
1166 | const BreakpointList &breakpoints = |
1167 | target.GetBreakpointList(internal: m_options.m_internal); |
1168 | std::unique_lock<std::recursive_mutex> lock; |
1169 | target.GetBreakpointList(internal: m_options.m_internal).GetListMutex(lock); |
1170 | |
1171 | size_t num_breakpoints = breakpoints.GetSize(); |
1172 | |
1173 | if (num_breakpoints == 0) { |
1174 | result.AppendMessage(in_string: "No breakpoints currently set." ); |
1175 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1176 | return; |
1177 | } |
1178 | |
1179 | Stream &output_stream = result.GetOutputStream(); |
1180 | |
1181 | if (command.empty()) { |
1182 | // No breakpoint selected; show info about all currently set breakpoints. |
1183 | result.AppendMessage(in_string: "Current breakpoints:" ); |
1184 | for (size_t i = 0; i < num_breakpoints; ++i) { |
1185 | Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(i).get(); |
1186 | if (breakpoint->AllowList()) |
1187 | AddBreakpointDescription(s: &output_stream, bp: breakpoint, |
1188 | level: m_options.m_level); |
1189 | } |
1190 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1191 | } else { |
1192 | // Particular breakpoints selected; show info about that breakpoint. |
1193 | BreakpointIDList valid_bp_ids; |
1194 | CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( |
1195 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
1196 | purpose: BreakpointName::Permissions::PermissionKinds::listPerm); |
1197 | |
1198 | if (result.Succeeded()) { |
1199 | for (size_t i = 0; i < valid_bp_ids.GetSize(); ++i) { |
1200 | BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index: i); |
1201 | Breakpoint *breakpoint = |
1202 | target.GetBreakpointByID(break_id: cur_bp_id.GetBreakpointID()).get(); |
1203 | AddBreakpointDescription(s: &output_stream, bp: breakpoint, |
1204 | level: m_options.m_level); |
1205 | } |
1206 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1207 | } else { |
1208 | result.AppendError(in_string: "Invalid breakpoint ID." ); |
1209 | } |
1210 | } |
1211 | } |
1212 | |
1213 | private: |
1214 | CommandOptions m_options; |
1215 | }; |
1216 | |
1217 | // CommandObjectBreakpointClear |
1218 | #pragma mark Clear::CommandOptions |
1219 | |
1220 | #define LLDB_OPTIONS_breakpoint_clear |
1221 | #include "CommandOptions.inc" |
1222 | |
1223 | #pragma mark Clear |
1224 | |
1225 | class CommandObjectBreakpointClear : public CommandObjectParsed { |
1226 | public: |
1227 | enum BreakpointClearType { eClearTypeInvalid, eClearTypeFileAndLine }; |
1228 | |
1229 | CommandObjectBreakpointClear(CommandInterpreter &interpreter) |
1230 | : CommandObjectParsed(interpreter, "breakpoint clear" , |
1231 | "Delete or disable breakpoints matching the " |
1232 | "specified source file and line." , |
1233 | "breakpoint clear <cmd-options>" ) {} |
1234 | |
1235 | ~CommandObjectBreakpointClear() override = default; |
1236 | |
1237 | Options *GetOptions() override { return &m_options; } |
1238 | |
1239 | class CommandOptions : public Options { |
1240 | public: |
1241 | CommandOptions() = default; |
1242 | |
1243 | ~CommandOptions() override = default; |
1244 | |
1245 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1246 | ExecutionContext *execution_context) override { |
1247 | Status error; |
1248 | const int short_option = m_getopt_table[option_idx].val; |
1249 | |
1250 | switch (short_option) { |
1251 | case 'f': |
1252 | m_filename.assign(str: std::string(option_arg)); |
1253 | break; |
1254 | |
1255 | case 'l': |
1256 | option_arg.getAsInteger(Radix: 0, Result&: m_line_num); |
1257 | break; |
1258 | |
1259 | default: |
1260 | llvm_unreachable("Unimplemented option" ); |
1261 | } |
1262 | |
1263 | return error; |
1264 | } |
1265 | |
1266 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1267 | m_filename.clear(); |
1268 | m_line_num = 0; |
1269 | } |
1270 | |
1271 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1272 | return llvm::ArrayRef(g_breakpoint_clear_options); |
1273 | } |
1274 | |
1275 | // Instance variables to hold the values for command options. |
1276 | |
1277 | std::string m_filename; |
1278 | uint32_t m_line_num = 0; |
1279 | }; |
1280 | |
1281 | protected: |
1282 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1283 | Target &target = GetSelectedOrDummyTarget(); |
1284 | |
1285 | // The following are the various types of breakpoints that could be |
1286 | // cleared: |
1287 | // 1). -f -l (clearing breakpoint by source location) |
1288 | |
1289 | BreakpointClearType break_type = eClearTypeInvalid; |
1290 | |
1291 | if (m_options.m_line_num != 0) |
1292 | break_type = eClearTypeFileAndLine; |
1293 | |
1294 | std::unique_lock<std::recursive_mutex> lock; |
1295 | target.GetBreakpointList().GetListMutex(lock); |
1296 | |
1297 | BreakpointList &breakpoints = target.GetBreakpointList(); |
1298 | size_t num_breakpoints = breakpoints.GetSize(); |
1299 | |
1300 | // Early return if there's no breakpoint at all. |
1301 | if (num_breakpoints == 0) { |
1302 | result.AppendError(in_string: "Breakpoint clear: No breakpoint cleared." ); |
1303 | return; |
1304 | } |
1305 | |
1306 | // Find matching breakpoints and delete them. |
1307 | |
1308 | // First create a copy of all the IDs. |
1309 | std::vector<break_id_t> BreakIDs; |
1310 | for (size_t i = 0; i < num_breakpoints; ++i) |
1311 | BreakIDs.push_back(x: breakpoints.GetBreakpointAtIndex(i)->GetID()); |
1312 | |
1313 | int num_cleared = 0; |
1314 | StreamString ss; |
1315 | switch (break_type) { |
1316 | case eClearTypeFileAndLine: // Breakpoint by source position |
1317 | { |
1318 | const ConstString filename(m_options.m_filename.c_str()); |
1319 | BreakpointLocationCollection loc_coll; |
1320 | |
1321 | for (size_t i = 0; i < num_breakpoints; ++i) { |
1322 | Breakpoint *bp = breakpoints.FindBreakpointByID(breakID: BreakIDs[i]).get(); |
1323 | |
1324 | if (bp->GetMatchingFileLine(filename, line_number: m_options.m_line_num, loc_coll)) { |
1325 | // If the collection size is 0, it's a full match and we can just |
1326 | // remove the breakpoint. |
1327 | if (loc_coll.GetSize() == 0) { |
1328 | bp->GetDescription(s: &ss, level: lldb::eDescriptionLevelBrief); |
1329 | ss.EOL(); |
1330 | target.RemoveBreakpointByID(break_id: bp->GetID()); |
1331 | ++num_cleared; |
1332 | } |
1333 | } |
1334 | } |
1335 | } break; |
1336 | |
1337 | default: |
1338 | break; |
1339 | } |
1340 | |
1341 | if (num_cleared > 0) { |
1342 | Stream &output_stream = result.GetOutputStream(); |
1343 | output_stream.Printf(format: "%d breakpoints cleared:\n" , num_cleared); |
1344 | output_stream << ss.GetString(); |
1345 | output_stream.EOL(); |
1346 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1347 | } else { |
1348 | result.AppendError(in_string: "Breakpoint clear: No breakpoint cleared." ); |
1349 | } |
1350 | } |
1351 | |
1352 | private: |
1353 | CommandOptions m_options; |
1354 | }; |
1355 | |
1356 | // CommandObjectBreakpointDelete |
1357 | #define LLDB_OPTIONS_breakpoint_delete |
1358 | #include "CommandOptions.inc" |
1359 | |
1360 | #pragma mark Delete |
1361 | |
1362 | class CommandObjectBreakpointDelete : public CommandObjectParsed { |
1363 | public: |
1364 | CommandObjectBreakpointDelete(CommandInterpreter &interpreter) |
1365 | : CommandObjectParsed(interpreter, "breakpoint delete" , |
1366 | "Delete the specified breakpoint(s). If no " |
1367 | "breakpoints are specified, delete them all." , |
1368 | nullptr) { |
1369 | CommandArgumentEntry arg; |
1370 | CommandObject::AddIDsArgumentData(arg, ID: eArgTypeBreakpointID, |
1371 | IDRange: eArgTypeBreakpointIDRange); |
1372 | // Add the entry for the first argument for this command to the object's |
1373 | // arguments vector. |
1374 | m_arguments.push_back(x: arg); |
1375 | } |
1376 | |
1377 | ~CommandObjectBreakpointDelete() override = default; |
1378 | |
1379 | void |
1380 | HandleArgumentCompletion(CompletionRequest &request, |
1381 | OptionElementVector &opt_element_vector) override { |
1382 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
1383 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eBreakpointCompletion, request, searcher: nullptr); |
1384 | } |
1385 | |
1386 | Options *GetOptions() override { return &m_options; } |
1387 | |
1388 | class CommandOptions : public Options { |
1389 | public: |
1390 | CommandOptions() = default; |
1391 | |
1392 | ~CommandOptions() override = default; |
1393 | |
1394 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1395 | ExecutionContext *execution_context) override { |
1396 | Status error; |
1397 | const int short_option = m_getopt_table[option_idx].val; |
1398 | |
1399 | switch (short_option) { |
1400 | case 'f': |
1401 | m_force = true; |
1402 | break; |
1403 | |
1404 | case 'D': |
1405 | m_use_dummy = true; |
1406 | break; |
1407 | |
1408 | case 'd': |
1409 | m_delete_disabled = true; |
1410 | break; |
1411 | |
1412 | default: |
1413 | llvm_unreachable("Unimplemented option" ); |
1414 | } |
1415 | |
1416 | return error; |
1417 | } |
1418 | |
1419 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1420 | m_use_dummy = false; |
1421 | m_force = false; |
1422 | m_delete_disabled = false; |
1423 | } |
1424 | |
1425 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1426 | return llvm::ArrayRef(g_breakpoint_delete_options); |
1427 | } |
1428 | |
1429 | // Instance variables to hold the values for command options. |
1430 | bool m_use_dummy = false; |
1431 | bool m_force = false; |
1432 | bool m_delete_disabled = false; |
1433 | }; |
1434 | |
1435 | protected: |
1436 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1437 | Target &target = GetSelectedOrDummyTarget(prefer_dummy: m_options.m_use_dummy); |
1438 | result.Clear(); |
1439 | |
1440 | std::unique_lock<std::recursive_mutex> lock; |
1441 | target.GetBreakpointList().GetListMutex(lock); |
1442 | |
1443 | BreakpointList &breakpoints = target.GetBreakpointList(); |
1444 | |
1445 | size_t num_breakpoints = breakpoints.GetSize(); |
1446 | |
1447 | if (num_breakpoints == 0) { |
1448 | result.AppendError(in_string: "No breakpoints exist to be deleted." ); |
1449 | return; |
1450 | } |
1451 | |
1452 | // Handle the delete all breakpoints case: |
1453 | if (command.empty() && !m_options.m_delete_disabled) { |
1454 | if (!m_options.m_force && |
1455 | !m_interpreter.Confirm( |
1456 | message: "About to delete all breakpoints, do you want to do that?" , |
1457 | default_answer: true)) { |
1458 | result.AppendMessage(in_string: "Operation cancelled..." ); |
1459 | } else { |
1460 | target.RemoveAllowedBreakpoints(); |
1461 | result.AppendMessageWithFormat( |
1462 | format: "All breakpoints removed. (%" PRIu64 " breakpoint%s)\n" , |
1463 | (uint64_t)num_breakpoints, num_breakpoints > 1 ? "s" : "" ); |
1464 | } |
1465 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1466 | return; |
1467 | } |
1468 | |
1469 | // Either we have some kind of breakpoint specification(s), |
1470 | // or we are handling "break disable --deleted". Gather the list |
1471 | // of breakpoints to delete here, the we'll delete them below. |
1472 | BreakpointIDList valid_bp_ids; |
1473 | |
1474 | if (m_options.m_delete_disabled) { |
1475 | BreakpointIDList excluded_bp_ids; |
1476 | |
1477 | if (!command.empty()) { |
1478 | CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( |
1479 | args&: command, target: &target, result, valid_ids: &excluded_bp_ids, |
1480 | purpose: BreakpointName::Permissions::PermissionKinds::deletePerm); |
1481 | if (!result.Succeeded()) |
1482 | return; |
1483 | } |
1484 | |
1485 | for (auto breakpoint_sp : breakpoints.Breakpoints()) { |
1486 | if (!breakpoint_sp->IsEnabled() && breakpoint_sp->AllowDelete()) { |
1487 | BreakpointID bp_id(breakpoint_sp->GetID()); |
1488 | if (!excluded_bp_ids.Contains(bp_id)) |
1489 | valid_bp_ids.AddBreakpointID(bp_id); |
1490 | } |
1491 | } |
1492 | if (valid_bp_ids.GetSize() == 0) { |
1493 | result.AppendError(in_string: "No disabled breakpoints." ); |
1494 | return; |
1495 | } |
1496 | } else { |
1497 | CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( |
1498 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
1499 | purpose: BreakpointName::Permissions::PermissionKinds::deletePerm); |
1500 | if (!result.Succeeded()) |
1501 | return; |
1502 | } |
1503 | |
1504 | int delete_count = 0; |
1505 | int disable_count = 0; |
1506 | const size_t count = valid_bp_ids.GetSize(); |
1507 | for (size_t i = 0; i < count; ++i) { |
1508 | BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index: i); |
1509 | |
1510 | if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) { |
1511 | if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) { |
1512 | Breakpoint *breakpoint = |
1513 | target.GetBreakpointByID(break_id: cur_bp_id.GetBreakpointID()).get(); |
1514 | BreakpointLocation *location = |
1515 | breakpoint->FindLocationByID(bp_loc_id: cur_bp_id.GetLocationID()).get(); |
1516 | // It makes no sense to try to delete individual locations, so we |
1517 | // disable them instead. |
1518 | if (location) { |
1519 | location->SetEnabled(false); |
1520 | ++disable_count; |
1521 | } |
1522 | } else { |
1523 | target.RemoveBreakpointByID(break_id: cur_bp_id.GetBreakpointID()); |
1524 | ++delete_count; |
1525 | } |
1526 | } |
1527 | } |
1528 | result.AppendMessageWithFormat( |
1529 | format: "%d breakpoints deleted; %d breakpoint locations disabled.\n" , |
1530 | delete_count, disable_count); |
1531 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
1532 | } |
1533 | |
1534 | private: |
1535 | CommandOptions m_options; |
1536 | }; |
1537 | |
1538 | // CommandObjectBreakpointName |
1539 | #define LLDB_OPTIONS_breakpoint_name |
1540 | #include "CommandOptions.inc" |
1541 | |
1542 | class BreakpointNameOptionGroup : public OptionGroup { |
1543 | public: |
1544 | BreakpointNameOptionGroup() |
1545 | : m_breakpoint(LLDB_INVALID_BREAK_ID), m_use_dummy(false) {} |
1546 | |
1547 | ~BreakpointNameOptionGroup() override = default; |
1548 | |
1549 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1550 | return llvm::ArrayRef(g_breakpoint_name_options); |
1551 | } |
1552 | |
1553 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1554 | ExecutionContext *execution_context) override { |
1555 | Status error; |
1556 | const int short_option = g_breakpoint_name_options[option_idx].short_option; |
1557 | |
1558 | switch (short_option) { |
1559 | case 'N': |
1560 | if (BreakpointID::StringIsBreakpointName(str: option_arg, error) && |
1561 | error.Success()) |
1562 | m_name.SetValueFromString(value: option_arg); |
1563 | break; |
1564 | case 'B': |
1565 | if (m_breakpoint.SetValueFromString(value: option_arg).Fail()) |
1566 | error.SetErrorStringWithFormat( |
1567 | "unrecognized value \"%s\" for breakpoint" , |
1568 | option_arg.str().c_str()); |
1569 | break; |
1570 | case 'D': |
1571 | if (m_use_dummy.SetValueFromString(value: option_arg).Fail()) |
1572 | error.SetErrorStringWithFormat( |
1573 | "unrecognized value \"%s\" for use-dummy" , |
1574 | option_arg.str().c_str()); |
1575 | break; |
1576 | case 'H': |
1577 | m_help_string.SetValueFromString(value: option_arg); |
1578 | break; |
1579 | |
1580 | default: |
1581 | llvm_unreachable("Unimplemented option" ); |
1582 | } |
1583 | return error; |
1584 | } |
1585 | |
1586 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
1587 | m_name.Clear(); |
1588 | m_breakpoint.Clear(); |
1589 | m_use_dummy.Clear(); |
1590 | m_use_dummy.SetDefaultValue(false); |
1591 | m_help_string.Clear(); |
1592 | } |
1593 | |
1594 | OptionValueString m_name; |
1595 | OptionValueUInt64 m_breakpoint; |
1596 | OptionValueBoolean m_use_dummy; |
1597 | OptionValueString m_help_string; |
1598 | }; |
1599 | |
1600 | #define LLDB_OPTIONS_breakpoint_access |
1601 | #include "CommandOptions.inc" |
1602 | |
1603 | class BreakpointAccessOptionGroup : public OptionGroup { |
1604 | public: |
1605 | BreakpointAccessOptionGroup() = default; |
1606 | |
1607 | ~BreakpointAccessOptionGroup() override = default; |
1608 | |
1609 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
1610 | return llvm::ArrayRef(g_breakpoint_access_options); |
1611 | } |
1612 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
1613 | ExecutionContext *execution_context) override { |
1614 | Status error; |
1615 | const int short_option = |
1616 | g_breakpoint_access_options[option_idx].short_option; |
1617 | |
1618 | switch (short_option) { |
1619 | case 'L': { |
1620 | bool value, success; |
1621 | value = OptionArgParser::ToBoolean(s: option_arg, fail_value: false, success_ptr: &success); |
1622 | if (success) { |
1623 | m_permissions.SetAllowList(value); |
1624 | } else |
1625 | error.SetErrorStringWithFormat( |
1626 | "invalid boolean value '%s' passed for -L option" , |
1627 | option_arg.str().c_str()); |
1628 | } break; |
1629 | case 'A': { |
1630 | bool value, success; |
1631 | value = OptionArgParser::ToBoolean(s: option_arg, fail_value: false, success_ptr: &success); |
1632 | if (success) { |
1633 | m_permissions.SetAllowDisable(value); |
1634 | } else |
1635 | error.SetErrorStringWithFormat( |
1636 | "invalid boolean value '%s' passed for -L option" , |
1637 | option_arg.str().c_str()); |
1638 | } break; |
1639 | case 'D': { |
1640 | bool value, success; |
1641 | value = OptionArgParser::ToBoolean(s: option_arg, fail_value: false, success_ptr: &success); |
1642 | if (success) { |
1643 | m_permissions.SetAllowDelete(value); |
1644 | } else |
1645 | error.SetErrorStringWithFormat( |
1646 | "invalid boolean value '%s' passed for -L option" , |
1647 | option_arg.str().c_str()); |
1648 | } break; |
1649 | default: |
1650 | llvm_unreachable("Unimplemented option" ); |
1651 | } |
1652 | |
1653 | return error; |
1654 | } |
1655 | |
1656 | void OptionParsingStarting(ExecutionContext *execution_context) override {} |
1657 | |
1658 | const BreakpointName::Permissions &GetPermissions() const { |
1659 | return m_permissions; |
1660 | } |
1661 | BreakpointName::Permissions m_permissions; |
1662 | }; |
1663 | |
1664 | class CommandObjectBreakpointNameConfigure : public CommandObjectParsed { |
1665 | public: |
1666 | CommandObjectBreakpointNameConfigure(CommandInterpreter &interpreter) |
1667 | : CommandObjectParsed( |
1668 | interpreter, "configure" , |
1669 | "Configure the options for the breakpoint" |
1670 | " name provided. " |
1671 | "If you provide a breakpoint id, the options will be copied from " |
1672 | "the breakpoint, otherwise only the options specified will be set " |
1673 | "on the name." , |
1674 | "breakpoint name configure <command-options> " |
1675 | "<breakpoint-name-list>" ) { |
1676 | // Create the first variant for the first (and only) argument for this |
1677 | // command. |
1678 | CommandArgumentEntry arg1; |
1679 | CommandArgumentData id_arg; |
1680 | id_arg.arg_type = eArgTypeBreakpointName; |
1681 | id_arg.arg_repetition = eArgRepeatOptional; |
1682 | arg1.push_back(x: id_arg); |
1683 | m_arguments.push_back(x: arg1); |
1684 | |
1685 | m_option_group.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); |
1686 | m_option_group.Append(group: &m_access_options, LLDB_OPT_SET_ALL, |
1687 | LLDB_OPT_SET_ALL); |
1688 | m_option_group.Append(group: &m_bp_id, LLDB_OPT_SET_2 | LLDB_OPT_SET_4, |
1689 | LLDB_OPT_SET_ALL); |
1690 | m_option_group.Finalize(); |
1691 | } |
1692 | |
1693 | ~CommandObjectBreakpointNameConfigure() override = default; |
1694 | |
1695 | Options *GetOptions() override { return &m_option_group; } |
1696 | |
1697 | protected: |
1698 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1699 | |
1700 | const size_t argc = command.GetArgumentCount(); |
1701 | if (argc == 0) { |
1702 | result.AppendError(in_string: "No names provided." ); |
1703 | return; |
1704 | } |
1705 | |
1706 | Target &target = GetSelectedOrDummyTarget(false); |
1707 | |
1708 | std::unique_lock<std::recursive_mutex> lock; |
1709 | target.GetBreakpointList().GetListMutex(lock); |
1710 | |
1711 | // Make a pass through first to see that all the names are legal. |
1712 | for (auto &entry : command.entries()) { |
1713 | Status error; |
1714 | if (!BreakpointID::StringIsBreakpointName(str: entry.ref(), error)) { |
1715 | result.AppendErrorWithFormat(format: "Invalid breakpoint name: %s - %s" , |
1716 | entry.c_str(), error.AsCString()); |
1717 | return; |
1718 | } |
1719 | } |
1720 | // Now configure them, we already pre-checked the names so we don't need to |
1721 | // check the error: |
1722 | BreakpointSP bp_sp; |
1723 | if (m_bp_id.m_breakpoint.OptionWasSet()) { |
1724 | lldb::break_id_t bp_id = |
1725 | m_bp_id.m_breakpoint.GetValueAs<uint64_t>().value_or(u: 0); |
1726 | bp_sp = target.GetBreakpointByID(break_id: bp_id); |
1727 | if (!bp_sp) { |
1728 | result.AppendErrorWithFormatv(format: "Could not find specified breakpoint {0}" , |
1729 | args&: bp_id); |
1730 | return; |
1731 | } |
1732 | } |
1733 | |
1734 | Status error; |
1735 | for (auto &entry : command.entries()) { |
1736 | ConstString name(entry.c_str()); |
1737 | BreakpointName *bp_name = target.FindBreakpointName(name, can_create: true, error); |
1738 | if (!bp_name) |
1739 | continue; |
1740 | if (m_bp_id.m_help_string.OptionWasSet()) |
1741 | bp_name->SetHelp(m_bp_id.m_help_string.GetValueAs<llvm::StringRef>() |
1742 | .value_or(u: "" ) |
1743 | .str() |
1744 | .c_str()); |
1745 | |
1746 | if (bp_sp) |
1747 | target.ConfigureBreakpointName(bp_name&: *bp_name, options: bp_sp->GetOptions(), |
1748 | permissions: m_access_options.GetPermissions()); |
1749 | else |
1750 | target.ConfigureBreakpointName(*bp_name, |
1751 | m_bp_opts.GetBreakpointOptions(), |
1752 | m_access_options.GetPermissions()); |
1753 | } |
1754 | } |
1755 | |
1756 | private: |
1757 | BreakpointNameOptionGroup m_bp_id; // Only using the id part of this. |
1758 | BreakpointOptionGroup m_bp_opts; |
1759 | BreakpointAccessOptionGroup m_access_options; |
1760 | OptionGroupOptions m_option_group; |
1761 | }; |
1762 | |
1763 | class CommandObjectBreakpointNameAdd : public CommandObjectParsed { |
1764 | public: |
1765 | CommandObjectBreakpointNameAdd(CommandInterpreter &interpreter) |
1766 | : CommandObjectParsed( |
1767 | interpreter, "add" , "Add a name to the breakpoints provided." , |
1768 | "breakpoint name add <command-options> <breakpoint-id-list>" ) { |
1769 | // Create the first variant for the first (and only) argument for this |
1770 | // command. |
1771 | CommandArgumentEntry arg1; |
1772 | CommandArgumentData id_arg; |
1773 | id_arg.arg_type = eArgTypeBreakpointID; |
1774 | id_arg.arg_repetition = eArgRepeatOptional; |
1775 | arg1.push_back(x: id_arg); |
1776 | m_arguments.push_back(x: arg1); |
1777 | |
1778 | m_option_group.Append(group: &m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); |
1779 | m_option_group.Finalize(); |
1780 | } |
1781 | |
1782 | ~CommandObjectBreakpointNameAdd() override = default; |
1783 | |
1784 | void |
1785 | HandleArgumentCompletion(CompletionRequest &request, |
1786 | OptionElementVector &opt_element_vector) override { |
1787 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
1788 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eBreakpointCompletion, request, searcher: nullptr); |
1789 | } |
1790 | |
1791 | Options *GetOptions() override { return &m_option_group; } |
1792 | |
1793 | protected: |
1794 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1795 | if (!m_name_options.m_name.OptionWasSet()) { |
1796 | result.AppendError(in_string: "No name option provided." ); |
1797 | return; |
1798 | } |
1799 | |
1800 | Target &target = |
1801 | GetSelectedOrDummyTarget(prefer_dummy: m_name_options.m_use_dummy.GetCurrentValue()); |
1802 | |
1803 | std::unique_lock<std::recursive_mutex> lock; |
1804 | target.GetBreakpointList().GetListMutex(lock); |
1805 | |
1806 | const BreakpointList &breakpoints = target.GetBreakpointList(); |
1807 | |
1808 | size_t num_breakpoints = breakpoints.GetSize(); |
1809 | if (num_breakpoints == 0) { |
1810 | result.AppendError(in_string: "No breakpoints, cannot add names." ); |
1811 | return; |
1812 | } |
1813 | |
1814 | // Particular breakpoint selected; disable that breakpoint. |
1815 | BreakpointIDList valid_bp_ids; |
1816 | CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( |
1817 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
1818 | purpose: BreakpointName::Permissions::PermissionKinds::listPerm); |
1819 | |
1820 | if (result.Succeeded()) { |
1821 | if (valid_bp_ids.GetSize() == 0) { |
1822 | result.AppendError(in_string: "No breakpoints specified, cannot add names." ); |
1823 | return; |
1824 | } |
1825 | size_t num_valid_ids = valid_bp_ids.GetSize(); |
1826 | const char *bp_name = m_name_options.m_name.GetCurrentValue(); |
1827 | Status error; // This error reports illegal names, but we've already |
1828 | // checked that, so we don't need to check it again here. |
1829 | for (size_t index = 0; index < num_valid_ids; index++) { |
1830 | lldb::break_id_t bp_id = |
1831 | valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID(); |
1832 | BreakpointSP bp_sp = breakpoints.FindBreakpointByID(breakID: bp_id); |
1833 | target.AddNameToBreakpoint(bp_sp, name: bp_name, error); |
1834 | } |
1835 | } |
1836 | } |
1837 | |
1838 | private: |
1839 | BreakpointNameOptionGroup m_name_options; |
1840 | OptionGroupOptions m_option_group; |
1841 | }; |
1842 | |
1843 | class CommandObjectBreakpointNameDelete : public CommandObjectParsed { |
1844 | public: |
1845 | CommandObjectBreakpointNameDelete(CommandInterpreter &interpreter) |
1846 | : CommandObjectParsed( |
1847 | interpreter, "delete" , |
1848 | "Delete a name from the breakpoints provided." , |
1849 | "breakpoint name delete <command-options> <breakpoint-id-list>" ) { |
1850 | // Create the first variant for the first (and only) argument for this |
1851 | // command. |
1852 | CommandArgumentEntry arg1; |
1853 | CommandArgumentData id_arg; |
1854 | id_arg.arg_type = eArgTypeBreakpointID; |
1855 | id_arg.arg_repetition = eArgRepeatOptional; |
1856 | arg1.push_back(x: id_arg); |
1857 | m_arguments.push_back(x: arg1); |
1858 | |
1859 | m_option_group.Append(group: &m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); |
1860 | m_option_group.Finalize(); |
1861 | } |
1862 | |
1863 | ~CommandObjectBreakpointNameDelete() override = default; |
1864 | |
1865 | void |
1866 | HandleArgumentCompletion(CompletionRequest &request, |
1867 | OptionElementVector &opt_element_vector) override { |
1868 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
1869 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eBreakpointCompletion, request, searcher: nullptr); |
1870 | } |
1871 | |
1872 | Options *GetOptions() override { return &m_option_group; } |
1873 | |
1874 | protected: |
1875 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1876 | if (!m_name_options.m_name.OptionWasSet()) { |
1877 | result.AppendError(in_string: "No name option provided." ); |
1878 | return; |
1879 | } |
1880 | |
1881 | Target &target = |
1882 | GetSelectedOrDummyTarget(prefer_dummy: m_name_options.m_use_dummy.GetCurrentValue()); |
1883 | |
1884 | std::unique_lock<std::recursive_mutex> lock; |
1885 | target.GetBreakpointList().GetListMutex(lock); |
1886 | |
1887 | const BreakpointList &breakpoints = target.GetBreakpointList(); |
1888 | |
1889 | size_t num_breakpoints = breakpoints.GetSize(); |
1890 | if (num_breakpoints == 0) { |
1891 | result.AppendError(in_string: "No breakpoints, cannot delete names." ); |
1892 | return; |
1893 | } |
1894 | |
1895 | // Particular breakpoint selected; disable that breakpoint. |
1896 | BreakpointIDList valid_bp_ids; |
1897 | CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( |
1898 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
1899 | purpose: BreakpointName::Permissions::PermissionKinds::deletePerm); |
1900 | |
1901 | if (result.Succeeded()) { |
1902 | if (valid_bp_ids.GetSize() == 0) { |
1903 | result.AppendError(in_string: "No breakpoints specified, cannot delete names." ); |
1904 | return; |
1905 | } |
1906 | ConstString bp_name(m_name_options.m_name.GetCurrentValue()); |
1907 | size_t num_valid_ids = valid_bp_ids.GetSize(); |
1908 | for (size_t index = 0; index < num_valid_ids; index++) { |
1909 | lldb::break_id_t bp_id = |
1910 | valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID(); |
1911 | BreakpointSP bp_sp = breakpoints.FindBreakpointByID(breakID: bp_id); |
1912 | target.RemoveNameFromBreakpoint(bp_sp, name: bp_name); |
1913 | } |
1914 | } |
1915 | } |
1916 | |
1917 | private: |
1918 | BreakpointNameOptionGroup m_name_options; |
1919 | OptionGroupOptions m_option_group; |
1920 | }; |
1921 | |
1922 | class CommandObjectBreakpointNameList : public CommandObjectParsed { |
1923 | public: |
1924 | CommandObjectBreakpointNameList(CommandInterpreter &interpreter) |
1925 | : CommandObjectParsed(interpreter, "list" , |
1926 | "List either the names for a breakpoint or info " |
1927 | "about a given name. With no arguments, lists all " |
1928 | "names" , |
1929 | "breakpoint name list <command-options>" ) { |
1930 | m_option_group.Append(group: &m_name_options, LLDB_OPT_SET_3, LLDB_OPT_SET_ALL); |
1931 | m_option_group.Finalize(); |
1932 | } |
1933 | |
1934 | ~CommandObjectBreakpointNameList() override = default; |
1935 | |
1936 | Options *GetOptions() override { return &m_option_group; } |
1937 | |
1938 | protected: |
1939 | void DoExecute(Args &command, CommandReturnObject &result) override { |
1940 | Target &target = |
1941 | GetSelectedOrDummyTarget(prefer_dummy: m_name_options.m_use_dummy.GetCurrentValue()); |
1942 | |
1943 | std::vector<std::string> name_list; |
1944 | if (command.empty()) { |
1945 | target.GetBreakpointNames(names&: name_list); |
1946 | } else { |
1947 | for (const Args::ArgEntry &arg : command) { |
1948 | name_list.push_back(x: arg.c_str()); |
1949 | } |
1950 | } |
1951 | |
1952 | if (name_list.empty()) { |
1953 | result.AppendMessage(in_string: "No breakpoint names found." ); |
1954 | } else { |
1955 | for (const std::string &name_str : name_list) { |
1956 | const char *name = name_str.c_str(); |
1957 | // First print out the options for the name: |
1958 | Status error; |
1959 | BreakpointName *bp_name = |
1960 | target.FindBreakpointName(name: ConstString(name), can_create: false, error); |
1961 | if (bp_name) { |
1962 | StreamString s; |
1963 | result.AppendMessageWithFormat(format: "Name: %s\n" , name); |
1964 | if (bp_name->GetDescription(s: &s, level: eDescriptionLevelFull)) { |
1965 | result.AppendMessage(in_string: s.GetString()); |
1966 | } |
1967 | |
1968 | std::unique_lock<std::recursive_mutex> lock; |
1969 | target.GetBreakpointList().GetListMutex(lock); |
1970 | |
1971 | BreakpointList &breakpoints = target.GetBreakpointList(); |
1972 | bool any_set = false; |
1973 | for (BreakpointSP bp_sp : breakpoints.Breakpoints()) { |
1974 | if (bp_sp->MatchesName(name)) { |
1975 | StreamString s; |
1976 | any_set = true; |
1977 | bp_sp->GetDescription(s: &s, level: eDescriptionLevelBrief); |
1978 | s.EOL(); |
1979 | result.AppendMessage(in_string: s.GetString()); |
1980 | } |
1981 | } |
1982 | if (!any_set) |
1983 | result.AppendMessage(in_string: "No breakpoints using this name." ); |
1984 | } else { |
1985 | result.AppendMessageWithFormat(format: "Name: %s not found.\n" , name); |
1986 | } |
1987 | } |
1988 | } |
1989 | } |
1990 | |
1991 | private: |
1992 | BreakpointNameOptionGroup m_name_options; |
1993 | OptionGroupOptions m_option_group; |
1994 | }; |
1995 | |
1996 | // CommandObjectBreakpointName |
1997 | class CommandObjectBreakpointName : public CommandObjectMultiword { |
1998 | public: |
1999 | CommandObjectBreakpointName(CommandInterpreter &interpreter) |
2000 | : CommandObjectMultiword( |
2001 | interpreter, "name" , "Commands to manage breakpoint names" ) { |
2002 | |
2003 | |
2004 | SetHelpLong( |
2005 | R"( |
2006 | Breakpoint names provide a general tagging mechanism for breakpoints. Each |
2007 | breakpoint name can be added to any number of breakpoints, and each breakpoint |
2008 | can have any number of breakpoint names attached to it. For instance: |
2009 | |
2010 | (lldb) break name add -N MyName 1-10 |
2011 | |
2012 | adds the name MyName to breakpoints 1-10, and: |
2013 | |
2014 | (lldb) break set -n myFunc -N Name1 -N Name2 |
2015 | |
2016 | adds two names to the breakpoint set at myFunc. |
2017 | |
2018 | They have a number of interrelated uses: |
2019 | |
2020 | 1) They provide a stable way to refer to a breakpoint (e.g. in another |
2021 | breakpoint's action). Using the breakpoint ID for this purpose is fragile, since |
2022 | it depends on the order of breakpoint creation. Giving a name to the breakpoint |
2023 | you want to act on, and then referring to it by name, is more robust: |
2024 | |
2025 | (lldb) break set -n myFunc -N BKPT1 |
2026 | (lldb) break set -n myOtherFunc -C "break disable BKPT1" |
2027 | |
2028 | 2) This is actually just a specific use of a more general feature of breakpoint |
2029 | names. The <breakpt-id-list> argument type used to specify one or more |
2030 | breakpoints in most of the commands that deal with breakpoints also accepts |
2031 | breakpoint names. That allows you to refer to one breakpoint in a stable |
2032 | manner, but also makes them a convenient grouping mechanism, allowing you to |
2033 | easily act on a group of breakpoints by using their name, for instance disabling |
2034 | them all in one action: |
2035 | |
2036 | (lldb) break set -n myFunc -N Group1 |
2037 | (lldb) break set -n myOtherFunc -N Group1 |
2038 | (lldb) break disable Group1 |
2039 | |
2040 | 3) But breakpoint names are also entities in their own right, and can be |
2041 | configured with all the modifiable attributes of a breakpoint. Then when you |
2042 | add a breakpoint name to a breakpoint, the breakpoint will be configured to |
2043 | match the state of the breakpoint name. The link between the name and the |
2044 | breakpoints sharing it remains live, so if you change the configuration on the |
2045 | name, it will also change the configurations on the breakpoints: |
2046 | |
2047 | (lldb) break name configure -i 10 IgnoreSome |
2048 | (lldb) break set -n myFunc -N IgnoreSome |
2049 | (lldb) break list IgnoreSome |
2050 | 2: name = 'myFunc', locations = 0 (pending) Options: ignore: 10 enabled |
2051 | Names: |
2052 | IgnoreSome |
2053 | (lldb) break name configure -i 5 IgnoreSome |
2054 | (lldb) break list IgnoreSome |
2055 | 2: name = 'myFunc', locations = 0 (pending) Options: ignore: 5 enabled |
2056 | Names: |
2057 | IgnoreSome |
2058 | |
2059 | Options that are not configured on a breakpoint name don't affect the value of |
2060 | those options on the breakpoints they are added to. So for instance, if Name1 |
2061 | has the -i option configured and Name2 the -c option, adding both names to a |
2062 | breakpoint will set the -i option from Name1 and the -c option from Name2, and |
2063 | the other options will be unaltered. |
2064 | |
2065 | If you add multiple names to a breakpoint which have configured values for |
2066 | the same option, the last name added's value wins. |
2067 | |
2068 | The "liveness" of these settings is one way, from name to breakpoint. |
2069 | If you use "break modify" to change an option that is also configured on a name |
2070 | which that breakpoint has, the "break modify" command will override the setting |
2071 | for that breakpoint, but won't change the value configured in the name or on the |
2072 | other breakpoints sharing that name. |
2073 | |
2074 | 4) Breakpoint names are also a convenient way to copy option sets from one |
2075 | breakpoint to another. Using the -B option to "breakpoint name configure" makes |
2076 | a name configured with all the options of the original breakpoint. Then |
2077 | adding that name to another breakpoint copies over all the values from the |
2078 | original breakpoint to the new one. |
2079 | |
2080 | 5) You can also use breakpoint names to hide breakpoints from the breakpoint |
2081 | operations that act on all breakpoints: "break delete", "break disable" and |
2082 | "break list". You do that by specifying a "false" value for the |
2083 | --allow-{list,delete,disable} options to "breakpoint name configure" and then |
2084 | adding that name to a breakpoint. |
2085 | |
2086 | This won't keep the breakpoint from being deleted or disabled if you refer to it |
2087 | specifically by ID. The point of the feature is to make sure users don't |
2088 | inadvertently delete or disable useful breakpoints (e.g. ones an IDE is using |
2089 | for its own purposes) as part of a "delete all" or "disable all" operation. The |
2090 | list hiding is because it's confusing for people to see breakpoints they |
2091 | didn't set. |
2092 | |
2093 | )" ); |
2094 | CommandObjectSP add_command_object( |
2095 | new CommandObjectBreakpointNameAdd(interpreter)); |
2096 | CommandObjectSP delete_command_object( |
2097 | new CommandObjectBreakpointNameDelete(interpreter)); |
2098 | CommandObjectSP list_command_object( |
2099 | new CommandObjectBreakpointNameList(interpreter)); |
2100 | CommandObjectSP configure_command_object( |
2101 | new CommandObjectBreakpointNameConfigure(interpreter)); |
2102 | |
2103 | LoadSubCommand(cmd_name: "add" , command_obj: add_command_object); |
2104 | LoadSubCommand(cmd_name: "delete" , command_obj: delete_command_object); |
2105 | LoadSubCommand(cmd_name: "list" , command_obj: list_command_object); |
2106 | LoadSubCommand(cmd_name: "configure" , command_obj: configure_command_object); |
2107 | } |
2108 | |
2109 | ~CommandObjectBreakpointName() override = default; |
2110 | }; |
2111 | |
2112 | // CommandObjectBreakpointRead |
2113 | #pragma mark Read::CommandOptions |
2114 | #define LLDB_OPTIONS_breakpoint_read |
2115 | #include "CommandOptions.inc" |
2116 | |
2117 | #pragma mark Read |
2118 | |
2119 | class CommandObjectBreakpointRead : public CommandObjectParsed { |
2120 | public: |
2121 | CommandObjectBreakpointRead(CommandInterpreter &interpreter) |
2122 | : CommandObjectParsed(interpreter, "breakpoint read" , |
2123 | "Read and set the breakpoints previously saved to " |
2124 | "a file with \"breakpoint write\". " , |
2125 | nullptr) {} |
2126 | |
2127 | ~CommandObjectBreakpointRead() override = default; |
2128 | |
2129 | Options *GetOptions() override { return &m_options; } |
2130 | |
2131 | class CommandOptions : public Options { |
2132 | public: |
2133 | CommandOptions() = default; |
2134 | |
2135 | ~CommandOptions() override = default; |
2136 | |
2137 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
2138 | ExecutionContext *execution_context) override { |
2139 | Status error; |
2140 | const int short_option = m_getopt_table[option_idx].val; |
2141 | |
2142 | switch (short_option) { |
2143 | case 'f': |
2144 | m_filename.assign(str: std::string(option_arg)); |
2145 | break; |
2146 | case 'N': { |
2147 | Status name_error; |
2148 | if (!BreakpointID::StringIsBreakpointName(str: llvm::StringRef(option_arg), |
2149 | error&: name_error)) { |
2150 | error.SetErrorStringWithFormat("Invalid breakpoint name: %s" , |
2151 | name_error.AsCString()); |
2152 | } |
2153 | m_names.push_back(std::string(option_arg)); |
2154 | break; |
2155 | } |
2156 | default: |
2157 | llvm_unreachable("Unimplemented option" ); |
2158 | } |
2159 | |
2160 | return error; |
2161 | } |
2162 | |
2163 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
2164 | m_filename.clear(); |
2165 | m_names.clear(); |
2166 | } |
2167 | |
2168 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
2169 | return llvm::ArrayRef(g_breakpoint_read_options); |
2170 | } |
2171 | |
2172 | void HandleOptionArgumentCompletion( |
2173 | CompletionRequest &request, OptionElementVector &opt_element_vector, |
2174 | int opt_element_index, CommandInterpreter &interpreter) override { |
2175 | int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; |
2176 | int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; |
2177 | |
2178 | switch (GetDefinitions()[opt_defs_index].short_option) { |
2179 | case 'f': |
2180 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
2181 | interpreter, completion_mask: lldb::eDiskFileCompletion, request, searcher: nullptr); |
2182 | break; |
2183 | |
2184 | case 'N': |
2185 | std::optional<FileSpec> file_spec; |
2186 | const llvm::StringRef dash_f("-f" ); |
2187 | for (int arg_idx = 0; arg_idx < opt_arg_pos; arg_idx++) { |
2188 | if (dash_f == request.GetParsedLine().GetArgumentAtIndex(idx: arg_idx)) { |
2189 | file_spec.emplace( |
2190 | request.GetParsedLine().GetArgumentAtIndex(idx: arg_idx + 1)); |
2191 | break; |
2192 | } |
2193 | } |
2194 | if (!file_spec) |
2195 | return; |
2196 | |
2197 | FileSystem::Instance().Resolve(*file_spec); |
2198 | Status error; |
2199 | StructuredData::ObjectSP input_data_sp = |
2200 | StructuredData::ParseJSONFromFile(file: *file_spec, error); |
2201 | if (!error.Success()) |
2202 | return; |
2203 | |
2204 | StructuredData::Array *bkpt_array = input_data_sp->GetAsArray(); |
2205 | if (!bkpt_array) |
2206 | return; |
2207 | |
2208 | const size_t num_bkpts = bkpt_array->GetSize(); |
2209 | for (size_t i = 0; i < num_bkpts; i++) { |
2210 | StructuredData::ObjectSP bkpt_object_sp = |
2211 | bkpt_array->GetItemAtIndex(idx: i); |
2212 | if (!bkpt_object_sp) |
2213 | return; |
2214 | |
2215 | StructuredData::Dictionary *bkpt_dict = |
2216 | bkpt_object_sp->GetAsDictionary(); |
2217 | if (!bkpt_dict) |
2218 | return; |
2219 | |
2220 | StructuredData::ObjectSP bkpt_data_sp = |
2221 | bkpt_dict->GetValueForKey(key: Breakpoint::GetSerializationKey()); |
2222 | if (!bkpt_data_sp) |
2223 | return; |
2224 | |
2225 | bkpt_dict = bkpt_data_sp->GetAsDictionary(); |
2226 | if (!bkpt_dict) |
2227 | return; |
2228 | |
2229 | StructuredData::Array *names_array; |
2230 | |
2231 | if (!bkpt_dict->GetValueForKeyAsArray(key: "Names" , result&: names_array)) |
2232 | return; |
2233 | |
2234 | size_t num_names = names_array->GetSize(); |
2235 | |
2236 | for (size_t i = 0; i < num_names; i++) { |
2237 | if (std::optional<llvm::StringRef> maybe_name = |
2238 | names_array->GetItemAtIndexAsString(idx: i)) |
2239 | request.TryCompleteCurrentArg(*maybe_name); |
2240 | } |
2241 | } |
2242 | } |
2243 | } |
2244 | |
2245 | std::string m_filename; |
2246 | std::vector<std::string> m_names; |
2247 | }; |
2248 | |
2249 | protected: |
2250 | void DoExecute(Args &command, CommandReturnObject &result) override { |
2251 | Target &target = GetSelectedOrDummyTarget(); |
2252 | |
2253 | std::unique_lock<std::recursive_mutex> lock; |
2254 | target.GetBreakpointList().GetListMutex(lock); |
2255 | |
2256 | FileSpec input_spec(m_options.m_filename); |
2257 | FileSystem::Instance().Resolve(file_spec&: input_spec); |
2258 | BreakpointIDList new_bps; |
2259 | Status error = target.CreateBreakpointsFromFile(input_spec, |
2260 | m_options.m_names, new_bps); |
2261 | |
2262 | if (!error.Success()) { |
2263 | result.AppendError(in_string: error.AsCString()); |
2264 | return; |
2265 | } |
2266 | |
2267 | Stream &output_stream = result.GetOutputStream(); |
2268 | |
2269 | size_t num_breakpoints = new_bps.GetSize(); |
2270 | if (num_breakpoints == 0) { |
2271 | result.AppendMessage(in_string: "No breakpoints added." ); |
2272 | } else { |
2273 | // No breakpoint selected; show info about all currently set breakpoints. |
2274 | result.AppendMessage(in_string: "New breakpoints:" ); |
2275 | for (size_t i = 0; i < num_breakpoints; ++i) { |
2276 | BreakpointID bp_id = new_bps.GetBreakpointIDAtIndex(index: i); |
2277 | Breakpoint *bp = target.GetBreakpointList() |
2278 | .FindBreakpointByID(breakID: bp_id.GetBreakpointID()) |
2279 | .get(); |
2280 | if (bp) |
2281 | bp->GetDescription(s: &output_stream, level: lldb::eDescriptionLevelInitial, |
2282 | show_locations: false); |
2283 | } |
2284 | } |
2285 | } |
2286 | |
2287 | private: |
2288 | CommandOptions m_options; |
2289 | }; |
2290 | |
2291 | // CommandObjectBreakpointWrite |
2292 | #pragma mark Write::CommandOptions |
2293 | #define LLDB_OPTIONS_breakpoint_write |
2294 | #include "CommandOptions.inc" |
2295 | |
2296 | #pragma mark Write |
2297 | class CommandObjectBreakpointWrite : public CommandObjectParsed { |
2298 | public: |
2299 | CommandObjectBreakpointWrite(CommandInterpreter &interpreter) |
2300 | : CommandObjectParsed(interpreter, "breakpoint write" , |
2301 | "Write the breakpoints listed to a file that can " |
2302 | "be read in with \"breakpoint read\". " |
2303 | "If given no arguments, writes all breakpoints." , |
2304 | nullptr) { |
2305 | CommandArgumentEntry arg; |
2306 | CommandObject::AddIDsArgumentData(arg, ID: eArgTypeBreakpointID, |
2307 | IDRange: eArgTypeBreakpointIDRange); |
2308 | // Add the entry for the first argument for this command to the object's |
2309 | // arguments vector. |
2310 | m_arguments.push_back(x: arg); |
2311 | } |
2312 | |
2313 | ~CommandObjectBreakpointWrite() override = default; |
2314 | |
2315 | void |
2316 | HandleArgumentCompletion(CompletionRequest &request, |
2317 | OptionElementVector &opt_element_vector) override { |
2318 | lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( |
2319 | interpreter&: GetCommandInterpreter(), completion_mask: lldb::eBreakpointCompletion, request, searcher: nullptr); |
2320 | } |
2321 | |
2322 | Options *GetOptions() override { return &m_options; } |
2323 | |
2324 | class CommandOptions : public Options { |
2325 | public: |
2326 | CommandOptions() = default; |
2327 | |
2328 | ~CommandOptions() override = default; |
2329 | |
2330 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
2331 | ExecutionContext *execution_context) override { |
2332 | Status error; |
2333 | const int short_option = m_getopt_table[option_idx].val; |
2334 | |
2335 | switch (short_option) { |
2336 | case 'f': |
2337 | m_filename.assign(str: std::string(option_arg)); |
2338 | break; |
2339 | case 'a': |
2340 | m_append = true; |
2341 | break; |
2342 | default: |
2343 | llvm_unreachable("Unimplemented option" ); |
2344 | } |
2345 | |
2346 | return error; |
2347 | } |
2348 | |
2349 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
2350 | m_filename.clear(); |
2351 | m_append = false; |
2352 | } |
2353 | |
2354 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
2355 | return llvm::ArrayRef(g_breakpoint_write_options); |
2356 | } |
2357 | |
2358 | // Instance variables to hold the values for command options. |
2359 | |
2360 | std::string m_filename; |
2361 | bool m_append = false; |
2362 | }; |
2363 | |
2364 | protected: |
2365 | void DoExecute(Args &command, CommandReturnObject &result) override { |
2366 | Target &target = GetSelectedOrDummyTarget(); |
2367 | |
2368 | std::unique_lock<std::recursive_mutex> lock; |
2369 | target.GetBreakpointList().GetListMutex(lock); |
2370 | |
2371 | BreakpointIDList valid_bp_ids; |
2372 | if (!command.empty()) { |
2373 | CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( |
2374 | args&: command, target: &target, result, valid_ids: &valid_bp_ids, |
2375 | purpose: BreakpointName::Permissions::PermissionKinds::listPerm); |
2376 | |
2377 | if (!result.Succeeded()) { |
2378 | result.SetStatus(eReturnStatusFailed); |
2379 | return; |
2380 | } |
2381 | } |
2382 | FileSpec file_spec(m_options.m_filename); |
2383 | FileSystem::Instance().Resolve(file_spec); |
2384 | Status error = target.SerializeBreakpointsToFile(file: file_spec, bp_ids: valid_bp_ids, |
2385 | append: m_options.m_append); |
2386 | if (!error.Success()) { |
2387 | result.AppendErrorWithFormat(format: "error serializing breakpoints: %s." , |
2388 | error.AsCString()); |
2389 | } |
2390 | } |
2391 | |
2392 | private: |
2393 | CommandOptions m_options; |
2394 | }; |
2395 | |
2396 | // CommandObjectMultiwordBreakpoint |
2397 | #pragma mark MultiwordBreakpoint |
2398 | |
2399 | CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint( |
2400 | CommandInterpreter &interpreter) |
2401 | : CommandObjectMultiword( |
2402 | interpreter, "breakpoint" , |
2403 | "Commands for operating on breakpoints (see 'help b' for shorthand.)" , |
2404 | "breakpoint <subcommand> [<command-options>]" ) { |
2405 | CommandObjectSP list_command_object( |
2406 | new CommandObjectBreakpointList(interpreter)); |
2407 | CommandObjectSP enable_command_object( |
2408 | new CommandObjectBreakpointEnable(interpreter)); |
2409 | CommandObjectSP disable_command_object( |
2410 | new CommandObjectBreakpointDisable(interpreter)); |
2411 | CommandObjectSP clear_command_object( |
2412 | new CommandObjectBreakpointClear(interpreter)); |
2413 | CommandObjectSP delete_command_object( |
2414 | new CommandObjectBreakpointDelete(interpreter)); |
2415 | CommandObjectSP set_command_object( |
2416 | new CommandObjectBreakpointSet(interpreter)); |
2417 | CommandObjectSP command_command_object( |
2418 | new CommandObjectBreakpointCommand(interpreter)); |
2419 | CommandObjectSP modify_command_object( |
2420 | new CommandObjectBreakpointModify(interpreter)); |
2421 | CommandObjectSP name_command_object( |
2422 | new CommandObjectBreakpointName(interpreter)); |
2423 | CommandObjectSP write_command_object( |
2424 | new CommandObjectBreakpointWrite(interpreter)); |
2425 | CommandObjectSP read_command_object( |
2426 | new CommandObjectBreakpointRead(interpreter)); |
2427 | |
2428 | list_command_object->SetCommandName("breakpoint list" ); |
2429 | enable_command_object->SetCommandName("breakpoint enable" ); |
2430 | disable_command_object->SetCommandName("breakpoint disable" ); |
2431 | clear_command_object->SetCommandName("breakpoint clear" ); |
2432 | delete_command_object->SetCommandName("breakpoint delete" ); |
2433 | set_command_object->SetCommandName("breakpoint set" ); |
2434 | command_command_object->SetCommandName("breakpoint command" ); |
2435 | modify_command_object->SetCommandName("breakpoint modify" ); |
2436 | name_command_object->SetCommandName("breakpoint name" ); |
2437 | write_command_object->SetCommandName("breakpoint write" ); |
2438 | read_command_object->SetCommandName("breakpoint read" ); |
2439 | |
2440 | LoadSubCommand(cmd_name: "list" , command_obj: list_command_object); |
2441 | LoadSubCommand(cmd_name: "enable" , command_obj: enable_command_object); |
2442 | LoadSubCommand(cmd_name: "disable" , command_obj: disable_command_object); |
2443 | LoadSubCommand(cmd_name: "clear" , command_obj: clear_command_object); |
2444 | LoadSubCommand(cmd_name: "delete" , command_obj: delete_command_object); |
2445 | LoadSubCommand(cmd_name: "set" , command_obj: set_command_object); |
2446 | LoadSubCommand(cmd_name: "command" , command_obj: command_command_object); |
2447 | LoadSubCommand(cmd_name: "modify" , command_obj: modify_command_object); |
2448 | LoadSubCommand(cmd_name: "name" , command_obj: name_command_object); |
2449 | LoadSubCommand(cmd_name: "write" , command_obj: write_command_object); |
2450 | LoadSubCommand(cmd_name: "read" , command_obj: read_command_object); |
2451 | } |
2452 | |
2453 | CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint() = default; |
2454 | |
2455 | void CommandObjectMultiwordBreakpoint::VerifyIDs( |
2456 | Args &args, Target *target, bool allow_locations, |
2457 | CommandReturnObject &result, BreakpointIDList *valid_ids, |
2458 | BreakpointName::Permissions ::PermissionKinds purpose) { |
2459 | // args can be strings representing 1). integers (for breakpoint ids) |
2460 | // 2). the full breakpoint & location |
2461 | // canonical representation |
2462 | // 3). the word "to" or a hyphen, |
2463 | // representing a range (in which case there |
2464 | // had *better* be an entry both before & |
2465 | // after of one of the first two types. |
2466 | // 4). A breakpoint name |
2467 | // If args is empty, we will use the last created breakpoint (if there is |
2468 | // one.) |
2469 | |
2470 | Args temp_args; |
2471 | |
2472 | if (args.empty()) { |
2473 | if (target->GetLastCreatedBreakpoint()) { |
2474 | valid_ids->AddBreakpointID(bp_id: BreakpointID( |
2475 | target->GetLastCreatedBreakpoint()->GetID(), LLDB_INVALID_BREAK_ID)); |
2476 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
2477 | } else { |
2478 | result.AppendError( |
2479 | in_string: "No breakpoint specified and no last created breakpoint." ); |
2480 | } |
2481 | return; |
2482 | } |
2483 | |
2484 | // Create a new Args variable to use; copy any non-breakpoint-id-ranges stuff |
2485 | // directly from the old ARGS to the new TEMP_ARGS. Do not copy breakpoint |
2486 | // id range strings over; instead generate a list of strings for all the |
2487 | // breakpoint ids in the range, and shove all of those breakpoint id strings |
2488 | // into TEMP_ARGS. |
2489 | |
2490 | if (llvm::Error err = BreakpointIDList::FindAndReplaceIDRanges( |
2491 | old_args&: args, target, allow_locations, purpose, new_args&: temp_args)) { |
2492 | result.SetError(std::move(err)); |
2493 | return; |
2494 | } |
2495 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
2496 | |
2497 | // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual |
2498 | // BreakpointIDList: |
2499 | |
2500 | for (llvm::StringRef temp_arg : temp_args.GetArgumentArrayRef()) |
2501 | if (auto bp_id = BreakpointID::ParseCanonicalReference(input: temp_arg)) |
2502 | valid_ids->AddBreakpointID(bp_id: *bp_id); |
2503 | |
2504 | // At this point, all of the breakpoint ids that the user passed in have |
2505 | // been converted to breakpoint IDs and put into valid_ids. |
2506 | |
2507 | // Now that we've converted everything from args into a list of breakpoint |
2508 | // ids, go through our tentative list of breakpoint id's and verify that |
2509 | // they correspond to valid/currently set breakpoints. |
2510 | |
2511 | const size_t count = valid_ids->GetSize(); |
2512 | for (size_t i = 0; i < count; ++i) { |
2513 | BreakpointID cur_bp_id = valid_ids->GetBreakpointIDAtIndex(index: i); |
2514 | Breakpoint *breakpoint = |
2515 | target->GetBreakpointByID(break_id: cur_bp_id.GetBreakpointID()).get(); |
2516 | if (breakpoint != nullptr) { |
2517 | const size_t num_locations = breakpoint->GetNumLocations(); |
2518 | if (static_cast<size_t>(cur_bp_id.GetLocationID()) > num_locations) { |
2519 | StreamString id_str; |
2520 | BreakpointID::GetCanonicalReference( |
2521 | s: &id_str, break_id: cur_bp_id.GetBreakpointID(), break_loc_id: cur_bp_id.GetLocationID()); |
2522 | i = valid_ids->GetSize() + 1; |
2523 | result.AppendErrorWithFormat( |
2524 | format: "'%s' is not a currently valid breakpoint/location id.\n" , |
2525 | id_str.GetData()); |
2526 | } |
2527 | } else { |
2528 | i = valid_ids->GetSize() + 1; |
2529 | result.AppendErrorWithFormat( |
2530 | format: "'%d' is not a currently valid breakpoint ID.\n" , |
2531 | cur_bp_id.GetBreakpointID()); |
2532 | } |
2533 | } |
2534 | } |
2535 | |