summaryrefslogtreecommitdiffstats
path: root/net/tools
diff options
context:
space:
mode:
authorcbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-19 18:26:06 +0000
committercbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-19 18:26:06 +0000
commit36132026f76105e8dd4d665d234206c6813c7ecf (patch)
tree290c4b0bf92c0a5cc40621b5d6f830e81da75d9a /net/tools
parentfd38cc8b1345df36a79c95a63845e95d28910b41 (diff)
downloadchromium_src-36132026f76105e8dd4d665d234206c6813c7ecf.zip
chromium_src-36132026f76105e8dd4d665d234206c6813c7ecf.tar.gz
chromium_src-36132026f76105e8dd4d665d234206c6813c7ecf.tar.bz2
Add replay log support to gdig.
This allows for batch resolutions in a time series. The input format is a text file where each line contains: start_time domain_name where start_time is time in milliseconds starting at 0, and should be sorted in ascending order. The output format is a text file where each line contains: index resolve_time_since_start resolve_time domain_name result [error_message|address_list] where: - index is the 1-based line number in the original file - resolve_time_since_start is the time in milliseconds since the start of all resolutions - resolve_time is the time in milliseconds to resolve this entry - domain_name is the domain_name - result is the numerical net_error result. - error_message is the plain text error message used when result is not 0 - address_list is a list of IPv4 and/or IPv6 addresses for the domain when result is 0 Review URL: https://chromiumcodereview.appspot.com/11554020 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173953 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/tools')
-rw-r--r--net/tools/gdig/gdig.cc224
1 files changed, 189 insertions, 35 deletions
diff --git a/net/tools/gdig/gdig.cc b/net/tools/gdig/gdig.cc
index 1728fc8..5e209c2 100644
--- a/net/tools/gdig/gdig.cc
+++ b/net/tools/gdig/gdig.cc
@@ -9,9 +9,11 @@
#include "base/bind.h"
#include "base/cancelable_callback.h"
#include "base/command_line.h"
+#include "base/file_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/string_number_conversions.h"
+#include "base/string_split.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/time.h"
@@ -93,6 +95,90 @@ std::string DnsHostsToString(const DnsHosts& dns_hosts) {
return output;
}
+struct ReplayLogEntry {
+ base::TimeDelta start_time;
+ std::string domain_name;
+};
+
+typedef std::vector<ReplayLogEntry> ReplayLog;
+
+// Loads and parses a replay log file and fills |replay_log| with a structured
+// representation. Returns whether the operation was successful. If not, the
+// contents of |replay_log| are undefined.
+//
+// The replay log is a text file where each line contains
+//
+// timestamp_in_milliseconds domain_name
+//
+// The timestamp_in_milliseconds needs to be an integral delta from start of
+// resolution and is in milliseconds. domain_name is the name to be resolved.
+//
+// The file should be sorted by timestamp in ascending time.
+bool LoadReplayLog(const FilePath& file_path, ReplayLog* replay_log) {
+ std::string original_replay_log_contents;
+ if (!file_util::ReadFileToString(file_path, &original_replay_log_contents)) {
+ fprintf(stderr, "Unable to open replay file %s\n",
+ file_path.MaybeAsASCII().c_str());
+ return false;
+ }
+
+ // Strip out \r characters for Windows files. This isn't as efficient as a
+ // smarter line splitter, but this particular use does not need to target
+ // efficiency.
+ std::string replay_log_contents;
+ RemoveChars(original_replay_log_contents, "\r", &replay_log_contents);
+
+ std::vector<std::string> lines;
+ base::SplitString(replay_log_contents, '\n', &lines);
+ base::TimeDelta previous_delta;
+ bool bad_parse = false;
+ for (size_t i = 0; i < lines.size(); ++i) {
+ if (lines[i].empty())
+ continue;
+ std::vector<std::string> time_and_name;
+ base::SplitString(lines[i], ' ', &time_and_name);
+ if (time_and_name.size() != 2) {
+ fprintf(
+ stderr,
+ "[%s %zu] replay log should have format 'timestamp domain_name\\n'\n",
+ file_path.MaybeAsASCII().c_str(),
+ i + 1);
+ bad_parse = true;
+ continue;
+ }
+
+ int64 delta_in_milliseconds;
+ if (!base::StringToInt64(time_and_name[0], &delta_in_milliseconds)) {
+ fprintf(
+ stderr,
+ "[%s %zu] replay log should have format 'timestamp domain_name\\n'\n",
+ file_path.MaybeAsASCII().c_str(),
+ i + 1);
+ bad_parse = true;
+ continue;
+ }
+
+ base::TimeDelta delta =
+ base::TimeDelta::FromMilliseconds(delta_in_milliseconds);
+ if (delta < previous_delta) {
+ fprintf(
+ stderr,
+ "[%s %zu] replay log should be sorted by time\n",
+ file_path.MaybeAsASCII().c_str(),
+ i + 1);
+ bad_parse = true;
+ continue;
+ }
+
+ previous_delta = delta;
+ ReplayLogEntry entry;
+ entry.start_time = delta;
+ entry.domain_name = time_and_name[1];
+ replay_log->push_back(entry);
+ }
+ return !bad_parse;
+}
+
class GDig {
public:
GDig();
@@ -114,18 +200,22 @@ class GDig {
void Finish(Result);
void OnDnsConfig(const DnsConfig& dns_config_const);
- void OnResolveComplete(int val);
+ void OnResolveComplete(size_t index, AddressList* address_list,
+ base::TimeDelta time_since_start, int val);
void OnTimeout();
+ void ReplayNextEntry();
base::TimeDelta config_timeout_;
- std::string domain_name_;
bool print_config_;
bool print_hosts_;
net::IPEndPoint nameserver_;
base::TimeDelta timeout_;
-
+ int parallellism_;
+ ReplayLog replay_log_;
+ size_t replay_log_index_;
+ base::Time start_time_;
+ int active_resolves_;
Result result_;
- AddressList addrlist_;
base::CancelableClosure timeout_closure_;
scoped_ptr<DnsConfigService> dns_config_service_;
@@ -136,7 +226,10 @@ class GDig {
GDig::GDig()
: config_timeout_(base::TimeDelta::FromSeconds(5)),
print_config_(false),
- print_hosts_(false) {
+ print_hosts_(false),
+ parallellism_(6),
+ replay_log_index_(0u),
+ active_resolves_(0) {
}
GDig::Result GDig::Main(int argc, const char* argv[]) {
@@ -145,8 +238,11 @@ GDig::Result GDig::Main(int argc, const char* argv[]) {
"usage: %s [--net_log[=<basic|no_bytes|all>]]"
" [--print_config] [--print_hosts]"
" [--nameserver=<ip_address[:port]>]"
- " [--timeout=<milliseconds>] [--config_timeout=<seconds>]"
- " domain_name\n",
+ " [--timeout=<milliseconds>]"
+ " [--config_timeout=<seconds>]"
+ " [--j=<parallel resolves>]"
+ " [--replay_file=<path>]"
+ " [domain_name]\n",
argv[0]);
return RESULT_WRONG_USAGE;
}
@@ -232,16 +328,38 @@ bool GDig::ParseCommandLine(int argc, const char* argv[]) {
}
}
+ if (parsed_command_line.HasSwitch("replay_file")) {
+ FilePath replay_path =
+ parsed_command_line.GetSwitchValuePath("replay_file");
+ if (!LoadReplayLog(replay_path, &replay_log_))
+ return false;
+ }
+
+ if (parsed_command_line.HasSwitch("j")) {
+ int parallellism = 0;
+ bool parsed = base::StringToInt(
+ parsed_command_line.GetSwitchValueASCII("j"),
+ &parallellism);
+ if (parsed && parallellism > 0) {
+ parallellism_ = parallellism;
+ } else {
+ fprintf(stderr, "Invalid parallellism parameter\n");
+ }
+ }
+
if (parsed_command_line.GetArgs().size() == 1) {
+ ReplayLogEntry entry;
+ entry.start_time = base::TimeDelta();
#if defined(OS_WIN)
- domain_name_ = WideToASCII(parsed_command_line.GetArgs()[0]);
+ entry.domain_name = WideToASCII(parsed_command_line.GetArgs()[0]);
#else
- domain_name_ = parsed_command_line.GetArgs()[0];
+ entry.domain_name = parsed_command_line.GetArgs()[0];
#endif
+ replay_log_.push_back(entry);
} else if (parsed_command_line.GetArgs().size() != 0) {
return false;
}
- return print_config_ || print_hosts_ || domain_name_.length() > 0;
+ return print_config_ || print_hosts_ || !replay_log_.empty();
}
void GDig::Start() {
@@ -286,8 +404,7 @@ void GDig::OnDnsConfig(const DnsConfig& dns_config_const) {
"%s", DnsHostsToString(dns_config.hosts).c_str());
}
- // If the user didn't specify a name to resolve we can stop here.
- if (domain_name_.length() == 0) {
+ if (replay_log_.empty()) {
Finish(RESULT_OK);
return;
}
@@ -297,40 +414,77 @@ void GDig::OnDnsConfig(const DnsConfig& dns_config_const) {
scoped_ptr<HostResolverImpl> resolver(
new HostResolverImpl(
HostCache::CreateDefaultCache(),
- PrioritizedDispatcher::Limits(NUM_PRIORITIES, 1),
+ PrioritizedDispatcher::Limits(NUM_PRIORITIES, parallellism_),
HostResolverImpl::ProcTaskParams(NULL, 1),
log_.get()));
resolver->SetDnsClient(dns_client.Pass());
resolver_ = resolver.Pass();
- HostResolver::RequestInfo info(HostPortPair(domain_name_.c_str(), 80));
-
- CompletionCallback callback = base::Bind(&GDig::OnResolveComplete,
- base::Unretained(this));
- int ret = resolver_->Resolve(
- info, &addrlist_, callback, NULL,
- BoundNetLog::Make(log_.get(), net::NetLog::SOURCE_NONE));
- switch (ret) {
- case OK:
- OnResolveComplete(ret);
- break;
- case ERR_IO_PENDING: break;
- default:
- Finish(RESULT_NO_RESOLVE);
- fprintf(stderr, "Error calling resolve %s\n", ErrorToString(ret));
+ start_time_ = base::Time::Now();
+
+ ReplayNextEntry();
+}
+
+void GDig::ReplayNextEntry() {
+ DCHECK_LT(replay_log_index_, replay_log_.size());
+
+ base::TimeDelta time_since_start = base::Time::Now() - start_time_;
+ while (replay_log_index_ < replay_log_.size()) {
+ const ReplayLogEntry& entry = replay_log_[replay_log_index_];
+ if (time_since_start < entry.start_time) {
+ // Delay call to next time and return.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&GDig::ReplayNextEntry, base::Unretained(this)),
+ entry.start_time - time_since_start);
+ return;
+ }
+
+ HostResolver::RequestInfo info(HostPortPair(entry.domain_name.c_str(), 80));
+ AddressList* addrlist = new AddressList();
+ size_t current_index = replay_log_index_;
+ CompletionCallback callback = base::Bind(&GDig::OnResolveComplete,
+ base::Unretained(this),
+ current_index,
+ base::Owned(addrlist),
+ time_since_start);
+ ++active_resolves_;
+ ++replay_log_index_;
+ int ret = resolver_->Resolve(
+ info, addrlist, callback, NULL,
+ BoundNetLog::Make(log_.get(), net::NetLog::SOURCE_NONE));
+ if (ret != ERR_IO_PENDING)
+ callback.Run(ret);
}
}
-void GDig::OnResolveComplete(int val) {
+void GDig::OnResolveComplete(size_t entry_index,
+ AddressList* address_list,
+ base::TimeDelta resolve_start_time,
+ int val) {
+ DCHECK_GT(active_resolves_, 0);
+ DCHECK(address_list);
+ DCHECK_LT(entry_index, replay_log_.size());
+ --active_resolves_;
+ base::TimeDelta resolve_end_time = base::Time::Now() - start_time_;
+ base::TimeDelta resolve_time = resolve_end_time - resolve_start_time;
+ printf("%zu %d %d %s %d ",
+ entry_index,
+ static_cast<int>(resolve_end_time.InMilliseconds()),
+ static_cast<int>(resolve_time.InMilliseconds()),
+ replay_log_[entry_index].domain_name.c_str(), val);
if (val != OK) {
- fprintf(stderr, "Error trying to resolve hostname %s: %s\n",
- domain_name_.c_str(), ErrorToString(val));
- Finish(RESULT_NO_RESOLVE);
+ printf("%s", ErrorToString(val));
} else {
- for (size_t i = 0; i < addrlist_.size(); ++i)
- printf("%s\n", addrlist_[i].ToStringWithoutPort().c_str());
- Finish(RESULT_OK);
+ for (size_t i = 0; i < address_list->size(); ++i) {
+ if (i != 0)
+ printf(" ");
+ printf("%s", (*address_list)[i].ToStringWithoutPort().c_str());
+ }
}
+ printf("\n");
+ if (active_resolves_ == 0 && replay_log_index_ >= replay_log_.size())
+ Finish(RESULT_OK);
}
void GDig::OnTimeout() {