passive_log_collector.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
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/passive_log_collector.h" 6 7#include <algorithm> 8 9#include "base/compiler_specific.h" 10#include "base/string_util.h" 11#include "base/format_macros.h" 12#include "chrome/browser/chrome_thread.h" 13#include "net/url_request/url_request_netlog_params.h" 14 15namespace { 16 17// TODO(eroman): Do something with the truncation count. 18 19const size_t kMaxNumEntriesPerLog = 30; 20 21void AddEntryToSourceInfo(const PassiveLogCollector::Entry& entry, 22 PassiveLogCollector::SourceInfo* out_info) { 23 // Start dropping new entries when the log has gotten too big. 24 if (out_info->entries.size() + 1 <= kMaxNumEntriesPerLog) { 25 out_info->entries.push_back(entry); 26 } else { 27 out_info->num_entries_truncated += 1; 28 out_info->entries[kMaxNumEntriesPerLog - 1] = entry; 29 } 30} 31 32// Comparator to sort entries by their |order| property, ascending. 33bool SortByOrderComparator(const PassiveLogCollector::Entry& a, 34 const PassiveLogCollector::Entry& b) { 35 return a.order < b.order; 36} 37 38} // namespace 39 40//---------------------------------------------------------------------------- 41// PassiveLogCollector 42//---------------------------------------------------------------------------- 43 44PassiveLogCollector::PassiveLogCollector() 45 : Observer(net::NetLog::LOG_BASIC), 46 ALLOW_THIS_IN_INITIALIZER_LIST(connect_job_tracker_(this)), 47 ALLOW_THIS_IN_INITIALIZER_LIST(url_request_tracker_(this)), 48 ALLOW_THIS_IN_INITIALIZER_LIST(socket_stream_tracker_(this)), 49 num_events_seen_(0) { 50 51 // Define the mapping between source types and the tracker objects. 52 memset(&trackers_[0], 0, sizeof(trackers_)); 53 trackers_[net::NetLog::SOURCE_NONE] = &global_source_tracker_; 54 trackers_[net::NetLog::SOURCE_URL_REQUEST] = &url_request_tracker_; 55 trackers_[net::NetLog::SOURCE_SOCKET_STREAM] = &socket_stream_tracker_; 56 trackers_[net::NetLog::SOURCE_CONNECT_JOB] = &connect_job_tracker_; 57 trackers_[net::NetLog::SOURCE_SOCKET] = &socket_tracker_; 58 trackers_[net::NetLog::SOURCE_INIT_PROXY_RESOLVER] = 59 &init_proxy_resolver_tracker_; 60 trackers_[net::NetLog::SOURCE_SPDY_SESSION] = &spdy_session_tracker_; 61 trackers_[net::NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST] = 62 &dns_request_tracker_; 63 trackers_[net::NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB] = &dns_job_tracker_; 64 // Make sure our mapping is up-to-date. 65 for (size_t i = 0; i < arraysize(trackers_); ++i) 66 DCHECK(trackers_[i]) << "Unhandled SourceType: " << i; 67} 68 69PassiveLogCollector::~PassiveLogCollector() { 70} 71 72void PassiveLogCollector::OnAddEntry( 73 net::NetLog::EventType type, 74 const base::TimeTicks& time, 75 const net::NetLog::Source& source, 76 net::NetLog::EventPhase phase, 77 net::NetLog::EventParameters* params) { 78 // Package the parameters into a single struct for convenience. 79 Entry entry(num_events_seen_++, type, time, source, phase, params); 80 81 SourceTrackerInterface* tracker = GetTrackerForSourceType(entry.source.type); 82 if (tracker) 83 tracker->OnAddEntry(entry); 84} 85 86PassiveLogCollector::SourceTrackerInterface* 87PassiveLogCollector::GetTrackerForSourceType( 88 net::NetLog::SourceType source_type) { 89 DCHECK_LE(source_type, static_cast<int>(arraysize(trackers_))); 90 DCHECK_GE(source_type, 0); 91 return trackers_[source_type]; 92} 93 94void PassiveLogCollector::Clear() { 95 for (size_t i = 0; i < arraysize(trackers_); ++i) 96 trackers_[i]->Clear(); 97} 98 99void PassiveLogCollector::GetAllCapturedEvents(EntryList* out) const { 100 out->clear(); 101 102 // Append all of the captured entries held by the various trackers to 103 // |out|. 104 for (size_t i = 0; i < arraysize(trackers_); ++i) 105 trackers_[i]->AppendAllEntries(out); 106 107 // Now sort the list of entries by their insertion time (ascending). 108 std::sort(out->begin(), out->end(), &SortByOrderComparator); 109} 110 111std::string PassiveLogCollector::SourceInfo::GetURL() const { 112 // Note: we look at the first *two* entries, since the outer REQUEST_ALIVE 113 // doesn't actually contain any data. 114 for (size_t i = 0; i < 2 && i < entries.size(); ++i) { 115 const PassiveLogCollector::Entry& entry = entries[i]; 116 if (entry.phase == net::NetLog::PHASE_BEGIN && entry.params) { 117 switch (entry.type) { 118 case net::NetLog::TYPE_URL_REQUEST_START_JOB: 119 return static_cast<URLRequestStartEventParameters*>( 120 entry.params.get())->url().possibly_invalid_spec(); 121 case net::NetLog::TYPE_SOCKET_STREAM_CONNECT: 122 return static_cast<net::NetLogStringParameter*>( 123 entry.params.get())->value(); 124 default: 125 break; 126 } 127 } 128 } 129 return std::string(); 130} 131 132//---------------------------------------------------------------------------- 133// GlobalSourceTracker 134//---------------------------------------------------------------------------- 135 136PassiveLogCollector::GlobalSourceTracker::GlobalSourceTracker() {} 137PassiveLogCollector::GlobalSourceTracker::~GlobalSourceTracker() {} 138 139void PassiveLogCollector::GlobalSourceTracker::OnAddEntry(const Entry& entry) { 140 const size_t kMaxEntries = 30u; 141 entries_.push_back(entry); 142 if (entries_.size() > kMaxEntries) 143 entries_.pop_front(); 144} 145 146void PassiveLogCollector::GlobalSourceTracker::Clear() { 147 entries_.clear(); 148} 149 150void PassiveLogCollector::GlobalSourceTracker::AppendAllEntries( 151 EntryList* out) const { 152 out->insert(out->end(), entries_.begin(), entries_.end()); 153} 154 155//---------------------------------------------------------------------------- 156// SourceTracker 157//---------------------------------------------------------------------------- 158 159PassiveLogCollector::SourceTracker::SourceTracker( 160 size_t max_num_sources, 161 size_t max_graveyard_size, 162 PassiveLogCollector* parent) 163 : max_num_sources_(max_num_sources), 164 max_graveyard_size_(max_graveyard_size), 165 parent_(parent) { 166} 167 168PassiveLogCollector::SourceTracker::~SourceTracker() {} 169 170void PassiveLogCollector::SourceTracker::OnAddEntry(const Entry& entry) { 171 // Lookup or insert a new entry into the bounded map. 172 SourceIDToInfoMap::iterator it = sources_.find(entry.source.id); 173 if (it == sources_.end()) { 174 if (sources_.size() >= max_num_sources_) { 175 LOG(WARNING) << "The passive log data has grown larger " 176 "than expected, resetting"; 177 Clear(); 178 } 179 it = sources_.insert( 180 SourceIDToInfoMap::value_type(entry.source.id, SourceInfo())).first; 181 it->second.source_id = entry.source.id; 182 } 183 184 SourceInfo& info = it->second; 185 Action result = DoAddEntry(entry, &info); 186 187 if (result != ACTION_NONE) { 188 // We are either queuing it for deletion, or deleting it immediately. 189 // If someone else holds a reference to this source, defer the deletion 190 // until all the references are released. 191 info.is_alive = false; 192 if (info.reference_count == 0) { 193 switch (result) { 194 case ACTION_MOVE_TO_GRAVEYARD: 195 AddToDeletionQueue(info.source_id); 196 break; 197 case ACTION_DELETE: 198 DeleteSourceInfo(info.source_id); 199 break; 200 default: 201 NOTREACHED(); 202 break; 203 } 204 } 205 } 206} 207 208void PassiveLogCollector::SourceTracker::DeleteSourceInfo( 209 uint32 source_id) { 210 SourceIDToInfoMap::iterator it = sources_.find(source_id); 211 if (it == sources_.end()) { 212 // TODO(eroman): Is this happening? And if so, why. Remove this 213 // once the cause is understood. 214 LOG(WARNING) << "Tried to delete info for nonexistent source"; 215 return; 216 } 217 // The source should not be in the deletion queue. 218 DCHECK(std::find(deletion_queue_.begin(), deletion_queue_.end(), 219 source_id) == deletion_queue_.end()); 220 ReleaseAllReferencesToDependencies(&(it->second)); 221 sources_.erase(it); 222} 223 224void PassiveLogCollector::SourceTracker::Clear() { 225 deletion_queue_.clear(); 226 227 // Release all references held to dependent sources. 228 for (SourceIDToInfoMap::iterator it = sources_.begin(); 229 it != sources_.end(); 230 ++it) { 231 ReleaseAllReferencesToDependencies(&(it->second)); 232 } 233 sources_.clear(); 234} 235 236void PassiveLogCollector::SourceTracker::AppendAllEntries( 237 EntryList* out) const { 238 // Append all of the entries for each of the sources. 239 for (SourceIDToInfoMap::const_iterator it = sources_.begin(); 240 it != sources_.end(); 241 ++it) { 242 const SourceInfo& info = it->second; 243 out->insert(out->end(), info.entries.begin(), info.entries.end()); 244 } 245} 246 247void PassiveLogCollector::SourceTracker::AddToDeletionQueue( 248 uint32 source_id) { 249 DCHECK(sources_.find(source_id) != sources_.end()); 250 DCHECK(!sources_.find(source_id)->second.is_alive); 251 DCHECK_GE(sources_.find(source_id)->second.reference_count, 0); 252 DCHECK_LE(deletion_queue_.size(), max_graveyard_size_); 253 254 DCHECK(std::find(deletion_queue_.begin(), deletion_queue_.end(), 255 source_id) == deletion_queue_.end()); 256 deletion_queue_.push_back(source_id); 257 258 // After the deletion queue has reached its maximum size, start 259 // deleting sources in FIFO order. 260 if (deletion_queue_.size() > max_graveyard_size_) { 261 uint32 oldest = deletion_queue_.front(); 262 deletion_queue_.pop_front(); 263 DeleteSourceInfo(oldest); 264 } 265} 266 267void PassiveLogCollector::SourceTracker::AdjustReferenceCountForSource( 268 int offset, uint32 source_id) { 269 DCHECK(offset == -1 || offset == 1) << "invalid offset: " << offset; 270 271 // In general it is invalid to call AdjustReferenceCountForSource() on 272 // source that doesn't exist. However, it is possible that if 273 // SourceTracker::Clear() was previously called this can happen. 274 SourceIDToInfoMap::iterator it = sources_.find(source_id); 275 if (it == sources_.end()) { 276 LOG(WARNING) << "Released a reference to nonexistent source."; 277 return; 278 } 279 280 SourceInfo& info = it->second; 281 DCHECK_GE(info.reference_count, 0); 282 info.reference_count += offset; 283 284 if (info.reference_count < 0) { 285 // In general this shouldn't happen, however it is possible to reach this 286 // state if SourceTracker::Clear() was called earlier. 287 LOG(WARNING) << "Released unmatched reference count."; 288 info.reference_count = 0; 289 } 290 291 if (!info.is_alive) { 292 if (info.reference_count == 1 && offset == 1) { 293 // If we just added a reference to a dead source that had no references, 294 // it must have been in the deletion queue, so remove it from the queue. 295 DeletionQueue::iterator it = 296 std::remove(deletion_queue_.begin(), deletion_queue_.end(), 297 source_id); 298 DCHECK(it != deletion_queue_.end()); 299 deletion_queue_.erase(it); 300 } else if (info.reference_count == 0) { 301 // If we just released the final reference to a dead source, go ahead 302 // and delete it right away. 303 DeleteSourceInfo(source_id); 304 } 305 } 306} 307 308void PassiveLogCollector::SourceTracker::AddReferenceToSourceDependency( 309 const net::NetLog::Source& source, SourceInfo* info) { 310 // Find the tracker which should be holding |source|. 311 DCHECK(parent_); 312 DCHECK_NE(source.type, net::NetLog::SOURCE_NONE); 313 SourceTracker* tracker = static_cast<SourceTracker*>( 314 parent_->GetTrackerForSourceType(source.type)); 315 DCHECK(tracker); 316 317 // Tell the owning tracker to increment the reference count of |source|. 318 tracker->AdjustReferenceCountForSource(1, source.id); 319 320 // Make a note to release this reference once |info| is destroyed. 321 info->dependencies.push_back(source); 322} 323 324void 325PassiveLogCollector::SourceTracker::ReleaseAllReferencesToDependencies( 326 SourceInfo* info) { 327 // Release all references |info| was holding to other sources. 328 for (SourceDependencyList::const_iterator it = info->dependencies.begin(); 329 it != info->dependencies.end(); ++it) { 330 const net::NetLog::Source& source = *it; 331 332 // Find the tracker which should be holding |source|. 333 DCHECK(parent_); 334 DCHECK_NE(source.type, net::NetLog::SOURCE_NONE); 335 SourceTracker* tracker = static_cast<SourceTracker*>( 336 parent_->GetTrackerForSourceType(source.type)); 337 DCHECK(tracker); 338 339 // Tell the owning tracker to decrement the reference count of |source|. 340 tracker->AdjustReferenceCountForSource(-1, source.id); 341 } 342 343 info->dependencies.clear(); 344} 345 346//---------------------------------------------------------------------------- 347// ConnectJobTracker 348//---------------------------------------------------------------------------- 349 350const size_t PassiveLogCollector::ConnectJobTracker::kMaxNumSources = 100; 351const size_t PassiveLogCollector::ConnectJobTracker::kMaxGraveyardSize = 15; 352 353PassiveLogCollector::ConnectJobTracker::ConnectJobTracker( 354 PassiveLogCollector* parent) 355 : SourceTracker(kMaxNumSources, kMaxGraveyardSize, parent) { 356} 357 358PassiveLogCollector::SourceTracker::Action 359PassiveLogCollector::ConnectJobTracker::DoAddEntry(const Entry& entry, 360 SourceInfo* out_info) { 361 AddEntryToSourceInfo(entry, out_info); 362 363 if (entry.type == net::NetLog::TYPE_CONNECT_JOB_SET_SOCKET) { 364 const net::NetLog::Source& source_dependency = 365 static_cast<net::NetLogSourceParameter*>(entry.params.get())->value(); 366 AddReferenceToSourceDependency(source_dependency, out_info); 367 } 368 369 // If this is the end of the connect job, move the source to the graveyard. 370 if (entry.type == net::NetLog::TYPE_SOCKET_POOL_CONNECT_JOB && 371 entry.phase == net::NetLog::PHASE_END) { 372 return ACTION_MOVE_TO_GRAVEYARD; 373 } 374 375 return ACTION_NONE; 376} 377 378//---------------------------------------------------------------------------- 379// SocketTracker 380//---------------------------------------------------------------------------- 381 382const size_t PassiveLogCollector::SocketTracker::kMaxNumSources = 200; 383const size_t PassiveLogCollector::SocketTracker::kMaxGraveyardSize = 15; 384 385PassiveLogCollector::SocketTracker::SocketTracker() 386 : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { 387} 388 389PassiveLogCollector::SourceTracker::Action 390PassiveLogCollector::SocketTracker::DoAddEntry(const Entry& entry, 391 SourceInfo* out_info) { 392 // TODO(eroman): aggregate the byte counts once truncation starts to happen, 393 // to summarize transaction read/writes for each SOCKET_IN_USE 394 // section. 395 if (entry.type == net::NetLog::TYPE_SOCKET_BYTES_SENT || 396 entry.type == net::NetLog::TYPE_SOCKET_BYTES_RECEIVED) { 397 return ACTION_NONE; 398 } 399 400 AddEntryToSourceInfo(entry, out_info); 401 402 if (entry.type == net::NetLog::TYPE_SOCKET_ALIVE && 403 entry.phase == net::NetLog::PHASE_END) { 404 return ACTION_MOVE_TO_GRAVEYARD; 405 } 406 407 return ACTION_NONE; 408} 409 410//---------------------------------------------------------------------------- 411// RequestTracker 412//---------------------------------------------------------------------------- 413 414const size_t PassiveLogCollector::RequestTracker::kMaxNumSources = 100; 415const size_t PassiveLogCollector::RequestTracker::kMaxGraveyardSize = 25; 416 417PassiveLogCollector::RequestTracker::RequestTracker(PassiveLogCollector* parent) 418 : SourceTracker(kMaxNumSources, kMaxGraveyardSize, parent) { 419} 420 421PassiveLogCollector::SourceTracker::Action 422PassiveLogCollector::RequestTracker::DoAddEntry(const Entry& entry, 423 SourceInfo* out_info) { 424 if (entry.type == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB || 425 entry.type == net::NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET) { 426 const net::NetLog::Source& source_dependency = 427 static_cast<net::NetLogSourceParameter*>(entry.params.get())->value(); 428 AddReferenceToSourceDependency(source_dependency, out_info); 429 } 430 431 AddEntryToSourceInfo(entry, out_info); 432 433 // If the request has ended, move it to the graveyard. 434 if (entry.type == net::NetLog::TYPE_REQUEST_ALIVE && 435 entry.phase == net::NetLog::PHASE_END) { 436 if (StartsWithASCII(out_info->GetURL(), "chrome://", false)) { 437 // Avoid sending "chrome://" requests to the graveyard, since it just 438 // adds to clutter. 439 return ACTION_DELETE; 440 } 441 return ACTION_MOVE_TO_GRAVEYARD; 442 } 443 444 return ACTION_NONE; 445} 446 447//---------------------------------------------------------------------------- 448// InitProxyResolverTracker 449//---------------------------------------------------------------------------- 450 451const size_t PassiveLogCollector::InitProxyResolverTracker::kMaxNumSources = 20; 452const size_t PassiveLogCollector::InitProxyResolverTracker::kMaxGraveyardSize = 453 3; 454 455PassiveLogCollector::InitProxyResolverTracker::InitProxyResolverTracker() 456 : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { 457} 458 459PassiveLogCollector::SourceTracker::Action 460PassiveLogCollector::InitProxyResolverTracker::DoAddEntry( 461 const Entry& entry, SourceInfo* out_info) { 462 AddEntryToSourceInfo(entry, out_info); 463 if (entry.type == net::NetLog::TYPE_INIT_PROXY_RESOLVER && 464 entry.phase == net::NetLog::PHASE_END) { 465 return ACTION_MOVE_TO_GRAVEYARD; 466 } else { 467 return ACTION_NONE; 468 } 469} 470 471//---------------------------------------------------------------------------- 472// SpdySessionTracker 473//---------------------------------------------------------------------------- 474 475const size_t PassiveLogCollector::SpdySessionTracker::kMaxNumSources = 50; 476const size_t PassiveLogCollector::SpdySessionTracker::kMaxGraveyardSize = 10; 477 478PassiveLogCollector::SpdySessionTracker::SpdySessionTracker() 479 : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { 480} 481 482PassiveLogCollector::SourceTracker::Action 483PassiveLogCollector::SpdySessionTracker::DoAddEntry(const Entry& entry, 484 SourceInfo* out_info) { 485 AddEntryToSourceInfo(entry, out_info); 486 if (entry.type == net::NetLog::TYPE_SPDY_SESSION && 487 entry.phase == net::NetLog::PHASE_END) { 488 return ACTION_MOVE_TO_GRAVEYARD; 489 } else { 490 return ACTION_NONE; 491 } 492} 493 494//---------------------------------------------------------------------------- 495// DNSRequestTracker 496//---------------------------------------------------------------------------- 497 498const size_t PassiveLogCollector::DNSRequestTracker::kMaxNumSources = 200; 499const size_t PassiveLogCollector::DNSRequestTracker::kMaxGraveyardSize = 20; 500 501PassiveLogCollector::DNSRequestTracker::DNSRequestTracker() 502 : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { 503} 504 505PassiveLogCollector::SourceTracker::Action 506PassiveLogCollector::DNSRequestTracker::DoAddEntry(const Entry& entry, 507 SourceInfo* out_info) { 508 AddEntryToSourceInfo(entry, out_info); 509 if (entry.type == net::NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST && 510 entry.phase == net::NetLog::PHASE_END) { 511 return ACTION_MOVE_TO_GRAVEYARD; 512 } else { 513 return ACTION_NONE; 514 } 515} 516 517//---------------------------------------------------------------------------- 518// DNSJobTracker 519//---------------------------------------------------------------------------- 520 521const size_t PassiveLogCollector::DNSJobTracker::kMaxNumSources = 100; 522const size_t PassiveLogCollector::DNSJobTracker::kMaxGraveyardSize = 15; 523 524PassiveLogCollector::DNSJobTracker::DNSJobTracker() 525 : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { 526} 527 528PassiveLogCollector::SourceTracker::Action 529PassiveLogCollector::DNSJobTracker::DoAddEntry(const Entry& entry, 530 SourceInfo* out_info) { 531 AddEntryToSourceInfo(entry, out_info); 532 if (entry.type == net::NetLog::TYPE_HOST_RESOLVER_IMPL_JOB && 533 entry.phase == net::NetLog::PHASE_END) { 534 return ACTION_MOVE_TO_GRAVEYARD; 535 } else { 536 return ACTION_NONE; 537 } 538} 539