| // 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 |