| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/console/commands/verb_watch.h" |
| |
| #include "src/developer/debug/zxdb/client/breakpoint.h" |
| #include "src/developer/debug/zxdb/client/session.h" |
| #include "src/developer/debug/zxdb/console/command.h" |
| #include "src/developer/debug/zxdb/console/command_utils.h" |
| #include "src/developer/debug/zxdb/console/console.h" |
| #include "src/developer/debug/zxdb/console/output_buffer.h" |
| #include "src/developer/debug/zxdb/console/verbs.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| const char kWatchShortHelp[] = "watch: Create a hardware write breakpoint on a variable."; |
| const char kWatchHelp[] = |
| R"*(watch <expression> |
| |
| The "watch" command is an easier way to create a hardware data write |
| breakpoint. It will stop the program when the given value changes. |
| |
| The expression is evaluated at the time the command is executed, and the |
| address and size of the result are used to create a memory write breakpoint. |
| The expression is not evaluated again. It is an alias for: |
| |
| break --type=write "* &(<expression>)" |
| |
| For control over more breakpoint settings, use the "break" command or edit the |
| breakpoint settings after creation with "bp set". See "bp get" for the list of |
| attributes that can be changed this way. |
| |
| Gotchas |
| |
| The expression has a different meaning than the "break" command. The "break" |
| command will evaluate an expression and will try to interpret the result as an |
| address. In contrast, the "watch" command expects a value to watch and will |
| implicitly take its address as the thing to watch. |
| |
| This is not the same thing as the more complicated GDB "watch" command: the |
| expression will be evaluated only once at input time. |
| |
| Examples |
| |
| watch i |
| Breaks when the value of "i" changes. |
| |
| process 1 thread 2 watch i |
| Breaks only on the given thread when the value of "i" changes. |
| |
| watch foo[5]->bar |
| Evaluates the expression and sets a watchpoint at the address of "bar". |
| It will NOT break if "foo[5]" changes to point to a different "bar". |
| )*"; |
| |
| Err RunVerbWatch(ConsoleContext* context, const Command& cmd) { |
| fxl::RefPtr<EvalContext> eval_context = GetEvalContextForCommand(cmd); |
| |
| BreakpointSettings settings; |
| settings.type = BreakpointSettings::Type::kWrite; |
| settings.scope = ExecutionScopeForCommand(cmd); |
| |
| auto data_provider = eval_context->GetDataProvider(); |
| return EvalCommandExpression( |
| cmd, "watch", eval_context, true, true, [settings, eval_context](ErrOrValue result) mutable { |
| Console* console = Console::get(); |
| if (result.has_error()) { |
| console->Output(result.err()); |
| return; |
| } |
| |
| // Validate the expression produced something with an address. |
| const ExprValue& value = result.value(); |
| const ExprValueSource& source = value.source(); |
| if (source.type() != ExprValueSource::Type::kMemory) { |
| console->Output( |
| Err("This expression's value is stored in a %s location. Only values\n" |
| "stored in memory can be watched.\n" |
| "\n" |
| "The watch command will implicitly take the address of the result of the\n" |
| "expression. To set a breakpoint on a literal address you can do either:\n" |
| "\n" |
| " watch *(uint32_t*)0x12345678\n" |
| " break --type=write --size=4 0x12345678\n", |
| ExprValueSource::TypeToString(source.type()))); |
| return; |
| } |
| |
| if (source.is_bitfield()) { |
| console->Output(Err("This expression's result is a bitfield which can't be watched.")); |
| return; |
| } |
| |
| // Size errors are very common if the object is too big. Catch those early before trying |
| // to create a breakpoint. |
| uint32_t size = static_cast<uint32_t>(value.data().size()); |
| if (Err err = BreakpointSettings::ValidateSize(console->context().session()->arch(), |
| settings.type, size); |
| err.has_error()) { |
| // Rewrite the error to list the size that this produced. Since "watch" implicitly gets |
| // the size, the user may have no idea how much they requested. |
| console->Output("Attempting to watch a variable of size " + std::to_string(size) + |
| ".\n\n" + err.msg()); |
| return; |
| } |
| |
| // Fill in the breakpoint location and set it. |
| settings.locations.emplace_back(source.address()); |
| settings.byte_size = size; |
| |
| Breakpoint* breakpoint = console->context().session()->system().CreateNewBreakpoint(); |
| console->context().SetActiveBreakpoint(breakpoint); |
| breakpoint->SetSettings(settings); |
| |
| // Created message. |
| OutputBuffer out; |
| out.Append("Created "); |
| out.Append(FormatBreakpoint(&console->context(), breakpoint, true)); |
| console->Output(out); |
| }); |
| return Err(); |
| } |
| |
| } // namespace |
| |
| VerbRecord GetWatchVerbRecord() { |
| return VerbRecord(&RunVerbWatch, {"watch"}, kWatchShortHelp, kWatchHelp, |
| CommandGroup::kBreakpoint); |
| } |
| |
| } // namespace zxdb |