精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深入理解 V8 Inspector中幾個關鍵的角色

開發 前端
本文介紹一下 V8 關于 Inspector 的實現,不過不會涉及到具體命令的實現,V8 Inspector 的命令非常多,了解了處理流程后,如果對某個命令感興趣的話,可以單獨去分析。

[[430568]]

前言:本文介紹一下 V8 關于 Inspector 的實現,不過不會涉及到具體命令的實現,V8 Inspector 的命令非常多,了解了處理流程后,如果對某個命令感興趣的話,可以單獨去分析。

首先來看一下 V8 Inspector 中幾個關鍵的角色。

 V8InspectorSession

  1. class V8_EXPORT V8InspectorSession { 
  2.  public
  3.   // 收到對端端消息,調用這個方法判斷是否可以分發 
  4.   static bool canDispatchMethod(StringView method); 
  5.   // 收到對端端消息,調用這個方法判斷分發 
  6.   virtual void dispatchProtocolMessage(StringView message) = 0; 
  7.  
  8. }; 

V8InspectorSession 是一個基類,本身實現了 canDispatchMethod 方法,由子類實現 dispatchProtocolMessage 方法。看一下 canDispatchMethod 的實現。

  1. bool V8InspectorSession::canDispatchMethod(StringView method) { 
  2.   return stringViewStartsWith(method, 
  3.                               protocol::Runtime::Metainfo::commandPrefix) || 
  4.          stringViewStartsWith(method, 
  5.                               protocol::Debugger::Metainfo::commandPrefix) || 
  6.          stringViewStartsWith(method, 
  7.                               protocol::Profiler::Metainfo::commandPrefix) || 
  8.          stringViewStartsWith( 
  9.              method, protocol::HeapProfiler::Metainfo::commandPrefix) || 
  10.          stringViewStartsWith(method, 
  11.                               protocol::Console::Metainfo::commandPrefix) || 
  12.          stringViewStartsWith(method, 
  13.                               protocol::Schema::Metainfo::commandPrefix); 
  14.  

canDispatchMethod 決定了 V8 目前支持哪些命令。接著看一下 V8InspectorSession 子類的實現。

  1. class V8InspectorSessionImpl : public V8InspectorSession, 
  2.                                public protocol::FrontendChannel { 
  3.  public
  4.   // 靜態方法,用于創建 V8InspectorSessionImpl 
  5.   static std::unique_ptr<V8InspectorSessionImpl> create(V8InspectorImpl*, 
  6.                                                         int contextGroupId, 
  7.                                                         int sessionId, 
  8.                                                         V8Inspector::Channel*, 
  9.                                                         StringView state); 
  10.   // 實現命令的分發 
  11.   void dispatchProtocolMessage(StringView message) override; 
  12.   // 支持哪些命令 
  13.   std::vector<std::unique_ptr<protocol::Schema::API::Domain>> supportedDomains() override; 
  14.  
  15.  private: 
  16.   // 發送消息給對端 
  17.   void SendProtocolResponse(int callId, std::unique_ptr<protocol::Serializable> message) override; 
  18.   void SendProtocolNotification(std::unique_ptr<protocol::Serializable> message) override; 
  19.  
  20.   // 會話 id 
  21.   int m_sessionId; 
  22.   // 關聯的 V8Inspector 對象 
  23.   V8InspectorImpl* m_inspector; 
  24.   // 關聯的 channel,channel 表示會話的兩端 
  25.   V8Inspector::Channel* m_channel; 
  26.   // 處理命令分發對象 
  27.   protocol::UberDispatcher m_dispatcher; 
  28.   // 處理某種命令的代理對象 
  29.   std::unique_ptr<V8RuntimeAgentImpl> m_runtimeAgent; 
  30.   std::unique_ptr<V8DebuggerAgentImpl> m_debuggerAgent; 
  31.   std::unique_ptr<V8HeapProfilerAgentImpl> m_heapProfilerAgent; 
  32.   std::unique_ptr<V8ProfilerAgentImpl> m_profilerAgent; 
  33.   std::unique_ptr<V8ConsoleAgentImpl> m_consoleAgent; 
  34.   std::unique_ptr<V8SchemaAgentImpl> m_schemaAgent; 
  35.  
  36. }; 

下面看一下核心方法的具體實現。

創建 V8InspectorSessionImpl

  1. V8InspectorSessionImpl::V8InspectorSessionImpl(V8InspectorImpl* inspector, 
  2.                                                int contextGroupId, 
  3.                                                int sessionId, 
  4.                                                V8Inspector::Channel* channel, 
  5.                                                StringView savedState) 
  6.     : m_contextGroupId(contextGroupId), 
  7.       m_sessionId(sessionId), 
  8.       m_inspector(inspector), 
  9.       m_channel(channel), 
  10.       m_customObjectFormatterEnabled(false), 
  11.       m_dispatcher(this), 
  12.       m_state(ParseState(savedState)), 
  13.       m_runtimeAgent(nullptr), 
  14.       m_debuggerAgent(nullptr), 
  15.       m_heapProfilerAgent(nullptr), 
  16.       m_profilerAgent(nullptr), 
  17.       m_consoleAgent(nullptr), 
  18.       m_schemaAgent(nullptr) { 
  19.  
  20.   m_runtimeAgent.reset(new V8RuntimeAgentImpl(this, this, agentState(protocol::Runtime::Metainfo::domainName))); 
  21.   protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get()); 
  22.  
  23.   m_debuggerAgent.reset(new V8DebuggerAgentImpl(this, this, agentState(protocol::Debugger::Metainfo::domainName))); 
  24.   protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get()); 
  25.  
  26.   m_profilerAgent.reset(new V8ProfilerAgentImpl(this, this, agentState(protocol::Profiler::Metainfo::domainName))); 
  27.   protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get()); 
  28.  
  29.   m_heapProfilerAgent.reset(new V8HeapProfilerAgentImpl(this, this, agentState(protocol::HeapProfiler::Metainfo::domainName))); 
  30.   protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher,m_heapProfilerAgent.get()); 
  31.  
  32.   m_consoleAgent.reset(new V8ConsoleAgentImpl(this, this, agentState(protocol::Console::Metainfo::domainName))); 
  33.   protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get()); 
  34.  
  35.   m_schemaAgent.reset(new V8SchemaAgentImpl(this, this, agentState(protocol::Schema::Metainfo::domainName))); 
  36.   protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get()); 
  37.  

