v8_context_tracker.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_H_
  5. #define COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_H_
  6. #include <memory>
  7. #include "base/memory/raw_ptr.h"
  8. #include "base/types/pass_key.h"
  9. #include "components/performance_manager/public/execution_context/execution_context.h"
  10. #include "components/performance_manager/public/graph/graph.h"
  11. #include "components/performance_manager/public/graph/graph_registered.h"
  12. #include "components/performance_manager/public/graph/node_data_describer.h"
  13. #include "components/performance_manager/public/graph/process_node.h"
  14. #include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
  15. #include "third_party/blink/public/common/tokens/tokens.h"
  16. namespace performance_manager {
  17. class FrameNodeImpl;
  18. class ProcessNodeImpl;
  19. namespace v8_memory {
  20. // Forward declaration.
  21. namespace internal {
  22. class V8ContextTrackerDataStore;
  23. } // namespace internal
  24. // A class that tracks individual V8Contexts in renderers as they go through
  25. // their lifecycle. This tracks information such as detached (leaked) contexts
  26. // and remote frame attribution, for surfacing in the performance.measureMemory
  27. // API. This information is tracked per-process in ProcessNode-attached data.
  28. // The tracker lets you query a V8ContextToken and retrieve information about
  29. // that context, including its iframe attributes and associated
  30. // ExecutionContext.
  31. //
  32. // Note that this component relies on the ExecutionContextRegistry having been
  33. // added to the Graph.
  34. class V8ContextTracker final
  35. : public execution_context::ExecutionContextObserverDefaultImpl,
  36. public GraphObserver,
  37. public GraphOwned,
  38. public GraphRegisteredImpl<V8ContextTracker>,
  39. public NodeDataDescriberDefaultImpl,
  40. public ProcessNode::ObserverDefaultImpl {
  41. public:
  42. using DataStore = internal::V8ContextTrackerDataStore;
  43. // Data about an individual ExecutionContext. Note that this information can
  44. // outlive the ExecutionContext itself, and in that case it stores information
  45. // about the last known state of the ExecutionContext prior to it being
  46. // torn down in a renderer.
  47. struct ExecutionContextState {
  48. ExecutionContextState() = delete;
  49. ExecutionContextState(
  50. const blink::ExecutionContextToken& token,
  51. mojom::IframeAttributionDataPtr iframe_attribution_data);
  52. ExecutionContextState(const ExecutionContextState&) = delete;
  53. ExecutionContextState& operator=(const ExecutionContextState&) = delete;
  54. virtual ~ExecutionContextState();
  55. // Returns the associated execution_context::ExecutionContext (which wraps
  56. // the underlying FrameNode or WorkerNode associated with this context) *if*
  57. // the node is available.
  58. const execution_context::ExecutionContext* GetExecutionContext() const;
  59. // The token identifying this context.
  60. const blink::ExecutionContextToken token;
  61. // The iframe attribution data most recently associated with this context.
  62. // This is sometimes only available asynchronously so is optional. Note that
  63. // this value can change over time, but will generally reflect the most up
  64. // to date data (with some lag).
  65. mojom::IframeAttributionDataPtr iframe_attribution_data;
  66. // Whether or not the corresponding blink::ExecutionContext has been
  67. // destroyed. This occurs when the main V8Context associated with this
  68. // execution context has itself become detached. This starts as false and
  69. // can transition to true exactly once.
  70. bool destroyed = false;
  71. };
  72. struct V8ContextState {
  73. V8ContextState() = delete;
  74. V8ContextState(const mojom::V8ContextDescription& description,
  75. ExecutionContextState* execution_context_state);
  76. V8ContextState(const V8ContextState&) = delete;
  77. V8ContextState& operator=(const V8ContextState&) = delete;
  78. virtual ~V8ContextState();
  79. // The full description of this context.
  80. const mojom::V8ContextDescription description;
  81. // A pointer to the upstream ExecutionContextState that this V8Context is
  82. // associated with. Note that this can be nullptr for V8Contexts that are
  83. // not associated with an ExecutionContext.
  84. raw_ptr<ExecutionContextState> execution_context_state;
  85. // Whether or not this context is detached. A context becomes detached
  86. // when the blink::ExecutionContext it was associated with is torn down.
  87. // When a V8Context remains detached for a long time (is not collected by
  88. // GC) it is effectively a leak (it is being kept alive by a stray
  89. // cross-context reference). This starts as false and can transition to
  90. // true exactly once.
  91. bool detached = false;
  92. };
  93. V8ContextTracker();
  94. ~V8ContextTracker() final;
  95. DataStore* data_store() const { return data_store_.get(); }
  96. // Returns the ExecutionContextState for the given token, nullptr if none
  97. // exists.
  98. const ExecutionContextState* GetExecutionContextState(
  99. const blink::ExecutionContextToken& token) const;
  100. // Returns V8ContextState for the given token, nullptr if none exists.
  101. const V8ContextState* GetV8ContextState(
  102. const blink::V8ContextToken& token) const;
  103. //////////////////////////////////////////////////////////////////////////////
  104. // The following functions handle inbound IPC, and are only meant to be
  105. // called from ProcessNodeImpl and FrameNodeImpl (hence the use of PassKey).
  106. // Notifies the context tracker of a V8Context being created in a renderer
  107. // process. If the context is associated with an ExecutionContext (EC) then
  108. // |description.execution_context_token| will be provided. If the EC is a
  109. // frame, and the parent of that frame is also in the same process, then
  110. // |iframe_attribution_data| will be provided, otherwise these will be empty.
  111. // In the case where they are empty the iframe data will be provided by a
  112. // separate call to OnIframeAttached() from the process hosting the
  113. // parent frame. See the V8ContextWorldType enum for a description of the
  114. // relationship between world types, world names and execution contexts.
  115. void OnV8ContextCreated(
  116. base::PassKey<ProcessNodeImpl> key,
  117. ProcessNodeImpl* process_node,
  118. const mojom::V8ContextDescription& description,
  119. mojom::IframeAttributionDataPtr iframe_attribution_data);
  120. // Notifies the tracker that a V8Context is now detached from its associated
  121. // ExecutionContext (if one was provided during OnV8ContextCreated). If the
  122. // context stays detached for a long time this is indicative of a Javascript
  123. // leak, with the context being kept alive by a stray reference from another
  124. // context. All ExecutionContext-associated V8Contexts will have this method
  125. // called before they are destroyed, and it will not be called for other
  126. // V8Contexts (they are never considered detached).
  127. void OnV8ContextDetached(base::PassKey<ProcessNodeImpl> key,
  128. ProcessNodeImpl* process_node,
  129. const blink::V8ContextToken& v8_context_token);
  130. // Notifies the tracker that a V8Context has been garbage collected. This will
  131. // only be called after OnV8ContextDetached if the OnV8ContextCreated had a
  132. // non-empty |execution_context_token|.
  133. void OnV8ContextDestroyed(base::PassKey<ProcessNodeImpl> key,
  134. ProcessNodeImpl* process_node,
  135. const blink::V8ContextToken& v8_context_token);
  136. // Notifies the tracker that a RemoteFrame child with a LocalFrame parent was
  137. // created in a renderer, providing the iframe.id and iframe.src from the
  138. // parent point of view. This will decorate the ExecutionContextData of the
  139. // appropriate child frame. We require the matching OnRemoteIframeDetached to
  140. // be called for bookkeeping. This should only be called once for a given
  141. // |remote_frame_token|.
  142. void OnRemoteIframeAttached(
  143. base::PassKey<ProcessNodeImpl> key,
  144. FrameNodeImpl* parent_frame_node,
  145. const blink::RemoteFrameToken& remote_frame_token,
  146. mojom::IframeAttributionDataPtr iframe_attribution_data);
  147. // TODO(chrisha): Add OnRemoteIframeAttributesChanged support.
  148. // Notifies the tracker that a RemoteFrame child with a LocalFrame parent was
  149. // detached from an iframe element in a renderer. This is used to cleanup
  150. // iframe data that is being tracked due to a previous call to
  151. // OnIframeAttached, unless the data was adopted by a call to
  152. // OnV8ContextCreated. Should only be called once for a given
  153. // |remote_frame_token|, and only after a matching "OnRemoteIframeAttached"
  154. // call.
  155. void OnRemoteIframeDetached(
  156. base::PassKey<ProcessNodeImpl> key,
  157. FrameNodeImpl* parent_frame_node,
  158. const blink::RemoteFrameToken& remote_frame_token);
  159. //////////////////////////////////////////////////////////////////////////////
  160. // The following functions are for testing only.
  161. void OnRemoteIframeAttachedForTesting(
  162. FrameNodeImpl* frame_node,
  163. FrameNodeImpl* parent_frame_node,
  164. const blink::RemoteFrameToken& remote_frame_token,
  165. mojom::IframeAttributionDataPtr iframe_attribution_data);
  166. void OnRemoteIframeDetachedForTesting(
  167. FrameNodeImpl* parent_frame_node,
  168. const blink::RemoteFrameToken& remote_frame_token);
  169. // System wide metrics.
  170. size_t GetExecutionContextCountForTesting() const;
  171. size_t GetV8ContextCountForTesting() const;
  172. size_t GetDestroyedExecutionContextCountForTesting() const;
  173. size_t GetDetachedV8ContextCountForTesting() const;
  174. private:
  175. // Implementation of execution_context::ExecutionContextObserverDefaultImpl.
  176. void OnBeforeExecutionContextRemoved(
  177. const execution_context::ExecutionContext* ec) final;
  178. // Implementation of GraphObserver.
  179. void OnBeforeGraphDestroyed(Graph* graph) final;
  180. // Implementation of GraphOwned.
  181. void OnPassedToGraph(Graph* graph) final;
  182. void OnTakenFromGraph(Graph* graph) final;
  183. // Implementation of NodeDataDescriber. We have things to say about
  184. // execution contexts (frames and workers), as well as processes.
  185. base::Value DescribeFrameNodeData(const FrameNode* node) const final;
  186. base::Value DescribeProcessNodeData(const ProcessNode* node) const final;
  187. base::Value DescribeWorkerNodeData(const WorkerNode* node) const final;
  188. // Implementation of ProcessNode::ObserverDefaultImpl.
  189. void OnBeforeProcessNodeRemoved(const ProcessNode* node) final;
  190. // OnIframeAttached bounces over to the UI thread to
  191. // lookup the RenderFrameHost* associated with a given RemoteFrameToken,
  192. // landing here.
  193. void OnRemoteIframeAttachedImpl(
  194. mojo::ReportBadMessageCallback bad_message_callback,
  195. FrameNodeImpl* frame_node,
  196. FrameNodeImpl* parent_frame_node,
  197. const blink::RemoteFrameToken& remote_frame_token,
  198. mojom::IframeAttributionDataPtr iframe_attribution_data);
  199. // To maintain strict ordering with OnRemoteIframeAttached events, detached
  200. // events also detour through the UI thread to arrive here.
  201. void OnRemoteIframeDetachedImpl(
  202. FrameNodeImpl* parent_frame_node,
  203. const blink::RemoteFrameToken& remote_frame_token);
  204. // Stores Chrome-wide data store used by the tracking.
  205. std::unique_ptr<DataStore> data_store_;
  206. };
  207. } // namespace v8_memory
  208. } // namespace performance_manager
  209. #endif // COMPONENTS_PERFORMANCE_MANAGER_V8_MEMORY_V8_CONTEXT_TRACKER_H_