C++程序  |  398行  |  13.42 KB

// 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_