| // Copyright 2018 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/client/breakpoint.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include "src/developer/debug/zxdb/client/session.h" |
| #include "src/developer/debug/zxdb/client/setting_schema.h" |
| #include "src/developer/debug/zxdb/client/setting_schema_definition.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| namespace zxdb { |
| |
| const char* ClientSettings::Breakpoint::kLocation = "location"; |
| const char* ClientSettings::Breakpoint::kLocationDescription = |
| R"( The location (symbol, line number, address, or expression) where this |
| breakpoint will be set. See "help break" for documentation on how to specify.)"; |
| |
| const char* ClientSettings::Breakpoint::kScope = "scope"; |
| const char* ClientSettings::Breakpoint::kScopeDescription = |
| R"( What this breakpoint applies to. Examples: |
| |
| global: All processes (the default). |
| "pr 3": All threads in a process 3. |
| "pr 3 t 2": Only thread 2 of process 3.)"; |
| |
| const char* ClientSettings::Breakpoint::kEnabled = "enabled"; |
| const char* ClientSettings::Breakpoint::kEnabledDescription = |
| R"( Whether this breakpoint is enabled. Disabled breakpoints keep their settings |
| but are not installed and will not stop or increment their hit count.)"; |
| |
| const char* ClientSettings::Breakpoint::kOneShot = "one-shot"; |
| const char* ClientSettings::Breakpoint::kOneShotDescription = |
| R"( Whether this breakpoint is one-shot. One-shot breakpoints are automatically |
| deleted when hit.)"; |
| |
| const char* ClientSettings::Breakpoint::kType = "type"; |
| const char* ClientSettings::Breakpoint::kTypeDescription = |
| " Type of breakpoint. Possible values are:\n\n" BREAKPOINT_TYPE_HELP(" "); |
| |
| const char* ClientSettings::Breakpoint::kType_Software = "software"; |
| const char* ClientSettings::Breakpoint::kType_Hardware = "execute"; |
| const char* ClientSettings::Breakpoint::kType_ReadWrite = "read-write"; |
| const char* ClientSettings::Breakpoint::kType_Write = "write"; |
| |
| const char* ClientSettings::Breakpoint::kSize = "size"; |
| const char* ClientSettings::Breakpoint::kSizeDescription = |
| R"( Byte size for hardware breakpoints. |
| |
| Hardware "write" and "read-write" breakpoints can be set on a range of |
| addresses. The supported ranges are architecture-specific, but sizes of 1, 2, |
| 4 and 8 bytes should be supported. The address will need to be aligned |
| to an even multiple of its size.)"; |
| |
| const char* ClientSettings::Breakpoint::kStopMode = "stop"; |
| const char* ClientSettings::Breakpoint::kStopModeDescription = |
| R"( What to stop when this breakpoint is hit. Possible values are: |
| |
| none |
| Do not stop anything when this breakpoint is hit. The breakpoint will |
| still be installed and will still accumulate hit counts. |
| |
| thread |
| Stop only the thread that hit the breakpoint. Other threads in the same |
| process and other processes will be unaffected. |
| |
| process |
| Stop all threads in the process that hit the breakpoint. Other processes |
| being debugged will be unaffected. |
| |
| all |
| Stop all processes currently being debugged.)"; |
| const char* ClientSettings::Breakpoint::kStopMode_None = "none"; |
| const char* ClientSettings::Breakpoint::kStopMode_Thread = "thread"; |
| const char* ClientSettings::Breakpoint::kStopMode_Process = "process"; |
| const char* ClientSettings::Breakpoint::kStopMode_All = "all"; |
| |
| const char* ClientSettings::Breakpoint::kHitCount = "hit-count"; |
| const char* ClientSettings::Breakpoint::kHitCountDescription = |
| R"( Number of times the breakpoint gets triggered.)"; |
| const char* ClientSettings::Breakpoint::kHitMult = "hit-mult"; |
| const char* ClientSettings::Breakpoint::kHitMultDescription = |
| R"( The breakpoint will only stop the execution when the hit-count is a multiple |
| of the hit-mult)"; |
| |
| namespace { |
| |
| fxl::RefPtr<SettingSchema> CreateSchema() { |
| auto schema = fxl::MakeRefCounted<SettingSchema>(); |
| schema->AddInputLocations(ClientSettings::Breakpoint::kLocation, |
| ClientSettings::Breakpoint::kLocationDescription); |
| schema->AddExecutionScope(ClientSettings::Breakpoint::kScope, |
| ClientSettings::Breakpoint::kScopeDescription); |
| schema->AddBool(ClientSettings::Breakpoint::kEnabled, |
| ClientSettings::Breakpoint::kEnabledDescription, true); |
| schema->AddBool(ClientSettings::Breakpoint::kOneShot, |
| ClientSettings::Breakpoint::kOneShotDescription, false); |
| schema->AddString( |
| ClientSettings::Breakpoint::kType, ClientSettings::Breakpoint::kTypeDescription, |
| ClientSettings::Breakpoint::kType_Software, |
| {ClientSettings::Breakpoint::kType_Software, ClientSettings::Breakpoint::kType_Hardware, |
| ClientSettings::Breakpoint::kType_ReadWrite, ClientSettings::Breakpoint::kType_Write}); |
| schema->AddInt(ClientSettings::Breakpoint::kSize, ClientSettings::Breakpoint::kSizeDescription); |
| schema->AddString( |
| ClientSettings::Breakpoint::kStopMode, ClientSettings::Breakpoint::kStopModeDescription, |
| ClientSettings::Breakpoint::kStopMode_All, |
| {ClientSettings::Breakpoint::kStopMode_None, ClientSettings::Breakpoint::kStopMode_Thread, |
| ClientSettings::Breakpoint::kStopMode_Process, ClientSettings::Breakpoint::kStopMode_All}); |
| schema->AddInt(ClientSettings::Breakpoint::kHitCount, |
| ClientSettings::Breakpoint::kHitCountDescription); |
| schema->AddInt(ClientSettings::Breakpoint::kHitMult, |
| ClientSettings::Breakpoint::kHitMultDescription, 1); |
| return schema; |
| } |
| |
| } // namespace |
| |
| Breakpoint::Settings::Settings(Breakpoint* bp) : SettingStore(Breakpoint::GetSchema()), bp_(bp) {} |
| |
| SettingValue Breakpoint::Settings::GetStorageValue(const std::string& key) const { |
| BreakpointSettings settings = bp_->GetSettings(); |
| if (key == ClientSettings::Breakpoint::kLocation) { |
| return SettingValue(settings.locations); |
| } else if (key == ClientSettings::Breakpoint::kScope) { |
| return SettingValue(settings.scope); |
| } else if (key == ClientSettings::Breakpoint::kStopMode) { |
| return SettingValue(BreakpointSettings::StopModeToString(settings.stop_mode)); |
| } else if (key == ClientSettings::Breakpoint::kEnabled) { |
| return SettingValue(settings.enabled); |
| } else if (key == ClientSettings::Breakpoint::kOneShot) { |
| return SettingValue(settings.one_shot); |
| } else if (key == ClientSettings::Breakpoint::kType) { |
| return SettingValue(BreakpointSettings::TypeToString(settings.type)); |
| } else if (key == ClientSettings::Breakpoint::kSize) { |
| return SettingValue(static_cast<int>(settings.byte_size)); |
| } else if (key == ClientSettings::Breakpoint::kHitCount) { |
| return SettingValue(static_cast<int>(bp_->GetStats().hit_count)); |
| } else if (key == ClientSettings::Breakpoint::kHitMult) { |
| return SettingValue(settings.hit_mult); |
| } |
| FX_NOTREACHED(); |
| return SettingValue(); |
| } |
| |
| Err Breakpoint::Settings::SetStorageValue(const std::string& key, SettingValue value) { |
| BreakpointSettings settings = bp_->GetSettings(); |
| |
| if (key == ClientSettings::Breakpoint::kLocation) { |
| settings.locations = value.get_input_locations(); |
| } else if (key == ClientSettings::Breakpoint::kScope) { |
| settings.scope = value.get_execution_scope(); |
| } else if (key == ClientSettings::Breakpoint::kStopMode) { |
| std::optional<BreakpointSettings::StopMode> stop_mode = |
| BreakpointSettings::StringToStopMode(value.get_string()); |
| FX_DCHECK(stop_mode); // Schema should have validated the input. |
| settings.stop_mode = *stop_mode; |
| } else if (key == ClientSettings::Breakpoint::kEnabled) { |
| settings.enabled = value.get_bool(); |
| } else if (key == ClientSettings::Breakpoint::kOneShot) { |
| settings.one_shot = value.get_bool(); |
| } else if (key == ClientSettings::Breakpoint::kType) { |
| std::optional<BreakpointSettings::Type> type = |
| BreakpointSettings::StringToType(value.get_string()); |
| FX_DCHECK(type); // Schema should have validated the input. |
| settings.type = *type; |
| } else if (key == ClientSettings::Breakpoint::kSize) { |
| // Validate the size. Providing the error here and failing to set is more clear to the user than |
| // getting a set error back asynchronously from the backend. |
| if (Err err = BreakpointSettings::ValidateSize(bp_->session()->arch(), settings.type, |
| value.get_int()); |
| err.has_error()) |
| return err; |
| |
| settings.byte_size = value.get_int(); |
| } else if (key == ClientSettings::Breakpoint::kHitMult) { |
| int hit_mult = value.get_int(); |
| if (hit_mult <= 0) |
| return Err("hit-mult must be positive."); |
| |
| settings.hit_mult = hit_mult; |
| } else { |
| return Err(fxl::StringPrintf("Setting \"%s\" is currently not supported.", key.c_str())); |
| } |
| |
| bp_->SetSettings(settings); |
| return Err(); |
| } |
| |
| Breakpoint::Breakpoint(Session* session) |
| : ClientObject(session), settings_(this), weak_factory_(this) {} |
| Breakpoint::~Breakpoint() {} |
| |
| fxl::WeakPtr<Breakpoint> Breakpoint::GetWeakPtr() { return weak_factory_.GetWeakPtr(); } |
| |
| // static |
| fxl::RefPtr<SettingSchema> Breakpoint::GetSchema() { |
| static fxl::RefPtr<SettingSchema> schema = CreateSchema(); |
| return schema; |
| } |
| |
| } // namespace zxdb |