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

Side by Side Diff: chrome/browser/page_load_metrics/page_load_tracker.cc

Issue 2820943002: Add detailed tracking for causes of invalid PageLoadTimings. (Closed)
Patch Set: add tests Created 3 years, 8 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "chrome/browser/page_load_metrics/page_load_tracker.h" 5 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <ostream> 8 #include <ostream>
9 #include <string> 9 #include <string>
10 #include <utility> 10 #include <utility>
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"; 53 "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit";
54 const char kClientRedirectFirstPaintToNavigation[] = 54 const char kClientRedirectFirstPaintToNavigation[] =
55 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation"; 55 "PageLoad.Internal.ClientRedirect.FirstPaintToNavigation";
56 const char kClientRedirectWithoutPaint[] = 56 const char kClientRedirectWithoutPaint[] =
57 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint"; 57 "PageLoad.Internal.ClientRedirect.NavigationWithoutPaint";
58 const char kPageLoadCompletedAfterAppBackground[] = 58 const char kPageLoadCompletedAfterAppBackground[] =
59 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground"; 59 "PageLoad.Internal.PageLoadCompleted.AfterAppBackground";
60 const char kPageLoadStartedInForeground[] = 60 const char kPageLoadStartedInForeground[] =
61 "PageLoad.Internal.NavigationStartedInForeground"; 61 "PageLoad.Internal.NavigationStartedInForeground";
62 const char kPageLoadPrerender[] = "PageLoad.Internal.Prerender"; 62 const char kPageLoadPrerender[] = "PageLoad.Internal.Prerender";
63 const char kPageLoadTimingStatus[] = "PageLoad.Internal.PageLoadTimingStatus";
63 64
64 } // namespace internal 65 } // namespace internal
65 66
66 void RecordInternalError(InternalErrorLoadEvent event) { 67 void RecordInternalError(InternalErrorLoadEvent event) {
67 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY); 68 UMA_HISTOGRAM_ENUMERATION(internal::kErrorEvents, event, ERR_LAST_ENTRY);
68 } 69 }
69 70
70 // TODO(csharrison): Add a case for client side redirects, which is what JS 71 // TODO(csharrison): Add a case for client side redirects, which is what JS
71 // initiated window.location / window.history navigations get set to. 72 // initiated window.location / window.history navigations get set to.
72 PageEndReason EndReasonForPageTransition(ui::PageTransition transition) { 73 PageEndReason EndReasonForPageTransition(ui::PageTransition transition) {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 // If second is non-zero, first must also be non-zero and less than or equal to 120 // If second is non-zero, first must also be non-zero and less than or equal to
120 // second. 121 // second.
121 bool EventsInOrder(const base::Optional<base::TimeDelta>& first, 122 bool EventsInOrder(const base::Optional<base::TimeDelta>& first,
122 const base::Optional<base::TimeDelta>& second) { 123 const base::Optional<base::TimeDelta>& second) {
123 if (!second) { 124 if (!second) {
124 return true; 125 return true;
125 } 126 }
126 return first && first <= second; 127 return first && first <= second;
127 } 128 }
128 129
129 bool IsValidPageLoadTiming(const PageLoadTiming& timing) { 130 internal::PageLoadTimingStatus IsValidPageLoadTiming(
131 const PageLoadTiming& timing) {
130 if (timing.IsEmpty()) 132 if (timing.IsEmpty())
131 return false; 133 return internal::INVALID_EMPTY_TIMING;
132 134
133 // If we have a non-empty timing, it should always have a navigation start. 135 // If we have a non-empty timing, it should always have a navigation start.
134 if (timing.navigation_start.is_null()) { 136 if (timing.navigation_start.is_null()) {
135 NOTREACHED() << "Received null navigation_start."; 137 LOG(ERROR) << "Received null navigation_start.";
136 return false; 138 return internal::INVALID_NULL_NAVIGATION_START;
137 } 139 }
138 140
139 // Verify proper ordering between the various timings. 141 // Verify proper ordering between the various timings.
140 142
141 if (!EventsInOrder(timing.response_start, timing.parse_timing.parse_start)) { 143 if (!EventsInOrder(timing.response_start, timing.parse_timing.parse_start)) {
142 // We sometimes get a zero response_start with a non-zero parse start. See 144 // We sometimes get a zero response_start with a non-zero parse start. See
143 // crbug.com/590212. 145 // crbug.com/590212.
144 NOTREACHED() << "Invalid response_start " << timing.response_start 146 LOG(ERROR) << "Invalid response_start " << timing.response_start
145 << " for parse_start " << timing.parse_timing.parse_start; 147 << " for parse_start " << timing.parse_timing.parse_start;
146 return false; 148 return internal::INVALID_ORDER_RESPONSE_START_PARSE_START;
147 } 149 }
148 150
149 if (!EventsInOrder(timing.parse_timing.parse_start, 151 if (!EventsInOrder(timing.parse_timing.parse_start,
150 timing.parse_timing.parse_stop)) { 152 timing.parse_timing.parse_stop)) {
151 NOTREACHED() << "Invalid parse_start " << timing.parse_timing.parse_start 153 LOG(ERROR) << "Invalid parse_start " << timing.parse_timing.parse_start
152 << " for parse_stop " << timing.parse_timing.parse_stop; 154 << " for parse_stop " << timing.parse_timing.parse_stop;
153 return false; 155 return internal::INVALID_ORDER_PARSE_START_PARSE_STOP;
154 } 156 }
155 157
156 if (timing.parse_timing.parse_stop) { 158 if (timing.parse_timing.parse_stop) {
157 const base::TimeDelta parse_duration = 159 const base::TimeDelta parse_duration =
158 timing.parse_timing.parse_stop.value() - 160 timing.parse_timing.parse_stop.value() -
159 timing.parse_timing.parse_start.value(); 161 timing.parse_timing.parse_start.value();
160 if (timing.parse_timing.parse_blocked_on_script_load_duration > 162 if (timing.parse_timing.parse_blocked_on_script_load_duration >
161 parse_duration) { 163 parse_duration) {
162 NOTREACHED() << "Invalid parse_blocked_on_script_load_duration " 164 LOG(ERROR) << "Invalid parse_blocked_on_script_load_duration "
163 << timing.parse_timing.parse_blocked_on_script_load_duration 165 << timing.parse_timing.parse_blocked_on_script_load_duration
164 << " for parse duration " << parse_duration; 166 << " for parse duration " << parse_duration;
165 return false; 167 return internal::INVALID_SCRIPT_LOAD_LONGER_THAN_PARSE;
166 } 168 }
167 if (timing.parse_timing.parse_blocked_on_script_execution_duration > 169 if (timing.parse_timing.parse_blocked_on_script_execution_duration >
168 parse_duration) { 170 parse_duration) {
169 NOTREACHED() 171 LOG(ERROR)
170 << "Invalid parse_blocked_on_script_execution_duration " 172 << "Invalid parse_blocked_on_script_execution_duration "
171 << timing.parse_timing.parse_blocked_on_script_execution_duration 173 << timing.parse_timing.parse_blocked_on_script_execution_duration
172 << " for parse duration " << parse_duration; 174 << " for parse duration " << parse_duration;
173 return false; 175 return internal::INVALID_SCRIPT_EXEC_LONGER_THAN_PARSE;
174 } 176 }
175 } 177 }
176 178
177 if (timing.parse_timing 179 if (timing.parse_timing
178 .parse_blocked_on_script_load_from_document_write_duration > 180 .parse_blocked_on_script_load_from_document_write_duration >
179 timing.parse_timing.parse_blocked_on_script_load_duration) { 181 timing.parse_timing.parse_blocked_on_script_load_duration) {
180 NOTREACHED() 182 LOG(ERROR)
181 << "Invalid parse_blocked_on_script_load_from_document_write_duration " 183 << "Invalid parse_blocked_on_script_load_from_document_write_duration "
182 << timing.parse_timing 184 << timing.parse_timing
183 .parse_blocked_on_script_load_from_document_write_duration 185 .parse_blocked_on_script_load_from_document_write_duration
184 << " for parse_blocked_on_script_load_duration " 186 << " for parse_blocked_on_script_load_duration "
185 << timing.parse_timing.parse_blocked_on_script_load_duration; 187 << timing.parse_timing.parse_blocked_on_script_load_duration;
186 return false; 188 return internal::INVALID_SCRIPT_LOAD_DOC_WRITE_LONGER_THAN_SCRIPT_LOAD;
187 } 189 }
188 190
189 if (timing.parse_timing 191 if (timing.parse_timing
190 .parse_blocked_on_script_execution_from_document_write_duration > 192 .parse_blocked_on_script_execution_from_document_write_duration >
191 timing.parse_timing.parse_blocked_on_script_execution_duration) { 193 timing.parse_timing.parse_blocked_on_script_execution_duration) {
192 NOTREACHED() 194 LOG(ERROR)
193 << "Invalid " 195 << "Invalid "
194 "parse_blocked_on_script_execution_from_document_write_duration " 196 "parse_blocked_on_script_execution_from_document_write_duration "
195 << timing.parse_timing 197 << timing.parse_timing
196 .parse_blocked_on_script_execution_from_document_write_duration 198 .parse_blocked_on_script_execution_from_document_write_duration
197 << " for parse_blocked_on_script_execution_duration " 199 << " for parse_blocked_on_script_execution_duration "
198 << timing.parse_timing.parse_blocked_on_script_execution_duration; 200 << timing.parse_timing.parse_blocked_on_script_execution_duration;
199 return false; 201 return internal::INVALID_SCRIPT_EXEC_DOC_WRITE_LONGER_THAN_SCRIPT_EXEC;
200 } 202 }
201 203
202 if (!EventsInOrder(timing.parse_timing.parse_stop, 204 if (!EventsInOrder(timing.parse_timing.parse_stop,
203 timing.document_timing.dom_content_loaded_event_start)) { 205 timing.document_timing.dom_content_loaded_event_start)) {
204 NOTREACHED() << "Invalid parse_stop " << timing.parse_timing.parse_stop 206 LOG(ERROR) << "Invalid parse_stop " << timing.parse_timing.parse_stop
205 << " for dom_content_loaded_event_start " 207 << " for dom_content_loaded_event_start "
206 << timing.document_timing.dom_content_loaded_event_start; 208 << timing.document_timing.dom_content_loaded_event_start;
207 return false; 209 return internal::INVALID_ORDER_PARSE_STOP_DOM_CONTENT_LOADED;
208 } 210 }
209 211
210 if (!EventsInOrder(timing.document_timing.dom_content_loaded_event_start, 212 if (!EventsInOrder(timing.document_timing.dom_content_loaded_event_start,
211 timing.document_timing.load_event_start)) { 213 timing.document_timing.load_event_start)) {
212 NOTREACHED() << "Invalid dom_content_loaded_event_start " 214 LOG(ERROR) << "Invalid dom_content_loaded_event_start "
213 << timing.document_timing.dom_content_loaded_event_start 215 << timing.document_timing.dom_content_loaded_event_start
214 << " for load_event_start " 216 << " for load_event_start "
215 << timing.document_timing.load_event_start; 217 << timing.document_timing.load_event_start;
216 return false; 218 return internal::INVALID_ORDER_DOM_CONTENT_LOADED_LOAD;
217 } 219 }
218 220
219 if (!EventsInOrder(timing.parse_timing.parse_start, 221 if (!EventsInOrder(timing.parse_timing.parse_start,
220 timing.document_timing.first_layout)) { 222 timing.document_timing.first_layout)) {
221 NOTREACHED() << "Invalid parse_start " << timing.parse_timing.parse_start 223 LOG(ERROR) << "Invalid parse_start " << timing.parse_timing.parse_start
222 << " for first_layout " << timing.document_timing.first_layout; 224 << " for first_layout " << timing.document_timing.first_layout;
223 return false; 225 return internal::INVALID_ORDER_PARSE_START_FIRST_LAYOUT;
224 } 226 }
225 227
226 if (!EventsInOrder(timing.document_timing.first_layout, 228 if (!EventsInOrder(timing.document_timing.first_layout,
227 timing.paint_timing.first_paint)) { 229 timing.paint_timing.first_paint)) {
228 // This can happen when we process an XHTML document that doesn't contain 230 // This can happen when we process an XHTML document that doesn't contain
229 // well formed XML. See crbug.com/627607. 231 // well formed XML. See crbug.com/627607.
230 DLOG(ERROR) << "Invalid first_layout " 232 DLOG(ERROR) << "Invalid first_layout "
231 << timing.document_timing.first_layout << " for first_paint " 233 << timing.document_timing.first_layout << " for first_paint "
232 << timing.paint_timing.first_paint; 234 << timing.paint_timing.first_paint;
233 return false; 235 return internal::INVALID_ORDER_FIRST_LAYOUT_FIRST_PAINT;
234 } 236 }
235 237
236 if (!EventsInOrder(timing.paint_timing.first_paint, 238 if (!EventsInOrder(timing.paint_timing.first_paint,
237 timing.paint_timing.first_text_paint)) { 239 timing.paint_timing.first_text_paint)) {
238 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint 240 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint
239 << " for first_text_paint " 241 << " for first_text_paint "
240 << timing.paint_timing.first_text_paint; 242 << timing.paint_timing.first_text_paint;
241 return false; 243 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_TEXT_PAINT;
242 } 244 }
243 245
244 if (!EventsInOrder(timing.paint_timing.first_paint, 246 if (!EventsInOrder(timing.paint_timing.first_paint,
245 timing.paint_timing.first_image_paint)) { 247 timing.paint_timing.first_image_paint)) {
246 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint 248 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint
247 << " for first_image_paint " 249 << " for first_image_paint "
248 << timing.paint_timing.first_image_paint; 250 << timing.paint_timing.first_image_paint;
249 return false; 251 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_IMAGE_PAINT;
250 } 252 }
251 253
252 if (!EventsInOrder(timing.paint_timing.first_paint, 254 if (!EventsInOrder(timing.paint_timing.first_paint,
253 timing.paint_timing.first_contentful_paint)) { 255 timing.paint_timing.first_contentful_paint)) {
254 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint 256 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint
255 << " for first_contentful_paint " 257 << " for first_contentful_paint "
256 << timing.paint_timing.first_contentful_paint; 258 << timing.paint_timing.first_contentful_paint;
257 return false; 259 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_CONTENTFUL_PAINT;
258 } 260 }
259 261
260 if (!EventsInOrder(timing.paint_timing.first_paint, 262 if (!EventsInOrder(timing.paint_timing.first_paint,
261 timing.paint_timing.first_meaningful_paint)) { 263 timing.paint_timing.first_meaningful_paint)) {
262 NOTREACHED() << "Invalid first_paint " << timing.paint_timing.first_paint 264 LOG(ERROR) << "Invalid first_paint " << timing.paint_timing.first_paint
263 << " for first_meaningful_paint " 265 << " for first_meaningful_paint "
264 << timing.paint_timing.first_meaningful_paint; 266 << timing.paint_timing.first_meaningful_paint;
265 return false; 267 return internal::INVALID_ORDER_FIRST_PAINT_FIRST_MEANINGFUL_PAINT;
266 } 268 }
267 269
268 return true; 270 return internal::VALID;
269 } 271 }
270 272
271 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) { 273 void RecordAppBackgroundPageLoadCompleted(bool completed_after_background) {
272 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground, 274 UMA_HISTOGRAM_BOOLEAN(internal::kPageLoadCompletedAfterAppBackground,
273 completed_after_background); 275 completed_after_background);
274 } 276 }
275 277
276 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer, 278 void DispatchObserverTimingCallbacks(PageLoadMetricsObserver* observer,
277 const PageLoadTiming& last_timing, 279 const PageLoadTiming& last_timing,
278 const PageLoadTiming& new_timing, 280 const PageLoadTiming& new_timing,
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
582 } 584 }
583 585
584 // Ensure flags sent previously are still present in the new metadata fields. 586 // Ensure flags sent previously are still present in the new metadata fields.
585 const bool valid_behavior_descendent = 587 const bool valid_behavior_descendent =
586 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) == 588 (main_frame_metadata_.behavior_flags & new_metadata.behavior_flags) ==
587 main_frame_metadata_.behavior_flags; 589 main_frame_metadata_.behavior_flags;
588 if (!valid_behavior_descendent) { 590 if (!valid_behavior_descendent) {
589 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT); 591 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_BEHAVIOR_DESCENDENT);
590 return; 592 return;
591 } 593 }
592 if (!IsValidPageLoadTiming(new_timing)) { 594 internal::PageLoadTimingStatus status = IsValidPageLoadTiming(new_timing);
595 UMA_HISTOGRAM_ENUMERATION(internal::kPageLoadTimingStatus, status,
596 internal::LAST_PAGE_LOAD_TIMING_STATUS);
597 if (status != internal::VALID) {
593 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING); 598 RecordInternalError(ERR_BAD_TIMING_IPC_INVALID_TIMING);
594 return; 599 return;
595 } 600 }
596 601
597 DCHECK(did_commit_); // OnCommit() must be called first. 602 DCHECK(did_commit_); // OnCommit() must be called first.
598 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo() 603 // There are some subtle ordering constraints here. GetPageLoadMetricsInfo()
599 // must be called before DispatchObserverTimingCallbacks, but its 604 // must be called before DispatchObserverTimingCallbacks, but its
600 // implementation depends on the state of main_frame_metadata_, so we need 605 // implementation depends on the state of main_frame_metadata_, so we need
601 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo. 606 // to update main_frame_metadata_ before calling GetPageLoadMetricsInfo.
602 // Thus, we make a copy of timing here, update timing_ and 607 // Thus, we make a copy of timing here, update timing_ and
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
792 observer->MediaStartedPlaying(video_type, is_in_main_frame); 797 observer->MediaStartedPlaying(video_type, is_in_main_frame);
793 } 798 }
794 799
795 void PageLoadTracker::OnNavigationDelayComplete(base::TimeDelta scheduled_delay, 800 void PageLoadTracker::OnNavigationDelayComplete(base::TimeDelta scheduled_delay,
796 base::TimeDelta actual_delay) { 801 base::TimeDelta actual_delay) {
797 for (const auto& observer : observers_) 802 for (const auto& observer : observers_)
798 observer->OnNavigationDelayComplete(scheduled_delay, actual_delay); 803 observer->OnNavigationDelayComplete(scheduled_delay, actual_delay);
799 } 804 }
800 805
801 } // namespace page_load_metrics 806 } // namespace page_load_metrics
OLDNEW
« no previous file with comments | « chrome/browser/page_load_metrics/page_load_tracker.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698