summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-14 03:01:09 +0000
committerericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-14 03:01:09 +0000
commit54e1377ea309c5d8ab318ee9691b46249d7441b6 (patch)
tree8471e44056027dff255ff5b22ba083a9482b41c3
parent672eca0fd191acd39c28430d1ed1f327594faff8 (diff)
downloadchromium_src-54e1377ea309c5d8ab318ee9691b46249d7441b6.zip
chromium_src-54e1377ea309c5d8ab318ee9691b46249d7441b6.tar.gz
chromium_src-54e1377ea309c5d8ab318ee9691b46249d7441b6.tar.bz2
Implement LoadLog, and hook up HostResolverImpl to LoadLog.
The functionality in load_log_util.h is currently unused, but since it motivates the implementation of LoadLog, figured it should be included with this review. Note that I decided against defining the loggable event types as LoadStates, since I wanted more flexibility in adding crazy stuff, and they really seem too implementation specific to be in LoadStates. BUG=http://crbug.com/14478 TEST=net_unittests Review URL: http://codereview.chromium.org/165404 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23412 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/base/host_resolver_impl.cc113
-rw-r--r--net/base/host_resolver_impl.h27
-rw-r--r--net/base/host_resolver_impl_unittest.cc49
-rw-r--r--net/base/load_log.cc37
-rw-r--r--net/base/load_log.h88
-rw-r--r--net/base/load_log_event_type_list.h34
-rw-r--r--net/base/load_log_unittest.cc105
-rw-r--r--net/base/load_log_unittest.h31
-rw-r--r--net/base/load_log_util.cc179
-rw-r--r--net/base/load_log_util.h74
-rw-r--r--net/base/load_log_util_unittest.cc57
-rw-r--r--net/net.gyp7
12 files changed, 751 insertions, 50 deletions
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc
index 111e8da..20e57e5 100644
--- a/net/base/host_resolver_impl.cc
+++ b/net/base/host_resolver_impl.cc
@@ -24,6 +24,7 @@
#include "base/worker_pool.h"
#include "net/base/address_list.h"
#include "net/base/host_resolver_proc.h"
+#include "net/base/load_log.h"
#include "net/base/net_errors.h"
#if defined(OS_WIN)
@@ -54,10 +55,18 @@ static int ResolveAddrInfo(HostResolverProc* resolver_proc,
class HostResolverImpl::Request {
public:
- Request(int id, const RequestInfo& info, CompletionCallback* callback,
+ Request(LoadLog* load_log,
+ int id,
+ const RequestInfo& info,
+ CompletionCallback* callback,
AddressList* addresses)
- : id_(id), info_(info), job_(NULL), callback_(callback),
- addresses_(addresses) {}
+ : load_log_(load_log),
+ id_(id),
+ info_(info),
+ job_(NULL),
+ callback_(callback),
+ addresses_(addresses) {
+ }
// Mark the request as cancelled.
void MarkAsCancelled() {
@@ -90,6 +99,10 @@ class HostResolverImpl::Request {
return job_;
}
+ LoadLog* load_log() const {
+ return load_log_;
+ }
+
int id() const {
return id_;
}
@@ -99,6 +112,8 @@ class HostResolverImpl::Request {
}
private:
+ scoped_refptr<LoadLog> load_log_;
+
// Unique ID for this request. Used by observers to identify requests.
int id_;
@@ -300,8 +315,8 @@ int HostResolverImpl::Resolve(LoadLog* load_log,
// Choose a unique ID number for observers to see.
int request_id = next_request_id_++;
- // Notify registered observers.
- NotifyObserversStartRequest(request_id, info);
+ // Update the load log and notify registered observers.
+ OnStartRequest(load_log, request_id, info);
// If we have an unexpired cache entry, use it.
if (info.allow_cached_response()) {
@@ -311,8 +326,8 @@ int HostResolverImpl::Resolve(LoadLog* load_log,
addresses->SetFrom(cache_entry->addrlist, info.port());
int error = cache_entry->error;
- // Notify registered observers.
- NotifyObserversFinishRequest(request_id, info, error);
+ // Update the load log and notify registered observers.
+ OnFinishRequest(load_log, request_id, info, error);
return error;
}
@@ -331,15 +346,15 @@ int HostResolverImpl::Resolve(LoadLog* load_log,
// Write to cache.
cache_.Set(info.hostname(), error, addrlist, base::TimeTicks::Now());
- // Notify registered observers.
- NotifyObserversFinishRequest(request_id, info, error);
+ // Update the load log and notify registered observers.
+ OnFinishRequest(load_log, request_id, info, error);
return error;
}
// Create a handle for this request, and pass it back to the user if they
// asked for it (out_req != NULL).
- Request* req = new Request(request_id, info, callback, addresses);
+ Request* req = new Request(load_log, request_id, info, callback, addresses);
if (out_req)
*out_req = reinterpret_cast<RequestHandle>(req);
@@ -382,7 +397,7 @@ void HostResolverImpl::CancelRequest(RequestHandle req_handle) {
DCHECK(req->job());
// NULL out the fields of req, to mark it as cancelled.
req->MarkAsCancelled();
- NotifyObserversCancelRequest(req->id(), req->info());
+ OnCancelRequest(req->load_log(), req->id(), req->info());
}
void HostResolverImpl::AddObserver(Observer* observer) {
@@ -449,8 +464,8 @@ void HostResolverImpl::OnJobComplete(Job* job,
if (!req->was_cancelled()) {
DCHECK_EQ(job, req->job());
- // Notify registered observers.
- NotifyObserversFinishRequest(req->id(), req->info(), error);
+ // Update the load log and notify registered observers.
+ OnFinishRequest(req->load_log(), req->id(), req->info(), error);
req->OnComplete(error, addrlist);
@@ -464,30 +479,68 @@ void HostResolverImpl::OnJobComplete(Job* job,
cur_completing_job_ = NULL;
}
-void HostResolverImpl::NotifyObserversStartRequest(int request_id,
- const RequestInfo& info) {
- for (ObserversList::iterator it = observers_.begin();
- it != observers_.end(); ++it) {
- (*it)->OnStartResolution(request_id, info);
+void HostResolverImpl::OnStartRequest(LoadLog* load_log,
+ int request_id,
+ const RequestInfo& info) {
+ LoadLog::BeginEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
+
+ // Notify the observers of the start.
+ if (!observers_.empty()) {
+ LoadLog::BeginEvent(
+ load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART);
+
+ for (ObserversList::iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ (*it)->OnStartResolution(request_id, info);
+ }
+
+ LoadLog::EndEvent(
+ load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART);
}
}
-void HostResolverImpl::NotifyObserversFinishRequest(int request_id,
- const RequestInfo& info,
- int error) {
- bool was_resolved = error == OK;
- for (ObserversList::iterator it = observers_.begin();
- it != observers_.end(); ++it) {
- (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info);
+void HostResolverImpl::OnFinishRequest(LoadLog* load_log,
+ int request_id,
+ const RequestInfo& info,
+ int error) {
+ // Notify the observers of the completion.
+ if (!observers_.empty()) {
+ LoadLog::BeginEvent(
+ load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH);
+
+ bool was_resolved = error == OK;
+ for (ObserversList::iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info);
+ }
+
+ LoadLog::EndEvent(
+ load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH);
}
+
+ LoadLog::EndEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
}
-void HostResolverImpl::NotifyObserversCancelRequest(int request_id,
- const RequestInfo& info) {
- for (ObserversList::iterator it = observers_.begin();
- it != observers_.end(); ++it) {
- (*it)->OnCancelResolution(request_id, info);
+void HostResolverImpl::OnCancelRequest(LoadLog* load_log,
+ int request_id,
+ const RequestInfo& info) {
+ LoadLog::AddEvent(load_log, LoadLog::TYPE_CANCELLED);
+
+ // Notify the observers of the cancellation.
+ if (!observers_.empty()) {
+ LoadLog::BeginEvent(
+ load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONCANCEL);
+
+ for (ObserversList::iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ (*it)->OnCancelResolution(request_id, info);
+ }
+
+ LoadLog::EndEvent(
+ load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONCANCEL);
}
+
+ LoadLog::EndEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
}
} // namespace net
diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h
index ff9e4e7..ede3d18 100644
--- a/net/base/host_resolver_impl.h
+++ b/net/base/host_resolver_impl.h
@@ -94,18 +94,21 @@ class HostResolverImpl : public HostResolver {
// Callback for when |job| has completed with |error| and |addrlist|.
void OnJobComplete(Job* job, int error, const AddressList& addrlist);
- // Notify all observers of the start of a resolve request.
- void NotifyObserversStartRequest(int request_id,
- const RequestInfo& info);
-
- // Notify all observers of the completion of a resolve request.
- void NotifyObserversFinishRequest(int request_id,
- const RequestInfo& info,
- int error);
-
- // Notify all observers of the cancellation of a resolve request.
- void NotifyObserversCancelRequest(int request_id,
- const RequestInfo& info);
+ // Called when a request has just been started.
+ void OnStartRequest(LoadLog* load_log,
+ int request_id,
+ const RequestInfo& info);
+
+ // Called when a request has just completed (before its callback is run).
+ void OnFinishRequest(LoadLog* load_log,
+ int request_id,
+ const RequestInfo& info,
+ int error);
+
+ // Called when a request has been cancelled.
+ void OnCancelRequest(LoadLog* load_log,
+ int request_id,
+ const RequestInfo& info);
// Cache of host resolution results.
HostCache cache_;
diff --git a/net/base/host_resolver_impl_unittest.cc b/net/base/host_resolver_impl_unittest.cc
index e33bbf4e..c59e477 100644
--- a/net/base/host_resolver_impl_unittest.cc
+++ b/net/base/host_resolver_impl_unittest.cc
@@ -18,12 +18,14 @@
#include "base/ref_counted.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
+#include "net/base/load_log_unittest.h"
#include "net/base/mock_host_resolver.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
using net::HostResolverImpl;
+using net::LoadLog;
using net::RuleBasedHostResolverProc;
using net::WaitingHostResolverProc;
@@ -187,9 +189,16 @@ TEST_F(HostResolverImplTest, SynchronousLookup) {
new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::HostResolver::RequestInfo info("just.testing", kPortnum);
- int err = host_resolver->Resolve(NULL, info, &adrlist, NULL, NULL);
+ scoped_refptr<LoadLog> log(new LoadLog);
+ int err = host_resolver->Resolve(log, info, &adrlist, NULL, NULL);
EXPECT_EQ(net::OK, err);
+ EXPECT_EQ(2u, log->events().size());
+ net::ExpectLogContains(log, 0, LoadLog::TYPE_HOST_RESOLVER_IMPL,
+ LoadLog::PHASE_BEGIN);
+ net::ExpectLogContains(log, 1, LoadLog::TYPE_HOST_RESOLVER_IMPL,
+ LoadLog::PHASE_END);
+
const struct addrinfo* ainfo = adrlist.head();
EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next);
EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen);
@@ -212,14 +221,23 @@ TEST_F(HostResolverImplTest, AsynchronousLookup) {
new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::HostResolver::RequestInfo info("just.testing", kPortnum);
- int err = host_resolver->Resolve(NULL, info, &adrlist, &callback_, NULL);
+ scoped_refptr<LoadLog> log(new LoadLog);
+ int err = host_resolver->Resolve(log, info, &adrlist, &callback_, NULL);
EXPECT_EQ(net::ERR_IO_PENDING, err);
+ EXPECT_EQ(1u, log->events().size());
+ net::ExpectLogContains(
+ log, 0, LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_BEGIN);
+
MessageLoop::current()->Run();
ASSERT_TRUE(callback_called_);
ASSERT_EQ(net::OK, callback_result_);
+ EXPECT_EQ(2u, log->events().size());
+ net::ExpectLogContains(log, 1, LoadLog::TYPE_HOST_RESOLVER_IMPL,
+ LoadLog::PHASE_END);
+
const struct addrinfo* ainfo = adrlist.head();
EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next);
EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen);
@@ -234,6 +252,7 @@ TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) {
scoped_refptr<WaitingHostResolverProc> resolver_proc =
new WaitingHostResolverProc(NULL);
+ scoped_refptr<LoadLog> log(new LoadLog);
{
scoped_refptr<net::HostResolver> host_resolver(
new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
@@ -241,7 +260,7 @@ TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) {
const int kPortnum = 80;
net::HostResolver::RequestInfo info("just.testing", kPortnum);
- int err = host_resolver->Resolve(NULL, info, &adrlist, &callback_, NULL);
+ int err = host_resolver->Resolve(log, info, &adrlist, &callback_, NULL);
EXPECT_EQ(net::ERR_IO_PENDING, err);
// Make sure we will exit the queue even when callback is not called.
@@ -253,6 +272,13 @@ TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) {
resolver_proc->Signal();
+ EXPECT_EQ(3u, log->events().size());
+ net::ExpectLogContains(log, 0, LoadLog::TYPE_HOST_RESOLVER_IMPL,
+ LoadLog::PHASE_BEGIN);
+ net::ExpectLogContains(log, 1, LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+ net::ExpectLogContains(log, 2, LoadLog::TYPE_HOST_RESOLVER_IMPL,
+ LoadLog::PHASE_END);
+
EXPECT_FALSE(callback_called_);
}
@@ -801,9 +827,24 @@ TEST_F(HostResolverImplTest, Observers) {
// Resolve "host1".
net::HostResolver::RequestInfo info1("host1", 70);
- int rv = host_resolver->Resolve(NULL, info1, &addrlist, NULL, NULL);
+ scoped_refptr<LoadLog> log(new LoadLog);
+ int rv = host_resolver->Resolve(log, info1, &addrlist, NULL, NULL);
EXPECT_EQ(net::OK, rv);
+ EXPECT_EQ(6u, log->events().size());
+ net::ExpectLogContains(log, 0,
+ LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_BEGIN);
+ net::ExpectLogContains(log, 1,
+ LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART, LoadLog::PHASE_BEGIN);
+ net::ExpectLogContains(log, 2,
+ LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART, LoadLog::PHASE_END);
+ net::ExpectLogContains(log, 3,
+ LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH, LoadLog::PHASE_BEGIN);
+ net::ExpectLogContains(log, 4,
+ LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH, LoadLog::PHASE_END);
+ net::ExpectLogContains(log, 5,
+ LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_END);
+
EXPECT_EQ(1U, observer.start_log.size());
EXPECT_EQ(1U, observer.finish_log.size());
EXPECT_EQ(0U, observer.cancel_log.size());
diff --git a/net/base/load_log.cc b/net/base/load_log.cc
new file mode 100644
index 0000000..120a58b
--- /dev/null
+++ b/net/base/load_log.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 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.
+
+#include "net/base/load_log.h"
+
+namespace net {
+
+LoadLog::LoadLog() {
+}
+
+// static
+const char* LoadLog::EventTypeToString(EventType event) {
+ switch (event) {
+#define EVENT_TYPE(label) case TYPE_ ## label: return #label;
+#include "net/base/load_log_event_type_list.h"
+#undef EVENT_TYPE
+ }
+ return NULL;
+}
+
+void LoadLog::Add(base::TimeTicks t, EventType event, EventPhase phase) {
+ // Minor optimization. TODO(eroman): use StackVector instead.
+ if (events_.empty())
+ events_.reserve(kMaxNumEntries / 2);
+
+ // Enforce a bound of kMaxNumEntries -- when we reach it, make it so the
+ // final entry in the list is |TYPE_LOG_TRUNCATED|.
+
+ if (events_.size() + 1 == kMaxNumEntries)
+ events_.push_back(Event(t, TYPE_LOG_TRUNCATED, PHASE_NONE));
+
+ if (events_.size() < kMaxNumEntries)
+ events_.push_back(Event(t, event, phase));
+}
+
+} // namespace net
diff --git a/net/base/load_log.h b/net/base/load_log.h
index 0efe242..fcb7d6f 100644
--- a/net/base/load_log.h
+++ b/net/base/load_log.h
@@ -5,7 +5,10 @@
#ifndef NET_BASE_LOAD_LOG_H_
#define NET_BASE_LOAD_LOG_H_
+#include <vector>
+
#include "base/ref_counted.h"
+#include "base/time.h"
namespace net {
@@ -13,10 +16,87 @@ namespace net {
// a request (waiting in queues, resolving hosts, resolving proxy, etc...).
class LoadLog : public base::RefCounted<LoadLog> {
public:
-// TODO(eroman): Add an API similar to:
-// void TrackEnterState(LoadState state);
-// void TrackLeaveState(LoadState state);
-// void Merge(const LoadLog* other);
+
+ enum EventType {
+#define EVENT_TYPE(label) TYPE_ ## label,
+#include "net/base/load_log_event_type_list.h"
+#undef EVENT_TYPE
+ };
+
+ // Whether this is the start/end of an event. Or in the case of EventTypes
+ // that are "instantaneous", kNone.
+ enum EventPhase {
+ PHASE_NONE,
+ PHASE_BEGIN,
+ PHASE_END,
+ };
+
+ // A logged event. Note that "phase" means if this is the start/end of a
+ // particular event type (in order to record a timestamp for both endpoints).
+ struct Event {
+ Event(base::TimeTicks time,
+ EventType type,
+ EventPhase phase)
+ : time(time), type(type), phase(phase) {
+ }
+
+ base::TimeTicks time;
+ EventType type;
+ EventPhase phase;
+ };
+
+ // The maximum size of |events_|.
+ enum { kMaxNumEntries = 25 };
+
+ // Ordered set of events that were logged.
+ // TODO(eroman): use a StackVector or array to avoid allocations.
+ typedef std::vector<Event> EventList;
+
+ // Create a log, which can hold up to |kMaxNumEntries| Events.
+ //
+ // If events are dropped because the log has grown too large, the final
+ // entry will be of type kLogTruncated.
+ LoadLog();
+
+ // --------------------------------------------------------------------------
+
+ // The public interface for adding events to the log are static methods.
+ // This makes it easier to deal with optionally NULL LoadLog.
+
+ // Adds an instantaneous event to the log.
+ static void AddEvent(LoadLog* log, EventType event) {
+ if (log)
+ log->Add(base::TimeTicks::Now(), event, PHASE_NONE);
+ }
+
+ // Adds the start of an event to the log. Presumably this event type measures
+ // a time duration, and will be matched by a call to EndEvent(event).
+ static void BeginEvent(LoadLog* log, EventType event) {
+ if (log)
+ log->Add(base::TimeTicks::Now(), event, PHASE_BEGIN);
+ }
+
+ // Adds the end of an event to the log. Presumably this event type measures
+ // a time duration, and we are matching an earlier call to BeginEvent(event).
+ static void EndEvent(LoadLog* log, EventType event) {
+ if (log)
+ log->Add(base::TimeTicks::Now(), event, PHASE_END);
+ }
+
+ // --------------------------------------------------------------------------
+
+ // Returns the list of all events in the log.
+ const EventList& events() const {
+ return events_;
+ }
+
+ // Returns a C-String symbolic name for |event|.
+ static const char* EventTypeToString(EventType event);
+
+ void Add(base::TimeTicks t, EventType event, EventPhase phase);
+
+ private:
+ EventList events_;
};
} // namespace net
diff --git a/net/base/load_log_event_type_list.h b/net/base/load_log_event_type_list.h
new file mode 100644
index 0000000..e259515
--- /dev/null
+++ b/net/base/load_log_event_type_list.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2009 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.
+
+// NOTE: No header guards are used, since this file is intended to be expanded
+// directly into load_log.h. DO NOT include this file anywhere else.
+
+// --------------------------------------------------------------------------
+// General pseudo-events
+// --------------------------------------------------------------------------
+
+// If a log is terminated by a kLogTruncated event, then it grew too large
+// and any subsequent events logged to it were discarded.
+EVENT_TYPE(LOG_TRUNCATED)
+
+// Something got cancelled (we determine what is cancelled based on the
+// log context around it.)
+EVENT_TYPE(CANCELLED)
+
+// ------------------------------------------------------------------------
+// HostResolverImpl
+// ------------------------------------------------------------------------
+
+// The start/end of a host resolve (DNS) request.
+EVENT_TYPE(HOST_RESOLVER_IMPL)
+
+// The start/end of HostResolver::Observer::OnStartResolution.
+EVENT_TYPE(HOST_RESOLVER_IMPL_OBSERVER_ONSTART)
+
+// The start/end of HostResolver::Observer::OnFinishResolutionWithStatus
+EVENT_TYPE(HOST_RESOLVER_IMPL_OBSERVER_ONFINISH)
+
+// The start/end of HostResolver::Observer::OnCancelResolution.
+EVENT_TYPE(HOST_RESOLVER_IMPL_OBSERVER_ONCANCEL)
diff --git a/net/base/load_log_unittest.cc b/net/base/load_log_unittest.cc
new file mode 100644
index 0000000..9634dd9
--- /dev/null
+++ b/net/base/load_log_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 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.
+
+#include "net/base/load_log_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+base::TimeTicks MakeTime(int t) {
+ base::TimeTicks ticks; // initialized to 0.
+ ticks += base::TimeDelta::FromMilliseconds(t);
+ return ticks;
+}
+
+void ExpectLogContains(const LoadLog* log,
+ size_t i,
+ LoadLog::EventType expected_event,
+ LoadLog::EventPhase expected_phase) {
+ ASSERT_LT(i, log->events().size());
+ EXPECT_EQ(expected_event, log->events()[i].type);
+ EXPECT_EQ(expected_phase, log->events()[i].phase);
+}
+
+void ExpectLogContains(const LoadLog* log,
+ size_t i,
+ base::TimeTicks expected_time,
+ LoadLog::EventType expected_event,
+ LoadLog::EventPhase expected_phase) {
+ ASSERT_LT(i, log->events().size());
+ EXPECT_TRUE(expected_time == log->events()[i].time);
+ EXPECT_EQ(expected_event, log->events()[i].type);
+ EXPECT_EQ(expected_phase, log->events()[i].phase);
+}
+
+namespace {
+
+TEST(LoadLogTest, Nullable) {
+ // Make sure that the static methods can be called with NULL (no-op).
+ // (Should not crash).
+ LoadLog::BeginEvent(NULL, LoadLog::TYPE_HOST_RESOLVER_IMPL);
+ LoadLog::AddEvent(NULL, LoadLog::TYPE_HOST_RESOLVER_IMPL);
+ LoadLog::EndEvent(NULL, LoadLog::TYPE_HOST_RESOLVER_IMPL);
+}
+
+TEST(LoadLogTest, Basic) {
+ scoped_refptr<LoadLog> log(new LoadLog);
+
+ // Logs start off empty.
+ EXPECT_EQ(0u, log->events().size());
+
+ // Add 3 entries.
+
+ log->Add(MakeTime(0), LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_BEGIN);
+ log->Add(MakeTime(2), LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+ log->Add(MakeTime(11), LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART,
+ LoadLog::PHASE_END);
+
+ EXPECT_EQ(3u, log->events().size());
+
+ ExpectLogContains(log, 0, MakeTime(0), LoadLog::TYPE_HOST_RESOLVER_IMPL,
+ LoadLog::PHASE_BEGIN);
+
+ ExpectLogContains(log, 1, MakeTime(2),
+ LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+
+ ExpectLogContains(log, 2, MakeTime(11),
+ LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART, LoadLog::PHASE_END);
+}
+
+// Test that the log's size is strictly bounded.
+TEST(LoadLogTest, Truncation) {
+ scoped_refptr<LoadLog> log(new LoadLog);
+
+ // Almost max it out.
+ for (size_t i = 0; i < LoadLog::kMaxNumEntries - 1; ++i) {
+ log->Add(MakeTime(0), LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+ }
+ EXPECT_EQ(LoadLog::kMaxNumEntries - 1,
+ static_cast<int>(log->events().size()));
+ EXPECT_EQ(LoadLog::TYPE_CANCELLED,
+ log->events()[LoadLog::kMaxNumEntries - 2].type);
+
+ // Max it out (none of these get appended to the log).
+ log->Add(MakeTime(0), LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+ log->Add(MakeTime(0), LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+ log->Add(MakeTime(0), LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+
+ EXPECT_EQ(LoadLog::kMaxNumEntries, log->events().size());
+
+ // We terminated with a "truncation" event.
+ ExpectLogContains(log, LoadLog::kMaxNumEntries - 1, MakeTime(0),
+ LoadLog::TYPE_LOG_TRUNCATED, LoadLog::PHASE_NONE);
+}
+
+TEST(LoadLogTest, EventTypeToString) {
+ EXPECT_STREQ("HOST_RESOLVER_IMPL",
+ LoadLog::EventTypeToString(LoadLog::TYPE_HOST_RESOLVER_IMPL));
+ EXPECT_STREQ("HOST_RESOLVER_IMPL_OBSERVER_ONSTART",
+ LoadLog::EventTypeToString(
+ LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART));
+}
+
+} // namespace
+} // namespace net
diff --git a/net/base/load_log_unittest.h b/net/base/load_log_unittest.h
new file mode 100644
index 0000000..f1e386a
--- /dev/null
+++ b/net/base/load_log_unittest.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2009 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 NET_BASE_LOAD_LOG_UNITTEST_H_
+#define NET_BASE_LOAD_LOG_UNITTEST_H_
+
+#include "net/base/load_log.h"
+
+namespace net {
+
+// Create a timestamp with internal value of |t| milliseconds from the epoch.
+base::TimeTicks MakeTime(int t);
+
+// Call gtest's EXPECT_* to verify that |log| contains the specified entry
+// at index |i|.
+void ExpectLogContains(const LoadLog* log,
+ size_t i,
+ base::TimeTicks expected_time,
+ LoadLog::EventType expected_event,
+ LoadLog::EventPhase expected_phase);
+
+// Same as above, but without an expectation for the timestamp.
+void ExpectLogContains(const LoadLog* log,
+ size_t i,
+ LoadLog::EventType expected_event,
+ LoadLog::EventPhase expected_phase);
+
+} // namespace net
+
+#endif // NET_BASE_LOAD_LOG_UNITTEST_H_
diff --git a/net/base/load_log_util.cc b/net/base/load_log_util.cc
new file mode 100644
index 0000000..28304d0
--- /dev/null
+++ b/net/base/load_log_util.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2009 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.
+
+#include "net/base/load_log_util.h"
+
+#include "base/string_util.h"
+
+namespace net {
+namespace {
+
+class FormatHelper {
+ public:
+ std::string ToString(const LoadLog* log) {
+ entries_.clear();
+
+ // Pass 1: Match the start/end of indentation blocks. Fills |entries_|
+ // with the results.
+ PopulateEntries(log);
+
+ // Pass 2: Figure out the maximum width of each column. This allows us
+ // to right-justify text within each column.
+ size_t max_time_width, max_indentation, max_type_width, max_dt_width;
+ GetMaxExtent(
+ &max_time_width, &max_indentation, &max_type_width, &max_dt_width);
+
+ // Pass 3: Assemble the string.
+ std::string result;
+
+ const int kSpacesPerIndentation = 2;
+
+ for (size_t i = 0; i < entries_.size(); ++i) {
+ if (i > 0)
+ result += "\n";
+
+ int indentation_spaces = entries_[i].indentation * kSpacesPerIndentation;
+ std::string event_str = GetEventString(i);
+
+ result += StringPrintf("t=%s: %s%s",
+ PadStringLeft(GetTimeString(i), max_time_width).c_str(),
+ PadStringLeft("", indentation_spaces).c_str(),
+ event_str.c_str());
+
+ if (entries_[i].event->phase == LoadLog::PHASE_BEGIN) {
+ // Summarize how long this block lasted.
+ int padding = ((max_indentation - entries_[i].indentation) *
+ kSpacesPerIndentation) + (max_type_width - event_str.size());
+ result += StringPrintf("%s [dt=%s]",
+ PadStringLeft("", padding).c_str(),
+ PadStringLeft(GetBlockDtString(i), max_dt_width).c_str());
+ }
+ }
+
+ return result;
+ }
+
+ private:
+ struct Entry {
+ explicit Entry(const LoadLog::Event* event)
+ : event(event), indentation(0), block_index(-1) {}
+
+ const LoadLog::Event* event;
+ size_t indentation;
+ int block_index; // The index of the matching start / end of block.
+ };
+
+ void PopulateEntries(const LoadLog* log) {
+ int current_indentation = 0;
+
+ for (size_t i = 0; i < log->events().size(); ++i) {
+ Entry entry(&log->events()[i]);
+
+ entry.indentation = current_indentation;
+
+ if (entry.event->phase == LoadLog::PHASE_BEGIN) {
+ // Indent everything contained in this block.
+ current_indentation++;
+ }
+
+ if (entry.event->phase == LoadLog::PHASE_END) {
+ int start_index = FindStartOfBlockIndex(entry);
+ if (start_index != -1) {
+ // Point the start / end of block at each other.
+ entry.block_index = start_index;
+ entries_[start_index].block_index = i;
+
+ // Restore the indentation prior to the block.
+ // (Could be more than 1 level if close of blocks are missing).
+ current_indentation = entries_[start_index].indentation;
+ entry.indentation = current_indentation;
+ }
+ }
+
+ entries_.push_back(entry);
+ }
+ }
+
+ int FindStartOfBlockIndex(const Entry& entry) {
+ DCHECK_EQ(LoadLog::PHASE_END, entry.event->phase);
+
+ // Find the matching start of block by scanning backwards.
+ for (int i = entries_.size() - 1; i >= 0; --i) {
+ if (entries_[i].event->phase == LoadLog::PHASE_BEGIN &&
+ entries_[i].event->type == entry.event->type) {
+ return i;
+ }
+ }
+ return -1; // Start not found.
+ }
+
+ void GetMaxExtent(size_t* max_time_width,
+ size_t* max_indentation,
+ size_t* max_type_width,
+ size_t* max_dt_width) {
+ *max_time_width = *max_indentation = *max_type_width = *max_dt_width = 0;
+ for (size_t i = 0; i < entries_.size(); ++i) {
+ *max_time_width = std::max(*max_time_width, GetTimeString(i).size());
+ *max_type_width = std::max(*max_type_width, GetEventString(i).size());
+ *max_indentation = std::max(*max_indentation, entries_[i].indentation);
+
+ if (entries_[i].event->phase == LoadLog::PHASE_BEGIN)
+ *max_dt_width = std::max(*max_dt_width, GetBlockDtString(i).size());
+ }
+ }
+
+ std::string GetBlockDtString(size_t start_index) {
+ int end_index = entries_[start_index].block_index;
+ if (end_index == -1) {
+ // Block is not closed, implicitly close it at EOF.
+ end_index = entries_.size() - 1;
+ }
+ int64 dt_ms = (entries_[end_index].event->time -
+ entries_[start_index].event->time).InMilliseconds();
+
+ return Int64ToString(dt_ms);
+ }
+
+ std::string GetTimeString(size_t index) {
+ int64 t_ms = (entries_[index].event->time -
+ base::TimeTicks()).InMilliseconds();
+ return Int64ToString(t_ms);
+ }
+
+ std::string GetEventString(size_t index) {
+ const LoadLog::Event* event = entries_[index].event;
+ const char* type_str = LoadLog::EventTypeToString(event->type);
+
+ switch (event->phase) {
+ case LoadLog::PHASE_BEGIN:
+ return std::string("+") + type_str;
+ case LoadLog::PHASE_END:
+ return std::string("-") + type_str;
+ case LoadLog::PHASE_NONE:
+ return std::string(" ") + type_str;
+ default:
+ NOTREACHED();
+ return std::string();
+ }
+ }
+
+ static std::string PadStringLeft(const std::string& str, size_t width) {
+ DCHECK_LE(str.size(), width);
+ std::string padding;
+ padding.resize(width - str.size(), ' ');
+ return padding + str;
+ }
+
+ std::vector<Entry> entries_;
+};
+
+} // namespace
+
+// static
+std::string LoadLogUtil::PrettyPrintAsEventTree(const LoadLog* log) {
+ FormatHelper helper;
+ return helper.ToString(log);
+}
+
+} // namespace net
diff --git a/net/base/load_log_util.h b/net/base/load_log_util.h
new file mode 100644
index 0000000..363e219
--- /dev/null
+++ b/net/base/load_log_util.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2009 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 NET_BASE_LOAD_LOG_UTIL_H_
+#define NET_BASE_LOAD_LOG_UTIL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "net/base/load_log.h"
+
+namespace net {
+
+// The LoadLogUtil utility class contains methods to analyze and visualize
+// LoadLogs.
+
+class LoadLogUtil {
+ public:
+ struct EventDuration {
+ LoadLog::EventType event;
+ base::TimeDelta duration;
+ };
+ typedef std::vector<EventDuration> EventDurationList;
+
+ // Builds a pretty printed ASCII tree showing the chronological order
+ // of events.
+ //
+ // The indentation reflects hiearchy, with the duration of each indented
+ // block noted on the right. The timestamp (tick count in milliseconds)
+ // is noted in the left column.
+ //
+ // This detailed view of what went into servicing a request can be used
+ // in logs, and is copy-pastable by users, for attaching to bug reports.
+ //
+ // Example A:
+ //
+ // t=0: +Event1 [dt = 8]
+ // t=1: +Event2 [dt = 0]
+ // t=1: EventX
+ // t=1: -Event2
+ // t=4: +Event3 [dt = 2]
+ // t=6: -Event3
+ // t=6: +Event2 [dt = 1]
+ // t=7: -Event2
+ // t=8: EventY
+ // t=8: -Event1
+ //
+ // Here we can see that:
+ // - Event1 started at t=0 and ended at t=8; the duration was 8 time units.
+ // - Event2 took place while Event1 was in progress, and was repeated
+ // at t=1 and t=6.
+ // - EventX took place while (the first) Event2 was in progress.
+ // - Event3 started and ended at the same time, taking 0 time.
+ // - EventY took place right before Event1 finished, at t=8
+ //
+ // In general the rules are:
+ // - Log entries added by BeginEvent() are prefixed with a '+' and
+ // start an indentation block.
+ // - Log entries added by EndEvent() are prefixed with a '-' and
+ // finish an indentation block.
+ // - Log entries added by AddEvent() have no prefix.
+ // - Time units are given as milliseconds.
+ //
+ static std::string PrettyPrintAsEventTree(const LoadLog* log);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LoadLogUtil);
+};
+
+} // namespace net
+
+#endif // NET_BASE_LOAD_LOG_UTIL_H_
diff --git a/net/base/load_log_util_unittest.cc b/net/base/load_log_util_unittest.cc
new file mode 100644
index 0000000..fbaa77b
--- /dev/null
+++ b/net/base/load_log_util_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2009 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.
+
+#include "net/base/load_log_unittest.h"
+#include "net/base/load_log_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+TEST(LoadLogUtilTest, Basic) {
+ scoped_refptr<LoadLog> log(new LoadLog);
+
+ log->Add(MakeTime(1), LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_BEGIN);
+ log->Add(MakeTime(5), LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART,
+ LoadLog::PHASE_BEGIN);
+ log->Add(MakeTime(8), LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART,
+ LoadLog::PHASE_END);
+ log->Add(MakeTime(12), LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+ log->Add(MakeTime(131), LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_END);
+
+ EXPECT_EQ(
+ "t= 1: +HOST_RESOLVER_IMPL [dt=130]\n"
+ "t= 5: +HOST_RESOLVER_IMPL_OBSERVER_ONSTART [dt= 3]\n"
+ "t= 8: -HOST_RESOLVER_IMPL_OBSERVER_ONSTART\n"
+ "t= 12: CANCELLED\n"
+ "t=131: -HOST_RESOLVER_IMPL",
+ LoadLogUtil::PrettyPrintAsEventTree(log));
+}
+
+TEST(LoadLogUtilTest, UnmatchedOpen) {
+ scoped_refptr<LoadLog> log(new LoadLog);
+
+ log->Add(MakeTime(3), LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_BEGIN);
+ // Note that there is no matching call to PHASE_END for all of the following.
+ log->Add(MakeTime(6), LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART,
+ LoadLog::PHASE_BEGIN);
+ log->Add(MakeTime(7), LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART,
+ LoadLog::PHASE_BEGIN);
+ log->Add(MakeTime(8), LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART,
+ LoadLog::PHASE_BEGIN);
+ log->Add(MakeTime(10), LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE);
+ log->Add(MakeTime(16), LoadLog::TYPE_HOST_RESOLVER_IMPL, LoadLog::PHASE_END);
+
+ EXPECT_EQ(
+ "t= 3: +HOST_RESOLVER_IMPL [dt=13]\n"
+ "t= 6: +HOST_RESOLVER_IMPL_OBSERVER_ONSTART [dt=10]\n"
+ "t= 7: +HOST_RESOLVER_IMPL_OBSERVER_ONSTART [dt= 9]\n"
+ "t= 8: +HOST_RESOLVER_IMPL_OBSERVER_ONSTART [dt= 8]\n"
+ "t=10: CANCELLED\n"
+ "t=16: -HOST_RESOLVER_IMPL",
+ LoadLogUtil::PrettyPrintAsEventTree(log));
+}
+
+} // namespace
+} // namespace net
diff --git a/net/net.gyp b/net/net.gyp
index 0f81b29..0a377d6 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -83,6 +83,10 @@
'base/listen_socket.h',
'base/load_flags.h',
'base/load_log.h',
+ 'base/load_log.cc',
+ 'base/load_log_event_type_list.h',
+ 'base/load_log_util.cc',
+ 'base/load_log_util.h',
'base/load_states.h',
'base/mime_sniffer.cc',
'base/mime_sniffer.h',
@@ -454,6 +458,9 @@
'base/gzip_filter_unittest.cc',
'base/host_cache_unittest.cc',
'base/host_resolver_impl_unittest.cc',
+ 'base/load_log_unittest.cc',
+ 'base/load_log_unittest.h',
+ 'base/load_log_util_unittest.cc',
'base/listen_socket_unittest.cc',
'base/listen_socket_unittest.h',
'base/mime_sniffer_unittest.cc',