diff --git a/src/inspector/domain_target.pdl b/src/inspector/domain_target.pdl index 3195b3a441d8ab..890e79db8eb6b3 100644 --- a/src/inspector/domain_target.pdl +++ b/src/inspector/domain_target.pdl @@ -20,6 +20,9 @@ experimental domain Target SessionID sessionId TargetInfo targetInfo boolean waitingForDebugger + command getTargets + returns + array of TargetInfo targetInfos command setAutoAttach parameters boolean autoAttach diff --git a/src/inspector/node_inspector.gypi b/src/inspector/node_inspector.gypi index e3e2fc140db7c3..a493f59465d207 100644 --- a/src/inspector/node_inspector.gypi +++ b/src/inspector/node_inspector.gypi @@ -32,6 +32,8 @@ 'src/inspector/network_inspector.h', 'src/inspector/network_agent.cc', 'src/inspector/network_agent.h', + 'src/inspector/target_manager.cc', + 'src/inspector/target_manager.h', 'src/inspector/target_agent.cc', 'src/inspector/target_agent.h', 'src/inspector/worker_inspector.cc', diff --git a/src/inspector/target_agent.cc b/src/inspector/target_agent.cc index 2f841cb2a9c54c..04cbe0b83750ce 100644 --- a/src/inspector/target_agent.cc +++ b/src/inspector/target_agent.cc @@ -8,10 +8,6 @@ namespace node { namespace inspector { namespace protocol { -std::unordered_map> - TargetAgent::target_session_id_worker_map_ = - std::unordered_map>(); -int TargetAgent::next_session_id_ = 1; class WorkerTargetDelegate : public WorkerDelegate { public: explicit WorkerTargetDelegate(std::shared_ptr target_agent) @@ -32,13 +28,14 @@ std::unique_ptr createTargetInfo( const std::string_view target_id, const std::string_view type, const std::string_view title, - const std::string_view url) { + const std::string_view url, + bool attached = false) { return Target::TargetInfo::create() .setTargetId(std::string(target_id)) .setType(std::string(type)) .setTitle(std::string(title)) .setUrl(std::string(url)) - .setAttached(false) + .setAttached(attached) .setCanAccessOpener(true) .build(); } @@ -57,11 +54,11 @@ void TargetAgent::createAndAttachIfNecessary( targetCreated(target_id, type, title, url); bool attached = false; - if (auto_attach_) { + if (target_manager_->auto_attach()) { attached = true; attachedToTarget(worker, target_id, type, title, url); } - targets_.push_back({target_id, type, title, url, worker, attached}); + target_manager_->AddTarget(worker, target_id, type, title, url, attached); } void TargetAgent::listenWorker(std::weak_ptr worker_manager) { @@ -87,12 +84,26 @@ void TargetAgent::targetCreated(const std::string_view target_id, frontend_->targetCreated(createTargetInfo(target_id, type, title, url)); } +crdtp::DispatchResponse TargetAgent::getTargets( + std::unique_ptr>* out_targetInfos) { + auto target_infos = std::make_unique>(); + for (const auto& target : target_manager_->GetTargetsSnapshot()) { + target_infos->push_back(createTargetInfo(target.target_id, + target.type, + target.title, + target.url, + target.attached)); + } + *out_targetInfos = std::move(target_infos); + return DispatchResponse::Success(); +} + int TargetAgent::getNextSessionId() { - return next_session_id_++; + return target_manager_->NextSessionId(); } int TargetAgent::getNextTargetId() { - return next_target_id_++; + return target_manager_->NextTargetId(); } void TargetAgent::attachedToTarget(std::shared_ptr worker, @@ -101,7 +112,7 @@ void TargetAgent::attachedToTarget(std::shared_ptr worker, const std::string& title, const std::string& url) { int session_id = getNextSessionId(); - target_session_id_worker_map_[session_id] = worker; + TargetManager::RegisterSessionWorker(session_id, worker); worker->SetTargetSessionId(session_id); frontend_->attachedToTarget(std::to_string(session_id), createTargetInfo(target_id, type, title, url), @@ -112,11 +123,10 @@ void TargetAgent::attachedToTarget(std::shared_ptr worker, // all threads. Modify it to be managed per worker thread. crdtp::DispatchResponse TargetAgent::setAutoAttach( bool auto_attach, bool wait_for_debugger_on_start) { - auto_attach_ = auto_attach; - wait_for_debugger_on_start_ = wait_for_debugger_on_start; + target_manager_->SetAutoAttach(auto_attach, wait_for_debugger_on_start); if (auto_attach) { - for (auto& target : targets_) { + for (auto& target : target_manager_->targets()) { if (!target.attached) { target.attached = true; attachedToTarget(target.worker, diff --git a/src/inspector/target_agent.h b/src/inspector/target_agent.h index 36a95f80dcad75..f7904e986751f3 100644 --- a/src/inspector/target_agent.h +++ b/src/inspector/target_agent.h @@ -1,9 +1,9 @@ #ifndef SRC_INSPECTOR_TARGET_AGENT_H_ #define SRC_INSPECTOR_TARGET_AGENT_H_ +#include #include -#include -#include +#include "inspector/target_manager.h" #include "inspector/worker_inspector.h" #include "node/inspector/protocol/Target.h" @@ -14,15 +14,6 @@ class TargetInspector; namespace protocol { -struct TargetInfo { - std::string target_id; - std::string type; - std::string title; - std::string url; - std::shared_ptr worker; - bool attached; -}; - class TargetAgent : public Target::Backend, public std::enable_shared_from_this { public: @@ -32,15 +23,14 @@ class TargetAgent : public Target::Backend, const std::string& title, const std::string& url); + DispatchResponse getTargets( + std::unique_ptr>* out_targetInfos) + override; DispatchResponse setAutoAttach(bool auto_attach, bool wait_for_debugger_on_start) override; void listenWorker(std::weak_ptr worker_manager); void reset(); - static std::unordered_map> - target_session_id_worker_map_; - - bool isThisThread(MainThreadHandle* worker) { return worker == main_thread_; } private: int getNextTargetId(); @@ -57,15 +47,9 @@ class TargetAgent : public Target::Backend, std::shared_ptr frontend_; std::weak_ptr worker_manager_; - static int next_session_id_; - int next_target_id_ = 1; std::unique_ptr worker_event_handle_ = nullptr; - bool auto_attach_ = false; - // TODO(islandryu): If false, implement it so that each thread does not wait - // for the worker to execute. - bool wait_for_debugger_on_start_ = true; - std::vector targets_; - MainThreadHandle* main_thread_; + std::unique_ptr target_manager_ = + std::make_unique(); }; } // namespace protocol diff --git a/src/inspector/target_manager.cc b/src/inspector/target_manager.cc new file mode 100644 index 00000000000000..318aa71e4e7526 --- /dev/null +++ b/src/inspector/target_manager.cc @@ -0,0 +1,66 @@ +#include "inspector/target_manager.h" + +#include "inspector/main_thread_interface.h" + +namespace node { +namespace inspector { + +Mutex TargetManager::session_state_lock_; +std::unordered_map> + TargetManager::session_worker_map_; +int TargetManager::next_session_id_ = 1; + +int TargetManager::NextTargetId() { + return next_target_id_++; +} + +int TargetManager::NextSessionId() { + Mutex::ScopedLock scoped_lock(session_state_lock_); + return next_session_id_++; +} + +void TargetManager::SetAutoAttach(bool auto_attach, + bool wait_for_debugger_on_start) { + auto_attach_ = auto_attach; + wait_for_debugger_on_start_ = wait_for_debugger_on_start; +} + +void TargetManager::AddTarget(std::shared_ptr worker, + const std::string& target_id, + const std::string& type, + const std::string& title, + const std::string& url, + bool attached) { + targets_.push_back({target_id, type, title, url, worker, attached}); +} + +std::vector TargetManager::GetTargetsSnapshot() + const { + std::vector result; + result.reserve(targets_.size()); + for (const auto& target : targets_) { + if (target.worker && !target.worker->Expired()) { + result.push_back(target); + } + } + return result; +} + +void TargetManager::RegisterSessionWorker( + int session_id, std::shared_ptr worker) { + Mutex::ScopedLock scoped_lock(session_state_lock_); + session_worker_map_[session_id] = std::move(worker); +} + +std::shared_ptr TargetManager::WorkerForSession( + int session_id) { + Mutex::ScopedLock scoped_lock(session_state_lock_); + auto it = session_worker_map_.find(session_id); + if (it == session_worker_map_.end()) { + return nullptr; + } + return it->second; +} + +} // namespace inspector +} // namespace node diff --git a/src/inspector/target_manager.h b/src/inspector/target_manager.h new file mode 100644 index 00000000000000..3d7984b866665f --- /dev/null +++ b/src/inspector/target_manager.h @@ -0,0 +1,70 @@ +#ifndef SRC_INSPECTOR_TARGET_MANAGER_H_ +#define SRC_INSPECTOR_TARGET_MANAGER_H_ + +#include +#include +#include +#include + +#include "node_mutex.h" + +namespace node { +namespace inspector { + +class MainThreadHandle; + +class TargetManager { + public: + struct TargetInfo { + std::string target_id; + std::string type; + std::string title; + std::string url; + std::shared_ptr worker; + bool attached; + }; + + TargetManager() = default; + + int NextTargetId(); + int NextSessionId(); + + void SetAutoAttach(bool auto_attach, bool wait_for_debugger_on_start); + bool auto_attach() const { return auto_attach_; } + bool wait_for_debugger_on_start() const { + return wait_for_debugger_on_start_; + } + + void AddTarget(std::shared_ptr worker, + const std::string& target_id, + const std::string& type, + const std::string& title, + const std::string& url, + bool attached); + std::vector GetTargetsSnapshot() const; + std::vector& targets() { return targets_; } + const std::vector& targets() const { return targets_; } + + static void RegisterSessionWorker(int session_id, + std::shared_ptr worker); + static std::shared_ptr WorkerForSession(int session_id); + + private: + static Mutex session_state_lock_; + static std::unordered_map> + session_worker_map_; + static int next_session_id_; + + int next_target_id_ = 1; + bool auto_attach_ = false; + // TODO(islandryu): Honor this flag for worker targets. It is stored here + // so Target.setAutoAttach() state can be tracked, but worker startup pause + // behavior does not change based on it yet. + bool wait_for_debugger_on_start_ = true; + std::vector targets_; +}; + +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_TARGET_MANAGER_H_ diff --git a/src/inspector_io.cc b/src/inspector_io.cc index 361d19349261e5..e0b4b6c3746162 100644 --- a/src/inspector_io.cc +++ b/src/inspector_io.cc @@ -7,6 +7,7 @@ #include "inspector/node_json.h" #include "inspector/node_string.h" #include "inspector/target_agent.h" +#include "inspector/target_manager.h" #include "inspector_socket_server.h" #include "ncrypto.h" #include "node.h" @@ -380,8 +381,7 @@ void InspectorIoDelegate::MessageReceived(int session_id, ::isdigit); if (is_number) { int target_session_id = std::stoi(*target_session_id_str); - worker = protocol::TargetAgent::target_session_id_worker_map_ - [target_session_id]; + worker = TargetManager::WorkerForSession(target_session_id); if (worker) { merged_session_id += target_session_id << 16; } diff --git a/test/parallel/test-inspector-worker-target.js b/test/parallel/test-inspector-worker-target.js index e5d2a0c2039712..6bfe8bf9304d5c 100644 --- a/test/parallel/test-inspector-worker-target.js +++ b/test/parallel/test-inspector-worker-target.js @@ -3,6 +3,8 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); +const assert = require('assert'); + common.skipIfInspectorDisabled(); const { NodeInstance } = require('../common/inspector-helper.js'); @@ -21,6 +23,15 @@ async function setupInspector(session, sessionId = undefined) { }); } +async function assertTargetAttachedState(session, targetId, attached) { + const { targetInfos } = await session.send({ method: 'Target.getTargets' }); + const targetInfo = targetInfos.find((target) => { + return target.targetId === targetId; + }); + assert.notStrictEqual(targetInfo, undefined); + assert.strictEqual(targetInfo.attached, attached); +} + async function test(isSetAutoAttachBeforeExecution) { const child = new NodeInstance(['--inspect-brk=0', '--experimental-worker-inspection'], '', @@ -38,7 +49,10 @@ async function test(isSetAutoAttachBeforeExecution) { await session.send({ method: 'Debugger.resume' }); const sessionId = '1'; - await session.waitForNotification('Target.targetCreated'); + const targetCreated = await session.waitForNotification('Target.targetCreated'); + const targetId = targetCreated.params.targetInfo.targetId; + + await assertTargetAttachedState(session, targetId, isSetAutoAttachBeforeExecution); if (!isSetAutoAttachBeforeExecution) { await session.send({ method: 'Target.setAutoAttach', params: { autoAttach: true, waitForDebuggerOnStart: true } }); @@ -47,6 +61,7 @@ async function test(isSetAutoAttachBeforeExecution) { return notification.method === 'Target.attachedToTarget' && notification.params.sessionId === sessionId; }); + await assertTargetAttachedState(session, targetId, true); await setupInspector(session, sessionId); await session.waitForNotification('Debugger.paused'); await session.send({ method: 'Debugger.resume', sessionId });