load_timing_observer.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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/load_timing_observer.h" 6 7#include "base/compiler_specific.h" 8#include "base/time.h" 9#include "net/base/load_flags.h" 10#include "net/url_request/url_request_netlog_params.h" 11 12using base::Time; 13using base::TimeTicks; 14using webkit_glue::ResourceLoaderBridge; 15 16const size_t kMaxNumEntries = 1000; 17 18namespace { 19 20// We know that this conversion is not solid and suffers from world clock 21// changes, but it should be good enough for the load timing info. 22static Time TimeTicksToTime(const TimeTicks& time_ticks) { 23 static int64 tick_to_time_offset; 24 static bool tick_to_time_offset_available = false; 25 if (!tick_to_time_offset_available) { 26 int64 cur_time = (Time::Now() - Time()).InMicroseconds(); 27 int64 cur_time_ticks = (TimeTicks::Now() - TimeTicks()).InMicroseconds(); 28 // If we add this number to a time tick value, it gives the timestamp. 29 tick_to_time_offset = cur_time - cur_time_ticks; 30 tick_to_time_offset_available = true; 31 } 32 return Time::FromInternalValue(time_ticks.ToInternalValue() + 33 tick_to_time_offset); 34} 35 36static int32 TimeTicksToOffset( 37 const TimeTicks& time_ticks, 38 LoadTimingObserver::URLRequestRecord* record) { 39 return static_cast<int32>( 40 (time_ticks - record->base_ticks).InMillisecondsRoundedUp()); 41} 42 43} 44 45LoadTimingObserver::URLRequestRecord::URLRequestRecord() 46 : connect_job_id(net::NetLog::Source::kInvalidId), 47 socket_log_id(net::NetLog::Source::kInvalidId), 48 socket_reused(false) { 49} 50 51LoadTimingObserver::LoadTimingObserver() { 52} 53 54LoadTimingObserver::~LoadTimingObserver() { 55} 56 57LoadTimingObserver::URLRequestRecord* 58LoadTimingObserver::GetURLRequestRecord(uint32 source_id) { 59 URLRequestToRecordMap::iterator it = url_request_to_record_.find(source_id); 60 if (it != url_request_to_record_.end()) 61 return &it->second; 62 return NULL; 63} 64 65void LoadTimingObserver::OnAddEntry(net::NetLog::EventType type, 66 const base::TimeTicks& time, 67 const net::NetLog::Source& source, 68 net::NetLog::EventPhase phase, 69 net::NetLog::EventParameters* params) { 70 if (source.type == net::NetLog::SOURCE_URL_REQUEST) 71 OnAddURLRequestEntry(type, time, source, phase, params); 72 else if (source.type == net::NetLog::SOURCE_CONNECT_JOB) 73 OnAddConnectJobEntry(type, time, source, phase, params); 74 else if (source.type == net::NetLog::SOURCE_SOCKET) 75 OnAddSocketEntry(type, time, source, phase, params); 76} 77 78void LoadTimingObserver::OnAddURLRequestEntry( 79 net::NetLog::EventType type, 80 const base::TimeTicks& time, 81 const net::NetLog::Source& source, 82 net::NetLog::EventPhase phase, 83 net::NetLog::EventParameters* params) { 84 bool is_begin = phase == net::NetLog::PHASE_BEGIN; 85 bool is_end = phase == net::NetLog::PHASE_END; 86 87 if (type == net::NetLog::TYPE_URL_REQUEST_START_JOB) { 88 if (is_begin) { 89 // Only record timing for entries with corresponding flag. 90 int load_flags = static_cast<URLRequestStartEventParameters*>(params)-> 91 load_flags(); 92 if (!(load_flags & net::LOAD_ENABLE_LOAD_TIMING)) 93 return; 94 95 // Prevents us from passively growing the memory memory unbounded in case 96 // something went wrong. Should not happen. 97 if (url_request_to_record_.size() > kMaxNumEntries) { 98 LOG(WARNING) << "The load timing observer url request count has grown " 99 "larger than expected, resetting"; 100 url_request_to_record_.clear(); 101 } 102 103 URLRequestRecord& record = url_request_to_record_[source.id]; 104 record.base_ticks = time; 105 record.timing.base_time = TimeTicksToTime(time); 106 } 107 return; 108 } else if (type == net::NetLog::TYPE_REQUEST_ALIVE) { 109 // Cleanup records based on the TYPE_REQUEST_ALIVE entry. 110 if (is_end) 111 url_request_to_record_.erase(source.id); 112 return; 113 } 114 115 URLRequestRecord* record = GetURLRequestRecord(source.id); 116 if (!record) 117 return; 118 119 ResourceLoaderBridge::LoadTimingInfo& timing = record->timing; 120 121 switch(type) { 122 case net::NetLog::TYPE_PROXY_SERVICE: 123 if (is_begin) 124 timing.proxy_start = TimeTicksToOffset(time, record); 125 else if (is_end) 126 timing.proxy_end = TimeTicksToOffset(time, record); 127 break; 128 case net::NetLog::TYPE_SOCKET_POOL: 129 if (is_begin) 130 timing.connect_start = TimeTicksToOffset(time, record); 131 else if (is_end) 132 timing.connect_end = TimeTicksToOffset(time, record); 133 break; 134 case net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB: 135 { 136 uint32 connect_job_id = static_cast<net::NetLogSourceParameter*>( 137 params)->value().id; 138 ConnectJobToRecordMap::iterator it = 139 connect_job_to_record_.find(connect_job_id); 140 if (it != connect_job_to_record_.end() && 141 !it->second.dns_start.is_null()) { 142 timing.dns_start = TimeTicksToOffset(it->second.dns_start, record); 143 timing.dns_end = TimeTicksToOffset(it->second.dns_end, record); 144 } 145 } 146 break; 147 case net::NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET: 148 record->socket_reused = true; 149 break; 150 case net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET: 151 record->socket_log_id = static_cast<net::NetLogSourceParameter*>( 152 params)->value().id; 153 if (!record->socket_reused) { 154 SocketToRecordMap::iterator it = 155 socket_to_record_.find(record->socket_log_id); 156 if (it != socket_to_record_.end() && !it->second.ssl_start.is_null()) { 157 timing.ssl_start = TimeTicksToOffset(it->second.ssl_start, record); 158 timing.ssl_end = TimeTicksToOffset(it->second.ssl_end, record); 159 } 160 } 161 break; 162 case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST: 163 case net::NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST: 164 if (is_begin) 165 timing.send_start = TimeTicksToOffset(time, record); 166 else if (is_end) 167 timing.send_end = TimeTicksToOffset(time, record); 168 break; 169 case net::NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS: 170 case net::NetLog::TYPE_SPDY_TRANSACTION_READ_HEADERS: 171 if (is_begin) 172 timing.receive_headers_start = TimeTicksToOffset(time, record); 173 else if (is_end) 174 timing.receive_headers_end = TimeTicksToOffset(time, record); 175 break; 176 default: 177 break; 178 } 179} 180 181void LoadTimingObserver::OnAddConnectJobEntry( 182 net::NetLog::EventType type, 183 const base::TimeTicks& time, 184 const net::NetLog::Source& source, 185 net::NetLog::EventPhase phase, 186 net::NetLog::EventParameters* params) { 187 bool is_begin = phase == net::NetLog::PHASE_BEGIN; 188 bool is_end = phase == net::NetLog::PHASE_END; 189 190 // Manage record lifetime based on the SOCKET_POOL_CONNECT_JOB entry. 191 if (type == net::NetLog::TYPE_SOCKET_POOL_CONNECT_JOB) { 192 if (is_begin) { 193 // Prevents us from passively growing the memory memory unbounded in case 194 // something went wrong. Should not happen. 195 if (connect_job_to_record_.size() > kMaxNumEntries) { 196 LOG(WARNING) << "The load timing observer connect job count has grown " 197 "larger than expected, resetting"; 198 connect_job_to_record_.clear(); 199 } 200 201 connect_job_to_record_.insert( 202 std::make_pair(source.id, ConnectJobRecord())); 203 } else if (is_end) { 204 connect_job_to_record_.erase(source.id); 205 } 206 } else if (type == net::NetLog::TYPE_HOST_RESOLVER_IMPL) { 207 ConnectJobToRecordMap::iterator it = 208 connect_job_to_record_.find(source.id); 209 if (it != connect_job_to_record_.end()) { 210 if (is_begin) 211 it->second.dns_start = time; 212 else if (is_end) 213 it->second.dns_end = time; 214 } 215 } 216} 217 218void LoadTimingObserver::OnAddSocketEntry( 219 net::NetLog::EventType type, 220 const base::TimeTicks& time, 221 const net::NetLog::Source& source, 222 net::NetLog::EventPhase phase, 223 net::NetLog::EventParameters* params) { 224 bool is_begin = phase == net::NetLog::PHASE_BEGIN; 225 bool is_end = phase == net::NetLog::PHASE_END; 226 227 // Manage record lifetime based on the SOCKET_ALIVE entry. 228 if (type == net::NetLog::TYPE_SOCKET_ALIVE) { 229 if (is_begin) { 230 // Prevents us from passively growing the memory memory unbounded in case 231 // something went wrong. Should not happen. 232 if (socket_to_record_.size() > kMaxNumEntries) { 233 LOG(WARNING) << "The load timing observer socket count has grown " 234 "larger than expected, resetting"; 235 socket_to_record_.clear(); 236 } 237 238 socket_to_record_.insert( 239 std::make_pair(source.id, SocketRecord())); 240 } else if (is_end) { 241 socket_to_record_.erase(source.id); 242 } 243 return; 244 } 245 SocketToRecordMap::iterator it = socket_to_record_.find(source.id); 246 if (it == socket_to_record_.end()) 247 return; 248 249 if (type == net::NetLog::TYPE_SSL_CONNECT) { 250 if (is_begin) 251 it->second.ssl_start = time; 252 else if (is_end) 253 it->second.ssl_end = time; 254 } 255} 256