1 // Copyright 2014 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.
5 #if !defined(OS_ANDROID)
7 #include "chrome/browser/metrics/first_web_contents_profiler.h"
11 #include "base/location.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/process/process_info.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_iterator.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "components/metrics/profiler/tracking_synchronizer.h"
21 #include "components/metrics/proto/profiler_event.pb.h"
22 #include "components/startup_metric_utils/startup_metric_utils.h"
23 #include "content/public/browser/browser_thread.h"
26 // The initial delay for responsiveness prober in milliseconds.
27 const int kInitialDelayMs
= 20;
29 // The following is the multiplier is used to delay the probe for
31 const double kBackoffMultiplier
= 1.5;
33 // The maximum backoff delay in milliseconds.
34 const int kMaxDelayMs
= 250;
36 void DelayedRecordUIResponsiveness(
37 base::HistogramBase
* responsiveness_histogram
,
38 base::HistogramBase
* unresponsiveness_histogram
,
39 base::Time start_recording_time
,
40 base::TimeDelta next_task_delay
);
42 // Records the elapsed time for a task to execute a UI task under 1/60s after
43 // the first WebContent was painted at least once. If after few tries it is
44 // unable to execute under 1/60s it records the execution time of a task.
45 void RecordUIResponsiveness(base::HistogramBase
* responsiveness_histogram
,
46 base::HistogramBase
* unresponsiveness_histogram
,
47 base::Time start_recording_time
,
48 base::Time task_posted_time
,
49 base::TimeDelta next_task_delay
) {
50 DCHECK(!start_recording_time
.is_null());
51 DCHECK(!task_posted_time
.is_null());
52 base::Time now
= base::Time::Now();
53 base::TimeDelta elapsed
= now
- task_posted_time
;
55 // Task executed in less then 1/60s.
56 if (elapsed
.InMilliseconds() <= (1000 / 60)) {
57 responsiveness_histogram
->AddTime(now
- start_recording_time
);
58 } else if (next_task_delay
.InMilliseconds() > kMaxDelayMs
) {
59 // Records elapsed time to execute last task.
60 unresponsiveness_histogram
->AddTime(elapsed
);
62 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
64 base::Bind(&DelayedRecordUIResponsiveness
, responsiveness_histogram
,
65 unresponsiveness_histogram
, start_recording_time
,
66 next_task_delay
* kBackoffMultiplier
),
71 // Used for posting |RecordUIResponsiveness| without delay, so that
72 // |RecordUIResponsiveness| can do more accurate time calculation for elapsed
73 // time of the task to complete.
74 void DelayedRecordUIResponsiveness(
75 base::HistogramBase
* responsiveness_histogram
,
76 base::HistogramBase
* unresponsiveness_histogram
,
77 base::Time start_recording_time
,
78 base::TimeDelta next_task_delay
) {
79 base::ThreadTaskRunnerHandle::Get()->PostTask(
80 FROM_HERE
, base::Bind(&RecordUIResponsiveness
, responsiveness_histogram
,
81 unresponsiveness_histogram
, start_recording_time
,
82 base::Time::Now(), next_task_delay
));
85 // Sends the first task for measuring UI Responsiveness.
86 void MeasureUIResponsiveness(base::HistogramBase
* responsiveness_histogram
,
87 base::HistogramBase
* unresponsiveness_histogram
) {
88 base::ThreadTaskRunnerHandle::Get()->PostTask(
90 base::Bind(&RecordUIResponsiveness
, responsiveness_histogram
,
91 unresponsiveness_histogram
, base::Time::Now(),
93 base::TimeDelta::FromMilliseconds(kInitialDelayMs
)));
98 scoped_ptr
<FirstWebContentsProfiler
>
99 FirstWebContentsProfiler::CreateProfilerForFirstWebContents(
100 Delegate
* delegate
) {
102 for (chrome::BrowserIterator iterator
; !iterator
.done(); iterator
.Next()) {
103 Browser
* browser
= *iterator
;
104 content::WebContents
* web_contents
=
105 browser
->tab_strip_model()->GetActiveWebContents();
107 return scoped_ptr
<FirstWebContentsProfiler
>(
108 new FirstWebContentsProfiler(web_contents
, delegate
));
114 FirstWebContentsProfiler::FirstWebContentsProfiler(
115 content::WebContents
* web_contents
,
117 : content::WebContentsObserver(web_contents
),
118 collected_paint_metric_(false),
119 collected_load_metric_(false),
121 responsiveness_histogram_(NULL
),
122 responsiveness_1sec_histogram_(NULL
),
123 responsiveness_10sec_histogram_(NULL
),
124 unresponsiveness_histogram_(NULL
),
125 unresponsiveness_1sec_histogram_(NULL
),
126 unresponsiveness_10sec_histogram_(NULL
) {
130 void FirstWebContentsProfiler::DidFirstVisuallyNonEmptyPaint() {
131 if (collected_paint_metric_
)
133 if (startup_metric_utils::WasNonBrowserUIDisplayed()) {
134 FinishedCollectingMetrics();
138 collected_paint_metric_
= true;
139 startup_metric_utils::RecordFirstWebContentsNonEmptyPaint(base::Time::Now());
141 metrics::TrackingSynchronizer::OnProfilingPhaseCompleted(
142 metrics::ProfilerEventProto::EVENT_FIRST_NONEMPTY_PAINT
);
144 // Measures responsiveness now.
145 MeasureUIResponsiveness(responsiveness_histogram_
,
146 unresponsiveness_histogram_
);
148 // As it was observed that sometimes the task queue can be free immediately
149 // after the first paint but get overloaded shortly thereafter, here we
150 // measures responsiveness after 1 second and 10 seconds to observe the
151 // possible effect of those late tasks.
152 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
154 base::Bind(&MeasureUIResponsiveness
, responsiveness_1sec_histogram_
,
155 unresponsiveness_1sec_histogram_
),
156 base::TimeDelta::FromSeconds(1));
158 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
160 base::Bind(&MeasureUIResponsiveness
, responsiveness_10sec_histogram_
,
161 unresponsiveness_10sec_histogram_
),
162 base::TimeDelta::FromSeconds(10));
164 if (IsFinishedCollectingMetrics())
165 FinishedCollectingMetrics();
168 void FirstWebContentsProfiler::DocumentOnLoadCompletedInMainFrame() {
169 if (collected_load_metric_
)
171 if (startup_metric_utils::WasNonBrowserUIDisplayed()) {
172 FinishedCollectingMetrics();
176 collected_load_metric_
= true;
177 startup_metric_utils::RecordFirstWebContentsMainFrameLoad(base::Time::Now());
179 if (IsFinishedCollectingMetrics())
180 FinishedCollectingMetrics();
183 void FirstWebContentsProfiler::WebContentsDestroyed() {
184 FinishedCollectingMetrics();
187 bool FirstWebContentsProfiler::IsFinishedCollectingMetrics() {
188 return collected_paint_metric_
&& collected_load_metric_
;
191 void FirstWebContentsProfiler::FinishedCollectingMetrics() {
192 delegate_
->ProfilerFinishedCollectingMetrics();
195 void FirstWebContentsProfiler::InitHistograms() {
196 const std::string responsiveness_histogram_name
=
197 "Startup.FirstWebContents.UIResponsive";
198 responsiveness_histogram_
= base::Histogram::FactoryTimeGet(
199 responsiveness_histogram_name
, base::TimeDelta::FromMilliseconds(1),
200 base::TimeDelta::FromSeconds(60), 100,
201 base::Histogram::kUmaTargetedHistogramFlag
);
203 const std::string responsiveness_1sec_histogram_name
=
204 "Startup.FirstWebContents.UIResponsive_1sec";
205 responsiveness_1sec_histogram_
= base::Histogram::FactoryTimeGet(
206 responsiveness_1sec_histogram_name
, base::TimeDelta::FromMilliseconds(1),
207 base::TimeDelta::FromSeconds(60), 100,
208 base::Histogram::kUmaTargetedHistogramFlag
);
210 const std::string responsiveness_10sec_histogram_name
=
211 "Startup.FirstWebContents.UIResponsive_10sec";
212 responsiveness_10sec_histogram_
= base::Histogram::FactoryTimeGet(
213 responsiveness_10sec_histogram_name
, base::TimeDelta::FromMilliseconds(1),
214 base::TimeDelta::FromSeconds(60), 100,
215 base::Histogram::kUmaTargetedHistogramFlag
);
217 const std::string unresponsiveness_histogram_name
=
218 "Startup.FirstWebContents.UINotResponsive";
219 unresponsiveness_histogram_
= base::Histogram::FactoryTimeGet(
220 unresponsiveness_histogram_name
, base::TimeDelta::FromMilliseconds(1),
221 base::TimeDelta::FromSeconds(60), 100,
222 base::Histogram::kUmaTargetedHistogramFlag
);
224 const std::string unresponsiveness_1sec_histogram_name
=
225 "Startup.FirstWebContents.UINotResponsive_1sec";
226 unresponsiveness_1sec_histogram_
= base::Histogram::FactoryTimeGet(
227 unresponsiveness_1sec_histogram_name
,
228 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(60),
229 100, base::Histogram::kUmaTargetedHistogramFlag
);
231 const std::string unresponsiveness_10sec_histogram_name
=
232 "Startup.FirstWebContents.UINotResponsive_10sec";
233 unresponsiveness_10sec_histogram_
= base::Histogram::FactoryTimeGet(
234 unresponsiveness_10sec_histogram_name
,
235 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(60),
236 100, base::Histogram::kUmaTargetedHistogramFlag
);
238 #endif // !defined(OS_ANDROID)