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