Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(149)

Side by Side Diff: content/browser/download/mhtml_generation_manager.cc

Issue 2364923004: Add UMA histograms to MHTML save operations. (Closed)
Patch Set: Ugly line breaks. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/download/mhtml_generation_manager.h" 5 #include "content/browser/download/mhtml_generation_manager.h"
6 6
7 #include <map> 7 #include <map>
8 #include <queue> 8 #include <queue>
9 #include <utility> 9 #include <utility>
10 10
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/files/file.h" 12 #include "base/files/file.h"
13 #include "base/guid.h" 13 #include "base/guid.h"
14 #include "base/macros.h" 14 #include "base/macros.h"
15 #include "base/metrics/histogram_macros.h"
15 #include "base/scoped_observer.h" 16 #include "base/scoped_observer.h"
16 #include "base/stl_util.h" 17 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h" 18 #include "base/strings/stringprintf.h"
19 #include "base/time/time.h"
18 #include "base/trace_event/trace_event.h" 20 #include "base/trace_event/trace_event.h"
19 #include "content/browser/bad_message.h" 21 #include "content/browser/bad_message.h"
20 #include "content/browser/frame_host/frame_tree_node.h" 22 #include "content/browser/frame_host/frame_tree_node.h"
21 #include "content/browser/frame_host/render_frame_host_impl.h" 23 #include "content/browser/frame_host/render_frame_host_impl.h"
22 #include "content/common/frame_messages.h" 24 #include "content/common/frame_messages.h"
23 #include "content/public/browser/browser_thread.h" 25 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/render_frame_host.h" 26 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/render_process_host.h" 27 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_process_host_observer.h" 28 #include "content/public/browser/render_process_host_observer.h"
27 #include "content/public/browser/web_contents.h" 29 #include "content/public/browser/web_contents.h"
28 #include "content/public/common/mhtml_generation_params.h" 30 #include "content/public/common/mhtml_generation_params.h"
29 #include "net/base/mime_util.h" 31 #include "net/base/mime_util.h"
30 32
31 namespace content { 33 namespace content {
32 34
33 // The class and all of its members live on the UI thread. Only static methods 35 // The class and all of its members live on the UI thread. Only static methods
34 // are executed on other threads. 36 // are executed on other threads.
35 class MHTMLGenerationManager::Job : public RenderProcessHostObserver { 37 class MHTMLGenerationManager::Job : public RenderProcessHostObserver {
36 public: 38 public:
37 Job(int job_id, 39 Job(int job_id,
38 WebContents* web_contents, 40 WebContents* web_contents,
39 const MHTMLGenerationParams& params, 41 const MHTMLGenerationParams& params,
40 const GenerateMHTMLCallback& callback); 42 const GenerateMHTMLCallback& callback);
41 ~Job() override; 43 ~Job() override;
42 44
43 int id() const { return job_id_; } 45 int id() const { return job_id_; }
44 void set_browser_file(base::File file) { browser_file_ = std::move(file); } 46 void set_browser_file(base::File file) { browser_file_ = std::move(file); }
47 const base::TimeTicks creation_time() { return creation_time_; }
45 48
46 const GenerateMHTMLCallback& callback() const { return callback_; } 49 const GenerateMHTMLCallback& callback() const { return callback_; }
47 50
48 // Indicates whether we expect a message from the |sender| at this time. 51 // Indicates whether we expect a message from the |sender| at this time.
49 // We expect only one message per frame - therefore calling this method 52 // We expect only one message per frame - therefore calling this method
50 // will always clear |frame_tree_node_id_of_busy_frame_|. 53 // will always clear |frame_tree_node_id_of_busy_frame_|.
51 bool IsMessageFromFrameExpected(RenderFrameHostImpl* sender); 54 bool IsMessageFromFrameExpected(RenderFrameHostImpl* sender);
52 55
53 // Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the 56 // Handler for FrameHostMsg_SerializeAsMHTMLResponse (a notification from the
54 // renderer that the MHTML generation for previous frame has finished). 57 // renderer that the MHTML generation for previous frame has finished).
(...skipping 20 matching lines...) Expand all
75 void CloseFile(base::Callback<void(int64_t file_size)> callback); 78 void CloseFile(base::Callback<void(int64_t file_size)> callback);
76 79
77 // RenderProcessHostObserver: 80 // RenderProcessHostObserver:
78 void RenderProcessExited(RenderProcessHost* host, 81 void RenderProcessExited(RenderProcessHost* host,
79 base::TerminationStatus status, 82 base::TerminationStatus status,
80 int exit_code) override; 83 int exit_code) override;
81 void RenderProcessHostDestroyed(RenderProcessHost* host) override; 84 void RenderProcessHostDestroyed(RenderProcessHost* host) override;
82 85
83 void MarkAsFinished(); 86 void MarkAsFinished();
84 87
88 void ReportRendererMainThreadTime(base::TimeDelta renderer_main_thread_time);
89
85 private: 90 private:
86 static int64_t CloseFileOnFileThread(base::File file); 91 static int64_t CloseFileOnFileThread(base::File file);
87 void AddFrame(RenderFrameHost* render_frame_host); 92 void AddFrame(RenderFrameHost* render_frame_host);
88 93
89 // Creates a new map with values (content ids) the same as in 94 // Creates a new map with values (content ids) the same as in
90 // |frame_tree_node_to_content_id_| map, but with the keys translated from 95 // |frame_tree_node_to_content_id_| map, but with the keys translated from
91 // frame_tree_node_id into a |site_instance|-specific routing_id. 96 // frame_tree_node_id into a |site_instance|-specific routing_id.
92 std::map<int, std::string> CreateFrameRoutingIdToContentId( 97 std::map<int, std::string> CreateFrameRoutingIdToContentId(
93 SiteInstance* site_instance); 98 SiteInstance* site_instance);
94 99
95 // Id used to map renderer responses to jobs. 100 // Id used to map renderer responses to jobs.
96 // See also MHTMLGenerationManager::id_to_job_ map. 101 // See also MHTMLGenerationManager::id_to_job_ map.
97 int job_id_; 102 const int job_id_;
103
104 // Time tracking for performance metrics reporting.
105 const base::TimeTicks creation_time_;
106 base::TimeTicks wait_on_renderer_start_time_;
107 base::TimeDelta all_renderers_wait_time_;
108 base::TimeDelta all_renderers_main_thread_time_;
98 109
99 // User-configurable parameters. Includes the file location, binary encoding 110 // User-configurable parameters. Includes the file location, binary encoding
100 // choices, and whether to skip storing resources marked 111 // choices, and whether to skip storing resources marked
101 // Cache-Control: no-store. 112 // Cache-Control: no-store.
102 MHTMLGenerationParams params_; 113 MHTMLGenerationParams params_;
103 114
104 // The IDs of frames that still need to be processed. 115 // The IDs of frames that still need to be processed.
105 std::queue<int> pending_frame_tree_node_ids_; 116 std::queue<int> pending_frame_tree_node_ids_;
106 117
107 // Identifies a frame to which we've sent FrameMsg_SerializeAsMHTML but for 118 // Identifies a frame to which we've sent FrameMsg_SerializeAsMHTML but for
(...skipping 28 matching lines...) Expand all
136 observed_renderer_process_host_; 147 observed_renderer_process_host_;
137 148
138 DISALLOW_COPY_AND_ASSIGN(Job); 149 DISALLOW_COPY_AND_ASSIGN(Job);
139 }; 150 };
140 151
141 MHTMLGenerationManager::Job::Job(int job_id, 152 MHTMLGenerationManager::Job::Job(int job_id,
142 WebContents* web_contents, 153 WebContents* web_contents,
143 const MHTMLGenerationParams& params, 154 const MHTMLGenerationParams& params,
144 const GenerateMHTMLCallback& callback) 155 const GenerateMHTMLCallback& callback)
145 : job_id_(job_id), 156 : job_id_(job_id),
157 creation_time_(base::TimeTicks::Now()),
146 params_(params), 158 params_(params),
147 frame_tree_node_id_of_busy_frame_(FrameTreeNode::kFrameTreeNodeInvalidId), 159 frame_tree_node_id_of_busy_frame_(FrameTreeNode::kFrameTreeNodeInvalidId),
148 mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()), 160 mhtml_boundary_marker_(net::GenerateMimeMultipartBoundary()),
149 salt_(base::GenerateGUID()), 161 salt_(base::GenerateGUID()),
150 callback_(callback), 162 callback_(callback),
151 is_finished_(false), 163 is_finished_(false),
152 observed_renderer_process_host_(this) { 164 observed_renderer_process_host_(this) {
153 DCHECK_CURRENTLY_ON(BrowserThread::UI); 165 DCHECK_CURRENTLY_ON(BrowserThread::UI);
154 web_contents->ForEachFrame(base::Bind( 166 web_contents->ForEachFrame(base::Bind(
155 &MHTMLGenerationManager::Job::AddFrame, 167 &MHTMLGenerationManager::Job::AddFrame,
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 CreateFrameRoutingIdToContentId(rfh->GetSiteInstance()); 232 CreateFrameRoutingIdToContentId(rfh->GetSiteInstance());
221 233
222 // Send the IPC asking the renderer to serialize the frame. 234 // Send the IPC asking the renderer to serialize the frame.
223 DCHECK_EQ(FrameTreeNode::kFrameTreeNodeInvalidId, 235 DCHECK_EQ(FrameTreeNode::kFrameTreeNodeInvalidId,
224 frame_tree_node_id_of_busy_frame_); 236 frame_tree_node_id_of_busy_frame_);
225 frame_tree_node_id_of_busy_frame_ = frame_tree_node_id; 237 frame_tree_node_id_of_busy_frame_ = frame_tree_node_id;
226 rfh->Send(new FrameMsg_SerializeAsMHTML(rfh->GetRoutingID(), ipc_params)); 238 rfh->Send(new FrameMsg_SerializeAsMHTML(rfh->GetRoutingID(), ipc_params));
227 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("page-serialization", "WaitingOnRenderer", 239 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("page-serialization", "WaitingOnRenderer",
228 this, "frame tree node id", 240 this, "frame tree node id",
229 frame_tree_node_id); 241 frame_tree_node_id);
242 DCHECK(wait_on_renderer_start_time_.is_null());
243 wait_on_renderer_start_time_ = base::TimeTicks::Now();
230 return true; 244 return true;
231 } 245 }
232 246
233 void MHTMLGenerationManager::Job::RenderProcessExited( 247 void MHTMLGenerationManager::Job::RenderProcessExited(
234 RenderProcessHost* host, 248 RenderProcessHost* host,
235 base::TerminationStatus status, 249 base::TerminationStatus status,
236 int exit_code) { 250 int exit_code) {
237 DCHECK_CURRENTLY_ON(BrowserThread::UI); 251 DCHECK_CURRENTLY_ON(BrowserThread::UI);
238 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this); 252 MHTMLGenerationManager::GetInstance()->RenderProcessExited(this);
239 } 253 }
240 254
241 void MHTMLGenerationManager::Job::MarkAsFinished() { 255 void MHTMLGenerationManager::Job::MarkAsFinished() {
242 DCHECK(!is_finished_); 256 DCHECK(!is_finished_);
243 is_finished_ = true; 257 is_finished_ = true;
244 TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("page-serialization", "JobFinished", 258 TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("page-serialization", "JobFinished",
245 this); 259 this);
246 260
261 // End of job timing reports.
262 if (!wait_on_renderer_start_time_.is_null()) {
263 base::TimeDelta renderer_wait_time =
264 base::TimeTicks::Now() - wait_on_renderer_start_time_;
265 UMA_HISTOGRAM_TIMES(
266 "PageSerialization.MhtmlGeneration.BrowserWaitForRendererTime."
267 "SingleFrame",
268 renderer_wait_time);
Ilya Sherman 2016/09/28 00:42:16 nit: One thing you could do to clean up the histog
carlosk 2016/09/28 17:02:44 The idea is clear but it doesn't solve my issue of
269 all_renderers_wait_time_ += renderer_wait_time;
270 }
271 if (!all_renderers_wait_time_.is_zero()) {
272 UMA_HISTOGRAM_TIMES(
273 "PageSerialization.MhtmlGeneration.BrowserWaitForRendererTime."
274 "FrameTree",
275 all_renderers_wait_time_);
276 }
277 if (!all_renderers_main_thread_time_.is_zero()) {
278 UMA_HISTOGRAM_TIMES(
279 "PageSerialization.MhtmlGeneration.RendererMainThreadTime.FrameTree",
280 all_renderers_main_thread_time_);
281 }
282
247 // Stopping RenderProcessExited notifications is needed to avoid calling 283 // Stopping RenderProcessExited notifications is needed to avoid calling
248 // JobFinished twice. See also https://crbug.com/612098. 284 // JobFinished twice. See also https://crbug.com/612098.
249 observed_renderer_process_host_.RemoveAll(); 285 observed_renderer_process_host_.RemoveAll();
250 } 286 }
251 287
288 void MHTMLGenerationManager::Job::ReportRendererMainThreadTime(
289 base::TimeDelta renderer_main_thread_time) {
290 DCHECK(renderer_main_thread_time > base::TimeDelta());
291 if (renderer_main_thread_time > base::TimeDelta())
292 all_renderers_main_thread_time_ += renderer_main_thread_time;
293 }
294
252 void MHTMLGenerationManager::Job::AddFrame(RenderFrameHost* render_frame_host) { 295 void MHTMLGenerationManager::Job::AddFrame(RenderFrameHost* render_frame_host) {
253 auto* rfhi = static_cast<RenderFrameHostImpl*>(render_frame_host); 296 auto* rfhi = static_cast<RenderFrameHostImpl*>(render_frame_host);
254 int frame_tree_node_id = rfhi->frame_tree_node()->frame_tree_node_id(); 297 int frame_tree_node_id = rfhi->frame_tree_node()->frame_tree_node_id();
255 pending_frame_tree_node_ids_.push(frame_tree_node_id); 298 pending_frame_tree_node_ids_.push(frame_tree_node_id);
256 299
257 std::string guid = base::GenerateGUID(); 300 std::string guid = base::GenerateGUID();
258 std::string content_id = base::StringPrintf("<frame-%d-%s@mhtml.blink>", 301 std::string content_id = base::StringPrintf("<frame-%d-%s@mhtml.blink>",
259 frame_tree_node_id, guid.c_str()); 302 frame_tree_node_id, guid.c_str());
260 frame_tree_node_to_content_id_[frame_tree_node_id] = content_id; 303 frame_tree_node_to_content_id_[frame_tree_node_id] = content_id;
261 } 304 }
(...skipping 28 matching lines...) Expand all
290 333
291 // We only expect one message per frame - let's make sure subsequent messages 334 // We only expect one message per frame - let's make sure subsequent messages
292 // from the same |sender| will be rejected. 335 // from the same |sender| will be rejected.
293 frame_tree_node_id_of_busy_frame_ = FrameTreeNode::kFrameTreeNodeInvalidId; 336 frame_tree_node_id_of_busy_frame_ = FrameTreeNode::kFrameTreeNodeInvalidId;
294 337
295 return true; 338 return true;
296 } 339 }
297 340
298 bool MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse( 341 bool MHTMLGenerationManager::Job::OnSerializeAsMHTMLResponse(
299 const std::set<std::string>& digests_of_uris_of_serialized_resources) { 342 const std::set<std::string>& digests_of_uris_of_serialized_resources) {
343 DCHECK(!wait_on_renderer_start_time_.is_null());
344 base::TimeDelta renderer_wait_time =
345 base::TimeTicks::Now() - wait_on_renderer_start_time_;
346 UMA_HISTOGRAM_TIMES(
347 "PageSerialization.MhtmlGeneration.BrowserWaitForRendererTime."
348 "SingleFrame",
349 renderer_wait_time);
350 all_renderers_wait_time_ += renderer_wait_time;
351 wait_on_renderer_start_time_ = base::TimeTicks();
352
300 // Renderer should be deduping resources with the same uris. 353 // Renderer should be deduping resources with the same uris.
301 DCHECK_EQ(0u, base::STLSetIntersection<std::set<std::string>>( 354 DCHECK_EQ(0u, base::STLSetIntersection<std::set<std::string>>(
302 digests_of_already_serialized_uris_, 355 digests_of_already_serialized_uris_,
303 digests_of_uris_of_serialized_resources).size()); 356 digests_of_uris_of_serialized_resources).size());
304 digests_of_already_serialized_uris_.insert( 357 digests_of_already_serialized_uris_.insert(
305 digests_of_uris_of_serialized_resources.begin(), 358 digests_of_uris_of_serialized_resources.begin(),
306 digests_of_uris_of_serialized_resources.end()); 359 digests_of_uris_of_serialized_resources.end());
307 360
308 if (pending_frame_tree_node_ids_.empty()) 361 if (pending_frame_tree_node_ids_.empty())
309 return true; // Report success - all frames have been processed. 362 return true; // Report success - all frames have been processed.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 base::Bind(&MHTMLGenerationManager::CreateFile, params.file_path), 399 base::Bind(&MHTMLGenerationManager::CreateFile, params.file_path),
347 base::Bind(&MHTMLGenerationManager::OnFileAvailable, 400 base::Bind(&MHTMLGenerationManager::OnFileAvailable,
348 base::Unretained(this), // Safe b/c |this| is a singleton. 401 base::Unretained(this), // Safe b/c |this| is a singleton.
349 job->id())); 402 job->id()));
350 } 403 }
351 404
352 void MHTMLGenerationManager::OnSerializeAsMHTMLResponse( 405 void MHTMLGenerationManager::OnSerializeAsMHTMLResponse(
353 RenderFrameHostImpl* sender, 406 RenderFrameHostImpl* sender,
354 int job_id, 407 int job_id,
355 bool mhtml_generation_in_renderer_succeeded, 408 bool mhtml_generation_in_renderer_succeeded,
356 const std::set<std::string>& digests_of_uris_of_serialized_resources) { 409 const std::set<std::string>& digests_of_uris_of_serialized_resources,
410 base::TimeDelta renderer_main_thread_time) {
357 DCHECK_CURRENTLY_ON(BrowserThread::UI); 411 DCHECK_CURRENTLY_ON(BrowserThread::UI);
358 412
359 Job* job = FindJob(job_id); 413 Job* job = FindJob(job_id);
360 if (!job || !job->IsMessageFromFrameExpected(sender)) { 414 if (!job || !job->IsMessageFromFrameExpected(sender)) {
361 NOTREACHED(); 415 NOTREACHED();
362 ReceivedBadMessage(sender->GetProcess(), 416 ReceivedBadMessage(sender->GetProcess(),
363 bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE); 417 bad_message::DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE);
364 return; 418 return;
365 } 419 }
366 420
367 TRACE_EVENT_NESTABLE_ASYNC_END0("page-serialization", "WaitingOnRenderer", 421 TRACE_EVENT_NESTABLE_ASYNC_END0("page-serialization", "WaitingOnRenderer",
368 job); 422 job);
423 job->ReportRendererMainThreadTime(renderer_main_thread_time);
369 424
370 if (!mhtml_generation_in_renderer_succeeded) { 425 if (!mhtml_generation_in_renderer_succeeded) {
371 JobFinished(job, JobStatus::FAILURE); 426 JobFinished(job, JobStatus::FAILURE);
372 return; 427 return;
373 } 428 }
374 429
375 if (!job->OnSerializeAsMHTMLResponse( 430 if (!job->OnSerializeAsMHTMLResponse(
376 digests_of_uris_of_serialized_resources)) { 431 digests_of_uris_of_serialized_resources)) {
377 JobFinished(job, JobStatus::FAILURE); 432 JobFinished(job, JobStatus::FAILURE);
378 return; 433 return;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
436 void MHTMLGenerationManager::OnFileClosed(int job_id, 491 void MHTMLGenerationManager::OnFileClosed(int job_id,
437 JobStatus job_status, 492 JobStatus job_status,
438 int64_t file_size) { 493 int64_t file_size) {
439 DCHECK_CURRENTLY_ON(BrowserThread::UI); 494 DCHECK_CURRENTLY_ON(BrowserThread::UI);
440 495
441 Job* job = FindJob(job_id); 496 Job* job = FindJob(job_id);
442 TRACE_EVENT_NESTABLE_ASYNC_END2( 497 TRACE_EVENT_NESTABLE_ASYNC_END2(
443 "page-serialization", "SavingMhtmlJob", job, "job result", 498 "page-serialization", "SavingMhtmlJob", job, "job result",
444 job_status == JobStatus::SUCCESS ? "success" : "failure", "file size", 499 job_status == JobStatus::SUCCESS ? "success" : "failure", "file size",
445 file_size); 500 file_size);
501 UMA_HISTOGRAM_TIMES("PageSerialization.MhtmlGeneration.FullPageSavingTime",
502 base::TimeTicks::Now() - job->creation_time());
446 job->callback().Run(job_status == JobStatus::SUCCESS ? file_size : -1); 503 job->callback().Run(job_status == JobStatus::SUCCESS ? file_size : -1);
447 id_to_job_.erase(job_id); 504 id_to_job_.erase(job_id);
448 delete job; 505 delete job;
449 } 506 }
450 507
451 MHTMLGenerationManager::Job* MHTMLGenerationManager::NewJob( 508 MHTMLGenerationManager::Job* MHTMLGenerationManager::NewJob(
452 WebContents* web_contents, 509 WebContents* web_contents,
453 const MHTMLGenerationParams& params, 510 const MHTMLGenerationParams& params,
454 const GenerateMHTMLCallback& callback) { 511 const GenerateMHTMLCallback& callback) {
455 DCHECK_CURRENTLY_ON(BrowserThread::UI); 512 DCHECK_CURRENTLY_ON(BrowserThread::UI);
(...skipping 14 matching lines...) Expand all
470 return iter->second; 527 return iter->second;
471 } 528 }
472 529
473 void MHTMLGenerationManager::RenderProcessExited(Job* job) { 530 void MHTMLGenerationManager::RenderProcessExited(Job* job) {
474 DCHECK_CURRENTLY_ON(BrowserThread::UI); 531 DCHECK_CURRENTLY_ON(BrowserThread::UI);
475 DCHECK(job); 532 DCHECK(job);
476 JobFinished(job, JobStatus::FAILURE); 533 JobFinished(job, JobStatus::FAILURE);
477 } 534 }
478 535
479 } // namespace content 536 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698