// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_NET_PASSIVE_LOG_COLLECTOR_H_ #define CHROME_BROWSER_NET_PASSIVE_LOG_COLLECTOR_H_ #pragma once #include <deque> #include <string> #include <vector> #include "base/gtest_prod_util.h" #include "base/hash_tables.h" #include "base/memory/ref_counted.h" #include "base/time.h" #include "chrome/browser/net/chrome_net_log.h" #include "net/base/net_log.h" // PassiveLogCollector watches the NetLog event stream, and saves the network // events for recent requests, in a circular buffer. // // This is done so that when a network problem is encountered (performance // problem, or error), about:net-internals can be opened shortly after the // problem and it will contain a trace for the problem request. // // (This is in contrast to the "active logging" which captures every single // network event, but requires capturing to have been enabled *prior* to // encountering the problem. Active capturing is enabled as long as // about:net-internals is open). // // The data captured by PassiveLogCollector is grouped by NetLog::Source, into // a SourceInfo structure. These in turn are grouped by NetLog::SourceType, and // owned by a SourceTracker instance for the specific source type. // // The PassiveLogCollector is owned by the ChromeNetLog itself, and is not // thread safe. The ChromeNetLog is responsible for calling it in a thread safe // manner. class PassiveLogCollector : public ChromeNetLog::ThreadSafeObserver { public: typedef std::vector<net::NetLog::Source> SourceDependencyList; struct SourceInfo { SourceInfo(); ~SourceInfo(); // Returns the URL that corresponds with this source. This is // only meaningful for certain source types (URL_REQUEST, SOCKET_STREAM). // For the rest, it will return an empty string. std::string GetURL() const; uint32 source_id; ChromeNetLog::EntryList entries; size_t num_entries_truncated; // List of other sources which contain information relevant to this // source (for example, a url request might depend on the log items // for a connect job and for a socket that were bound to it.) SourceDependencyList dependencies; // Holds the count of how many other sources have added this as a // dependent source. When it is 0, it means noone has referenced it so it // can be deleted normally. int reference_count; // |is_alive| is set to false once the source has been added to the // tracker's graveyard (it may still be kept around due to a non-zero // reference_count, but it is still considered "dead"). bool is_alive; }; typedef std::vector<SourceInfo> SourceInfoList; // Interface for consuming a NetLog entry. class SourceTrackerInterface { public: virtual ~SourceTrackerInterface() {} virtual void OnAddEntry(const ChromeNetLog::Entry& entry) = 0; // Clears all the passively logged data from this tracker. virtual void Clear() = 0; // Appends all the captured entries to |out|. The ordering is undefined. virtual void AppendAllEntries(ChromeNetLog::EntryList* out) const = 0; }; // This source tracker is intended for TYPE_NONE. All entries go into a // circular buffer, and there is no concept of live/dead requests. class GlobalSourceTracker : public SourceTrackerInterface { public: GlobalSourceTracker(); ~GlobalSourceTracker(); // SourceTrackerInterface implementation: virtual void OnAddEntry(const ChromeNetLog::Entry& entry); virtual void Clear(); virtual void AppendAllEntries(ChromeNetLog::EntryList* out) const; private: typedef std::deque<ChromeNetLog::Entry> CircularEntryList; CircularEntryList entries_; DISALLOW_COPY_AND_ASSIGN(GlobalSourceTracker); }; // This class stores and manages the passively logged information for // URLRequests/SocketStreams/ConnectJobs. class SourceTracker : public SourceTrackerInterface { public: // Creates a SourceTracker that will track at most |max_num_sources|. // Up to |max_graveyard_size| unreferenced sources will be kept around // before deleting them for good. |parent| may be NULL, and points to // the owning PassiveLogCollector (it is used when adding references // to other sources). SourceTracker(size_t max_num_sources, size_t max_graveyard_size, PassiveLogCollector* parent); virtual ~SourceTracker(); // SourceTrackerInterface implementation: virtual void OnAddEntry(const ChromeNetLog::Entry& entry); virtual void Clear(); virtual void AppendAllEntries(ChromeNetLog::EntryList* out) const; #ifdef UNIT_TEST // Helper used to inspect the current state by unit-tests. // Retuns a copy of the source infos held by the tracker. SourceInfoList GetAllDeadOrAliveSources(bool is_alive) const { SourceInfoList result; for (SourceIDToInfoMap::const_iterator it = sources_.begin(); it != sources_.end(); ++it) { if (it->second.is_alive == is_alive) result.push_back(it->second); } return result; } #endif protected: enum Action { ACTION_NONE, ACTION_DELETE, ACTION_MOVE_TO_GRAVEYARD, }; // Makes |info| hold a reference to |source|. This way |source| will be // kept alive at least as long as |info|. void AddReferenceToSourceDependency(const net::NetLog::Source& source, SourceInfo* info); private: typedef base::hash_map<uint32, SourceInfo> SourceIDToInfoMap; typedef std::deque<uint32> DeletionQueue; // Updates |out_info| with the information from |entry|. Returns an action // to perform for this map entry on completion. virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info) = 0; // Removes |source_id| from |sources_|. This also releases any references // to dependencies held by this source. void DeleteSourceInfo(uint32 source_id); // Adds |source_id| to the FIFO queue (graveyard) for deletion. void AddToDeletionQueue(uint32 source_id); // Removes |source_id| from the |deletion_queue_| container. void EraseFromDeletionQueue(uint32 source_id); // Adds/Releases a reference from the source with ID |source_id|. // Use |offset=-1| to do a release, and |offset=1| for an addref. void AdjustReferenceCountForSource(int offset, uint32 source_id); // Releases all the references to sources held by |info|. void ReleaseAllReferencesToDependencies(SourceInfo* info); // This map contains all of the sources being tracked by this tracker. // (It includes both the "live" sources, and the "dead" ones.) SourceIDToInfoMap sources_; size_t max_num_sources_; size_t max_graveyard_size_; // FIFO queue for entries in |sources_| that are no longer alive, and // can be deleted. This buffer is also called "graveyard" elsewhere. We // queue sources for deletion so they can persist a bit longer. DeletionQueue deletion_queue_; PassiveLogCollector* parent_; DISALLOW_COPY_AND_ASSIGN(SourceTracker); }; // Specialization of SourceTracker for handling ConnectJobs. class ConnectJobTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; explicit ConnectJobTracker(PassiveLogCollector* parent); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(ConnectJobTracker); }; // Specialization of SourceTracker for handling Sockets. class SocketTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; SocketTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(SocketTracker); }; // Specialization of SourceTracker for handling net::URLRequest/SocketStream. class RequestTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; explicit RequestTracker(PassiveLogCollector* parent); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(RequestTracker); }; // Specialization of SourceTracker for handling // SOURCE_INIT_PROXY_RESOLVER. class InitProxyResolverTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; InitProxyResolverTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(InitProxyResolverTracker); }; // Tracks the log entries for the last seen SOURCE_SPDY_SESSION. class SpdySessionTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; SpdySessionTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(SpdySessionTracker); }; // Tracks the log entries for the last seen SOURCE_HOST_RESOLVER_IMPL_REQUEST. class DNSRequestTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; DNSRequestTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(DNSRequestTracker); }; // Tracks the log entries for the last seen SOURCE_HOST_RESOLVER_IMPL_JOB. class DNSJobTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; DNSJobTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(DNSJobTracker); }; // Tracks the log entries for the last seen SOURCE_DISK_CACHE_ENTRY. class DiskCacheEntryTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; DiskCacheEntryTracker(); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(DiskCacheEntryTracker); }; // Tracks the log entries for the last seen SOURCE_DISK_CACHE_ENTRY. class MemCacheEntryTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; MemCacheEntryTracker(); protected: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); private: DISALLOW_COPY_AND_ASSIGN(MemCacheEntryTracker); }; class HttpStreamJobTracker : public SourceTracker { public: static const size_t kMaxNumSources; static const size_t kMaxGraveyardSize; explicit HttpStreamJobTracker(PassiveLogCollector* parent); private: virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, SourceInfo* out_info); DISALLOW_COPY_AND_ASSIGN(HttpStreamJobTracker); }; PassiveLogCollector(); ~PassiveLogCollector(); // ThreadSafeObserver implementation: virtual void OnAddEntry(net::NetLog::EventType type, const base::TimeTicks& time, const net::NetLog::Source& source, net::NetLog::EventPhase phase, net::NetLog::EventParameters* params); // Clears all of the passively logged data. void Clear(); // Fills |out| with the full list of events that have been passively // captured. The list is ordered by capture time. void GetAllCapturedEvents(ChromeNetLog::EntryList* out) const; private: // Returns the tracker to use for sources of type |source_type|, or NULL. SourceTrackerInterface* GetTrackerForSourceType( net::NetLog::SourceType source_type); FRIEND_TEST_ALL_PREFIXES(PassiveLogCollectorTest, HoldReferenceToDependentSource); FRIEND_TEST_ALL_PREFIXES(PassiveLogCollectorTest, HoldReferenceToDeletedSource); GlobalSourceTracker global_source_tracker_; ConnectJobTracker connect_job_tracker_; SocketTracker socket_tracker_; RequestTracker url_request_tracker_; RequestTracker socket_stream_tracker_; InitProxyResolverTracker init_proxy_resolver_tracker_; SpdySessionTracker spdy_session_tracker_; DNSRequestTracker dns_request_tracker_; DNSJobTracker dns_job_tracker_; DiskCacheEntryTracker disk_cache_entry_tracker_; MemCacheEntryTracker mem_cache_entry_tracker_; HttpStreamJobTracker http_stream_job_tracker_; // This array maps each NetLog::SourceType to one of the tracker instances // defined above. Use of this array avoid duplicating the list of trackers // elsewhere. SourceTrackerInterface* trackers_[net::NetLog::SOURCE_COUNT]; // The count of how many events have flowed through this log. Used to set the // "order" field on captured events. uint32 num_events_seen_; DISALLOW_COPY_AND_ASSIGN(PassiveLogCollector); }; #endif // CHROME_BROWSER_NET_PASSIVE_LOG_COLLECTOR_H_