blob: f9174280e139b362d2e246341df159ee5a4d2073 [file]
// Copyright 2026 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/tracepoint.h"
#include <utility>
#include "src/developer/debug/zxdb/client/breakpoint.h"
#include "src/developer/debug/zxdb/client/breakpoint_settings.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/system.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/expr/expr_parser.h"
#include "src/developer/debug/zxdb/symbols/identifier.h"
#include "src/developer/debug/zxdb/symbols/input_location.h"
namespace zxdb {
Tracepoint::Tracepoint(Session* session, uint32_t id)
: ClientObject(session), id_(id), weak_factory_(this) {
EnsureBreakpoint();
UpdateBreakpoint(SetCallback());
}
Tracepoint::~Tracepoint() {
if (breakpoint_ && session()) {
session()->system().DeleteBreakpoint(breakpoint_.get());
breakpoint_.reset();
}
}
fxl::WeakPtr<Tracepoint> Tracepoint::GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
Breakpoint* Tracepoint::EnsureBreakpoint() {
if (!breakpoint_ && session()) {
Breakpoint* bp = session()->system().CreateNewInternalBreakpoint();
breakpoint_ = bp->GetWeakPtr();
}
return breakpoint_.get();
}
void Tracepoint::SetSpec(const TracepointSpec& spec, SetCallback cb) {
if (Err err = spec.Validate(); err.has_error()) {
spec_ = spec;
if (breakpoint_) {
BreakpointSettings settings = breakpoint_->GetSettings();
settings.enabled = false;
settings.locations.clear();
breakpoint_->SetSettings(settings);
}
if (cb)
cb(err);
return;
}
spec_ = spec;
UpdateBreakpoint(std::move(cb));
}
void Tracepoint::SetScope(const ExecutionScope& scope) {
if (scope_.type() == scope.type() && scope_.target() == scope.target() &&
scope_.thread() == scope.thread()) {
return;
}
scope_ = scope;
if (spec_.is_valid()) {
UpdateBreakpoint(SetCallback());
} else if (breakpoint_) {
BreakpointSettings settings = breakpoint_->GetSettings();
settings.scope = scope_;
breakpoint_->SetSettings(settings);
}
}
bool Tracepoint::enabled() const {
return enabled_ && (!breakpoint_ || breakpoint_->GetSettings().enabled);
}
void Tracepoint::SetEnabled(bool enabled) {
if (enabled) {
auto_disabled_ = false;
hit_count_ = 0;
if (!breakpoint_) {
// The internal breakpoint can be gone, for example after System deletes it. A new one starts
// with default settings, so apply the full spec, stop mode, and scope to it.
enabled_ = true;
UpdateBreakpoint(SetCallback());
return;
}
}
if (enabled_ == enabled && (!breakpoint_ || breakpoint_->GetSettings().enabled == enabled))
return;
enabled_ = enabled;
if (breakpoint_) {
BreakpointSettings settings = breakpoint_->GetSettings();
settings.enabled = enabled_;
breakpoint_->SetSettings(settings);
}
}
bool Tracepoint::verified() const {
if (!breakpoint_ || !enabled())
return false;
return !breakpoint_->GetLocations().empty();
}
void Tracepoint::UpdateBreakpoint(SetCallback cb) {
if (!EnsureBreakpoint()) {
if (cb)
cb(Err("No underlying breakpoint attached to tracepoint."));
return;
}
BreakpointSettings settings;
settings.enabled = enabled_;
settings.stop_mode = BreakpointSettings::StopMode::kNone;
settings.scope = scope_;
if (spec_.function.has_value() && !spec_.function->empty()) {
Identifier ident;
if (Err err = ExprParser::ParseIdentifier(*spec_.function, &ident); err.has_error()) {
ident = Identifier(*spec_.function);
}
settings.locations.emplace_back(std::move(ident));
} else if (spec_.file_line.has_value() && spec_.file_line->is_valid()) {
settings.locations.emplace_back(*spec_.file_line);
} else if (spec_.address.has_value()) {
settings.locations.emplace_back(*spec_.address);
} else if (spec_.watch.has_value()) {
settings.type = spec_.watch->access_type;
settings.byte_size = spec_.watch->byte_size;
if (spec_.watch->address.has_value()) {
settings.locations.emplace_back(*spec_.watch->address);
}
}
breakpoint_->SetSettings(settings, std::move(cb));
}
} // namespace zxdb