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

Side by Side Diff: chrome/browser/predictors/resource_prefetch_predictor.cc

Issue 117933003: Remove the speculative resource prefetching code. This was experimental code added 1.5 years ago an… (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: sync Created 6 years, 11 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/predictors/resource_prefetch_predictor.h"
6
7 #include <map>
8 #include <set>
9 #include <utility>
10
11 #include "base/command_line.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/history/history_database.h"
19 #include "chrome/browser/history/history_db_task.h"
20 #include "chrome/browser/history/history_notifications.h"
21 #include "chrome/browser/history/history_service.h"
22 #include "chrome/browser/history/history_service_factory.h"
23 #include "chrome/browser/predictors/predictor_database.h"
24 #include "chrome/browser/predictors/predictor_database_factory.h"
25 #include "chrome/browser/predictors/resource_prefetcher_manager.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/url_constants.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/navigation_controller.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_source.h"
33 #include "content/public/browser/notification_types.h"
34 #include "content/public/browser/resource_request_info.h"
35 #include "content/public/browser/web_contents.h"
36 #include "net/base/mime_util.h"
37 #include "net/http/http_response_headers.h"
38 #include "net/url_request/url_request.h"
39 #include "net/url_request/url_request_context_getter.h"
40
41 using content::BrowserThread;
42
43 namespace {
44
45 // For reporting whether a subresource is handled or not, and for what reasons.
46 enum ResourceStatus {
47 RESOURCE_STATUS_HANDLED = 0,
48 RESOURCE_STATUS_NOT_HTTP_PAGE = 1,
49 RESOURCE_STATUS_NOT_HTTP_RESOURCE = 2,
50 RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE = 4,
51 RESOURCE_STATUS_NOT_GET = 8,
52 RESOURCE_STATUS_URL_TOO_LONG = 16,
53 RESOURCE_STATUS_NOT_CACHEABLE = 32,
54 RESOURCE_STATUS_HEADERS_MISSING = 64,
55 RESOURCE_STATUS_MAX = 128,
56 };
57
58 // For reporting various interesting events that occur during the loading of a
59 // single main frame.
60 enum NavigationEvent {
61 NAVIGATION_EVENT_REQUEST_STARTED = 0,
62 NAVIGATION_EVENT_REQUEST_REDIRECTED = 1,
63 NAVIGATION_EVENT_REQUEST_REDIRECTED_EMPTY_URL = 2,
64 NAVIGATION_EVENT_REQUEST_EXPIRED = 3,
65 NAVIGATION_EVENT_RESPONSE_STARTED = 4,
66 NAVIGATION_EVENT_ONLOAD = 5,
67 NAVIGATION_EVENT_ONLOAD_EMPTY_URL = 6,
68 NAVIGATION_EVENT_ONLOAD_UNTRACKED_URL = 7,
69 NAVIGATION_EVENT_ONLOAD_TRACKED_URL = 8,
70 NAVIGATION_EVENT_SHOULD_TRACK_URL = 9,
71 NAVIGATION_EVENT_SHOULD_NOT_TRACK_URL = 10,
72 NAVIGATION_EVENT_URL_TABLE_FULL = 11,
73 NAVIGATION_EVENT_HAVE_PREDICTIONS_FOR_URL = 12,
74 NAVIGATION_EVENT_NO_PREDICTIONS_FOR_URL = 13,
75 NAVIGATION_EVENT_MAIN_FRAME_URL_TOO_LONG = 14,
76 NAVIGATION_EVENT_HOST_TOO_LONG = 15,
77 NAVIGATION_EVENT_COUNT = 16,
78 };
79
80 // For reporting events of interest that are not tied to any navigation.
81 enum ReportingEvent {
82 REPORTING_EVENT_ALL_HISTORY_CLEARED = 0,
83 REPORTING_EVENT_PARTIAL_HISTORY_CLEARED = 1,
84 REPORTING_EVENT_COUNT = 2
85 };
86
87 void RecordNavigationEvent(NavigationEvent event) {
88 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.NavigationEvent",
89 event,
90 NAVIGATION_EVENT_COUNT);
91 }
92
93 } // namespace
94
95 namespace predictors {
96
97 ////////////////////////////////////////////////////////////////////////////////
98 // History lookup task.
99
100 // Used to fetch the visit count for a URL from the History database.
101 class GetUrlVisitCountTask : public history::HistoryDBTask {
102 public:
103 typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary;
104 typedef base::Callback<void(
105 int, // Visit count.
106 const NavigationID&,
107 const std::vector<URLRequestSummary>&)> VisitInfoCallback;
108
109 GetUrlVisitCountTask(
110 const NavigationID& navigation_id,
111 std::vector<URLRequestSummary>* requests,
112 VisitInfoCallback callback)
113 : visit_count_(0),
114 navigation_id_(navigation_id),
115 requests_(requests),
116 callback_(callback) {
117 DCHECK(requests_.get());
118 }
119
120 virtual bool RunOnDBThread(history::HistoryBackend* backend,
121 history::HistoryDatabase* db) OVERRIDE {
122 history::URLRow url_row;
123 if (db->GetRowForURL(navigation_id_.main_frame_url, &url_row))
124 visit_count_ = url_row.visit_count();
125 return true;
126 }
127
128 virtual void DoneRunOnMainThread() OVERRIDE {
129 callback_.Run(visit_count_, navigation_id_, *requests_);
130 }
131
132 private:
133 virtual ~GetUrlVisitCountTask() { }
134
135 int visit_count_;
136 NavigationID navigation_id_;
137 scoped_ptr<std::vector<URLRequestSummary> > requests_;
138 VisitInfoCallback callback_;
139
140 DISALLOW_COPY_AND_ASSIGN(GetUrlVisitCountTask);
141 };
142
143 ////////////////////////////////////////////////////////////////////////////////
144 // ResourcePrefetchPredictor static functions.
145
146 // static
147 bool ResourcePrefetchPredictor::ShouldRecordRequest(
148 net::URLRequest* request,
149 ResourceType::Type resource_type) {
150 return resource_type == ResourceType::MAIN_FRAME &&
151 IsHandledMainPage(request);
152 }
153
154 // static
155 bool ResourcePrefetchPredictor::ShouldRecordResponse(
156 net::URLRequest* response) {
157 const content::ResourceRequestInfo* request_info =
158 content::ResourceRequestInfo::ForRequest(response);
159 if (!request_info)
160 return false;
161
162 return request_info->GetResourceType() == ResourceType::MAIN_FRAME ?
163 IsHandledMainPage(response) : IsHandledSubresource(response);
164 }
165
166 // static
167 bool ResourcePrefetchPredictor::ShouldRecordRedirect(
168 net::URLRequest* response) {
169 const content::ResourceRequestInfo* request_info =
170 content::ResourceRequestInfo::ForRequest(response);
171 if (!request_info)
172 return false;
173
174 return request_info->GetResourceType() == ResourceType::MAIN_FRAME &&
175 IsHandledMainPage(response);
176 }
177
178 // static
179 bool ResourcePrefetchPredictor::IsHandledMainPage(net::URLRequest* request) {
180 return request->original_url().scheme() == content::kHttpScheme;
181 }
182
183 // static
184 bool ResourcePrefetchPredictor::IsHandledSubresource(
185 net::URLRequest* response) {
186 int resource_status = 0;
187 if (response->first_party_for_cookies().scheme() != content::kHttpScheme)
188 resource_status |= RESOURCE_STATUS_NOT_HTTP_PAGE;
189
190 if (response->original_url().scheme() != content::kHttpScheme)
191 resource_status |= RESOURCE_STATUS_NOT_HTTP_RESOURCE;
192
193 std::string mime_type;
194 response->GetMimeType(&mime_type);
195 if (!mime_type.empty() &&
196 !net::IsSupportedImageMimeType(mime_type.c_str()) &&
197 !net::IsSupportedJavascriptMimeType(mime_type.c_str()) &&
198 !net::MatchesMimeType("text/css", mime_type)) {
199 resource_status |= RESOURCE_STATUS_UNSUPPORTED_MIME_TYPE;
200 }
201
202 if (response->method() != "GET")
203 resource_status |= RESOURCE_STATUS_NOT_GET;
204
205 if (response->original_url().spec().length() >
206 ResourcePrefetchPredictorTables::kMaxStringLength) {
207 resource_status |= RESOURCE_STATUS_URL_TOO_LONG;
208 }
209
210 if (!response->response_info().headers.get())
211 resource_status |= RESOURCE_STATUS_HEADERS_MISSING;
212
213 if (!IsCacheable(response))
214 resource_status |= RESOURCE_STATUS_NOT_CACHEABLE;
215
216 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ResourceStatus",
217 resource_status,
218 RESOURCE_STATUS_MAX);
219
220 return resource_status == 0;
221 }
222
223 // static
224 bool ResourcePrefetchPredictor::IsCacheable(const net::URLRequest* response) {
225 if (response->was_cached())
226 return true;
227
228 // For non cached responses, we will ensure that the freshness lifetime is
229 // some sane value.
230 const net::HttpResponseInfo& response_info = response->response_info();
231 if (!response_info.headers.get())
232 return false;
233 base::Time response_time(response_info.response_time);
234 response_time += base::TimeDelta::FromSeconds(1);
235 base::TimeDelta freshness = response_info.headers->GetFreshnessLifetime(
236 response_time);
237 return freshness > base::TimeDelta();
238 }
239
240 // static
241 ResourceType::Type ResourcePrefetchPredictor::GetResourceTypeFromMimeType(
242 const std::string& mime_type,
243 ResourceType::Type fallback) {
244 if (net::IsSupportedImageMimeType(mime_type.c_str()))
245 return ResourceType::IMAGE;
246 else if (net::IsSupportedJavascriptMimeType(mime_type.c_str()))
247 return ResourceType::SCRIPT;
248 else if (net::MatchesMimeType("text/css", mime_type))
249 return ResourceType::STYLESHEET;
250 else
251 return fallback;
252 }
253
254 ////////////////////////////////////////////////////////////////////////////////
255 // ResourcePrefetchPredictor structs.
256
257 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary()
258 : resource_type(ResourceType::LAST_TYPE),
259 was_cached(false) {
260 }
261
262 ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary(
263 const URLRequestSummary& other)
264 : navigation_id(other.navigation_id),
265 resource_url(other.resource_url),
266 resource_type(other.resource_type),
267 mime_type(other.mime_type),
268 was_cached(other.was_cached),
269 redirect_url(other.redirect_url) {
270 }
271
272 ResourcePrefetchPredictor::URLRequestSummary::~URLRequestSummary() {
273 }
274
275 ResourcePrefetchPredictor::Result::Result(
276 PrefetchKeyType i_key_type,
277 ResourcePrefetcher::RequestVector* i_requests)
278 : key_type(i_key_type),
279 requests(i_requests) {
280 }
281
282 ResourcePrefetchPredictor::Result::~Result() {
283 }
284
285 ////////////////////////////////////////////////////////////////////////////////
286 // ResourcePrefetchPredictor.
287
288 ResourcePrefetchPredictor::ResourcePrefetchPredictor(
289 const ResourcePrefetchPredictorConfig& config,
290 Profile* profile)
291 : profile_(profile),
292 config_(config),
293 initialization_state_(NOT_INITIALIZED),
294 tables_(PredictorDatabaseFactory::GetForProfile(
295 profile)->resource_prefetch_tables()),
296 results_map_deleter_(&results_map_) {
297 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298
299 // Some form of learning has to be enabled.
300 DCHECK(config_.IsLearningEnabled());
301 if (config_.IsURLPrefetchingEnabled())
302 DCHECK(config_.IsURLLearningEnabled());
303 if (config_.IsHostPrefetchingEnabled())
304 DCHECK(config_.IsHostLearningEnabled());
305 }
306
307 ResourcePrefetchPredictor::~ResourcePrefetchPredictor() {
308 }
309
310 void ResourcePrefetchPredictor::RecordURLRequest(
311 const URLRequestSummary& request) {
312 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313 if (initialization_state_ != INITIALIZED)
314 return;
315
316 CHECK_EQ(request.resource_type, ResourceType::MAIN_FRAME);
317 OnMainFrameRequest(request);
318 }
319
320 void ResourcePrefetchPredictor::RecordURLResponse(
321 const URLRequestSummary& response) {
322 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 if (initialization_state_ != INITIALIZED)
324 return;
325
326 if (response.resource_type == ResourceType::MAIN_FRAME)
327 OnMainFrameResponse(response);
328 else
329 OnSubresourceResponse(response);
330 }
331
332 void ResourcePrefetchPredictor::RecordURLRedirect(
333 const URLRequestSummary& response) {
334 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
335 if (initialization_state_ != INITIALIZED)
336 return;
337
338 CHECK_EQ(response.resource_type, ResourceType::MAIN_FRAME);
339 OnMainFrameRedirect(response);
340 }
341
342 void ResourcePrefetchPredictor::RecordMainFrameLoadComplete(
343 const NavigationID& navigation_id) {
344 switch (initialization_state_) {
345 case NOT_INITIALIZED:
346 StartInitialization();
347 break;
348 case INITIALIZING:
349 break;
350 case INITIALIZED: {
351 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD);
352 // WebContents can return an empty URL if the navigation entry
353 // corresponding to the navigation has not been created yet.
354 if (navigation_id.main_frame_url.is_empty())
355 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD_EMPTY_URL);
356 else
357 OnNavigationComplete(navigation_id);
358 break;
359 }
360 default:
361 NOTREACHED() << "Unexpected initialization_state_: "
362 << initialization_state_;
363 }
364 }
365
366 void ResourcePrefetchPredictor::FinishedPrefetchForNavigation(
367 const NavigationID& navigation_id,
368 PrefetchKeyType key_type,
369 ResourcePrefetcher::RequestVector* requests) {
370 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
371
372 Result* result = new Result(key_type, requests);
373 // Add the results to the results map.
374 if (!results_map_.insert(std::make_pair(navigation_id, result)).second) {
375 DLOG(FATAL) << "Returning results for existing navigation.";
376 delete result;
377 }
378 }
379
380 void ResourcePrefetchPredictor::Observe(
381 int type,
382 const content::NotificationSource& source,
383 const content::NotificationDetails& details) {
384 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
385
386 switch (type) {
387 case chrome::NOTIFICATION_HISTORY_LOADED: {
388 DCHECK_EQ(initialization_state_, INITIALIZING);
389 notification_registrar_.Remove(this,
390 chrome::NOTIFICATION_HISTORY_LOADED,
391 content::Source<Profile>(profile_));
392 OnHistoryAndCacheLoaded();
393 break;
394 }
395
396 case chrome::NOTIFICATION_HISTORY_URLS_DELETED: {
397 DCHECK_EQ(initialization_state_, INITIALIZED);
398 const content::Details<const history::URLsDeletedDetails>
399 urls_deleted_details =
400 content::Details<const history::URLsDeletedDetails>(details);
401 if (urls_deleted_details->all_history) {
402 DeleteAllUrls();
403 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ReportingEvent",
404 REPORTING_EVENT_ALL_HISTORY_CLEARED,
405 REPORTING_EVENT_COUNT);
406 } else {
407 DeleteUrls(urls_deleted_details->rows);
408 UMA_HISTOGRAM_ENUMERATION("ResourcePrefetchPredictor.ReportingEvent",
409 REPORTING_EVENT_PARTIAL_HISTORY_CLEARED,
410 REPORTING_EVENT_COUNT);
411 }
412 break;
413 }
414
415 default:
416 NOTREACHED() << "Unexpected notification observed.";
417 break;
418 }
419 }
420
421 void ResourcePrefetchPredictor::Shutdown() {
422 if (prefetch_manager_.get()) {
423 prefetch_manager_->ShutdownOnUIThread();
424 prefetch_manager_ = NULL;
425 }
426 }
427
428 void ResourcePrefetchPredictor::OnMainFrameRequest(
429 const URLRequestSummary& request) {
430 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431 DCHECK_EQ(INITIALIZED, initialization_state_);
432
433 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_STARTED);
434
435 StartPrefetching(request.navigation_id);
436
437 // Cleanup older navigations.
438 CleanupAbandonedNavigations(request.navigation_id);
439
440 // New empty navigation entry.
441 inflight_navigations_.insert(std::make_pair(
442 request.navigation_id,
443 make_linked_ptr(new std::vector<URLRequestSummary>())));
444 }
445
446 void ResourcePrefetchPredictor::OnMainFrameResponse(
447 const URLRequestSummary& response) {
448 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449 if (initialization_state_ != INITIALIZED)
450 return;
451
452 RecordNavigationEvent(NAVIGATION_EVENT_RESPONSE_STARTED);
453
454 StopPrefetching(response.navigation_id);
455 }
456
457 void ResourcePrefetchPredictor::OnMainFrameRedirect(
458 const URLRequestSummary& response) {
459 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
460
461 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_REDIRECTED);
462
463 // TODO(shishir): There are significant gains to be had here if we can use the
464 // start URL in a redirect chain as the key to start prefetching. We can save
465 // of redirect times considerably assuming that the redirect chains do not
466 // change.
467
468 // Stop any inflight prefetching. Remove the older navigation.
469 StopPrefetching(response.navigation_id);
470 inflight_navigations_.erase(response.navigation_id);
471
472 // A redirect will not lead to another OnMainFrameRequest call, so record the
473 // redirect url as a new navigation.
474
475 // The redirect url may be empty if the url was invalid.
476 if (response.redirect_url.is_empty()) {
477 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_REDIRECTED_EMPTY_URL);
478 return;
479 }
480
481 NavigationID navigation_id(response.navigation_id);
482 navigation_id.main_frame_url = response.redirect_url;
483 inflight_navigations_.insert(std::make_pair(
484 navigation_id,
485 make_linked_ptr(new std::vector<URLRequestSummary>())));
486 }
487
488 void ResourcePrefetchPredictor::OnSubresourceResponse(
489 const URLRequestSummary& response) {
490 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
491
492 if (inflight_navigations_.find(response.navigation_id) ==
493 inflight_navigations_.end()) {
494 return;
495 }
496
497 inflight_navigations_[response.navigation_id]->push_back(response);
498 }
499
500 void ResourcePrefetchPredictor::OnNavigationComplete(
501 const NavigationID& navigation_id) {
502 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
503
504 NavigationMap::const_iterator nav_it =
505 inflight_navigations_.find(navigation_id);
506 if (nav_it == inflight_navigations_.end()) {
507 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD_UNTRACKED_URL);
508 return;
509 }
510 RecordNavigationEvent(NAVIGATION_EVENT_ONLOAD_TRACKED_URL);
511
512 // Report any stats.
513 if (prefetch_manager_.get()) {
514 ResultsMap::iterator results_it = results_map_.find(navigation_id);
515 bool have_prefetch_results = results_it != results_map_.end();
516 UMA_HISTOGRAM_BOOLEAN("ResourcePrefetchPredictor.HavePrefetchResults",
517 have_prefetch_results);
518 if (have_prefetch_results) {
519 ReportAccuracyStats(results_it->second->key_type,
520 *(nav_it->second),
521 results_it->second->requests.get());
522 }
523 } else {
524 scoped_ptr<ResourcePrefetcher::RequestVector> requests(
525 new ResourcePrefetcher::RequestVector);
526 PrefetchKeyType key_type;
527 if (GetPrefetchData(navigation_id, requests.get(), &key_type)){
528 RecordNavigationEvent(NAVIGATION_EVENT_HAVE_PREDICTIONS_FOR_URL);
529 ReportPredictedAccuracyStats(key_type,
530 *(nav_it->second),
531 *requests);
532 } else {
533 RecordNavigationEvent(NAVIGATION_EVENT_NO_PREDICTIONS_FOR_URL);
534 }
535 }
536
537 // Remove the navigation from the inflight navigations.
538 std::vector<URLRequestSummary>* requests =
539 inflight_navigations_[navigation_id].release();
540 inflight_navigations_.erase(navigation_id);
541
542 // Kick off history lookup to determine if we should record the URL.
543 HistoryService* history_service = HistoryServiceFactory::GetForProfile(
544 profile_, Profile::EXPLICIT_ACCESS);
545 DCHECK(history_service);
546 history_service->ScheduleDBTask(
547 new GetUrlVisitCountTask(
548 navigation_id,
549 requests,
550 base::Bind(&ResourcePrefetchPredictor::OnVisitCountLookup,
551 AsWeakPtr())),
552 &history_lookup_consumer_);
553 }
554
555 bool ResourcePrefetchPredictor::GetPrefetchData(
556 const NavigationID& navigation_id,
557 ResourcePrefetcher::RequestVector* prefetch_requests,
558 PrefetchKeyType* key_type) {
559 DCHECK(prefetch_requests);
560 DCHECK(key_type);
561
562 *key_type = PREFETCH_KEY_TYPE_URL;
563 const GURL& main_frame_url = navigation_id.main_frame_url;
564
565 bool use_url_data = config_.IsPrefetchingEnabled() ?
566 config_.IsURLPrefetchingEnabled() : config_.IsURLLearningEnabled();
567 if (use_url_data) {
568 PrefetchDataMap::const_iterator iterator =
569 url_table_cache_->find(main_frame_url.spec());
570 if (iterator != url_table_cache_->end())
571 PopulatePrefetcherRequest(iterator->second, prefetch_requests);
572 }
573 if (!prefetch_requests->empty())
574 return true;
575
576 bool use_host_data = config_.IsPrefetchingEnabled() ?
577 config_.IsHostPrefetchingEnabled() : config_.IsHostLearningEnabled();
578 if (use_host_data) {
579 PrefetchDataMap::const_iterator iterator =
580 host_table_cache_->find(main_frame_url.host());
581 if (iterator != host_table_cache_->end()) {
582 *key_type = PREFETCH_KEY_TYPE_HOST;
583 PopulatePrefetcherRequest(iterator->second, prefetch_requests);
584 }
585 }
586
587 return !prefetch_requests->empty();
588 }
589
590 void ResourcePrefetchPredictor::PopulatePrefetcherRequest(
591 const PrefetchData& data,
592 ResourcePrefetcher::RequestVector* requests) {
593 for (ResourceRows::const_iterator it = data.resources.begin();
594 it != data.resources.end(); ++it) {
595 float confidence = static_cast<float>(it->number_of_hits) /
596 (it->number_of_hits + it->number_of_misses);
597 if (confidence < config_.min_resource_confidence_to_trigger_prefetch ||
598 it->number_of_hits < config_.min_resource_hits_to_trigger_prefetch) {
599 continue;
600 }
601
602 ResourcePrefetcher::Request* req = new ResourcePrefetcher::Request(
603 it->resource_url);
604 requests->push_back(req);
605 }
606 }
607
608 void ResourcePrefetchPredictor::StartPrefetching(
609 const NavigationID& navigation_id) {
610 if (!prefetch_manager_.get()) // Prefetching not enabled.
611 return;
612
613 // Prefer URL based data first.
614 scoped_ptr<ResourcePrefetcher::RequestVector> requests(
615 new ResourcePrefetcher::RequestVector);
616 PrefetchKeyType key_type;
617 if (!GetPrefetchData(navigation_id, requests.get(), &key_type)) {
618 // No prefetching data at host or URL level.
619 return;
620 }
621
622 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
623 base::Bind(&ResourcePrefetcherManager::MaybeAddPrefetch,
624 prefetch_manager_,
625 navigation_id,
626 key_type,
627 base::Passed(&requests)));
628 }
629
630 void ResourcePrefetchPredictor::StopPrefetching(
631 const NavigationID& navigation_id) {
632 if (!prefetch_manager_.get()) // Not enabled.
633 return;
634
635 BrowserThread::PostTask(
636 BrowserThread::IO, FROM_HERE,
637 base::Bind(&ResourcePrefetcherManager::MaybeRemovePrefetch,
638 prefetch_manager_,
639 navigation_id));
640 }
641
642 void ResourcePrefetchPredictor::StartInitialization() {
643 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
644
645 DCHECK_EQ(initialization_state_, NOT_INITIALIZED);
646 initialization_state_ = INITIALIZING;
647
648 // Create local caches using the database as loaded.
649 scoped_ptr<PrefetchDataMap> url_data_map(new PrefetchDataMap());
650 scoped_ptr<PrefetchDataMap> host_data_map(new PrefetchDataMap());
651 PrefetchDataMap* url_data_ptr = url_data_map.get();
652 PrefetchDataMap* host_data_ptr = host_data_map.get();
653
654 BrowserThread::PostTaskAndReply(
655 BrowserThread::DB, FROM_HERE,
656 base::Bind(&ResourcePrefetchPredictorTables::GetAllData,
657 tables_, url_data_ptr, host_data_ptr),
658 base::Bind(&ResourcePrefetchPredictor::CreateCaches, AsWeakPtr(),
659 base::Passed(&url_data_map), base::Passed(&host_data_map)));
660 }
661
662 void ResourcePrefetchPredictor::CreateCaches(
663 scoped_ptr<PrefetchDataMap> url_data_map,
664 scoped_ptr<PrefetchDataMap> host_data_map) {
665 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
666
667 DCHECK_EQ(initialization_state_, INITIALIZING);
668 DCHECK(!url_table_cache_);
669 DCHECK(!host_table_cache_);
670 DCHECK(inflight_navigations_.empty());
671
672 url_table_cache_.reset(url_data_map.release());
673 host_table_cache_.reset(host_data_map.release());
674
675 UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.UrlTableMainFrameUrlCount",
676 url_table_cache_->size());
677 UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HostTableHostCount",
678 host_table_cache_->size());
679
680 // Add notifications for history loading if it is not ready.
681 HistoryService* history_service = HistoryServiceFactory::GetForProfile(
682 profile_, Profile::EXPLICIT_ACCESS);
683 if (!history_service) {
684 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_LOADED,
685 content::Source<Profile>(profile_));
686 } else {
687 OnHistoryAndCacheLoaded();
688 }
689 }
690
691 void ResourcePrefetchPredictor::OnHistoryAndCacheLoaded() {
692 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
693 DCHECK_EQ(initialization_state_, INITIALIZING);
694
695 notification_registrar_.Add(this,
696 chrome::NOTIFICATION_HISTORY_URLS_DELETED,
697 content::Source<Profile>(profile_));
698
699 // Initialize the prefetch manager only if prefetching is enabled.
700 if (config_.IsPrefetchingEnabled()) {
701 prefetch_manager_ = new ResourcePrefetcherManager(
702 this, config_, profile_->GetRequestContext());
703 }
704
705 initialization_state_ = INITIALIZED;
706 }
707
708 void ResourcePrefetchPredictor::CleanupAbandonedNavigations(
709 const NavigationID& navigation_id) {
710 static const base::TimeDelta max_navigation_age =
711 base::TimeDelta::FromSeconds(config_.max_navigation_lifetime_seconds);
712
713 base::TimeTicks time_now = base::TimeTicks::Now();
714 for (NavigationMap::iterator it = inflight_navigations_.begin();
715 it != inflight_navigations_.end();) {
716 if (it->first.IsSameRenderer(navigation_id) ||
717 (time_now - it->first.creation_time > max_navigation_age)) {
718 inflight_navigations_.erase(it++);
719 RecordNavigationEvent(NAVIGATION_EVENT_REQUEST_EXPIRED);
720 } else {
721 ++it;
722 }
723 }
724 for (ResultsMap::iterator it = results_map_.begin();
725 it != results_map_.end();) {
726 if (it->first.IsSameRenderer(navigation_id) ||
727 (time_now - it->first.creation_time > max_navigation_age)) {
728 delete it->second;
729 results_map_.erase(it++);
730 } else {
731 ++it;
732 }
733 }
734 }
735
736 void ResourcePrefetchPredictor::DeleteAllUrls() {
737 inflight_navigations_.clear();
738 url_table_cache_->clear();
739 host_table_cache_->clear();
740
741 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
742 base::Bind(&ResourcePrefetchPredictorTables::DeleteAllData, tables_));
743 }
744
745 void ResourcePrefetchPredictor::DeleteUrls(const history::URLRows& urls) {
746 // Check all the urls in the database and pick out the ones that are present
747 // in the cache.
748 std::vector<std::string> urls_to_delete, hosts_to_delete;
749
750 for (history::URLRows::const_iterator it = urls.begin(); it != urls.end();
751 ++it) {
752 const std::string url_spec = it->url().spec();
753 if (url_table_cache_->find(url_spec) != url_table_cache_->end()) {
754 urls_to_delete.push_back(url_spec);
755 url_table_cache_->erase(url_spec);
756 }
757
758 const std::string host = it->url().host();
759 if (host_table_cache_->find(host) != host_table_cache_->end()) {
760 hosts_to_delete.push_back(host);
761 host_table_cache_->erase(host);
762 }
763 }
764
765 if (!urls_to_delete.empty() || !hosts_to_delete.empty()) {
766 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
767 base::Bind(&ResourcePrefetchPredictorTables::DeleteData,
768 tables_,
769 urls_to_delete,
770 hosts_to_delete));
771 }
772 }
773
774 void ResourcePrefetchPredictor::RemoveOldestEntryInPrefetchDataMap(
775 PrefetchKeyType key_type,
776 PrefetchDataMap* data_map) {
777 if (data_map->empty())
778 return;
779
780 base::Time oldest_time;
781 std::string key_to_delete;
782 for (PrefetchDataMap::iterator it = data_map->begin();
783 it != data_map->end(); ++it) {
784 if (key_to_delete.empty() || it->second.last_visit < oldest_time) {
785 key_to_delete = it->first;
786 oldest_time = it->second.last_visit;
787 }
788 }
789
790 data_map->erase(key_to_delete);
791 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
792 base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint,
793 tables_,
794 key_to_delete,
795 key_type));
796 }
797
798 void ResourcePrefetchPredictor::OnVisitCountLookup(
799 int visit_count,
800 const NavigationID& navigation_id,
801 const std::vector<URLRequestSummary>& requests) {
802 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
803
804 UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HistoryVisitCountForUrl",
805 visit_count);
806
807 // URL level data - merge only if we are already saving the data, or we it
808 // meets the cutoff requirement.
809 const std::string url_spec = navigation_id.main_frame_url.spec();
810 bool already_tracking = url_table_cache_->find(url_spec) !=
811 url_table_cache_->end();
812 bool should_track_url = already_tracking ||
813 (visit_count >= config_.min_url_visit_count);
814
815 if (should_track_url) {
816 RecordNavigationEvent(NAVIGATION_EVENT_SHOULD_TRACK_URL);
817
818 if (config_.IsURLLearningEnabled()) {
819 LearnNavigation(url_spec, PREFETCH_KEY_TYPE_URL, requests,
820 config_.max_urls_to_track, url_table_cache_.get());
821 }
822 } else {
823 RecordNavigationEvent(NAVIGATION_EVENT_SHOULD_NOT_TRACK_URL);
824 }
825
826 // Host level data - no cutoff, always learn the navigation if enabled.
827 if (config_.IsHostLearningEnabled()) {
828 LearnNavigation(navigation_id.main_frame_url.host(),
829 PREFETCH_KEY_TYPE_HOST,
830 requests,
831 config_.max_hosts_to_track,
832 host_table_cache_.get());
833 }
834
835 // Remove the navigation from the results map.
836 delete results_map_[navigation_id];
837 results_map_.erase(navigation_id);
838 }
839
840 void ResourcePrefetchPredictor::LearnNavigation(
841 const std::string& key,
842 PrefetchKeyType key_type,
843 const std::vector<URLRequestSummary>& new_resources,
844 int max_data_map_size,
845 PrefetchDataMap* data_map) {
846 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
847
848 // If the primary key is too long reject it.
849 if (key.length() > ResourcePrefetchPredictorTables::kMaxStringLength) {
850 if (key_type == PREFETCH_KEY_TYPE_HOST)
851 RecordNavigationEvent(NAVIGATION_EVENT_HOST_TOO_LONG);
852 else
853 RecordNavigationEvent(NAVIGATION_EVENT_MAIN_FRAME_URL_TOO_LONG);
854 return;
855 }
856
857 PrefetchDataMap::iterator cache_entry = data_map->find(key);
858 if (cache_entry == data_map->end()) {
859 if (static_cast<int>(data_map->size()) >= max_data_map_size) {
860 // The table is full, delete an entry.
861 RemoveOldestEntryInPrefetchDataMap(key_type, data_map);
862 }
863
864 cache_entry = data_map->insert(std::make_pair(
865 key, PrefetchData(key_type, key))).first;
866 cache_entry->second.last_visit = base::Time::Now();
867 int new_resources_size = static_cast<int>(new_resources.size());
868 std::set<GURL> resources_seen;
869 for (int i = 0; i < new_resources_size; ++i) {
870 if (resources_seen.find(new_resources[i].resource_url) !=
871 resources_seen.end()) {
872 continue;
873 }
874 ResourceRow row_to_add;
875 row_to_add.resource_url = new_resources[i].resource_url;
876 row_to_add.resource_type = new_resources[i].resource_type;
877 row_to_add.number_of_hits = 1;
878 row_to_add.average_position = i + 1;
879 cache_entry->second.resources.push_back(row_to_add);
880 resources_seen.insert(new_resources[i].resource_url);
881 }
882 } else {
883 ResourceRows& old_resources = cache_entry->second.resources;
884 cache_entry->second.last_visit = base::Time::Now();
885
886 // Build indices over the data.
887 std::map<GURL, int> new_index, old_index;
888 int new_resources_size = static_cast<int>(new_resources.size());
889 for (int i = 0; i < new_resources_size; ++i) {
890 const URLRequestSummary& summary = new_resources[i];
891 // Take the first occurence of every url.
892 if (new_index.find(summary.resource_url) == new_index.end())
893 new_index[summary.resource_url] = i;
894 }
895 int old_resources_size = static_cast<int>(old_resources.size());
896 for (int i = 0; i < old_resources_size; ++i) {
897 const ResourceRow& row = old_resources[i];
898 DCHECK(old_index.find(row.resource_url) == old_index.end());
899 old_index[row.resource_url] = i;
900 }
901
902 // Go through the old urls and update their hit/miss counts.
903 for (int i = 0; i < old_resources_size; ++i) {
904 ResourceRow& old_row = old_resources[i];
905 if (new_index.find(old_row.resource_url) == new_index.end()) {
906 ++old_row.number_of_misses;
907 ++old_row.consecutive_misses;
908 } else {
909 const URLRequestSummary& new_row =
910 new_resources[new_index[old_row.resource_url]];
911
912 // Update the resource type since it could have changed.
913 if (new_row.resource_type != ResourceType::LAST_TYPE)
914 old_row.resource_type = new_row.resource_type;
915
916 int position = new_index[old_row.resource_url] + 1;
917 int total = old_row.number_of_hits + old_row.number_of_misses;
918 old_row.average_position =
919 ((old_row.average_position * total) + position) / (total + 1);
920 ++old_row.number_of_hits;
921 old_row.consecutive_misses = 0;
922 }
923 }
924
925 // Add the new ones that we have not seen before.
926 for (int i = 0; i < new_resources_size; ++i) {
927 const URLRequestSummary& summary = new_resources[i];
928 if (old_index.find(summary.resource_url) != old_index.end())
929 continue;
930
931 // Only need to add new stuff.
932 ResourceRow row_to_add;
933 row_to_add.resource_url = summary.resource_url;
934 row_to_add.resource_type = summary.resource_type;
935 row_to_add.number_of_hits = 1;
936 row_to_add.average_position = i + 1;
937 old_resources.push_back(row_to_add);
938
939 // To ensure we dont add the same url twice.
940 old_index[summary.resource_url] = 0;
941 }
942 }
943
944 // Trim and sort the resources after the update.
945 ResourceRows& resources = cache_entry->second.resources;
946 for (ResourceRows::iterator it = resources.begin();
947 it != resources.end();) {
948 it->UpdateScore();
949 if (it->consecutive_misses >= config_.max_consecutive_misses)
950 it = resources.erase(it);
951 else
952 ++it;
953 }
954 std::sort(resources.begin(), resources.end(),
955 ResourcePrefetchPredictorTables::ResourceRowSorter());
956 if (static_cast<int>(resources.size()) > config_.max_resources_per_entry)
957 resources.resize(config_.max_resources_per_entry);
958
959 // If the row has no resources, remove it from the cache and delete the
960 // entry in the database. Else update the database.
961 if (resources.size() == 0) {
962 data_map->erase(key);
963 BrowserThread::PostTask(
964 BrowserThread::DB, FROM_HERE,
965 base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint,
966 tables_,
967 key,
968 key_type));
969 } else {
970 bool is_host = key_type == PREFETCH_KEY_TYPE_HOST;
971 PrefetchData empty_data(
972 !is_host ? PREFETCH_KEY_TYPE_HOST : PREFETCH_KEY_TYPE_URL,
973 std::string());
974 const PrefetchData& host_data = is_host ? cache_entry->second : empty_data;
975 const PrefetchData& url_data = is_host ? empty_data : cache_entry->second;
976 BrowserThread::PostTask(
977 BrowserThread::DB, FROM_HERE,
978 base::Bind(&ResourcePrefetchPredictorTables::UpdateData,
979 tables_,
980 url_data,
981 host_data));
982 }
983 }
984
985 ////////////////////////////////////////////////////////////////////////////////
986 // Accuracy measurement.
987
988 void ResourcePrefetchPredictor::ReportAccuracyStats(
989 PrefetchKeyType key_type,
990 const std::vector<URLRequestSummary>& actual,
991 ResourcePrefetcher::RequestVector* prefetched) const {
992 // Annotate the results.
993 std::map<GURL, bool> actual_resources;
994 for (std::vector<URLRequestSummary>::const_iterator it = actual.begin();
995 it != actual.end(); ++it) {
996 actual_resources[it->resource_url] = it->was_cached;
997 }
998
999 int prefetch_cancelled = 0, prefetch_failed = 0, prefetch_not_started = 0;
1000 // 'a_' -> actual, 'p_' -> predicted.
1001 int p_cache_a_cache = 0, p_cache_a_network = 0, p_cache_a_notused = 0,
1002 p_network_a_cache = 0, p_network_a_network = 0, p_network_a_notused = 0;
1003
1004 for (ResourcePrefetcher::RequestVector::iterator it = prefetched->begin();
1005 it != prefetched->end(); ++it) {
1006 ResourcePrefetcher::Request* req = *it;
1007
1008 // Set the usage states if the resource was actually used.
1009 std::map<GURL, bool>::iterator actual_it = actual_resources.find(
1010 req->resource_url);
1011 if (actual_it != actual_resources.end()) {
1012 if (actual_it->second) {
1013 req->usage_status =
1014 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE;
1015 } else {
1016 req->usage_status =
1017 ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK;
1018 }
1019 }
1020
1021 switch (req->prefetch_status) {
1022
1023 // TODO(shishir): Add histogram for each cancellation reason.
1024 case ResourcePrefetcher::Request::PREFETCH_STATUS_REDIRECTED:
1025 case ResourcePrefetcher::Request::PREFETCH_STATUS_AUTH_REQUIRED:
1026 case ResourcePrefetcher::Request::PREFETCH_STATUS_CERT_REQUIRED:
1027 case ResourcePrefetcher::Request::PREFETCH_STATUS_CERT_ERROR:
1028 case ResourcePrefetcher::Request::PREFETCH_STATUS_CANCELLED:
1029 ++prefetch_cancelled;
1030 break;
1031
1032 case ResourcePrefetcher::Request::PREFETCH_STATUS_FAILED:
1033 ++prefetch_failed;
1034 break;
1035
1036 case ResourcePrefetcher::Request::PREFETCH_STATUS_FROM_CACHE:
1037 if (req->usage_status ==
1038 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE)
1039 ++p_cache_a_cache;
1040 else if (req->usage_status ==
1041 ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK)
1042 ++p_cache_a_network;
1043 else
1044 ++p_cache_a_notused;
1045 break;
1046
1047 case ResourcePrefetcher::Request::PREFETCH_STATUS_FROM_NETWORK:
1048 if (req->usage_status ==
1049 ResourcePrefetcher::Request::USAGE_STATUS_FROM_CACHE)
1050 ++p_network_a_cache;
1051 else if (req->usage_status ==
1052 ResourcePrefetcher::Request::USAGE_STATUS_FROM_NETWORK)
1053 ++p_network_a_network;
1054 else
1055 ++p_network_a_notused;
1056 break;
1057
1058 case ResourcePrefetcher::Request::PREFETCH_STATUS_NOT_STARTED:
1059 ++prefetch_not_started;
1060 break;
1061
1062 case ResourcePrefetcher::Request::PREFETCH_STATUS_STARTED:
1063 DLOG(FATAL) << "Invalid prefetch status";
1064 break;
1065 }
1066 }
1067
1068 int total_prefetched = p_cache_a_cache + p_cache_a_network + p_cache_a_notused
1069 + p_network_a_cache + p_network_a_network + p_network_a_notused;
1070
1071 std::string histogram_type = key_type == PREFETCH_KEY_TYPE_HOST ? "Host." :
1072 "Url.";
1073
1074 // Macros to avoid using the STATIC_HISTOGRAM_POINTER_BLOCK in UMA_HISTOGRAM
1075 // definitions.
1076 #define RPP_HISTOGRAM_PERCENTAGE(suffix, value) \
1077 { \
1078 std::string name = "ResourcePrefetchPredictor." + histogram_type + suffix; \
1079 std::string g_name = "ResourcePrefetchPredictor." + std::string(suffix); \
1080 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( \
1081 name, 1, 101, 102, base::Histogram::kUmaTargetedHistogramFlag); \
1082 histogram->Add(value); \
1083 UMA_HISTOGRAM_PERCENTAGE(g_name, value); \
1084 }
1085
1086 RPP_HISTOGRAM_PERCENTAGE("PrefetchCancelled",
1087 prefetch_cancelled * 100.0 / total_prefetched);
1088 RPP_HISTOGRAM_PERCENTAGE("PrefetchFailed",
1089 prefetch_failed * 100.0 / total_prefetched);
1090 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromCacheUsedFromCache",
1091 p_cache_a_cache * 100.0 / total_prefetched);
1092 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromCacheUsedFromNetwork",
1093 p_cache_a_network * 100.0 / total_prefetched);
1094 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromCacheNotUsed",
1095 p_cache_a_notused * 100.0 / total_prefetched);
1096 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromNetworkUsedFromCache",
1097 p_network_a_cache * 100.0 / total_prefetched);
1098 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromNetworkUsedFromNetwork",
1099 p_network_a_network * 100.0 / total_prefetched);
1100 RPP_HISTOGRAM_PERCENTAGE("PrefetchFromNetworkNotUsed",
1101 p_network_a_notused * 100.0 / total_prefetched);
1102
1103 RPP_HISTOGRAM_PERCENTAGE(
1104 "PrefetchNotStarted",
1105 prefetch_not_started * 100.0 / (prefetch_not_started + total_prefetched));
1106
1107 #undef RPP_HISTOGRAM_PERCENTAGE
1108 }
1109
1110 void ResourcePrefetchPredictor::ReportPredictedAccuracyStats(
1111 PrefetchKeyType key_type,
1112 const std::vector<URLRequestSummary>& actual,
1113 const ResourcePrefetcher::RequestVector& predicted) const {
1114 std::map<GURL, bool> actual_resources;
1115 int from_network = 0;
1116 for (std::vector<URLRequestSummary>::const_iterator it = actual.begin();
1117 it != actual.end(); ++it) {
1118 actual_resources[it->resource_url] = it->was_cached;
1119 if (!it->was_cached)
1120 ++from_network;
1121 }
1122
1123 // Measure the accuracy at 25, 50 predicted resources.
1124 ReportPredictedAccuracyStatsHelper(key_type, predicted, actual_resources,
1125 from_network, 25);
1126 ReportPredictedAccuracyStatsHelper(key_type, predicted, actual_resources,
1127 from_network, 50);
1128 }
1129
1130 void ResourcePrefetchPredictor::ReportPredictedAccuracyStatsHelper(
1131 PrefetchKeyType key_type,
1132 const ResourcePrefetcher::RequestVector& predicted,
1133 const std::map<GURL, bool>& actual,
1134 int total_resources_fetched_from_network,
1135 int max_assumed_prefetched) const {
1136 int prefetch_cached = 0, prefetch_network = 0, prefetch_missed = 0;
1137 int num_assumed_prefetched = std::min(static_cast<int>(predicted.size()),
1138 max_assumed_prefetched);
1139 if (num_assumed_prefetched == 0)
1140 return;
1141
1142 for (int i = 0; i < num_assumed_prefetched; ++i) {
1143 const ResourcePrefetcher::Request& row = *(predicted[i]);
1144 std::map<GURL, bool>::const_iterator it = actual.find(row.resource_url);
1145 if (it == actual.end()) {
1146 ++prefetch_missed;
1147 } else if (it->second) {
1148 ++prefetch_cached;
1149 } else {
1150 ++prefetch_network;
1151 }
1152 }
1153
1154 std::string prefix = key_type == PREFETCH_KEY_TYPE_HOST ?
1155 "ResourcePrefetchPredictor.Host.Predicted" :
1156 "ResourcePrefetchPredictor.Url.Predicted";
1157 std::string suffix = "_" + base::IntToString(max_assumed_prefetched);
1158
1159 // Macros to avoid using the STATIC_HISTOGRAM_POINTER_BLOCK in UMA_HISTOGRAM
1160 // definitions.
1161 #define RPP_PREDICTED_HISTOGRAM_COUNTS(name, value) \
1162 { \
1163 std::string full_name = prefix + name + suffix; \
1164 base::HistogramBase* histogram = base::Histogram::FactoryGet( \
1165 full_name, 1, 1000000, 50, \
1166 base::Histogram::kUmaTargetedHistogramFlag); \
1167 histogram->Add(value); \
1168 }
1169
1170 #define RPP_PREDICTED_HISTOGRAM_PERCENTAGE(name, value) \
1171 { \
1172 std::string full_name = prefix + name + suffix; \
1173 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( \
1174 full_name, 1, 101, 102, base::Histogram::kUmaTargetedHistogramFlag); \
1175 histogram->Add(value); \
1176 }
1177
1178 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchCount", num_assumed_prefetched);
1179 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchMisses_Count", prefetch_missed);
1180 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchFromCache_Count", prefetch_cached);
1181 RPP_PREDICTED_HISTOGRAM_COUNTS("PrefetchFromNetwork_Count", prefetch_network);
1182
1183 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1184 "PrefetchMisses_PercentOfTotalPrefetched",
1185 prefetch_missed * 100.0 / num_assumed_prefetched);
1186 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1187 "PrefetchFromCache_PercentOfTotalPrefetched",
1188 prefetch_cached * 100.0 / num_assumed_prefetched);
1189 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1190 "PrefetchFromNetwork_PercentOfTotalPrefetched",
1191 prefetch_network * 100.0 / num_assumed_prefetched);
1192
1193 // Measure the ratio of total number of resources prefetched from network vs
1194 // the total number of resources fetched by the page from the network.
1195 if (total_resources_fetched_from_network > 0) {
1196 RPP_PREDICTED_HISTOGRAM_PERCENTAGE(
1197 "PrefetchFromNetworkPercentOfTotalFromNetwork",
1198 prefetch_network * 100.0 / total_resources_fetched_from_network);
1199 }
1200
1201 #undef RPP_PREDICTED_HISTOGRAM_PERCENTAGE
1202 #undef RPP_PREDICTED_HISTOGRAM_COUNTS
1203 }
1204
1205 } // namespace predictors
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698