V8 支持很多種命令,在創建 V8InspectorSessionImpl 對象時,會注冊所有命令和處理該命令的處理器。我們一會單獨分析。

 接收請求

  1. void V8InspectorSessionImpl::dispatchProtocolMessage(StringView message) { 
  2.   using v8_crdtp::span; 
  3.   using v8_crdtp::SpanFrom; 
  4.   span<uint8_t> cbor; 
  5.   std::vector<uint8_t> converted_cbor; 
  6.   if (IsCBORMessage(message)) { 
  7.     use_binary_protocol_ = true
  8.     m_state->setBoolean("use_binary_protocol"true); 
  9.     cbor = span<uint8_t>(message.characters8(), message.length()); 
  10.   } else { 
  11.     auto status = ConvertToCBOR(message, &converted_cbor); 
  12.     cbor = SpanFrom(converted_cbor); 
  13.   } 
  14.   v8_crdtp::Dispatchable dispatchable(cbor); 
  15.   // 消息分發 
  16.   m_dispatcher.Dispatch(dispatchable).Run(); 
  17.  

接收消息后,在內部通過 m_dispatcher.Dispatch 進行分發,這就好比我們在 Node.js 里收到請求后,根據路由分發一樣。具體的分發邏輯一會單獨分析。3. 響應請求

  1. void V8InspectorSessionImpl::SendProtocolResponse( 
  2.     int callId, std::unique_ptr<protocol::Serializable> message) { 
  3.   m_channel->sendResponse(callId, serializeForFrontend(std::move(message))); 
  4.  

具體的處理邏輯由 channel 實現,channel 由 V8 的使用者實現,比如 Node.js。

數據推送

  1. void V8InspectorSessionImpl::SendProtocolNotification( 
  2.     std::unique_ptr<protocol::Serializable> message) { 
  3.   m_channel->sendNotification(serializeForFrontend(std::move(message))); 
  4.  

除了一個請求對應一個響應,V8 Inspector 還需要主動推送的能力,具體處理邏輯也是由 channel 實現。從上面點分析可以看到 V8InspectorSessionImpl 的概念相當于一個服務器,在啟動的時候注冊了一系列路由,當建立一個連接時,就會創建一個 Channel 對象表示。調用方可以通過 Channel 完成請求和接收響應。結構如下圖所示。

 V8Inspector

  1. class V8_EXPORT V8Inspector { 
  2.  public
  3.   // 靜態方法,用于創建 V8Inspector 
  4.   static std::unique_ptr<V8Inspector> create(v8::Isolate*, V8InspectorClient*); 
  5.   // 用于創建一個 V8InspectorSession 
  6.   virtual std::unique_ptr<V8InspectorSession> connect(int contextGroupId, 
  7.                                                       Channel*, 
  8.                                                       StringView state) = 0; 
  9.  
  10. }; 

V8Inspector 是一個通信的總管,他不負責具體的通信,他只是負責管理通信者,Channel 才是負責通信的角色。下面看一下 V8Inspector 子類的實現 。

  1. class V8InspectorImpl : public V8Inspector { 
  2.  public
  3.   V8InspectorImpl(v8::Isolate*, V8InspectorClient*); 
  4.   // 創建一個會話 
  5.   std::unique_ptr<V8InspectorSession> connect(int contextGroupId, 
  6.                                               V8Inspector::Channel*, 
  7.                                               StringView state) override; 
  8.  
  9.  private: 
  10.   v8::Isolate* m_isolate; 
  11.   // 關聯的 V8InspectorClient 對象,V8InspectorClient 封裝了 V8Inspector,由調用方實現 
  12.   V8InspectorClient* m_client; 
  13.   // 保存所有的會話 
  14.   std::unordered_map<int, std::map<int, V8InspectorSessionImpl*>> m_sessions; 
  15.  
  16. }; 

V8InspectorImpl 提供了創建會話的方法并保存了所有創建的會話,看一下創建會話的邏輯。

  1. std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(int contextGroupId, V8Inspector::Channel* channel, StringView state) { 
  2.   int sessionId = ++m_lastSessionId; 
  3.   std::unique_ptr<V8InspectorSessionImpl> session = V8InspectorSessionImpl::create(this, contextGroupId, sessionId, channel, state); 
  4.   m_sessions[contextGroupId][sessionId] = session.get(); 
  5.   return std::move(session); 
  6.  

connect 是創建了一個 V8InspectorSessionImpl 對象,并通過 id 保存到 map中。結構圖如下。

UberDispatcher

UberDispatcher 是一個命令分發器。

  1. class UberDispatcher { 
  2.  public
  3.   // 表示分發結果的對象 
  4.   class DispatchResult {}; 
  5.   // 分發處理函數 
  6.   DispatchResult Dispatch(const Dispatchable& dispatchable) const; 
  7.   // 注冊命令和處理器  
  8.   void WireBackend(span<uint8_t> domain, 
  9.                    const std::vector<std::pair<span<uint8_t>, span<uint8_t>>>&, 
  10.                    std::unique_ptr<DomainDispatcher> dispatcher); 
  11.  
  12.  private: 
  13.   // 查找命令對應的處理器,Dispatch 中使用 
  14.   DomainDispatcher* findDispatcher(span<uint8_t> method); 
  15.   // 關聯的 channel 
  16.   FrontendChannel* const frontend_channel_; 
  17.   std::vector<std::pair<span<uint8_t>, span<uint8_t>>> redirects_; 
  18.   // 命令處理器隊列 
  19.   std::vector<std::pair<span<uint8_t>, std::unique_ptr<DomainDispatcher>>> 
  20.       dispatchers_; 
  21.  
  22. }; 

下面看一下注冊和分發的實現。

注冊

  1. void UberDispatcher::WireBackend(span<uint8_t> domain, std::unique_ptr<DomainDispatcher> dispatcher) { 
  2.   dispatchers_.insert(dispatchers_.end(), std::make_pair(domain, std::move(dispatcher)));); 
  3.  

WireBackend 就是在隊列里插入一個新的 domain 和 處理器組合。

分發命令

  1. UberDispatcher::DispatchResult UberDispatcher::Dispatch( 
  2.     const Dispatchable& dispatchable) const { 
  3.   span<uint8_t> method = FindByFirst(redirects_, dispatchable.Method(), 
  4.                                      /*default_value=*/dispatchable.Method()); 
  5.   // 找到 . 的偏移,命令格式是 A.B                                    
  6.   size_t dot_idx = DotIdx(method); 
  7.   // 拿到 domain,即命令的第一部分 
  8.   span<uint8_t> domain = method.subspan(0, dot_idx); 
  9.   // 拿到命令 
  10.   span<uint8_t> command = method.subspan(dot_idx + 1); 
  11.   // 通過 domain 查找對應的處理器 
  12.   DomainDispatcher* dispatcher = FindByFirst(dispatchers_, domain); 
  13.   if (dispatcher) { 
  14.     // 交給 domain 對應的處理器繼續處理 
  15.     std::function<void(const Dispatchable&)> dispatched = 
  16.         dispatcher->Dispatch(command); 
  17.     if (dispatched) { 
  18.       return DispatchResult( 
  19.           true, [dispatchable, dispatched = std::move(dispatched)]() { 
  20.             dispatched(dispatchable); 
  21.           }); 
  22.     } 
  23.   } 
  24.  

 DomainDispatcher

剛才分析了 UberDispatcher,UberDispatcher 是一個命令一級分發器,因為命令是 domain.cmd 的格式,UberDispatcher 是根據 domain 進行初步分發,DomainDispatcher 則是找到具體命令對應的處理器。

  1. class DomainDispatcher { 
  2.   // 分發邏輯,子類實現 
  3.   virtual std::function<void(const Dispatchable&)> Dispatch(span<uint8_t> command_name) = 0; 
  4.  
  5.   // 處理完后響應 
  6.   void sendResponse(int call_id, 
  7.                     const DispatchResponse&, 
  8.                     std::unique_ptr<Serializable> result = nullptr); 
  9.  private: 
  10.   // 關聯的 channel 
  11.   FrontendChannel* frontend_channel_; 
  12.  
  13. }; 

DomainDispatcher 定義了命令分發和響應的邏輯,不同的 domain 的分發邏輯會有不同的實現,但是響應邏輯是一樣的,所以基類實現了。

  1. void DomainDispatcher::sendResponse(int call_id, 
  2.                                     const DispatchResponse& response, 
  3.                                     std::unique_ptr<Serializable> result) { 
  4.   std::unique_ptr<Serializableserializable
  5.   if (response.IsError()) { 
  6.     serializable = CreateErrorResponse(call_id, response); 
  7.   } else { 
  8.     serializable = CreateResponse(call_id, std::move(result)); 
  9.   } 
  10.   frontend_channel_->SendProtocolResponse(call_id, std::move(serializable)); 
  11.  

通過 frontend_channel_ 返回響應。接下來看子類的實現,這里以 HeapProfiler 為例。

  1. class DomainDispatcherImpl : public protocol::DomainDispatcher { 
  2. public
  3.     DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) 
  4.         : DomainDispatcher(frontendChannel) 
  5.         , m_backend(backend) {} 
  6.     ~DomainDispatcherImpl() override { } 
  7.  
  8.     using CallHandler = void (DomainDispatcherImpl::*)(const v8_crdtp::Dispatchable& dispatchable); 
  9.     // 分發的實現 
  10.     std::function<void(const v8_crdtp::Dispatchable&)> Dispatch(v8_crdtp::span<uint8_t> command_name) override; 
  11.     // HeapProfiler 支持的命令 
  12.     void addInspectedHeapObject(const v8_crdtp::Dispatchable& dispatchable); 
  13.     void collectGarbage(const v8_crdtp::Dispatchable& dispatchable); 
  14.     void disable(const v8_crdtp::Dispatchable& dispatchable); 
  15.     void enable(const v8_crdtp::Dispatchable& dispatchable); 
  16.     void getHeapObjectId(const v8_crdtp::Dispatchable& dispatchable); 
  17.     void getObjectByHeapObjectId(const v8_crdtp::Dispatchable& dispatchable); 
  18.     void getSamplingProfile(const v8_crdtp::Dispatchable& dispatchable); 
  19.     void startSampling(const v8_crdtp::Dispatchable& dispatchable); 
  20.     void startTrackingHeapObjects(const v8_crdtp::Dispatchable& dispatchable); 
  21.     void stopSampling(const v8_crdtp::Dispatchable& dispatchable); 
  22.     void stopTrackingHeapObjects(const v8_crdtp::Dispatchable& dispatchable); 
  23.     void takeHeapSnapshot(const v8_crdtp::Dispatchable& dispatchable); 
  24.  protected: 
  25.     Backend* m_backend; 
  26.  
  27. }; 

DomainDispatcherImpl 定義了 HeapProfiler 支持的命令,下面分析一下命令的注冊和分發的處理邏輯。下面是 HeapProfiler 注冊 domain 和 處理器的邏輯(創建 V8InspectorSessionImpl 時)

  1. // backend 是處理命令的具體對象,對于 HeapProfiler domain 是 V8HeapProfilerAgentImpl 
  2.  
  3. void Dispatcher::wire(UberDispatcher* uber, Backend* backend){    
  4.      
  5.  
  6.     // channel 是通信的對端 
  7.     auto dispatcher = std::make_unique<DomainDispatcherImpl>(uber->channel(), backend); 
  8.     // 注冊 domain 對應的處理器 
  9.     uber->WireBackend(v8_crdtp::SpanFrom("HeapProfiler"), std::move(dispatcher)); 
  10.  

接下來看一下收到命令時具體的分發邏輯。

  1. std::function<void(const v8_crdtp::Dispatchable&)> DomainDispatcherImpl::Dispatch(v8_crdtp::span<uint8_t> command_name) { 
  2.   // 根據命令查找處理函數 
  3.   CallHandler handler = CommandByName(command_name); 
  4.   // 返回個函數,外層執行 
  5.   return [this, handler](const v8_crdtp::Dispatchable& dispatchable) { 
  6.     (this->*handler)(dispatchable); 
  7.   }; 
  8.  

看一下查找的邏輯。

  1. DomainDispatcherImpl::CallHandler CommandByName(v8_crdtp::span<uint8_t> command_name) { 
  2.   static auto* commands = [](){ 
  3.     auto* commands = new std::vector<std::pair<v8_crdtp::span<uint8_t>, DomainDispatcherImpl::CallHandler>>{ 
  4.         // 太多,不一一列舉 
  5.         { 
  6.           v8_crdtp::SpanFrom("enable"), 
  7.           &DomainDispatcherImpl::enable 
  8.         }, 
  9.     }; 
  10.     return commands; 
  11.   }(); 
  12.   return v8_crdtp::FindByFirst<DomainDispatcherImpl::CallHandler>(*commands, command_name, nullptr); 
  13.  

再看一下 DomainDispatcherImpl::enable 的實現。

  1. void DomainDispatcherImpl::enable(const v8_crdtp::Dispatchable& dispatchable){ 
  2.     std::unique_ptr<DomainDispatcher::WeakPtr> weak = weakPtr(); 
  3.     // 調用 m_backend 也就是 V8HeapProfilerAgentImpl 的 enable 
  4.     DispatchResponse response = m_backend->enable(); 
  5.     if (response.IsFallThrough()) { 
  6.         channel()->FallThrough(dispatchable.CallId(), v8_crdtp::SpanFrom("HeapProfiler.enable"), dispatchable.Serialized()); 
  7.         return
  8.     } 
  9.     if (weak->get()) 
  10.         weak->get()->sendResponse(dispatchable.CallId(), response); 
  11.     return
  12.  

DomainDispatcherImpl 只是封裝,具體的命令處理交給 m_backend 所指向的對象,這里是 V8HeapProfilerAgentImpl。下面是 V8HeapProfilerAgentImpl enable 的實現。

  1. Response V8HeapProfilerAgentImpl::enable() { 
  2.   m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); 
  3.   return Response::Success(); 
  4.  

結構圖如下。

V8HeapProfilerAgentImpl

剛才分析了 V8HeapProfilerAgentImpl 的 enable 函數,這里以 V8HeapProfilerAgentImpl 為例子分析一下命令處理器類的邏輯。

  1. class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { 
  2.  public
  3.   V8HeapProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, 
  4.                           protocol::DictionaryValue* state); 
  5.  
  6.  private: 
  7.  
  8.   V8InspectorSessionImpl* m_session; 
  9.   v8::Isolate* m_isolate; 
  10.   // protocol::HeapProfiler::Frontend 定義了支持哪些事件 
  11.   protocol::HeapProfiler::Frontend m_frontend; 
  12.   protocol::DictionaryValue* m_state; 
  13.  
  14. }; 

V8HeapProfilerAgentImpl 通過 protocol::HeapProfiler::Frontend 定義了支持的事件,因為 Inspector 不僅可以處理調用方發送的命令,還可以主動給調用方推送消息,這種推送就是以事件的方式觸發的。

  1. class  Frontend { 
  2. public
  3.   explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} 
  4.     void addHeapSnapshotChunk(const String& chunk); 
  5.     void heapStatsUpdate(std::unique_ptr<protocol::Array<int>> statsUpdate); 
  6.     void lastSeenObjectId(int lastSeenObjectId, double timestamp); 
  7.     void reportHeapSnapshotProgress(int done, int total, Maybe<bool> finished = Maybe<bool>()); 
  8.     void resetProfiles(); 
  9.  
  10.   void flush(); 
  11.   void sendRawNotification(std::unique_ptr<Serializable>); 
  12.  private: 
  13.   // 指向 V8InspectorSessionImpl 對象 
  14.   FrontendChannel* frontend_channel_; 
  15.  
  16. }; 

下面看一下 addHeapSnapshotChunk,這是獲取堆快照時用到的邏輯。

  1. void Frontend::addHeapSnapshotChunk(const String& chunk){ 
  2.     v8_crdtp::ObjectSerializer serializer; 
  3.     serializer.AddField(v8_crdtp::MakeSpan("chunk"), chunk); 
  4.     frontend_channel_->SendProtocolNotification(v8_crdtp::CreateNotification("HeapProfiler.addHeapSnapshotChunk", serializer.Finish())); 
  5.  

最終觸發了 HeapProfiler.addHeapSnapshotChunk 事件。另外 V8HeapProfilerAgentImpl 繼承了 Backend 定義了支持哪些請求命令和 DomainDispatcherImpl 中的函數對應,比如獲取堆快照。

  1. class  Backend { 
  2. public
  3.     virtual ~Backend() { } 
  4.     // 不一一列舉 
  5.     virtual DispatchResponse takeHeapSnapshot(Maybe<bool> in_reportProgress, Maybe<bool> in_treatGlobalObjectsAsRoots, Maybe<bool> in_captureNumericValue) = 0; 
  6.  
  7. }; 

結構圖如下。

Node.js 對 V8 Inspector 的封裝

接下來看一下 Node.js 中是如何使用 V8 Inspector 的,V8 Inspector 的使用方需要實現 V8InspectorClient 和 V8Inspector::Channel。下面看一下 Node.js 的實現。

  1. class NodeInspectorClient : public V8InspectorClient { 
  2.  public
  3.   explicit NodeInspectorClient() { 
  4.     // 創建一個 V8Inspector 
  5.     client_ = V8Inspector::create(env->isolate(), this); 
  6.   } 
  7.  
  8.   int connectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate, 
  9.                       bool prevent_shutdown) { 
  10.     int session_id = next_session_id_++; 
  11.     channels_[session_id] = std::make_unique<ChannelImpl>(env_, 
  12.                                                           client_, 
  13.                                                           getWorkerManager(), 
  14.                                                           // 收到數據后由 delegate 處理 
  15.                                                           std::move(delegate), 
  16.                                                           getThreadHandle(), 
  17.                                                           prevent_shutdown); 
  18.     return session_id; 
  19.   } 
  20.  
  21.   std::unique_ptr<V8Inspector> client_; 
  22.   std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels_; 
  23.  
  24. }; 

NodeInspectorClient 封裝了 V8Inspector,并且維護了多個 channel。Node.js 的上層代碼可以通過 connectFrontend 連接到 V8 Inspector,并拿到 session_id,這個連接用 ChannelImpl 來實現,來看一下 ChannelImpl 的實現。

  1. explicit ChannelImpl(const std::unique_ptr<V8Inspector>& inspector,  
  2.                      std::unique_ptr<InspectorSessionDelegate> delegate):  
  3.                      // delegate_ 負責處理 V8 發過來的數據 
  4.                      delegate_(std::move(delegate)) { 
  5.     session_ = inspector->connect(CONTEXT_GROUP_ID, this, StringView()); 
  6.  

ChannelImpl 是對 V8InspectorSession 的封裝,通過 V8InspectorSession 實現發送命令,ChannelImpl 自己實現了接收響應和接收 V8 推送數據的邏輯。了解了封裝 V8 Inspector 的能力后,通過一個例子看一下整個處理過程。通常我們通過以下方式和 V8 Inspector 通信。

  1. const { Session } = require('inspector'); 
  2. new Session().connect(); 

我們從 connect 開始分析。

  1. connect() { 
  2.     this[connectionSymbol] = new Connection((message) => this[onMessageSymbol](message)); 

新建一個 C++ 層的對象 JSBindingsConnection。

  1. JSBindingsConnection(Environment* env, 
  2.                        Local<Object> wrap, 
  3.                        Local<Function> callback) 
  4.                        : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING), 
  5.                          callback_(env->isolate(), callback) { 
  6.     Agent* inspector = env->inspector_agent(); 
  7.     session_ = LocalConnection::Connect(inspector, std::make_unique<JSBindingsSessionDelegate>(env, this));}static std::unique_ptr<InspectorSession> Connect
  8.      Agent* inspector, std::unique_ptr<InspectorSessionDelegate> delegate) { 
  9.    return inspector->Connect(std::move(delegate), false); 
  10.  
  11.  
  12.  
  13. std::unique_ptr<InspectorSession> Agent::Connect
  14.     std::unique_ptr<InspectorSessionDelegate> delegate, 
  15.     bool prevent_shutdown) { 
  16.   int session_id = client_->connectFrontend(std::move(delegate), 
  17.                                             prevent_shutdown); 
  18.   return std::unique_ptr<InspectorSession>( 
  19.       new SameThreadInspectorSession(session_id, client_)); 
  20.  

JSBindingsConnection 初始化時會通過 agent->Connect 最終調用 Agent::Connect 建立到 V8 的通道,并傳入 JSBindingsSessionDelegate 作為數據處理的代理(channel 中使用)。最后返回一個 SameThreadInspectorSession 對象保存到 session_ 中,后續就可以開始通信了,繼續看一下 通過 JS 層的 post 發送請求時的邏輯。

  1. post(method, params, callback) { 
  2.     const id = this[nextIdSymbol]++; 
  3.     const message = { id, method }; 
  4.     if (params) { 
  5.       message.params = params; 
  6.     } 
  7.     if (callback) { 
  8.       this[messageCallbacksSymbol].set(id, callback); 
  9.     } 
  10.     this[connectionSymbol].dispatch(JSONStringify(message)); 

為每一個請求生成一個 id,因為是異步返回的,最后調用 dispatch 函數。

  1. static void Dispatch(const FunctionCallbackInfo<Value>& info) { 
  2.     Environment* env = Environment::GetCurrent(info); 
  3.     JSBindingsConnection* session; 
  4.     ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder()); 
  5.  
  6.     if (session->session_) { 
  7.       session->session_->Dispatch( 
  8.           ToProtocolString(env->isolate(), info[0])->string()); 
  9.     } 

看一下 SameThreadInspectorSession::Dispatch (即session->session_->Dispatch)。

  1. void SameThreadInspectorSession::Dispatch( 
  2.     const v8_inspector::StringView& message) { 
  3.   auto client = client_.lock(); 
  4.   if (client) 
  5.     client->dispatchMessageFromFrontend(session_id_, message); 
  6.  

SameThreadInspectorSession 中維護了一個sessionId,繼續調用 client->dispatchMessageFromFrontend, client 是 NodeInspectorClient 對象。

  1. void dispatchMessageFromFrontend(int session_id, const StringView& message) { 
  2.    channels_[session_id]->dispatchProtocolMessage(message); 

dispatchMessageFromFrontend 通過 sessionId 找到對應的 channel。繼續調 channel 的 dispatchProtocolMessage。

  1. void dispatchProtocolMessage(const StringView& message) { 
  2.     std::string raw_message = protocol::StringUtil::StringViewToUtf8(message); 
  3.     std::unique_ptr<protocol::DictionaryValue> value = 
  4.         protocol::DictionaryValue::cast(protocol::StringUtil::parseMessage( 
  5.             raw_message, false)); 
  6.     int call_id; 
  7.     std::string method; 
  8.     node_dispatcher_->parseCommand(value.get(), &call_id, &method); 
  9.     if (v8_inspector::V8InspectorSession::canDispatchMethod( 
  10.             Utf8ToStringView(method)->string())) { 
  11.       session_->dispatchProtocolMessage(message); 
  12.     } 

最終調用 V8InspectorSessionImpl 的 session_->dispatchProtocolMessage(message),后面的內容前面就講過了,就不再分析。最后看一下數據響應或者推送時的邏輯。下面代碼來自 ChannelImpl。

  1. void sendResponse( 
  2.   int callId, 
  3.     std::unique_ptr<v8_inspector::StringBuffer> message) override { 
  4.   sendMessageToFrontend(message->string()); 
  5.  
  6.  
  7.  
  8.  
  9. void sendNotification( 
  10.  
  11.     std::unique_ptr<v8_inspector::StringBuffer> message) override { 
  12.   sendMessageToFrontend(message->string()); 
  13.  
  14.  
  15.  
  16.  
  17. void sendMessageToFrontend(const StringView& message) { 
  18.  
  19.   delegate_->SendMessageToFrontend(message); 
  20.  

我們看到最終調用了 delegate_->SendMessageToFrontend, delegate 是 JSBindingsSessionDelegate對象。

  1. void SendMessageToFrontend(const v8_inspector::StringView& message) 
  2.         override { 
  3.   Isolate* isolate = env_->isolate(); 
  4.   HandleScope handle_scope(isolate); 
  5.   Context::Scope context_scope(env_->context()); 
  6.   MaybeLocal<String> v8string = String::NewFromTwoByte(isolate, message.characters16(), 
  7.                              NewStringType::kNormal, message.length()); 
  8.   Local<Value> argument = v8string.ToLocalChecked().As<Value>(); 
  9.   connection_->OnMessage(argument); 
  10.  

接著調用 connection_->OnMessage(argument),connection 是 JSBindingsConnection 對象。

  1. void OnMessage(Local<Value> value) { 
  2.   MakeCallback(callback_.Get(env()->isolate()), 1, &value); 
  3.  

C++ 層回調 JS 層。

  1. [onMessageSymbol](message) { 
  2.     const parsed = JSONParse(message); 
  3.     try { 
  4.       // 通過有沒有 id 判斷是響應還是推送 
  5.       if (parsed.id) { 
  6.         const callback = this[messageCallbacksSymbol].get(parsed.id); 
  7.         this[messageCallbacksSymbol].delete(parsed.id); 
  8.         if (callback) { 
  9.           if (parsed.error) { 
  10.             return callback(new ERR_INSPECTOR_COMMAND(parsed.error.code, 
  11.                                                       parsed.error.message)); 
  12.           } 
  13.  
  14.           callback(null, parsed.result); 
  15.         } 
  16.       } else { 
  17.         this.emit(parsed.method, parsed); 
  18.         this.emit('inspectorNotification', parsed); 
  19.       } 
  20.     } catch (error) { 
  21.       process.emitWarning(error); 
  22.     } 

以上就完成了整個鏈路的分析。整體結構圖如下。

總結

V8 Inspector 的設計和實現上比較復雜,對象間關系錯綜復雜。因為 V8 提供調試和診斷 JS 的文檔似乎不多,也不是很完善,就是簡單描述一下命令是干啥的,很多時候不一定夠用,了解了具體實現后,后續碰到問題,可以自己去看具體實現。

 

責任編輯:姜華 來源: 編程雜技
相關推薦

2020-09-27 07:32:18

V8

2021-08-05 05:46:06

Node.jsInspector工具

2022-06-29 08:05:25

Volatile關鍵字類型

2023-10-04 00:04:00

C++extern

2024-03-15 09:44:17

WPFDispatcherUI線程

2019-09-04 14:14:52

Java編程數據

2024-07-18 10:12:04

2021-05-28 05:30:55

HandleV8代碼

2020-12-16 09:47:01

JavaScript箭頭函數開發

2018-07-09 15:11:14

Java逃逸JVM

2016-08-31 15:50:50

PythonThreadLocal變量

2010-06-28 10:12:01

PHP匿名函數

2023-10-08 08:53:36

數據庫MySQL算法

2014-06-23 10:42:56

iOS開發UIScrollVie

2015-09-17 10:51:35

修改hostnameLinux

2016-12-08 15:36:59

HashMap數據結構hash函數

2020-07-21 08:26:08

SpringSecurity過濾器

2010-06-01 15:25:27

JavaCLASSPATH

2013-11-05 13:29:04

JavaScriptreplace

2013-06-20 10:25:56

點贊
收藏

51CTO技術棧公眾號

精品三区视频| 亚洲男人天堂久久| 五月天久久网站| 91精品国产美女浴室洗澡无遮挡| 黄色一级大片免费| 日韩av高清在线| 久久精品72免费观看| 欧美精品日韩www.p站| 国产老熟女伦老熟妇露脸| 韩国成人漫画| 一级做a爱片久久| 日韩av影视| 国产小视频一区| 青青草国产成人av片免费| 欧美激情视频网| 99久久99久久精品免费看小说.| 99久久香蕉| 欧美日韩高清一区二区不卡| 日本a视频在线观看| 在线免费看黄网站| 99re在线视频这里只有精品| 91久久国产婷婷一区二区| 91av在线免费视频| 中文无码久久精品| 国产一区二区三区精品久久久| 亚洲成人手机在线观看| 电影亚洲精品噜噜在线观看| 亚洲国产综合色| 一本—道久久a久久精品蜜桃| 欧美女子与性| 成人久久久精品乱码一区二区三区| 国产精品99蜜臀久久不卡二区| 精品人妻在线播放| 自拍欧美日韩| 中文字幕欧美日韩| 精品人妻无码一区二区三区| 亚洲日本va| 欧美视频一区二区在线观看| 北条麻妃在线视频观看| 色婷婷在线播放| 亚洲欧美一区二区视频| 亚洲欧美99| 国产午夜精品一区理论片| 91在线观看地址| 国产精品日韩二区| 亚洲成人一级片| 国产一区二区三区免费看| 国产精品你懂得| 国产精品va无码一区二区三区| 亚洲精品日本| 久久乐国产精品| 欧美极品aaaaabbbbb| 一区二区三区国产精华| 日韩在线观看网址| 欧美激情精品久久久久久免费| 不卡av一区二区| 国产一区二区激情| 国内精品卡一卡二卡三| 精品国产欧美日韩| 国产亚洲精品日韩| 精品人体无码一区二区三区| 不卡中文字幕| 色偷偷噜噜噜亚洲男人| 手机在线免费看片| 欧美激情91| 欧美黑人又粗大| 国产在线观看免费av| 亚洲三级电影在线观看| 2021国产精品视频| 天天干,天天干| 日韩黄色一级片| 国产主播精品在线| www.xxxx国产| 成人av免费网站| 欧美日韩电影一区二区三区| av大片在线观看| 亚洲猫色日本管| 国产日本在线播放| 刘亦菲一区二区三区免费看| 欧美性受极品xxxx喷水| 色黄视频免费看| 国产香蕉精品| 亚洲人精品午夜在线观看| 91精品国自产在线| 亚洲大全视频| 国语自产在线不卡| 91porny九色| 黄页视频在线91| 国产精选在线观看91| 国产黄色免费在线观看| 亚洲欧美韩国综合色| 国产精品入口芒果| 国产在线|日韩| 欧美一区二区三区免费| 性久久久久久久久久| 成人亚洲一区| 高清一区二区三区四区五区| 婷婷激情五月综合| 国产黑丝在线一区二区三区| 免费日韩av电影| 成人ww免费完整版在线观看| 欧美色videos| 国产高清999| 天海翼精品一区二区三区| 综合国产在线视频| 日韩人妻无码一区二区三区99| 卡一卡二国产精品| 国产中文一区二区| 日本在线观看www| 欧美性生交大片免费| 91aaa精品| 国产一区99| 九九精品在线播放| 亚洲精品毛片一区二区三区| 成人免费高清在线观看| 日本特级黄色大片| 欧美无毛视频| 精品乱人伦小说| 中文字幕美女视频| 久久久久.com| 精品欧美国产一区二区三区不卡| 黄视频在线观看网站| 91九色最新地址| 中文字幕一区二区人妻电影丶| 91综合久久一区二区| 日本久久久久久久| 亚洲三级中文字幕| 一区二区三区在线播放| 国内自拍第二页| 精品久久久中文字幕| 97超级碰在线看视频免费在线看| 999国产精品视频免费| 欧美经典一区二区三区| 日本在线观看a| 久久悠悠精品综合网| 免费av一区二区| 一级成人免费视频| 中文字幕精品—区二区四季| 日韩黄色片视频| 欧美人妖在线观看| 国产综合在线看| 精品女同一区二区三区| 亚洲美女视频一区| 91aaa精品| 午夜精品国产| caoporen国产精品| av免费网站在线观看| 9191成人精品久久| 一级免费黄色录像| 久久超碰97中文字幕| 伊人精品久久久久7777| 日韩护士脚交太爽了| 最新亚洲国产精品| 97成人在线观看| 亚洲欧洲三级电影| 成人高清在线观看视频| 欧美91精品| 国产精品成人一区二区三区| 欧美精品videossex少妇| 日韩精品一区在线| 国产精品99精品无码视| 波多野结衣中文字幕一区| 日韩中文字幕在线视频观看| 秋霞在线一区| 国产91热爆ts人妖在线| 国产h在线观看| 欧美精品免费视频| 2018天天弄| 99免费精品视频| 欧美日韩中文在线视频| 日韩av在线播放网址| 成人免费网站在线观看| 日韩123区| 精品无人区乱码1区2区3区在线| 伦av综合一区| 国产午夜亚洲精品不卡| 精品亚洲视频在线| 欧美久久久久| 欧美精品欧美精品| 日本欧美在线| 国内久久久精品| 精品电影在线| 91精品婷婷国产综合久久竹菊| 国产亚洲精品久久777777| www国产成人免费观看视频 深夜成人网| 农村妇女精品一二区| 久久久五月天| 精品无人区一区二区三区 | 亚洲精品免费一二三区| 日本道中文字幕| 日本sm残虐另类| www.国产二区| 欧美三级美国一级| 99久久无色码| 桃花岛成人影院| 九色成人免费视频| 国产98在线| 欧美zozozo| 又骚又黄的视频| 亚洲777理论| 狂野欧美性猛交| av电影在线观看完整版一区二区| 香港日本韩国三级网站| 国模吧视频一区| 一区二区不卡视频| 日本欧美高清| caoporen国产精品| 九九热这里有精品| 欧美中文字幕在线播放| 亚洲欧美成人影院| 在线观看中文字幕亚洲| 少妇喷水在线观看| 日韩一区二区三区观看| 最近中文字幕在线观看视频| 天天爽夜夜爽夜夜爽精品视频 | 亚洲国产精品自拍| 蜜桃av.com| 久久精品欧美一区二区三区不卡| 4438x全国最大成人| 美国十次了思思久久精品导航| 久久久久久人妻一区二区三区| 99国产精品免费视频观看| 欧美日韩在线不卡一区| 国产精品xxx在线观看| 91视频国产一区| 国产资源一区| 国产精品美乳在线观看| 中国色在线日|韩| 久久免费福利视频| 日本无删减在线| 久久伊人精品视频| 麻豆视频在线观看免费网站| 在线日韩欧美视频| 国产1区2区3区在线| 亚洲精品午夜精品| 天堂av电影在线观看| 亚洲精品在线免费观看视频| 国产高清精品软件丝瓜软件| 欧美日韩国产另类一区| 超碰在线97观看| 日本韩国一区二区三区| 国产精品视频免费播放| 天天影视涩香欲综合网| www.毛片.com| 日韩欧美国产免费播放| 久久久久久少妇| 色综合视频一区二区三区高清| 毛片视频网站在线观看| 无吗不卡中文字幕| 六月丁香激情综合| 岛国av在线不卡| 日韩色图在线观看| 在线精品视频免费播放| 少妇又紧又色又爽又刺激视频| 在线亚洲人成电影网站色www| 国产精品久久久久久人| 日本乱码高清不卡字幕| 这里只有久久精品视频| 精品视频免费看| 国产乱淫av片免费| 欧美电影精品一区二区| 欧美一级淫片aaaaaa| 亚洲精品国产精品乱码不99按摩 | 国产最新免费视频| 噜噜噜91成人网| 国产日韩欧美久久| 国产美女一区二区| 亚洲香蕉中文网| 久久男人中文字幕资源站| 色一情一交一乱一区二区三区| 欧美国产精品久久| 侵犯稚嫩小箩莉h文系列小说| 亚洲一区二区在线视频| 成年免费在线观看| 欧美性色综合网| 国产人妖一区二区三区| 亚洲电影免费观看| 国产一区二区三区福利| 久久成人精品电影| 色戒汤唯在线| 国产欧美日韩91| 欧美人与动xxxxz0oz| 亚洲v国产v| 一区在线播放| 中文字幕在线导航| 国产激情视频一区二区在线观看 | 一本大道久久a久久精二百| 中文字幕 自拍偷拍| 日韩欧美一区二区久久婷婷| 视频在线不卡| 精品国产一区av| aa国产成人| 成人中心免费视频| 香蕉久久精品日日躁夜夜躁| 一区二区三区偷拍| 99综合在线| www.桃色.com| 国产午夜精品一区二区三区视频| 欧美成人国产精品高潮| 日本高清成人免费播放| 高清乱码毛片入口| 色爱精品视频一区| 一个人www视频在线免费观看| 成人乱人伦精品视频在线观看| 日本一道高清一区二区三区| 国产一区一区三区| 久久久久国产精品一区三寸| 精品久久久久久无码人妻| 国产精品无码永久免费888| 日韩字幕在线观看| 91精品国产综合久久精品| 国产在线视频网| 51精品国产黑色丝袜高跟鞋| 欧美精品三级在线| 亚洲欧美日韩综合一区| 久久成人亚洲| 久久国产精品无码一级毛片| 亚洲精品国产无套在线观| 中文人妻熟女乱又乱精品| 亚洲精品白浆高清久久久久久| а√天堂在线官网| 国产欧美日韩免费| 精品国产乱码久久久久久果冻传媒| 久久国产精品视频在线观看| 国产乱码一区二区三区| 在线观看日本中文字幕| 欧美天堂在线观看| 天天综合网在线观看| 欧美精品亚州精品| 激情久久免费视频| 在线视频不卡一区二区| 久久99精品国产麻豆婷婷| 少妇无套高潮一二三区| 欧美视频中文字幕在线| 日韩在线视频第一页| 久久人人97超碰精品888| www国产精品| 人人妻人人澡人人爽欧美一区双| 国产91精品免费| 久久免费少妇高潮99精品| 精品美女在线观看| 国产盗摄在线视频网站| 国产99在线免费| 在线观看一区| 亚洲av人人澡人人爽人人夜夜| 亚洲一区二区视频在线观看| 内射无码专区久久亚洲| 国内伊人久久久久久网站视频| 精品视频自拍| 丰满爆乳一区二区三区| 久久先锋影音av鲁色资源| 国内精品福利视频| 亚洲色图35p| 成人精品三级| 在线视频91| 国产成人精品影视| 国产污视频在线看| 日韩av一区在线观看| 台湾佬中文娱乐网欧美电影| 欧美日韩在线精品一区二区三区| 久久国产精品久久久久久电车| 色欲AV无码精品一区二区久久 | 久草视频福利在线| 亚洲成人av电影| 玖玖综合伊人| 国产精品自产拍在线观看中文| 91麻豆国产自产在线观看亚洲| 国产农村妇女精品久久| 亚洲最新在线观看| 毛片网站在线观看| 国产精品视频自拍| 欧美性色综合| 亚洲色图14p| 欧美日韩一区不卡| 青青草视频在线免费直播| 久久av一区二区三区亚洲| 男女男精品网站| 日日骚一区二区三区| 日韩久久免费电影| 欧美xxxx性| 极品粉嫩国产18尤物| 国产日韩精品久久久| 性一交一乱一乱一视频| 青青草精品毛片| 小处雏高清一区二区三区| 日本一级片在线播放| 欧美在线视频你懂得| 国内高清免费在线视频| 品久久久久久久久久96高清| 国产精品亚洲一区二区三区妖精 | av日韩中文| 亚洲乱码一区二区三区| 成人av影院在线| 正在播放木下凛凛xv99| 久久人人爽人人爽人人片av高请 | 2014亚洲片线观看视频免费| 依依成人在线视频| 午夜精品理论片| 99久精品视频在线观看视频| 成年人网站免费看|