v8_memory_test_helpers.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. #include "components/performance_manager/v8_memory/v8_memory_test_helpers.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback.h"
  8. #include "base/check.h"
  9. #include "base/command_line.h"
  10. #include "base/feature_list.h"
  11. #include "base/run_loop.h"
  12. #include "base/test/bind.h"
  13. #include "base/test/task_environment.h"
  14. #include "components/performance_manager/embedder/graph_features.h"
  15. #include "components/performance_manager/graph/frame_node_impl.h"
  16. #include "components/performance_manager/graph/page_node_impl.h"
  17. #include "components/performance_manager/graph/process_node_impl.h"
  18. #include "components/performance_manager/public/features.h"
  19. #include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
  20. #include "components/performance_manager/public/performance_manager.h"
  21. #include "components/performance_manager/v8_memory/v8_context_tracker.h"
  22. #include "content/public/browser/render_process_host.h"
  23. #include "content/public/browser/web_contents.h"
  24. #include "content/public/test/navigation_simulator.h"
  25. #include "content/public/test/test_renderer_host.h"
  26. #include "content/public/test/test_utils.h"
  27. #include "url/gurl.h"
  28. namespace performance_manager {
  29. namespace v8_memory {
  30. using ::testing::_;
  31. ////////////////////////////////////////////////////////////////////////////////
  32. // LenientMockV8DetailedMemoryReporter
  33. LenientMockV8DetailedMemoryReporter::LenientMockV8DetailedMemoryReporter() =
  34. default;
  35. LenientMockV8DetailedMemoryReporter::~LenientMockV8DetailedMemoryReporter() =
  36. default;
  37. void LenientMockV8DetailedMemoryReporter::Bind(
  38. mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
  39. pending_receiver) {
  40. return receiver_.Bind(std::move(pending_receiver));
  41. }
  42. ////////////////////////////////////////////////////////////////////////////////
  43. // V8MemoryTestBase
  44. V8MemoryTestBase::V8MemoryTestBase()
  45. : bind_callback_(
  46. base::BindRepeating(&V8MemoryTestBase::BindReceiverOnMainSequence,
  47. base::Unretained(this))) {
  48. internal::SetBindV8DetailedMemoryReporterCallbackForTesting(&bind_callback_);
  49. }
  50. V8MemoryTestBase::~V8MemoryTestBase() {
  51. internal::SetBindV8DetailedMemoryReporterCallbackForTesting(nullptr);
  52. }
  53. void V8MemoryTestBase::ReplyWithData(
  54. blink::mojom::PerProcessV8MemoryUsagePtr data,
  55. MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback) {
  56. std::move(callback).Run(std::move(data));
  57. }
  58. void V8MemoryTestBase::DelayedReplyWithData(
  59. const base::TimeDelta& delay,
  60. blink::mojom::PerProcessV8MemoryUsagePtr data,
  61. MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback) {
  62. GetMainThreadTaskRunner()->PostDelayedTask(
  63. FROM_HERE, base::BindOnce(std::move(callback), std::move(data)), delay);
  64. }
  65. void V8MemoryTestBase::ExpectQuery(
  66. MockV8DetailedMemoryReporter* mock_reporter,
  67. base::RepeatingCallback<
  68. void(MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback)>
  69. responder,
  70. ExpectedMode expected_mode) {
  71. EXPECT_CALL(*mock_reporter, GetV8MemoryUsage(expected_mode, _))
  72. .WillOnce(
  73. [this, responder](
  74. ExpectedMode mode,
  75. MockV8DetailedMemoryReporter::GetV8MemoryUsageCallback callback) {
  76. this->last_query_time_ = base::TimeTicks::Now();
  77. responder.Run(std::move(callback));
  78. });
  79. }
  80. void V8MemoryTestBase::ExpectQueryAndReply(
  81. MockV8DetailedMemoryReporter* mock_reporter,
  82. blink::mojom::PerProcessV8MemoryUsagePtr data,
  83. ExpectedMode expected_mode) {
  84. ExpectQuery(mock_reporter,
  85. base::BindRepeating(&V8MemoryTestBase::ReplyWithData,
  86. base::Unretained(this), base::Passed(&data)),
  87. expected_mode);
  88. }
  89. void V8MemoryTestBase::ExpectQueryAndDelayReply(
  90. MockV8DetailedMemoryReporter* mock_reporter,
  91. const base::TimeDelta& delay,
  92. blink::mojom::PerProcessV8MemoryUsagePtr data,
  93. ExpectedMode expected_mode) {
  94. ExpectQuery(
  95. mock_reporter,
  96. base::BindRepeating(&V8MemoryTestBase::DelayedReplyWithData,
  97. base::Unretained(this), delay, base::Passed(&data)),
  98. expected_mode);
  99. }
  100. void V8MemoryTestBase::ExpectBindReceiver(
  101. MockV8DetailedMemoryReporter* mock_reporter,
  102. RenderProcessHostId expected_process_id) {
  103. using ::testing::Eq;
  104. using ::testing::Invoke;
  105. using ::testing::Property;
  106. using ::testing::WithArg;
  107. // Arg 0 is a
  108. // mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>. Pass it
  109. // to mock_reporter->Bind().
  110. //
  111. // Arg 1 is a RenderProcessHostProxy. Expect it to have the expected
  112. // process ID.
  113. EXPECT_CALL(*this,
  114. BindReceiverWithProxyHost(
  115. _, Property(&RenderProcessHostProxy::render_process_host_id,
  116. Eq(expected_process_id))))
  117. .WillOnce(WithArg<0>(
  118. Invoke(mock_reporter, &MockV8DetailedMemoryReporter::Bind)));
  119. }
  120. void V8MemoryTestBase::ExpectBindAndRespondToQuery(
  121. MockV8DetailedMemoryReporter* mock_reporter,
  122. blink::mojom::PerProcessV8MemoryUsagePtr data,
  123. RenderProcessHostId expected_process_id,
  124. ExpectedMode expected_mode) {
  125. ::testing::InSequence seq;
  126. ExpectBindReceiver(mock_reporter, expected_process_id);
  127. ExpectQueryAndReply(mock_reporter, std::move(data), expected_mode);
  128. }
  129. void V8MemoryTestBase::BindReceiverOnMainSequence(
  130. mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
  131. pending_receiver,
  132. RenderProcessHostProxy proxy) {
  133. GetMainThreadTaskRunner()->PostTask(
  134. FROM_HERE, base::BindOnce(&V8MemoryTestBase::BindReceiverWithProxyHost,
  135. base::Unretained(this),
  136. std::move(pending_receiver), proxy));
  137. }
  138. // Storage for static members.
  139. constexpr RenderProcessHostId V8MemoryTestBase::kTestProcessID;
  140. ////////////////////////////////////////////////////////////////////////////////
  141. // V8MemoryPerformanceManagerTestHarness
  142. V8MemoryPerformanceManagerTestHarness::V8MemoryPerformanceManagerTestHarness()
  143. : PerformanceManagerTestHarness(
  144. // Use MOCK_TIME so that ExpectQueryAndDelayReply can be used.
  145. base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
  146. GetGraphFeatures().EnableExecutionContextRegistry();
  147. GetGraphFeatures().EnableV8ContextTracker();
  148. }
  149. V8MemoryPerformanceManagerTestHarness::
  150. ~V8MemoryPerformanceManagerTestHarness() = default;
  151. void V8MemoryPerformanceManagerTestHarness::SetUp() {
  152. PerformanceManagerTestHarness::SetUp();
  153. if (!base::FeatureList::IsEnabled(features::kRunOnMainThread)) {
  154. // Precondition: CallOnGraph must run on a different sequence. Note that
  155. // all tasks passed to CallOnGraph will only run when run_loop.Run() is
  156. // called.
  157. ASSERT_TRUE(GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
  158. base::RunLoop run_loop;
  159. PerformanceManager::CallOnGraph(
  160. FROM_HERE, base::BindLambdaForTesting([&] {
  161. EXPECT_FALSE(
  162. this->GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
  163. run_loop.Quit();
  164. }));
  165. run_loop.Run();
  166. }
  167. // Set the active contents and simulate a navigation, which adds nodes to
  168. // the graph.
  169. content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
  170. SetContents(CreateTestWebContents());
  171. main_frame_ = content::NavigationSimulator::NavigateAndCommitFromBrowser(
  172. web_contents(), GURL(kMainFrameUrl));
  173. main_process_id_ = RenderProcessHostId(main_frame_->GetProcess()->GetID());
  174. }
  175. void V8MemoryPerformanceManagerTestHarness::CreateCrossProcessChildFrame() {
  176. // Since kMainFrameUrl has a different domain than kChildFrameUrl, the main
  177. // and child frames should end up in different processes.
  178. child_frame_ =
  179. content::RenderFrameHostTester::For(main_frame_)->AppendChild("frame1");
  180. child_frame_ = content::NavigationSimulator::NavigateAndCommitFromDocument(
  181. GURL(kChildFrameUrl), child_frame_);
  182. child_process_id_ = RenderProcessHostId(child_frame_->GetProcess()->GetID());
  183. ASSERT_NE(main_process_id_, child_process_id_);
  184. }
  185. scoped_refptr<base::SingleThreadTaskRunner>
  186. V8MemoryPerformanceManagerTestHarness::GetMainThreadTaskRunner() {
  187. return task_environment()->GetMainThreadTaskRunner();
  188. }
  189. // Storage for static members.
  190. constexpr char V8MemoryPerformanceManagerTestHarness::kMainFrameUrl[];
  191. constexpr char V8MemoryPerformanceManagerTestHarness::kChildFrameUrl[];
  192. ////////////////////////////////////////////////////////////////////////////////
  193. // WebMemoryTestHarness
  194. WebMemoryTestHarness::WebMemoryTestHarness() = default;
  195. WebMemoryTestHarness::~WebMemoryTestHarness() = default;
  196. void WebMemoryTestHarness::SetUp() {
  197. GetGraphFeatures().EnableV8ContextTracker();
  198. Super::SetUp();
  199. process_ = CreateNode<ProcessNodeImpl>();
  200. other_process_ = CreateNode<ProcessNodeImpl>();
  201. pages_.push_back(CreateNode<PageNodeImpl>());
  202. }
  203. int WebMemoryTestHarness::GetNextUniqueId() {
  204. return next_unique_id_++;
  205. }
  206. FrameNodeImpl* WebMemoryTestHarness::AddFrameNodeImpl(
  207. absl::optional<std::string> url,
  208. int browsing_instance_id,
  209. Bytes memory_usage,
  210. FrameNodeImpl* parent,
  211. FrameNodeImpl* opener,
  212. ProcessNodeImpl* process,
  213. absl::optional<std::string> id_attribute,
  214. absl::optional<std::string> src_attribute,
  215. Bytes canvas_memory_usage) {
  216. // If there's an opener, the new frame is also a new page.
  217. auto* page = pages_.front().get();
  218. if (opener) {
  219. pages_.push_back(CreateNode<PageNodeImpl>());
  220. page = pages_.back().get();
  221. page->SetOpenerFrameNode(opener);
  222. }
  223. int frame_routing_id = GetNextUniqueId();
  224. auto frame_token = blink::LocalFrameToken();
  225. auto frame = CreateNode<FrameNodeImpl>(
  226. process, page, parent, frame_routing_id, frame_token,
  227. content::BrowsingInstanceId(browsing_instance_id));
  228. if (url) {
  229. frame->OnNavigationCommitted(GURL(*url), /*same document*/ true);
  230. }
  231. if (memory_usage || canvas_memory_usage) {
  232. auto* data =
  233. V8DetailedMemoryExecutionContextData::CreateForTesting(frame.get());
  234. if (memory_usage)
  235. data->set_v8_bytes_used(memory_usage.value());
  236. if (canvas_memory_usage)
  237. data->set_canvas_bytes_used(canvas_memory_usage.value());
  238. }
  239. frames_.push_back(std::move(frame));
  240. FrameNodeImpl* frame_impl = frames_.back().get();
  241. // Create a V8ContextDescription with attribution data for this frame. (In
  242. // production this is done by PerformanceManager monitoring frame lifetime
  243. // events.)
  244. auto description = mojom::V8ContextDescription::New();
  245. description->token = blink::V8ContextToken();
  246. description->world_type = mojom::V8ContextWorldType::kMain;
  247. description->execution_context_token = frame_token;
  248. mojom::IframeAttributionDataPtr attribution;
  249. if (parent) {
  250. // Frame attribution attributes come from the frame's parent node, so
  251. // V8ContextTracker expects an IframeAttributionData. The attribute values
  252. // may be empty.
  253. attribution = mojom::IframeAttributionData::New();
  254. attribution->id = id_attribute;
  255. attribution->src = src_attribute;
  256. } else {
  257. // V8ContextTracker expects no IframeAttributionData.
  258. DCHECK(!id_attribute);
  259. DCHECK(!src_attribute);
  260. }
  261. // If the frame is in the same process as its parent include the attribution
  262. // in OnV8ContextCreated, otherwise it must be attached separately with
  263. // OnRemoteIframeAttached.
  264. DCHECK(frame_impl->process_node());
  265. if (parent && parent->process_node() != frame_impl->process_node()) {
  266. frame_impl->process_node()->OnV8ContextCreated(
  267. std::move(description), mojom::IframeAttributionDataPtr());
  268. V8ContextTracker::GetFromGraph(graph())->OnRemoteIframeAttachedForTesting(
  269. frame_impl, parent, blink::RemoteFrameToken(), std::move(attribution));
  270. } else {
  271. frame_impl->process_node()->OnV8ContextCreated(std::move(description),
  272. std::move(attribution));
  273. }
  274. return frame_impl;
  275. }
  276. WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNode(
  277. WorkerNode::WorkerType worker_type,
  278. std::string url,
  279. Bytes bytes,
  280. FrameNodeImpl* parent) {
  281. auto* worker_node = AddWorkerNodeImpl(worker_type, url, bytes);
  282. worker_node->AddClientFrame(parent);
  283. return worker_node;
  284. }
  285. WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNodeWithoutData(
  286. WorkerNode::WorkerType worker_type,
  287. FrameNodeImpl* parent) {
  288. auto worker_node = CreateNode<WorkerNodeImpl>(worker_type, process_.get());
  289. worker_node->AddClientFrame(parent);
  290. workers_.push_back(std::move(worker_node));
  291. return workers_.back().get();
  292. }
  293. WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNode(
  294. WorkerNode::WorkerType worker_type,
  295. std::string url,
  296. Bytes bytes,
  297. WorkerNodeImpl* parent) {
  298. auto* worker_node = AddWorkerNodeImpl(worker_type, url, bytes);
  299. worker_node->AddClientWorker(parent);
  300. return worker_node;
  301. }
  302. WorkerNodeImpl* WebMemoryTestHarness::AddWorkerNodeImpl(
  303. WorkerNode::WorkerType worker_type,
  304. std::string url,
  305. Bytes bytes) {
  306. auto worker_node = CreateNode<WorkerNodeImpl>(worker_type, process_.get());
  307. worker_node->OnFinalResponseURLDetermined(GURL(url));
  308. if (bytes) {
  309. V8DetailedMemoryExecutionContextData::CreateForTesting(worker_node.get())
  310. ->set_v8_bytes_used(*bytes);
  311. }
  312. workers_.push_back(std::move(worker_node));
  313. return workers_.back().get();
  314. }
  315. void WebMemoryTestHarness::SetBlinkMemory(Bytes bytes) {
  316. V8DetailedMemoryProcessData::GetOrCreateForTesting(process_node())
  317. ->set_blink_bytes_used(*bytes);
  318. }
  319. ////////////////////////////////////////////////////////////////////////////////
  320. // Free functions
  321. blink::mojom::PerProcessV8MemoryUsagePtr NewPerProcessV8MemoryUsage(
  322. size_t number_of_isolates) {
  323. auto data = blink::mojom::PerProcessV8MemoryUsage::New();
  324. for (size_t i = 0; i < number_of_isolates; ++i) {
  325. data->isolates.push_back(blink::mojom::PerIsolateV8MemoryUsage::New());
  326. }
  327. return data;
  328. }
  329. void AddIsolateMemoryUsage(blink::ExecutionContextToken token,
  330. uint64_t bytes_used,
  331. blink::mojom::PerIsolateV8MemoryUsage* isolate) {
  332. for (auto& entry : isolate->contexts) {
  333. if (entry->token == token) {
  334. entry->bytes_used = bytes_used;
  335. return;
  336. }
  337. }
  338. auto context = blink::mojom::PerContextV8MemoryUsage::New();
  339. context->token = token;
  340. context->bytes_used = bytes_used;
  341. isolate->contexts.push_back(std::move(context));
  342. }
  343. void AddIsolateCanvasMemoryUsage(
  344. blink::ExecutionContextToken token,
  345. uint64_t bytes_used,
  346. blink::mojom::PerIsolateV8MemoryUsage* isolate) {
  347. for (auto& entry : isolate->canvas_contexts) {
  348. if (entry->token == token) {
  349. entry->bytes_used = bytes_used;
  350. return;
  351. }
  352. }
  353. auto context = blink::mojom::PerContextCanvasMemoryUsage::New();
  354. context->token = token;
  355. context->bytes_used = bytes_used;
  356. isolate->canvas_contexts.push_back(std::move(context));
  357. }
  358. } // namespace v8_memory
  359. } // namespace performance_manager