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

Side by Side Diff: chrome/browser/net/cache_stats.cc

Issue 10736066: Adding histograms showing fraction of page load times (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 5 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/net/cache_stats.h"
6
7 #include <vector>
8
9 #include "base/hash_tables.h"
10 #include "base/metrics/histogram.h"
11 #include "base/stl_util.h"
12 #include "base/string_number_conversions.h"
13 #include "base/timer.h"
14 #include "chrome/browser/ui/tab_contents/tab_contents.h"
15 #include "content/common/view_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/resource_request_info.h"
20 #include "content/public/browser/web_contents.h"
21 #include "net/url_request/url_request.h"
22
23 using content::BrowserThread;
24 using content::ResourceRequestInfo;
25 using content::RenderViewHost;
26
27 #if defined(COMPILER_GCC)
28
29 namespace BASE_HASH_NAMESPACE {
30 template <>
31 struct hash<const net::URLRequest*> {
32 std::size_t operator()(const net::URLRequest* value) const {
33 return reinterpret_cast<std::size_t>(value);
34 }
35 };
36 }
37
38 #endif
39
40 namespace chrome_browser_net {
41
42 namespace {
43
44 bool GetRenderView(const net::URLRequest& request,
45 int* process_id, int* route_id) {
46 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(&request);
47 if (!info)
48 return false;
49
50 return info->GetAssociatedRenderView(process_id, route_id);
51 }
52
53 // Times after a load has started at which stats are collected.
54 const int kStatsCollectionTimesMs[] = {
55 500,
56 1000,
57 2000,
58 3000,
59 4000,
60 5000,
61 7500,
62 10000,
63 15000,
64 20000
65 };
66
67 } // namespace
68
69 // static
70 CacheStats* CacheStats::GetInstance() {
71 return Singleton<CacheStats>::get();
72 }
73
74 struct CacheStats::TabLoadStats {
75 TabLoadStats()
76 : num_active_(0),
77 spinner_started_(false),
78 timer_(false, false) {
79 }
80
81 int num_active_;
82 bool spinner_started_;
83 base::TimeTicks load_start_time_;
84 base::TimeTicks cache_start_time_;
85 base::TimeDelta cache_total_time_;
86 base::Timer timer_;
87 typedef base::hash_map<const net::URLRequest*, int> PerRequestNumActiveMap;
88 PerRequestNumActiveMap per_request_num_active_;
rvargas (doing something else) 2012/07/23 22:22:36 This deserves a comment.
tburkard 2012/07/24 01:03:12 Done.
rvargas (doing something else) 2012/07/26 02:13:43 I meant the map.
89 };
90
91 CacheStatsRenderViewHostObserver::CacheStatsRenderViewHostObserver(
92 RenderViewHost* host)
93 : content::RenderViewHostObserver(host),
94 cache_stats_(CacheStats::GetInstance()),
95 is_loading_(false) {
96 int process_id = render_view_host()->GetProcess()->GetID();
97 int route_id = render_view_host()->GetRoutingID();
98 render_view_id_ = std::pair<int, int>(process_id, route_id);
99 }
100
101 bool CacheStatsRenderViewHostObserver::OnMessageReceived(
102 const IPC::Message& message) {
103 IPC_BEGIN_MESSAGE_MAP(CacheStatsRenderViewHostObserver, message)
104 IPC_MESSAGE_HANDLER(ViewHostMsg_DidStartProvisionalLoadForFrame,
105 OnDidStartProvisionalLoadForFrame)
106 IPC_MESSAGE_HANDLER(ViewHostMsg_DidStopLoading, OnDidStopLoading)
107 IPC_END_MESSAGE_MAP()
108
109 return false;
110 }
111
112 void CacheStatsRenderViewHostObserver::OnDidStartProvisionalLoadForFrame(
113 int64 frame_id,
114 bool is_main_frame,
115 const GURL& opener_url,
116 const GURL& url) {
117 if (!is_main_frame)
118 return;
119 if (is_loading_)
120 OnDidStopLoading();
121 if (!url.SchemeIs("http"))
122 return;
123 is_loading_ = true;
124 NotifyCacheStats(CacheStats::SPINNER_START);
125 }
126
127 void CacheStatsRenderViewHostObserver::OnDidStopLoading() {
128 is_loading_ = false;
129 NotifyCacheStats(CacheStats::SPINNER_STOP);
130 }
131
132 CacheStatsRenderViewHostObserver::~CacheStatsRenderViewHostObserver() {
133 NotifyCacheStats(CacheStats::RENDERER_DESTROY);
134 }
135
136 void CacheStatsRenderViewHostObserver::NotifyCacheStats(
137 CacheStats::TabEvent event) {
138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
139 BrowserThread::PostTask(
140 BrowserThread::IO, FROM_HERE,
141 base::Bind(&CacheStats::OnTabEvent,
142 base::Unretained(cache_stats_),
143 render_view_id_, event));
144 }
145
146 CacheStats::CacheStats() :
147 registered_message_loop_destruction_observer_(false) {
148 for (int i = 0;
149 i < static_cast<int>(arraysize(kStatsCollectionTimesMs));
150 i++) {
151 final_histograms_.push_back(
152 base::LinearHistogram::FactoryGet(
153 "DiskCache.FractionCacheUseFinalPLT_" +
rvargas (doing something else) 2012/07/23 22:22:36 Don't use DiskCache.x here
tburkard 2012/07/24 01:03:12 Done.
154 base::IntToString(kStatsCollectionTimesMs[i]),
155 0, 101, 102, base::Histogram::kUmaTargetedHistogramFlag));
156 intermediate_histograms_.push_back(
157 base::LinearHistogram::FactoryGet(
158 "DiskCache.FractionCacheUseIntermediatePLT_" +
159 base::IntToString(kStatsCollectionTimesMs[i]),
160 0, 101, 102, base::Histogram::kNoFlags));
161 }
162 DCHECK_EQ(final_histograms_.size(), arraysize(kStatsCollectionTimesMs));
163 DCHECK_EQ(intermediate_histograms_.size(),
164 arraysize(kStatsCollectionTimesMs));
165 }
166
167 CacheStats::~CacheStats() {
168 }
169
170 void CacheStats::WillDestroyCurrentMessageLoop() {
171 MessageLoop::current()->RemoveDestructionObserver(this);
172 STLDeleteValues(&tab_load_stats_);
173 registered_message_loop_destruction_observer_ = false;
174 }
175
176 CacheStats::TabLoadStats* CacheStats::GetTabLoadStats(
177 std::pair<int, int> render_view_id) {
178 if (!registered_message_loop_destruction_observer_) {
179 MessageLoop::current()->AddDestructionObserver(this);
180 registered_message_loop_destruction_observer_ = true;
181 }
182 if (tab_load_stats_.count(render_view_id) < 1)
183 tab_load_stats_[render_view_id] = new TabLoadStats();
184 return tab_load_stats_[render_view_id];
185 }
186
187 void CacheStats::RemoveTabLoadStats(std::pair<int, int> render_view_id) {
188 TabLoadStatsMap::iterator it = tab_load_stats_.find(render_view_id);
189 if (it != tab_load_stats_.end()) {
190 delete it->second;
191 tab_load_stats_.erase(it);
192 }
193 }
194
195 void CacheStats::OnCacheWaitStateChange(
196 const net::URLRequest& request,
197 net::NetworkDelegate::CacheWaitState state) {
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
199 int process_id, route_id;
200 if (!GetRenderView(request, &process_id, &route_id))
201 return;
202 TabLoadStats* stats =
203 GetTabLoadStats(std::pair<int, int>(process_id, route_id));
204 bool newly_started = false;
205 bool newly_finished = false;
206 std::pair<TabLoadStats::PerRequestNumActiveMap::iterator, bool> insert_ret =
207 stats->per_request_num_active_.insert(
208 std::pair<const net::URLRequest*, int>(&request, 0));
209 TabLoadStats::PerRequestNumActiveMap::iterator entry = insert_ret.first;
210 DCHECK_GE(entry->second, 0);
211 switch (state) {
212 case net::NetworkDelegate::CACHE_WAIT_STATE_START:
213 if (entry->second == 0)
214 newly_started = true;
215 entry->second++;
rvargas (doing something else) 2012/07/23 22:22:36 Isn't second > 1 a bug?
tburkard 2012/07/24 01:03:12 Not necessarily, you could imagine at some point h
rvargas (doing something else) 2012/07/24 03:10:38 We should wait until we have the need to support t
tburkard 2012/07/24 22:23:26 Done.
rvargas (doing something else) 2012/07/26 02:13:43 Actually, using a boolean is better. In fact, a se
tburkard 2012/07/26 02:50:54 Done.
216 break;
217 case net::NetworkDelegate::CACHE_WAIT_STATE_FINISH:
218 if (entry->second > 0) {
219 entry->second--;
220 if (entry->second == 0)
221 newly_finished = true;
222 }
223 break;
224 case net::NetworkDelegate::CACHE_WAIT_STATE_DONE:
225 if (entry->second > 0) {
226 entry->second = 0;
227 newly_finished = true;
228 }
229 break;
230 }
231 DCHECK_GE(entry->second, 0);
232 if (newly_started) {
233 DCHECK(!newly_finished);
234 if (stats->num_active_ == 0) {
235 stats->cache_start_time_ = base::TimeTicks::Now();
236 }
237 stats->num_active_++;
238 }
239 if (newly_finished) {
240 DCHECK(!newly_started);
241 if (stats->num_active_ == 1) {
242 stats->cache_total_time_ +=
243 base::TimeTicks::Now() - stats->cache_start_time_;
244 }
245 stats->num_active_--;
246 }
247 }
248
249 void CacheStats::OnTabEvent(std::pair<int, int> render_view_id,
250 TabEvent event) {
251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
252 if (event == RENDERER_DESTROY) {
253 RemoveTabLoadStats(render_view_id);
254 return;
255 }
256 TabLoadStats* stats = GetTabLoadStats(render_view_id);
257 if (event == SPINNER_START) {
258 stats->spinner_started_ = true;
259 stats->cache_total_time_ = base::TimeDelta();
260 stats->cache_start_time_ = base::TimeTicks::Now();
261 stats->load_start_time_ = base::TimeTicks::Now();
262 ScheduleTimer(stats, 0);
263 } else {
264 DCHECK_EQ(event, SPINNER_STOP);
265 stats->timer_.Stop();
266 if (stats->spinner_started_) {
267 stats->spinner_started_ = false;
268 base::TimeDelta load_time =
269 base::TimeTicks::Now() - stats->load_start_time_;
270 if (stats->num_active_ > 1)
271 stats->cache_total_time_ +=
272 base::TimeTicks::Now() - stats->cache_start_time_;
273 RecordCacheFractionHistogram(load_time, stats->cache_total_time_, true);
274 }
275 RemoveTabLoadStats(render_view_id);
276 }
277 }
278
279 void CacheStats::ScheduleTimer(TabLoadStats* stats, int timer_index) {
280 DCHECK(timer_index >= 0 &&
281 timer_index < static_cast<int>(arraysize(kStatsCollectionTimesMs)));
282 base::TimeDelta delta =
283 base::TimeDelta::FromMilliseconds(kStatsCollectionTimesMs[timer_index]);
284 delta -= base::TimeTicks::Now() - stats->load_start_time_;
285 stats->timer_.Start(FROM_HERE,
286 delta,
287 base::Bind(&CacheStats::TimerCb,
288 base::Unretained(this),
289 base::Unretained(stats),
290 timer_index));
291 }
292
293 void CacheStats::TimerCb(TabLoadStats* stats, int timer_index) {
294 DCHECK(stats->spinner_started_);
295 base::TimeDelta load_time = base::TimeTicks::Now() - stats->load_start_time_;
296 base::TimeDelta cache_time = stats->cache_total_time_;
297 if (stats->num_active_ > 1)
298 cache_time += base::TimeTicks::Now() - stats->cache_start_time_;
299 RecordCacheFractionHistogram(load_time, cache_time, false);
300 timer_index++;
301 if (timer_index < static_cast<int>(arraysize(kStatsCollectionTimesMs)))
302 ScheduleTimer(stats, timer_index);
303 }
304
305 void CacheStats::RecordCacheFractionHistogram(base::TimeDelta elapsed,
306 base::TimeDelta cache_time,
307 bool is_load_done) {
308 if (elapsed.InMilliseconds() <= 0)
309 return;
310
311 double cache_fraction =
rvargas (doing something else) 2012/07/23 22:22:36 This is not needed. TimeXx are 64 bit values.
tburkard 2012/07/24 01:03:12 But if I divide two 64 bit integers, I will get an
rvargas (doing something else) 2012/07/24 03:10:38 Not if you multiply the numerator by 100 beforehan
tburkard 2012/07/24 22:23:26 Done.
312 static_cast<double>(cache_time.InMilliseconds()) /
313 static_cast<double>(elapsed.InMilliseconds());
314
315 DCHECK(cache_fraction >= 0.0 && cache_fraction <= 1.0);
316 int cache_fraction_percentage = cache_fraction * 100;
317 DCHECK(cache_fraction_percentage >= 0 && cache_fraction_percentage < 100);
318
319 int index = 0;
320 while (index + 1 < static_cast<int>(arraysize(kStatsCollectionTimesMs)) &&
321 base::TimeDelta::FromMilliseconds(kStatsCollectionTimesMs[index + 1]) <
322 elapsed) {
323 index++;
324 }
325
326 if (is_load_done) {
327 final_histograms_[index]->Add(cache_fraction_percentage);
328 } else {
329 intermediate_histograms_[index]->Add(cache_fraction_percentage);
330 }
331 }
332
333 } // namespace chrome_browser_net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698