diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/net/dns_host_info.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net/dns_host_info.cc')
-rw-r--r-- | chrome/browser/net/dns_host_info.cc | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/chrome/browser/net/dns_host_info.cc b/chrome/browser/net/dns_host_info.cc new file mode 100644 index 0000000..589e621 --- /dev/null +++ b/chrome/browser/net/dns_host_info.cc @@ -0,0 +1,361 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See header file for description of class + +#include "chrome/browser/net/dns_host_info.h" + +#include <math.h> + +#include <algorithm> +#include <string> + +#include "base/histogram.h" +#include "base/logging.h" +#include "base/string_util.h" + +namespace chrome_browser_net { + +static bool detailed_logging_enabled = false; + +// Use command line switch to enable detailed logging. +void EnableDnsDetailedLog(bool enable) { + detailed_logging_enabled = enable; +} + +// static +int DnsHostInfo::sequence_counter = 1; + + +bool DnsHostInfo::NeedsDnsUpdate(const std::string& hostname) { + DCHECK(hostname == hostname_); + switch (state_) { + case PENDING: // Just now created info. + return true; + + case QUEUED: // In queue. + case ASSIGNED: // Slave is working on it. + case ASSIGNED_BUT_MARKED: // Slave is working on it. + return false; // We're already working on it + + case NO_SUCH_NAME: // Lookup failed. + case FOUND: // Lookup succeeded. + return !IsStillCached(); // See if DNS cache expired. + + default: + DCHECK(false); + return false; + } +} + +const TimeDelta DnsHostInfo::kNullDuration(TimeDelta::FromMilliseconds(-1)); + +TimeDelta DnsHostInfo::kCacheExpirationDuration(TimeDelta::FromMinutes(5)); + +const TimeDelta DnsHostInfo::kMaxNonNetworkDnsLookupDuration( + TimeDelta::FromMilliseconds(15)); + +void DnsHostInfo::set_cache_expiration(TimeDelta time) { + kCacheExpirationDuration = time; +} + +void DnsHostInfo::SetQueuedState() { + DCHECK(PENDING == state_ || FOUND == state_ || NO_SUCH_NAME == state_); + state_ = QUEUED; + queue_duration_ = resolve_duration_ = kNullDuration; + GetDuration(); // Set time_ + DLogResultsStats("DNS Prefetch in queue"); +} + +void DnsHostInfo::SetAssignedState() { + DCHECK(QUEUED == state_); + state_ = ASSIGNED; + queue_duration_ = GetDuration(); + DLogResultsStats("DNS Prefetch assigned"); + DHISTOGRAM_TIMES(L"DNS.PrefetchQueue", queue_duration_); +} + +void DnsHostInfo::SetPendingDeleteState() { + DCHECK(ASSIGNED == state_ || ASSIGNED_BUT_MARKED == state_); + state_ = ASSIGNED_BUT_MARKED; +} + +void DnsHostInfo::SetFoundState() { + DCHECK(ASSIGNED == state_); + state_ = FOUND; + resolve_duration_ = GetDuration(); + if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { + UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchFoundNameL", resolve_duration_); + // Record potential beneficial time, and maybe we'll get a cache hit. + // We keep the maximum, as the warming we did earlier may still be + // helping with a cache upstream in DNS resolution. + benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_); + } + sequence_number_ = sequence_counter++; + DLogResultsStats("DNS PrefetchFound"); +} + +void DnsHostInfo::SetNoSuchNameState() { + DCHECK(ASSIGNED == state_); + state_ = NO_SUCH_NAME; + resolve_duration_ = GetDuration(); + if (kMaxNonNetworkDnsLookupDuration <= resolve_duration_) { + DHISTOGRAM_TIMES(L"DNS.PrefetchNotFoundName", resolve_duration_); + // Record potential beneficial time, and maybe we'll get a cache hit. + benefits_remaining_ = std::max(resolve_duration_, benefits_remaining_); + } + sequence_number_ = sequence_counter++; + DLogResultsStats("DNS PrefetchNotFound"); +} + +void DnsHostInfo::SetStartedState() { + DCHECK(PENDING == state_); + state_ = STARTED; + queue_duration_ = resolve_duration_ = TimeDelta(); // 0ms. + GetDuration(); // Set time. +} + +void DnsHostInfo::SetFinishedState(bool was_resolved) { + DCHECK(STARTED == state_); + state_ = was_resolved ? FINISHED : FINISHED_UNRESOLVED; + resolve_duration_ = GetDuration(); + // TODO(jar): Sequence number should be incremented in prefetched HostInfo. + DLogResultsStats("DNS HTTP Finished"); +} + +// IsStillCached() guesses if the DNS cache still has IP data, +// or at least remembers results about "not finding host." +bool DnsHostInfo::IsStillCached() const { + DCHECK(FOUND == state_ || NO_SUCH_NAME == state_); + + // Default MS OS does not cache failures. Hence we could return false almost + // all the time for that case. However, we'd never try again to prefetch + // the value if we returned false that way. Hence we'll just let the lookup + // time out the same way as FOUND case. + + if (sequence_counter - sequence_number_ > kMaxGuaranteedCacheSize) + return false; + + TimeDelta time_since_resolution = TimeTicks::Now() - time_; + + if (FOUND == state_ && resolve_duration_ < kMaxNonNetworkDnsLookupDuration) { + // Since cache was warm (no apparent network activity during resolution), + // we assume it was "really" found (via network activity) twice as long + // ago as when we got our FOUND result. + time_since_resolution *= 2; + } + + return time_since_resolution < kCacheExpirationDuration; +} + +// Compare the later results, to the previously prefetched info. +DnsBenefit DnsHostInfo::AcruePrefetchBenefits(DnsHostInfo* later_host_info) { + DCHECK(FINISHED == later_host_info->state_ + || FINISHED_UNRESOLVED == later_host_info->state_); + DCHECK(0 == later_host_info->hostname_.compare(hostname_.data())); + if ((0 == benefits_remaining_.InMilliseconds()) || + (FOUND != state_ && NO_SUCH_NAME != state_)) { + return PREFETCH_NO_BENEFIT; + } + + TimeDelta benefit = benefits_remaining_ - later_host_info->resolve_duration_; + later_host_info->benefits_remaining_ = benefits_remaining_; + benefits_remaining_ = TimeDelta(); // zero ms. + + if (later_host_info->resolve_duration_ > kMaxNonNetworkDnsLookupDuration) { + // Our precache effort didn't help since HTTP stack hit the network. + DHISTOGRAM_TIMES(L"DNS.PrefetchCacheEviction", resolve_duration_); + DLogResultsStats("DNS PrefetchCacheEviction"); + return PREFETCH_CACHE_EVICTION; + } + + if (NO_SUCH_NAME == state_) { + UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchNegativeHitL", benefit); + DLogResultsStats("DNS PrefetchNegativeHit"); + return PREFETCH_NAME_NONEXISTANT; + } + + DCHECK_EQ(FOUND, state_); + UMA_HISTOGRAM_LONG_TIMES(L"DNS.PrefetchPositiveHitL", benefit); + DLogResultsStats("DNS PrefetchPositiveHit"); + return PREFETCH_NAME_FOUND; +} + +void DnsHostInfo::DLogResultsStats(const char* message) const { + if (!detailed_logging_enabled) + return; + DLOG(INFO) << "\t" << message << "\tq=" + << queue_duration().InMilliseconds() << "ms,\tr=" + << resolve_duration().InMilliseconds() << "ms\tp=" + << benefits_remaining_.InMilliseconds() << "ms\tseq=" + << sequence_number_ + << "\t" << hostname_; +} + +//------------------------------------------------------------------------------ +// This last section supports HTML output, such as seen in about:dns. +//------------------------------------------------------------------------------ + +// Preclude any possibility of Java Script or markup in the text, by only +// allowing alphanumerics, ".", and whitespace. +static std::string RemoveJs(const std::string& text) { + std::string output(text); + size_t length = output.length(); + for (size_t i = 0; i < 1; i++) { + char next = output[i]; + if (isalnum(next) || isspace(next) || '.' == next) + continue; + output[i] = '?'; + } + return output; +} + +class MinMaxAverage { + public: + MinMaxAverage() + : sum_(0), square_sum_(0), count_(0), + minimum_(kint64max), maximum_(kint64min) { + } + + // Return values for use in printf formatted as "%d" + int sample(int64 value) { + sum_ += value; + square_sum_ += value * value; + count_++; + minimum_ = std::min(minimum_, value); + maximum_ = std::max(maximum_, value); + return static_cast<int>(value); + } + int minimum() const { return static_cast<int>(minimum_); } + int maximum() const { return static_cast<int>(maximum_); } + int average() const { return static_cast<int>(sum_/count_); } + int sum() const { return static_cast<int>(sum_); } + + int standard_deviation() const { + double average = static_cast<float>(sum_) / count_; + double variance = static_cast<float>(square_sum_)/count_ + - average * average; + return static_cast<int>(floor(sqrt(variance) + .5)); + } + + private: + int64 sum_; + int64 square_sum_; + int count_; + int64 minimum_; + int64 maximum_; + + // DISALLOW_EVIL_CONSTRUCTORS(MinMaxAverage); +}; + +static std::string HoursMinutesSeconds(int seconds) { + std::string result; + int print_seconds = seconds % 60; + int minutes = seconds / 60; + int print_minutes = minutes % 60; + int print_hours = minutes/60; + if (print_hours) + StringAppendF(&result, "%.2d:", print_hours); + if (print_hours || print_minutes) + StringAppendF(&result, "%2.2d:", print_minutes); + StringAppendF(&result, "%2.2d", print_seconds); + return result; +} + +// static +void DnsHostInfo::GetHtmlTable(const DnsInfoTable host_infos, + const char* description, + const bool brief, + std::string* output) { + if (0 == host_infos.size()) + return; + output->append(description); + StringAppendF(output, "%d %s", host_infos.size(), + (1 == host_infos.size()) ? "hostname" : "hostnames"); + + if (brief) { + output->append("<br><br>"); + return; + } + + char* row_format = "<tr align=right><td>%s</td>" + "<td>%d</td><td>%d</td><td>%s</td></tr>"; + + output->append("<br><table border=1>"); + StringAppendF(output, "<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>", + "Host name", "Applicable Prefetch<br>Time (ms)", + "Recent Resolution<br>Time(ms)", "How long ago<br>(HH:MM:SS)"); + + // Print bulk of table, and gather stats at same time. + MinMaxAverage queue, resolve, preresolve, when; + TimeTicks current_time = TimeTicks::Now(); + for (DnsInfoTable::const_iterator it(host_infos.begin()); + it != host_infos.end(); it++) { + queue.sample((it->queue_duration_.InMilliseconds())); + StringAppendF(output, row_format, + RemoveJs(it->hostname_).c_str(), + preresolve.sample((it->benefits_remaining_.InMilliseconds())), + resolve.sample((it->resolve_duration_.InMilliseconds())), + HoursMinutesSeconds(when.sample( + (current_time - it->time_).InSeconds())).c_str()); + } + // Write min, max, and average summary lines. + if (host_infos.size() > 2) { + output->append("<B>"); + StringAppendF(output, row_format, + "<b>---minimum---</b>", + preresolve.minimum(), resolve.minimum(), + HoursMinutesSeconds(when.minimum()).c_str()); + StringAppendF(output, row_format, + "<b>---average---</b>", + preresolve.average(), resolve.average(), + HoursMinutesSeconds(when.average()).c_str()); + StringAppendF(output, row_format, + "<b>standard deviation</b>", + preresolve.standard_deviation(), + resolve.standard_deviation(), "n/a"); + StringAppendF(output, row_format, + "<b>---maximum---</b>", + preresolve.maximum(), resolve.maximum(), + HoursMinutesSeconds(when.maximum()).c_str()); + StringAppendF(output, row_format, + "<b>-----SUM-----</b>", + preresolve.sum(), resolve.sum(), "n/a"); + } + output->append("</table>"); + +#ifdef DEBUG + StringAppendF(output, + "Prefetch Queue Durations: min=%d, avg=%d, max=%d<br><br>", + queue.minimum(), queue.average(), queue.maximum()); +#endif + + output->append("<br>"); +} +} // namespace chrome_browser_net |