v8_detailed_memory_decorator.cc 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  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_detailed_memory_decorator.h"
  5. #include <utility>
  6. #include <vector>
  7. #include "base/bind.h"
  8. #include "base/callback.h"
  9. #include "base/check.h"
  10. #include "base/containers/contains.h"
  11. #include "base/containers/cxx20_erase.h"
  12. #include "base/containers/flat_map.h"
  13. #include "base/memory/raw_ptr.h"
  14. #include "base/memory/weak_ptr.h"
  15. #include "base/timer/timer.h"
  16. #include "base/values.h"
  17. #include "components/performance_manager/public/execution_context/execution_context.h"
  18. #include "components/performance_manager/public/execution_context/execution_context_attached_data.h"
  19. #include "components/performance_manager/public/graph/frame_node.h"
  20. #include "components/performance_manager/public/graph/node_attached_data.h"
  21. #include "components/performance_manager/public/graph/node_data_describer_registry.h"
  22. #include "components/performance_manager/public/graph/worker_node.h"
  23. #include "components/performance_manager/public/render_process_host_proxy.h"
  24. #include "components/performance_manager/public/v8_memory/v8_detailed_memory.h"
  25. #include "content/public/browser/browser_task_traits.h"
  26. #include "content/public/browser/browser_thread.h"
  27. #include "content/public/browser/render_process_host.h"
  28. #include "content/public/common/process_type.h"
  29. #include "mojo/public/cpp/bindings/remote.h"
  30. #include "third_party/blink/public/common/tokens/tokens.h"
  31. using blink::ExecutionContextToken;
  32. using blink::mojom::PerContextCanvasMemoryUsagePtr;
  33. using blink::mojom::PerContextV8MemoryUsagePtr;
  34. namespace performance_manager {
  35. namespace v8_memory {
  36. class V8DetailedMemoryRequestQueue {
  37. public:
  38. V8DetailedMemoryRequestQueue() = default;
  39. ~V8DetailedMemoryRequestQueue();
  40. const V8DetailedMemoryRequest* GetNextRequest() const;
  41. const V8DetailedMemoryRequest* GetNextBoundedRequest() const;
  42. void AddMeasurementRequest(V8DetailedMemoryRequest* request);
  43. // Removes |request| if it is part of this queue, and returns the number of
  44. // elements removed (will be 0 or 1).
  45. size_t RemoveMeasurementRequest(V8DetailedMemoryRequest* request);
  46. void NotifyObserversOnMeasurementAvailable(
  47. const ProcessNode* process_node) const;
  48. void OnOwnerUnregistered();
  49. // Check the data invariant on the measurement request lists.
  50. void Validate();
  51. private:
  52. void ApplyToAllRequests(
  53. base::RepeatingCallback<void(V8DetailedMemoryRequest*)> callback) const;
  54. // Lists of requests sorted by min_time_between_requests (lowest first).
  55. std::vector<V8DetailedMemoryRequest*> bounded_measurement_requests_
  56. GUARDED_BY_CONTEXT(sequence_checker_);
  57. std::vector<V8DetailedMemoryRequest*> lazy_measurement_requests_
  58. GUARDED_BY_CONTEXT(sequence_checker_);
  59. SEQUENCE_CHECKER(sequence_checker_);
  60. };
  61. // This class is allowed to access
  62. // V8DetailedMemoryDecorator::NotifyObserversOnMeasurementAvailable.
  63. class V8DetailedMemoryDecorator::ObserverNotifier {
  64. public:
  65. void NotifyObserversOnMeasurementAvailable(const ProcessNode* process_node) {
  66. auto* decorator =
  67. V8DetailedMemoryDecorator::GetFromGraph(process_node->GetGraph());
  68. if (decorator)
  69. decorator->NotifyObserversOnMeasurementAvailable(
  70. base::PassKey<ObserverNotifier>(), process_node);
  71. }
  72. };
  73. namespace {
  74. using MeasurementMode = V8DetailedMemoryRequest::MeasurementMode;
  75. // Forwards the pending receiver to the RenderProcessHost and binds it on the
  76. // UI thread.
  77. void BindReceiverOnUIThread(
  78. mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
  79. pending_receiver,
  80. RenderProcessHostProxy proxy) {
  81. auto* render_process_host = proxy.Get();
  82. if (render_process_host) {
  83. render_process_host->BindReceiver(std::move(pending_receiver));
  84. }
  85. }
  86. bool IsMeasurementBounded(MeasurementMode mode) {
  87. switch (mode) {
  88. case MeasurementMode::kLazy:
  89. return false;
  90. case MeasurementMode::kBounded:
  91. return true;
  92. case MeasurementMode::kEagerForTesting:
  93. return true;
  94. }
  95. }
  96. // Returns the higher priority request of |a| and |b|, either of which can be
  97. // null, or nullptr if both are null.
  98. const V8DetailedMemoryRequest* ChooseHigherPriorityRequest(
  99. const V8DetailedMemoryRequest* a,
  100. const V8DetailedMemoryRequest* b) {
  101. if (!a)
  102. return b;
  103. if (!b)
  104. return a;
  105. if (a->min_time_between_requests() < b->min_time_between_requests())
  106. return a;
  107. if (b->min_time_between_requests() < a->min_time_between_requests())
  108. return b;
  109. // Break ties by prioritizing bounded requests.
  110. if (IsMeasurementBounded(a->mode()))
  111. return a;
  112. return b;
  113. }
  114. // May only be used from the performance manager sequence.
  115. internal::BindV8DetailedMemoryReporterCallback* g_test_bind_callback = nullptr;
  116. // Per-frame memory measurement involves the following classes that live on the
  117. // PM sequence:
  118. //
  119. // V8DetailedMemoryDecorator: Central rendezvous point. Coordinates
  120. // V8DetailedMemoryRequest and V8DetailedMemoryObserver objects. Owned by
  121. // the graph; created the first time
  122. // V8DetailedMemoryRequest::StartMeasurement is called.
  123. // TODO(b/1080672): Currently this lives forever; should be cleaned up when
  124. // there are no more measurements scheduled.
  125. //
  126. // V8DetailedMemoryRequest: Indicates that a caller wants memory to be measured
  127. // at a specific interval. Owned by the caller but must live on the PM
  128. // sequence. V8DetailedMemoryRequest objects register themselves with
  129. // V8DetailedMemoryDecorator on creation and unregister themselves on
  130. // deletion, which cancels the corresponding measurement.
  131. //
  132. // V8DetailedMemoryRequestQueue: A priority queue of memory requests. The
  133. // decorator will hold a global queue of requests that measure every
  134. // process, and each ProcessNode will have a queue of requests that measure
  135. // only that process.
  136. //
  137. // NodeAttachedProcessData: Private class that schedules measurements and holds
  138. // the results for an individual process. Owned by the ProcessNode; created
  139. // when measurements start.
  140. // TODO(b/1080672): Currently this lives forever; should be cleaned up when
  141. // there are no more measurements scheduled.
  142. //
  143. // V8DetailedMemoryProcessData: Public accessor to the measurement results held
  144. // in a NodeAttachedProcessData, which owns it.
  145. //
  146. // ExecutionContextAttachedData: Private class that holds the measurement
  147. // results for an execution context. Owned by the ExecutionContext; created
  148. // when a measurement result arrives.
  149. // TODO(b/1080672): Currently this lives forever; should be cleaned up when
  150. // there are no more measurements scheduled.
  151. //
  152. // V8DetailedMemoryExecutionContextData: Public accessor to the measurement
  153. // results held in a ExecutionContextAttachedData, which owns it.
  154. //
  155. // V8DetailedMemoryObserver: Callers can implement this and register with
  156. // V8DetailedMemoryDecorator::AddObserver() to be notified when
  157. // measurements are available for a process. Owned by the caller but must
  158. // live on the PM sequence.
  159. //
  160. // Additional wrapper classes can access these classes from other sequences:
  161. //
  162. // V8DetailedMemoryRequestAnySeq: Wraps V8DetailedMemoryRequest. Owned by the
  163. // caller and lives on any sequence.
  164. //
  165. // V8DetailedMemoryObserverAnySeq: Callers can implement this and register it
  166. // with V8DetailedMemoryRequestAnySeq::AddObserver() to be notified when
  167. // measurements are available for a process. Owned by the caller and lives
  168. // on the same sequence as the V8DetailedMemoryRequestAnySeq.
  169. ////////////////////////////////////////////////////////////////////////////////
  170. // ExecutionContextAttachedData
  171. class ExecutionContextAttachedData
  172. : public execution_context::ExecutionContextAttachedData<
  173. ExecutionContextAttachedData> {
  174. public:
  175. explicit ExecutionContextAttachedData(
  176. const execution_context::ExecutionContext* ec) {}
  177. ~ExecutionContextAttachedData() override = default;
  178. ExecutionContextAttachedData(const ExecutionContextAttachedData&) = delete;
  179. ExecutionContextAttachedData& operator=(const ExecutionContextAttachedData&) =
  180. delete;
  181. const V8DetailedMemoryExecutionContextData* data() const {
  182. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  183. return data_available_ ? &data_ : nullptr;
  184. }
  185. static V8DetailedMemoryExecutionContextData*
  186. GetOrCreateForTesting( // IN-TEST
  187. const execution_context::ExecutionContext* ec);
  188. private:
  189. friend class NodeAttachedProcessData;
  190. V8DetailedMemoryExecutionContextData data_
  191. GUARDED_BY_CONTEXT(sequence_checker_);
  192. bool data_available_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
  193. SEQUENCE_CHECKER(sequence_checker_);
  194. };
  195. // static
  196. V8DetailedMemoryExecutionContextData*
  197. ExecutionContextAttachedData::GetOrCreateForTesting(
  198. const execution_context::ExecutionContext* ec) {
  199. auto* ec_data = ExecutionContextAttachedData::GetOrCreate(ec);
  200. DCHECK_CALLED_ON_VALID_SEQUENCE(ec_data->sequence_checker_);
  201. ec_data->data_available_ = true;
  202. return &ec_data->data_;
  203. }
  204. ////////////////////////////////////////////////////////////////////////////////
  205. // NodeAttachedProcessData
  206. class NodeAttachedProcessData
  207. : public ExternalNodeAttachedDataImpl<NodeAttachedProcessData> {
  208. public:
  209. explicit NodeAttachedProcessData(const ProcessNode* process_node);
  210. ~NodeAttachedProcessData() override = default;
  211. NodeAttachedProcessData(const NodeAttachedProcessData&) = delete;
  212. NodeAttachedProcessData& operator=(const NodeAttachedProcessData&) = delete;
  213. // Runs the given |callback| for every ProcessNode in |graph| with type
  214. // PROCESS_TYPE_RENDERER, passing the NodeAttachedProcessData attached to the
  215. // node.
  216. static void ApplyToAllRenderers(
  217. Graph* graph,
  218. base::RepeatingCallback<void(NodeAttachedProcessData*)> callback);
  219. const V8DetailedMemoryProcessData* data() const {
  220. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  221. return data_available_ ? &data_ : nullptr;
  222. }
  223. void ScheduleNextMeasurement();
  224. V8DetailedMemoryRequestQueue& process_measurement_requests() {
  225. return process_measurement_requests_;
  226. }
  227. static V8DetailedMemoryProcessData* GetOrCreateForTesting( // IN-TEST
  228. const ProcessNode* process_node);
  229. private:
  230. void StartMeasurement(MeasurementMode mode);
  231. void ScheduleUpgradeToBoundedMeasurement();
  232. void UpgradeToBoundedMeasurementIfNeeded(MeasurementMode bounded_mode);
  233. void EnsureRemote();
  234. void OnV8MemoryUsage(blink::mojom::PerProcessV8MemoryUsagePtr result);
  235. const raw_ptr<const ProcessNode> process_node_;
  236. // Measurement requests that will be sent to this process only.
  237. V8DetailedMemoryRequestQueue process_measurement_requests_
  238. GUARDED_BY_CONTEXT(sequence_checker_);
  239. mojo::Remote<blink::mojom::V8DetailedMemoryReporter> resource_usage_reporter_
  240. GUARDED_BY_CONTEXT(sequence_checker_);
  241. // State transitions:
  242. //
  243. // +-----------------------------------+
  244. // | |
  245. // | +-> MeasuringLazy +-+
  246. // v | +
  247. // Idle +-> Waiting +> |
  248. // ^ | v
  249. // | +-> MeasuringBounded +-+
  250. // | |
  251. // +--------------------------------------+
  252. enum class State {
  253. kIdle, // No measurements scheduled.
  254. kWaiting, // Waiting to take a measurement.
  255. kMeasuringBounded, // Waiting for results from a bounded measurement.
  256. kMeasuringLazy, // Waiting for results from a lazy measurement.
  257. };
  258. State state_ GUARDED_BY_CONTEXT(sequence_checker_) = State::kIdle;
  259. // Used to schedule the next measurement.
  260. base::TimeTicks last_request_time_ GUARDED_BY_CONTEXT(sequence_checker_);
  261. base::OneShotTimer request_timer_ GUARDED_BY_CONTEXT(sequence_checker_);
  262. base::OneShotTimer bounded_upgrade_timer_
  263. GUARDED_BY_CONTEXT(sequence_checker_);
  264. V8DetailedMemoryProcessData data_ GUARDED_BY_CONTEXT(sequence_checker_);
  265. bool data_available_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
  266. SEQUENCE_CHECKER(sequence_checker_);
  267. base::WeakPtrFactory<NodeAttachedProcessData> weak_factory_
  268. GUARDED_BY_CONTEXT(sequence_checker_){this};
  269. };
  270. NodeAttachedProcessData::NodeAttachedProcessData(
  271. const ProcessNode* process_node)
  272. : process_node_(process_node) {
  273. ScheduleNextMeasurement();
  274. }
  275. // static
  276. void NodeAttachedProcessData::ApplyToAllRenderers(
  277. Graph* graph,
  278. base::RepeatingCallback<void(NodeAttachedProcessData*)> callback) {
  279. for (const ProcessNode* node : graph->GetAllProcessNodes()) {
  280. NodeAttachedProcessData* process_data = NodeAttachedProcessData::Get(node);
  281. if (!process_data) {
  282. // NodeAttachedProcessData should have been created for all renderer
  283. // processes in OnProcessNodeAdded.
  284. DCHECK_NE(content::PROCESS_TYPE_RENDERER, node->GetProcessType());
  285. continue;
  286. }
  287. callback.Run(process_data);
  288. }
  289. }
  290. void NodeAttachedProcessData::ScheduleNextMeasurement() {
  291. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  292. process_measurement_requests_.Validate();
  293. if (state_ == State::kMeasuringLazy) {
  294. // Upgrade to a bounded measurement if the lazy measurement is taking too
  295. // long. Otherwise do nothing until the current measurement finishes.
  296. // ScheduleNextMeasurement will be called again at that point.
  297. ScheduleUpgradeToBoundedMeasurement();
  298. return;
  299. }
  300. if (state_ == State::kMeasuringBounded) {
  301. // Don't restart the timer until the current measurement finishes.
  302. // ScheduleNextMeasurement will be called again at that point.
  303. return;
  304. }
  305. // Find the next request for this process, checking both the per-process
  306. // queue and the global queue.
  307. const V8DetailedMemoryRequest* next_request =
  308. process_measurement_requests_.GetNextRequest();
  309. auto* decorator =
  310. V8DetailedMemoryDecorator::GetFromGraph(process_node_->GetGraph());
  311. if (decorator) {
  312. next_request =
  313. ChooseHigherPriorityRequest(next_request, decorator->GetNextRequest());
  314. }
  315. if (!next_request) {
  316. // All measurements have been cancelled, or decorator was removed from
  317. // graph.
  318. state_ = State::kIdle;
  319. request_timer_.Stop();
  320. bounded_upgrade_timer_.Stop();
  321. last_request_time_ = base::TimeTicks();
  322. return;
  323. }
  324. state_ = State::kWaiting;
  325. if (last_request_time_.is_null()) {
  326. // This is the first measurement. Perform it immediately.
  327. StartMeasurement(next_request->mode());
  328. return;
  329. }
  330. base::TimeTicks next_request_time =
  331. last_request_time_ + next_request->min_time_between_requests();
  332. request_timer_.Start(
  333. FROM_HERE, next_request_time - base::TimeTicks::Now(),
  334. base::BindOnce(&NodeAttachedProcessData::StartMeasurement,
  335. base::Unretained(this), next_request->mode()));
  336. }
  337. void NodeAttachedProcessData::StartMeasurement(MeasurementMode mode) {
  338. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  339. if (IsMeasurementBounded(mode)) {
  340. DCHECK(state_ == State::kWaiting || state_ == State::kMeasuringLazy);
  341. state_ = State::kMeasuringBounded;
  342. } else {
  343. DCHECK_EQ(state_, State::kWaiting);
  344. state_ = State::kMeasuringLazy;
  345. // Ensure this lazy measurement doesn't starve any bounded measurements in
  346. // the queue.
  347. ScheduleUpgradeToBoundedMeasurement();
  348. }
  349. last_request_time_ = base::TimeTicks::Now();
  350. EnsureRemote();
  351. // TODO(b/1080672): WeakPtr is used in case NodeAttachedProcessData is
  352. // cleaned up while a request to a renderer is outstanding. Currently this
  353. // never actually happens (it is destroyed only when the graph is torn down,
  354. // which should happen after renderers are destroyed). Should clean up
  355. // NodeAttachedProcessData when the last V8DetailedMemoryRequest is deleted,
  356. // which could happen at any time.
  357. blink::mojom::V8DetailedMemoryReporter::Mode mojo_mode;
  358. switch (mode) {
  359. case MeasurementMode::kLazy:
  360. mojo_mode = blink::mojom::V8DetailedMemoryReporter::Mode::LAZY;
  361. break;
  362. case MeasurementMode::kBounded:
  363. mojo_mode = blink::mojom::V8DetailedMemoryReporter::Mode::DEFAULT;
  364. break;
  365. case MeasurementMode::kEagerForTesting:
  366. mojo_mode = blink::mojom::V8DetailedMemoryReporter::Mode::EAGER;
  367. break;
  368. }
  369. resource_usage_reporter_->GetV8MemoryUsage(
  370. mojo_mode, base::BindOnce(&NodeAttachedProcessData::OnV8MemoryUsage,
  371. weak_factory_.GetWeakPtr()));
  372. }
  373. void NodeAttachedProcessData::ScheduleUpgradeToBoundedMeasurement() {
  374. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  375. DCHECK_EQ(state_, State::kMeasuringLazy);
  376. const V8DetailedMemoryRequest* bounded_request =
  377. process_measurement_requests_.GetNextBoundedRequest();
  378. auto* decorator =
  379. V8DetailedMemoryDecorator::GetFromGraph(process_node_->GetGraph());
  380. if (decorator) {
  381. bounded_request = ChooseHigherPriorityRequest(
  382. bounded_request, decorator->GetNextBoundedRequest());
  383. }
  384. if (!bounded_request) {
  385. // All measurements have been cancelled, or decorator was removed from
  386. // graph.
  387. return;
  388. }
  389. base::TimeTicks bounded_request_time =
  390. last_request_time_ + bounded_request->min_time_between_requests();
  391. bounded_upgrade_timer_.Start(
  392. FROM_HERE, bounded_request_time - base::TimeTicks::Now(),
  393. base::BindOnce(
  394. &NodeAttachedProcessData::UpgradeToBoundedMeasurementIfNeeded,
  395. base::Unretained(this), bounded_request->mode()));
  396. }
  397. void NodeAttachedProcessData::UpgradeToBoundedMeasurementIfNeeded(
  398. MeasurementMode bounded_mode) {
  399. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  400. if (state_ != State::kMeasuringLazy) {
  401. // State changed before timer expired.
  402. return;
  403. }
  404. DCHECK(IsMeasurementBounded(bounded_mode));
  405. StartMeasurement(bounded_mode);
  406. }
  407. void NodeAttachedProcessData::OnV8MemoryUsage(
  408. blink::mojom::PerProcessV8MemoryUsagePtr result) {
  409. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  410. // Data has arrived so don't upgrade lazy requests to bounded, even if
  411. // another lazy request is issued before the timer expires.
  412. bounded_upgrade_timer_.Stop();
  413. // Distribute the data to the frames.
  414. // If a frame doesn't have corresponding data in the result, clear any data
  415. // it may have had. Any datum in the result that doesn't correspond to an
  416. // existing frame is likewise accrued to detached bytes.
  417. uint64_t detached_v8_bytes_used = 0;
  418. uint64_t detached_canvas_bytes_used = 0;
  419. uint64_t shared_v8_bytes_used = 0;
  420. uint64_t blink_bytes_used = 0;
  421. // Create a mapping from token to execution context usage for the merge below.
  422. std::vector<std::pair<ExecutionContextToken, PerContextV8MemoryUsagePtr>>
  423. v8_memory;
  424. std::vector<std::pair<ExecutionContextToken, PerContextCanvasMemoryUsagePtr>>
  425. canvas_memory;
  426. for (auto& isolate : result->isolates) {
  427. for (auto& entry : isolate->contexts) {
  428. v8_memory.emplace_back(entry->token, std::move(entry));
  429. }
  430. for (auto& entry : isolate->canvas_contexts) {
  431. canvas_memory.emplace_back(entry->token, std::move(entry));
  432. }
  433. detached_v8_bytes_used += isolate->detached_bytes_used;
  434. shared_v8_bytes_used += isolate->shared_bytes_used;
  435. blink_bytes_used += isolate->blink_bytes_used;
  436. }
  437. size_t v8_frame_count = v8_memory.size();
  438. size_t canvas_frame_count = canvas_memory.size();
  439. base::flat_map<ExecutionContextToken, PerContextV8MemoryUsagePtr>
  440. associated_v8_memory(std::move(v8_memory));
  441. base::flat_map<ExecutionContextToken, PerContextCanvasMemoryUsagePtr>
  442. associated_canvas_memory(std::move(canvas_memory));
  443. // Validate that the frame tokens were all unique. If there are duplicates,
  444. // the map will arbitrarily drop all but one record per unique token.
  445. DCHECK_EQ(associated_v8_memory.size(), v8_frame_count);
  446. DCHECK_EQ(associated_canvas_memory.size(), canvas_frame_count);
  447. std::vector<const execution_context::ExecutionContext*> execution_contexts;
  448. for (auto* node : process_node_->GetFrameNodes()) {
  449. execution_contexts.push_back(
  450. execution_context::ExecutionContext::From(node));
  451. }
  452. for (auto* node : process_node_->GetWorkerNodes()) {
  453. execution_contexts.push_back(
  454. execution_context::ExecutionContext::From(node));
  455. }
  456. for (const execution_context::ExecutionContext* ec : execution_contexts) {
  457. auto it = associated_v8_memory.find(ec->GetToken());
  458. auto it_canvas = associated_canvas_memory.find(ec->GetToken());
  459. if (it == associated_v8_memory.end()) {
  460. // No data for this node, clear any data associated with it.
  461. // Note that we may have canvas memory for the context even if there
  462. // is no V8 memory (e.g. when the context was added after V8 memory
  463. // measurement but before canvas measurement). We drop such canvas
  464. // memory for simplicity because such case are rare and not important
  465. // for the users.
  466. ExecutionContextAttachedData::Destroy(ec);
  467. } else {
  468. ExecutionContextAttachedData* ec_data =
  469. ExecutionContextAttachedData::GetOrCreate(ec);
  470. DCHECK_CALLED_ON_VALID_SEQUENCE(ec_data->sequence_checker_);
  471. ec_data->data_available_ = true;
  472. ec_data->data_.set_v8_bytes_used(it->second->bytes_used);
  473. ec_data->data_.set_url(std::move(it->second->url));
  474. // Zero out this datum as its usage has been consumed.
  475. // We avoid erase() here because it may take O(n) time.
  476. it->second.reset();
  477. if (it_canvas != associated_canvas_memory.end()) {
  478. ec_data->data_.set_canvas_bytes_used(it_canvas->second->bytes_used);
  479. it_canvas->second.reset();
  480. }
  481. }
  482. }
  483. for (const auto& it : associated_v8_memory) {
  484. if (it.second.is_null()) {
  485. // Execution context was already consumed.
  486. continue;
  487. }
  488. // Accrue the data for non-existent frames to detached bytes.
  489. detached_v8_bytes_used += it.second->bytes_used;
  490. }
  491. for (const auto& it : associated_canvas_memory) {
  492. if (it.second.is_null()) {
  493. // Execution context was already consumed.
  494. continue;
  495. }
  496. // Accrue the data for non-existent frames to detached bytes.
  497. detached_canvas_bytes_used += it.second->bytes_used;
  498. }
  499. data_available_ = true;
  500. data_.set_detached_v8_bytes_used(detached_v8_bytes_used);
  501. data_.set_detached_canvas_bytes_used(detached_canvas_bytes_used);
  502. data_.set_shared_v8_bytes_used(shared_v8_bytes_used);
  503. data_.set_blink_bytes_used(blink_bytes_used);
  504. // Schedule another measurement for this process node unless one is already
  505. // scheduled.
  506. if (state_ != State::kWaiting) {
  507. state_ = State::kIdle;
  508. ScheduleNextMeasurement();
  509. }
  510. process_measurement_requests_.NotifyObserversOnMeasurementAvailable(
  511. process_node_);
  512. V8DetailedMemoryDecorator::ObserverNotifier()
  513. .NotifyObserversOnMeasurementAvailable(process_node_);
  514. }
  515. void NodeAttachedProcessData::EnsureRemote() {
  516. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  517. if (resource_usage_reporter_.is_bound())
  518. return;
  519. // This interface is implemented in //content/renderer/performance_manager.
  520. mojo::PendingReceiver<blink::mojom::V8DetailedMemoryReporter>
  521. pending_receiver = resource_usage_reporter_.BindNewPipeAndPassReceiver();
  522. RenderProcessHostProxy proxy = process_node_->GetRenderProcessHostProxy();
  523. if (g_test_bind_callback) {
  524. g_test_bind_callback->Run(std::move(pending_receiver), std::move(proxy));
  525. } else {
  526. content::GetUIThreadTaskRunner({})->PostTask(
  527. FROM_HERE,
  528. base::BindOnce(&BindReceiverOnUIThread, std::move(pending_receiver),
  529. std::move(proxy)));
  530. }
  531. }
  532. // static
  533. V8DetailedMemoryProcessData* NodeAttachedProcessData::GetOrCreateForTesting(
  534. const ProcessNode* process_node) {
  535. auto* node_data = NodeAttachedProcessData::GetOrCreate(process_node);
  536. DCHECK_CALLED_ON_VALID_SEQUENCE(node_data->sequence_checker_);
  537. node_data->data_available_ = true;
  538. return &node_data->data_;
  539. }
  540. } // namespace
  541. namespace internal {
  542. void SetBindV8DetailedMemoryReporterCallbackForTesting( // IN-TEST
  543. BindV8DetailedMemoryReporterCallback* callback) {
  544. g_test_bind_callback = callback;
  545. }
  546. void DestroyV8DetailedMemoryDecoratorForTesting(Graph* graph) {
  547. auto* decorator = V8DetailedMemoryDecorator::GetFromGraph(graph);
  548. if (decorator)
  549. graph->TakeFromGraph(decorator);
  550. }
  551. } // namespace internal
  552. ////////////////////////////////////////////////////////////////////////////////
  553. // V8DetailedMemoryDecorator
  554. V8DetailedMemoryDecorator::V8DetailedMemoryDecorator()
  555. : measurement_requests_(std::make_unique<V8DetailedMemoryRequestQueue>()) {}
  556. V8DetailedMemoryDecorator::~V8DetailedMemoryDecorator() = default;
  557. void V8DetailedMemoryDecorator::OnPassedToGraph(Graph* graph) {
  558. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  559. DCHECK_EQ(nullptr, graph_);
  560. graph_ = graph;
  561. graph->RegisterObject(this);
  562. // Iterate over the existing process nodes to put them under observation.
  563. for (const ProcessNode* process_node : graph->GetAllProcessNodes())
  564. OnProcessNodeAdded(process_node);
  565. graph->AddProcessNodeObserver(this);
  566. graph->GetNodeDataDescriberRegistry()->RegisterDescriber(
  567. this, "V8DetailedMemoryDecorator");
  568. }
  569. void V8DetailedMemoryDecorator::OnTakenFromGraph(Graph* graph) {
  570. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  571. DCHECK_EQ(graph, graph_);
  572. ApplyToAllRequestQueues(
  573. base::BindRepeating(&V8DetailedMemoryRequestQueue::OnOwnerUnregistered));
  574. UpdateProcessMeasurementSchedules();
  575. graph->GetNodeDataDescriberRegistry()->UnregisterDescriber(this);
  576. graph->RemoveProcessNodeObserver(this);
  577. graph->UnregisterObject(this);
  578. graph_ = nullptr;
  579. }
  580. void V8DetailedMemoryDecorator::OnProcessNodeAdded(
  581. const ProcessNode* process_node) {
  582. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  583. DCHECK_EQ(nullptr, NodeAttachedProcessData::Get(process_node));
  584. // Only renderer processes have frames. Don't attempt to connect to other
  585. // process types.
  586. if (process_node->GetProcessType() != content::PROCESS_TYPE_RENDERER)
  587. return;
  588. // Creating the NodeAttachedProcessData will start a measurement.
  589. NodeAttachedProcessData::GetOrCreate(process_node);
  590. }
  591. void V8DetailedMemoryDecorator::OnBeforeProcessNodeRemoved(
  592. const ProcessNode* process_node) {
  593. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  594. // Only renderer processes have data.
  595. if (process_node->GetProcessType() != content::PROCESS_TYPE_RENDERER)
  596. return;
  597. auto* process_data = NodeAttachedProcessData::Get(process_node);
  598. DCHECK(process_data);
  599. process_data->process_measurement_requests().OnOwnerUnregistered();
  600. }
  601. base::Value V8DetailedMemoryDecorator::DescribeFrameNodeData(
  602. const FrameNode* frame_node) const {
  603. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  604. const auto* const frame_data =
  605. V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node);
  606. if (!frame_data)
  607. return base::Value();
  608. base::Value dict(base::Value::Type::DICTIONARY);
  609. dict.SetIntKey("v8_bytes_used", frame_data->v8_bytes_used());
  610. return dict;
  611. }
  612. base::Value V8DetailedMemoryDecorator::DescribeProcessNodeData(
  613. const ProcessNode* process_node) const {
  614. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  615. const auto* const process_data =
  616. V8DetailedMemoryProcessData::ForProcessNode(process_node);
  617. if (!process_data)
  618. return base::Value();
  619. DCHECK_EQ(content::PROCESS_TYPE_RENDERER, process_node->GetProcessType());
  620. base::Value dict(base::Value::Type::DICTIONARY);
  621. dict.SetIntKey("detached_v8_bytes_used",
  622. process_data->detached_v8_bytes_used());
  623. dict.SetIntKey("shared_v8_bytes_used", process_data->shared_v8_bytes_used());
  624. return dict;
  625. }
  626. const V8DetailedMemoryRequest* V8DetailedMemoryDecorator::GetNextRequest()
  627. const {
  628. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  629. return measurement_requests_->GetNextRequest();
  630. }
  631. const V8DetailedMemoryRequest*
  632. V8DetailedMemoryDecorator::GetNextBoundedRequest() const {
  633. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  634. return measurement_requests_->GetNextBoundedRequest();
  635. }
  636. void V8DetailedMemoryDecorator::AddMeasurementRequest(
  637. base::PassKey<V8DetailedMemoryRequest> key,
  638. V8DetailedMemoryRequest* request,
  639. const ProcessNode* process_node) {
  640. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  641. if (process_node) {
  642. auto* process_data = NodeAttachedProcessData::Get(process_node);
  643. DCHECK(process_data);
  644. process_data->process_measurement_requests().AddMeasurementRequest(request);
  645. } else {
  646. measurement_requests_->AddMeasurementRequest(request);
  647. }
  648. UpdateProcessMeasurementSchedules();
  649. }
  650. void V8DetailedMemoryDecorator::RemoveMeasurementRequest(
  651. base::PassKey<V8DetailedMemoryRequest> key,
  652. V8DetailedMemoryRequest* request) {
  653. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  654. // Attempt to remove this request from all process-specific queues and the
  655. // global queue. It will only be in one of them.
  656. size_t removal_count = 0;
  657. ApplyToAllRequestQueues(base::BindRepeating(
  658. // Raw pointers are safe because this callback is synchronous.
  659. [](V8DetailedMemoryRequest* request, size_t* removal_count,
  660. V8DetailedMemoryRequestQueue* queue) {
  661. (*removal_count) += queue->RemoveMeasurementRequest(request);
  662. },
  663. request, &removal_count));
  664. DCHECK_EQ(removal_count, 1ULL);
  665. UpdateProcessMeasurementSchedules();
  666. }
  667. void V8DetailedMemoryDecorator::ApplyToAllRequestQueues(
  668. RequestQueueCallback callback) const {
  669. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  670. callback.Run(measurement_requests_.get());
  671. NodeAttachedProcessData::ApplyToAllRenderers(
  672. graph_, base::BindRepeating(
  673. [](RequestQueueCallback callback,
  674. NodeAttachedProcessData* process_data) {
  675. callback.Run(&process_data->process_measurement_requests());
  676. },
  677. std::move(callback)));
  678. }
  679. void V8DetailedMemoryDecorator::UpdateProcessMeasurementSchedules() const {
  680. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  681. DCHECK(graph_);
  682. measurement_requests_->Validate();
  683. NodeAttachedProcessData::ApplyToAllRenderers(
  684. graph_,
  685. base::BindRepeating(&NodeAttachedProcessData::ScheduleNextMeasurement));
  686. }
  687. void V8DetailedMemoryDecorator::NotifyObserversOnMeasurementAvailable(
  688. base::PassKey<ObserverNotifier> key,
  689. const ProcessNode* process_node) const {
  690. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  691. measurement_requests_->NotifyObserversOnMeasurementAvailable(process_node);
  692. }
  693. // static
  694. const V8DetailedMemoryExecutionContextData*
  695. V8DetailedMemoryDecorator::GetExecutionContextData(const FrameNode* node) {
  696. const auto* ec = execution_context::ExecutionContext::From(node);
  697. return GetExecutionContextData(ec);
  698. }
  699. // static
  700. const V8DetailedMemoryExecutionContextData*
  701. V8DetailedMemoryDecorator::GetExecutionContextData(const WorkerNode* node) {
  702. const auto* ec = execution_context::ExecutionContext::From(node);
  703. return GetExecutionContextData(ec);
  704. }
  705. // static
  706. const V8DetailedMemoryExecutionContextData*
  707. V8DetailedMemoryDecorator::GetExecutionContextData(
  708. const execution_context::ExecutionContext* ec) {
  709. const auto* node_data = ExecutionContextAttachedData::Get(ec);
  710. return node_data ? node_data->data() : nullptr;
  711. }
  712. // static
  713. V8DetailedMemoryExecutionContextData*
  714. V8DetailedMemoryDecorator::CreateExecutionContextDataForTesting(
  715. const FrameNode* node) {
  716. const auto* ec = execution_context::ExecutionContext::From(node);
  717. return ExecutionContextAttachedData::GetOrCreateForTesting(ec);
  718. }
  719. // static
  720. V8DetailedMemoryExecutionContextData*
  721. V8DetailedMemoryDecorator::CreateExecutionContextDataForTesting(
  722. const WorkerNode* node) {
  723. const auto* ec = execution_context::ExecutionContext::From(node);
  724. return ExecutionContextAttachedData::GetOrCreateForTesting(ec);
  725. }
  726. // static
  727. const V8DetailedMemoryProcessData* V8DetailedMemoryDecorator::GetProcessData(
  728. const ProcessNode* node) {
  729. auto* node_data = NodeAttachedProcessData::Get(node);
  730. return node_data ? node_data->data() : nullptr;
  731. }
  732. // static
  733. V8DetailedMemoryProcessData*
  734. V8DetailedMemoryDecorator::CreateProcessDataForTesting(
  735. const ProcessNode* node) {
  736. return NodeAttachedProcessData::GetOrCreateForTesting(node);
  737. }
  738. ////////////////////////////////////////////////////////////////////////////////
  739. // V8DetailedMemoryRequestQueue
  740. V8DetailedMemoryRequestQueue::~V8DetailedMemoryRequestQueue() {
  741. DCHECK(bounded_measurement_requests_.empty());
  742. DCHECK(lazy_measurement_requests_.empty());
  743. }
  744. const V8DetailedMemoryRequest* V8DetailedMemoryRequestQueue::GetNextRequest()
  745. const {
  746. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  747. return ChooseHigherPriorityRequest(GetNextBoundedRequest(),
  748. lazy_measurement_requests_.empty()
  749. ? nullptr
  750. : lazy_measurement_requests_.front());
  751. }
  752. const V8DetailedMemoryRequest*
  753. V8DetailedMemoryRequestQueue::GetNextBoundedRequest() const {
  754. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  755. return bounded_measurement_requests_.empty()
  756. ? nullptr
  757. : bounded_measurement_requests_.front();
  758. }
  759. void V8DetailedMemoryRequestQueue::AddMeasurementRequest(
  760. V8DetailedMemoryRequest* request) {
  761. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  762. DCHECK(request);
  763. std::vector<V8DetailedMemoryRequest*>& measurement_requests =
  764. IsMeasurementBounded(request->mode()) ? bounded_measurement_requests_
  765. : lazy_measurement_requests_;
  766. DCHECK(!base::Contains(measurement_requests, request))
  767. << "V8DetailedMemoryRequest object added twice";
  768. // Each user of the decorator is expected to issue a single
  769. // V8DetailedMemoryRequest, so the size of measurement_requests is too low
  770. // to make the complexity of real priority queue worthwhile.
  771. for (std::vector<V8DetailedMemoryRequest*>::const_iterator it =
  772. measurement_requests.begin();
  773. it != measurement_requests.end(); ++it) {
  774. if (request->min_time_between_requests() <
  775. (*it)->min_time_between_requests()) {
  776. measurement_requests.insert(it, request);
  777. return;
  778. }
  779. }
  780. measurement_requests.push_back(request);
  781. }
  782. size_t V8DetailedMemoryRequestQueue::RemoveMeasurementRequest(
  783. V8DetailedMemoryRequest* request) {
  784. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  785. DCHECK(request);
  786. return base::Erase(IsMeasurementBounded(request->mode())
  787. ? bounded_measurement_requests_
  788. : lazy_measurement_requests_,
  789. request);
  790. }
  791. void V8DetailedMemoryRequestQueue::NotifyObserversOnMeasurementAvailable(
  792. const ProcessNode* process_node) const {
  793. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  794. // Raw pointers are safe because the callback is synchronous.
  795. ApplyToAllRequests(base::BindRepeating(
  796. [](const ProcessNode* process_node, V8DetailedMemoryRequest* request) {
  797. request->NotifyObserversOnMeasurementAvailable(
  798. base::PassKey<V8DetailedMemoryRequestQueue>(), process_node);
  799. },
  800. process_node));
  801. }
  802. void V8DetailedMemoryRequestQueue::OnOwnerUnregistered() {
  803. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  804. ApplyToAllRequests(base::BindRepeating([](V8DetailedMemoryRequest* request) {
  805. request->OnOwnerUnregistered(base::PassKey<V8DetailedMemoryRequestQueue>());
  806. }));
  807. bounded_measurement_requests_.clear();
  808. lazy_measurement_requests_.clear();
  809. }
  810. void V8DetailedMemoryRequestQueue::Validate() {
  811. #if DCHECK_IS_ON()
  812. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  813. auto check_invariants =
  814. [](const std::vector<V8DetailedMemoryRequest*>& measurement_requests,
  815. bool is_bounded) {
  816. for (size_t i = 1; i < measurement_requests.size(); ++i) {
  817. DCHECK(measurement_requests[i - 1]);
  818. DCHECK(measurement_requests[i]);
  819. DCHECK_EQ(IsMeasurementBounded(measurement_requests[i - 1]->mode()),
  820. is_bounded);
  821. DCHECK_EQ(IsMeasurementBounded(measurement_requests[i]->mode()),
  822. is_bounded);
  823. DCHECK_LE(measurement_requests[i - 1]->min_time_between_requests(),
  824. measurement_requests[i]->min_time_between_requests());
  825. }
  826. };
  827. check_invariants(bounded_measurement_requests_, true);
  828. check_invariants(lazy_measurement_requests_, false);
  829. #endif
  830. }
  831. void V8DetailedMemoryRequestQueue::ApplyToAllRequests(
  832. base::RepeatingCallback<void(V8DetailedMemoryRequest*)> callback) const {
  833. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  834. // First collect all requests to notify. The callback may add or remove
  835. // requests from the queue, invalidating iterators.
  836. std::vector<V8DetailedMemoryRequest*> requests_to_notify;
  837. requests_to_notify.insert(requests_to_notify.end(),
  838. bounded_measurement_requests_.begin(),
  839. bounded_measurement_requests_.end());
  840. requests_to_notify.insert(requests_to_notify.end(),
  841. lazy_measurement_requests_.begin(),
  842. lazy_measurement_requests_.end());
  843. for (V8DetailedMemoryRequest* request : requests_to_notify) {
  844. callback.Run(request);
  845. // The callback may have deleted |request| so it is no longer safe to
  846. // reference.
  847. }
  848. }
  849. } // namespace v8_memory
  850. } // namespace performance_manager