| // Copyright 2023 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 "component_watcher.h" |
| |
| #include <lib/component/incoming/cpp/protocol.h> |
| #include <lib/syslog/cpp/macros.h> |
| |
| zx::result<> profiler::ComponentWatcher::Watch() { |
| auto client_end = component::Connect<fuchsia_component::EventStream>(); |
| if (client_end.is_error()) { |
| FX_LOGS(ERROR) << "Failed to connect to event stream"; |
| return client_end.take_error(); |
| } |
| fidl::SyncClient sync_stream_client{std::move(*client_end)}; |
| fidl::Result<fuchsia_component::EventStream::WaitForReady> ready_result = |
| sync_stream_client->WaitForReady(); |
| if (ready_result.is_error()) { |
| zx_status_t err = ready_result.error_value().status(); |
| FX_PLOGS(ERROR, err) << "Failed to wait for ready"; |
| return zx::error(err); |
| } |
| stream_client_ = fidl::Client(sync_stream_client.TakeClientEnd(), dispatcher_); |
| stream_client_->GetNext().Then( |
| [this](fidl::Result<fuchsia_component::EventStream::GetNext> &res) { |
| this->HandleEvent(res); |
| }); |
| return zx::ok(); |
| } |
| |
| zx::result<> profiler::ComponentWatcher::Reset() { |
| stream_client_ = fidl::Client<fuchsia_component::EventStream>(); |
| return zx::ok(); |
| } |
| |
| zx::result<> profiler::ComponentWatcher::WatchForMoniker(std::string moniker, |
| ComponentEventHandler handler) { |
| // The events api strips leading "./"s from monikers |
| std::string normalized = moniker; |
| if (normalized[0] == '.' && normalized[1] == '/') { |
| normalized = normalized.substr(2); |
| } |
| |
| if (moniker_watchers_.find(normalized) != moniker_watchers_.end()) { |
| return zx::error(ZX_ERR_ALREADY_EXISTS); |
| } |
| moniker_watchers_[std::move(normalized)] = std::move(handler); |
| return zx::ok(); |
| } |
| |
| zx::result<> profiler::ComponentWatcher::WatchForUrl(std::string url, |
| ComponentEventHandler handler) { |
| if (url_watchers_.find(url) != url_watchers_.end()) { |
| return zx::error(ZX_ERR_ALREADY_EXISTS); |
| } |
| url_watchers_[std::move(url)] = std::move(handler); |
| return zx::ok(); |
| } |
| |
| void profiler::ComponentWatcher::HandleEvent( |
| fidl::Result<fuchsia_component::EventStream::GetNext> &res) { |
| if (res.is_error()) { |
| FX_LOGS(ERROR) << "GetEventFailed"; |
| return; |
| } |
| std::vector<fuchsia_component::Event> events = std::move(res->events()); |
| if (events.empty()) { |
| return; |
| } |
| |
| for (fuchsia_component::Event &event : events) { |
| if (!event.header().has_value()) { |
| continue; |
| } |
| if (!event.header()->moniker() || !event.header()->component_url()) { |
| FX_LOGS(WARNING) << "Event didn't have a moniker or url?"; |
| continue; |
| } |
| std::string component_url = *event.header()->component_url(); |
| std::string moniker = *event.header()->moniker(); |
| if (event.header()->event_type()) { |
| fuchsia_component::EventType event_type = event.header()->event_type().value(); |
| switch (event_type) { |
| case fuchsia_component::EventType::kDebugStarted: { |
| fidl::SyncClient<fuchsia_io::Directory> dir_client{ |
| std::move(*event.payload()->debug_started()->runtime_dir())}; |
| auto moniker_handler = moniker_watchers_.find(moniker); |
| if (moniker_handler != moniker_watchers_.end()) { |
| moniker_handler->second(moniker, component_url); |
| moniker_watchers_.erase(moniker_handler); |
| } |
| if (auto url_handler = url_watchers_.find(component_url); |
| url_handler != url_watchers_.end()) { |
| FX_LOGS(INFO) << "Got debug started for " << moniker << " (" << component_url << ")"; |
| url_handler->second(moniker, component_url); |
| url_watchers_.erase(url_handler); |
| } |
| break; |
| } |
| default: |
| // We only have subscribed to debug started events |
| break; |
| } |
| } |
| } |
| |
| stream_client_->GetNext().Then( |
| [this](fidl::Result<fuchsia_component::EventStream::GetNext> &res) { |
| this->HandleEvent(res); |
| }); |
| } |