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