We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / net / tools / gdig / gdig.cc
blobfaaffa7ce4228db59ab01596cb71dfe73066052c
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <stdio.h>
6 #include <string>
8 #include "base/at_exit.h"
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/cancelable_callback.h"
12 #include "base/command_line.h"
13 #include "base/files/file_util.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
22 #include "net/base/address_list.h"
23 #include "net/base/ip_endpoint.h"
24 #include "net/base/net_errors.h"
25 #include "net/base/net_util.h"
26 #include "net/dns/dns_client.h"
27 #include "net/dns/dns_config_service.h"
28 #include "net/dns/dns_protocol.h"
29 #include "net/dns/host_cache.h"
30 #include "net/dns/host_resolver_impl.h"
31 #include "net/log/net_log.h"
32 #include "net/tools/gdig/file_net_log.h"
34 #if defined(OS_MACOSX)
35 #include "base/mac/scoped_nsautorelease_pool.h"
36 #endif
38 namespace net {
40 namespace {
42 bool StringToIPEndPoint(const std::string& ip_address_and_port,
43 IPEndPoint* ip_end_point) {
44 DCHECK(ip_end_point);
46 std::string ip;
47 int port;
48 if (!ParseHostAndPort(ip_address_and_port, &ip, &port))
49 return false;
50 if (port == -1)
51 port = dns_protocol::kDefaultPort;
53 net::IPAddressNumber ip_number;
54 if (!net::ParseIPLiteralToNumber(ip, &ip_number))
55 return false;
57 *ip_end_point = net::IPEndPoint(ip_number, static_cast<uint16>(port));
58 return true;
61 // Convert DnsConfig to human readable text omitting the hosts member.
62 std::string DnsConfigToString(const DnsConfig& dns_config) {
63 std::string output;
64 output.append("search ");
65 for (size_t i = 0; i < dns_config.search.size(); ++i) {
66 output.append(dns_config.search[i] + " ");
68 output.append("\n");
70 for (size_t i = 0; i < dns_config.nameservers.size(); ++i) {
71 output.append("nameserver ");
72 output.append(dns_config.nameservers[i].ToString()).append("\n");
75 base::StringAppendF(&output, "options ndots:%d\n", dns_config.ndots);
76 base::StringAppendF(&output, "options timeout:%d\n",
77 static_cast<int>(dns_config.timeout.InMilliseconds()));
78 base::StringAppendF(&output, "options attempts:%d\n", dns_config.attempts);
79 if (dns_config.rotate)
80 output.append("options rotate\n");
81 if (dns_config.edns0)
82 output.append("options edns0\n");
83 return output;
86 // Convert DnsConfig hosts member to a human readable text.
87 std::string DnsHostsToString(const DnsHosts& dns_hosts) {
88 std::string output;
89 for (DnsHosts::const_iterator i = dns_hosts.begin();
90 i != dns_hosts.end();
91 ++i) {
92 const DnsHostsKey& key = i->first;
93 std::string host_name = key.first;
94 output.append(IPEndPoint(i->second, 0).ToStringWithoutPort());
95 output.append(" ").append(host_name).append("\n");
97 return output;
100 struct ReplayLogEntry {
101 base::TimeDelta start_time;
102 std::string domain_name;
105 typedef std::vector<ReplayLogEntry> ReplayLog;
107 // Loads and parses a replay log file and fills |replay_log| with a structured
108 // representation. Returns whether the operation was successful. If not, the
109 // contents of |replay_log| are undefined.
111 // The replay log is a text file where each line contains
113 // timestamp_in_milliseconds domain_name
115 // The timestamp_in_milliseconds needs to be an integral delta from start of
116 // resolution and is in milliseconds. domain_name is the name to be resolved.
118 // The file should be sorted by timestamp in ascending time.
119 bool LoadReplayLog(const base::FilePath& file_path, ReplayLog* replay_log) {
120 std::string original_replay_log_contents;
121 if (!base::ReadFileToString(file_path, &original_replay_log_contents)) {
122 fprintf(stderr, "Unable to open replay file %s\n",
123 file_path.MaybeAsASCII().c_str());
124 return false;
127 // Strip out \r characters for Windows files. This isn't as efficient as a
128 // smarter line splitter, but this particular use does not need to target
129 // efficiency.
130 std::string replay_log_contents;
131 base::RemoveChars(original_replay_log_contents, "\r", &replay_log_contents);
133 std::vector<std::string> lines;
134 base::SplitString(replay_log_contents, '\n', &lines);
135 base::TimeDelta previous_delta;
136 bool bad_parse = false;
137 for (unsigned i = 0; i < lines.size(); ++i) {
138 if (lines[i].empty())
139 continue;
140 std::vector<std::string> time_and_name;
141 base::SplitString(lines[i], ' ', &time_and_name);
142 if (time_and_name.size() != 2) {
143 fprintf(
144 stderr,
145 "[%s %u] replay log should have format 'timestamp domain_name\\n'\n",
146 file_path.MaybeAsASCII().c_str(),
147 i + 1);
148 bad_parse = true;
149 continue;
152 int64 delta_in_milliseconds;
153 if (!base::StringToInt64(time_and_name[0], &delta_in_milliseconds)) {
154 fprintf(
155 stderr,
156 "[%s %u] replay log should have format 'timestamp domain_name\\n'\n",
157 file_path.MaybeAsASCII().c_str(),
158 i + 1);
159 bad_parse = true;
160 continue;
163 base::TimeDelta delta =
164 base::TimeDelta::FromMilliseconds(delta_in_milliseconds);
165 if (delta < previous_delta) {
166 fprintf(
167 stderr,
168 "[%s %u] replay log should be sorted by time\n",
169 file_path.MaybeAsASCII().c_str(),
170 i + 1);
171 bad_parse = true;
172 continue;
175 previous_delta = delta;
176 ReplayLogEntry entry;
177 entry.start_time = delta;
178 entry.domain_name = time_and_name[1];
179 replay_log->push_back(entry);
181 return !bad_parse;
184 class GDig {
185 public:
186 GDig();
187 ~GDig();
189 enum Result {
190 RESULT_NO_RESOLVE = -3,
191 RESULT_NO_CONFIG = -2,
192 RESULT_WRONG_USAGE = -1,
193 RESULT_OK = 0,
194 RESULT_PENDING = 1,
197 Result Main(int argc, const char* argv[]);
199 private:
200 bool ParseCommandLine(int argc, const char* argv[]);
202 void Start();
203 void Finish(Result);
205 void OnDnsConfig(const DnsConfig& dns_config_const);
206 void OnResolveComplete(unsigned index, AddressList* address_list,
207 base::TimeDelta time_since_start, int val);
208 void OnTimeout();
209 void ReplayNextEntry();
211 base::TimeDelta config_timeout_;
212 bool print_config_;
213 bool print_hosts_;
214 net::IPEndPoint nameserver_;
215 base::TimeDelta timeout_;
216 int parallellism_;
217 ReplayLog replay_log_;
218 unsigned replay_log_index_;
219 base::Time start_time_;
220 int active_resolves_;
221 Result result_;
223 base::CancelableClosure timeout_closure_;
224 scoped_ptr<DnsConfigService> dns_config_service_;
225 scoped_ptr<FileNetLogObserver> log_observer_;
226 scoped_ptr<NetLog> log_;
227 scoped_ptr<HostResolver> resolver_;
229 #if defined(OS_MACOSX)
230 // Without this there will be a mem leak on osx.
231 base::mac::ScopedNSAutoreleasePool scoped_pool_;
232 #endif
234 // Need AtExitManager to support AsWeakPtr (in NetLog).
235 base::AtExitManager exit_manager_;
238 GDig::GDig()
239 : config_timeout_(base::TimeDelta::FromSeconds(5)),
240 print_config_(false),
241 print_hosts_(false),
242 parallellism_(6),
243 replay_log_index_(0u),
244 active_resolves_(0) {
247 GDig::~GDig() {
248 if (log_)
249 log_->DeprecatedRemoveObserver(log_observer_.get());
252 GDig::Result GDig::Main(int argc, const char* argv[]) {
253 if (!ParseCommandLine(argc, argv)) {
254 fprintf(stderr,
255 "usage: %s [--net_log[=<basic|no_bytes|all>]]"
256 " [--print_config] [--print_hosts]"
257 " [--nameserver=<ip_address[:port]>]"
258 " [--timeout=<milliseconds>]"
259 " [--config_timeout=<seconds>]"
260 " [--j=<parallel resolves>]"
261 " [--replay_file=<path>]"
262 " [domain_name]\n",
263 argv[0]);
264 return RESULT_WRONG_USAGE;
267 base::MessageLoopForIO loop;
269 result_ = RESULT_PENDING;
270 Start();
271 if (result_ == RESULT_PENDING)
272 base::MessageLoop::current()->Run();
274 // Destroy it while MessageLoopForIO is alive.
275 dns_config_service_.reset();
276 return result_;
279 bool GDig::ParseCommandLine(int argc, const char* argv[]) {
280 base::CommandLine::Init(argc, argv);
281 const base::CommandLine& parsed_command_line =
282 *base::CommandLine::ForCurrentProcess();
284 if (parsed_command_line.HasSwitch("config_timeout")) {
285 int timeout_seconds = 0;
286 bool parsed = base::StringToInt(
287 parsed_command_line.GetSwitchValueASCII("config_timeout"),
288 &timeout_seconds);
289 if (parsed && timeout_seconds > 0) {
290 config_timeout_ = base::TimeDelta::FromSeconds(timeout_seconds);
291 } else {
292 fprintf(stderr, "Invalid config_timeout parameter\n");
293 return false;
297 if (parsed_command_line.HasSwitch("net_log")) {
298 std::string log_param = parsed_command_line.GetSwitchValueASCII("net_log");
299 NetLog::LogLevel level = NetLog::LOG_ALL_BUT_BYTES;
301 if (log_param.length() > 0) {
302 std::map<std::string, NetLog::LogLevel> log_levels;
303 log_levels["all"] = NetLog::LOG_ALL;
304 log_levels["no_bytes"] = NetLog::LOG_ALL_BUT_BYTES;
306 if (log_levels.find(log_param) != log_levels.end()) {
307 level = log_levels[log_param];
308 } else {
309 fprintf(stderr, "Invalid net_log parameter\n");
310 return false;
313 log_.reset(new NetLog);
314 log_observer_.reset(new FileNetLogObserver(stderr));
315 log_->DeprecatedAddObserver(log_observer_.get(), level);
318 print_config_ = parsed_command_line.HasSwitch("print_config");
319 print_hosts_ = parsed_command_line.HasSwitch("print_hosts");
321 if (parsed_command_line.HasSwitch("nameserver")) {
322 std::string nameserver =
323 parsed_command_line.GetSwitchValueASCII("nameserver");
324 if (!StringToIPEndPoint(nameserver, &nameserver_)) {
325 fprintf(stderr,
326 "Cannot parse the namerserver string into an IPEndPoint\n");
327 return false;
331 if (parsed_command_line.HasSwitch("timeout")) {
332 int timeout_millis = 0;
333 bool parsed = base::StringToInt(
334 parsed_command_line.GetSwitchValueASCII("timeout"),
335 &timeout_millis);
336 if (parsed && timeout_millis > 0) {
337 timeout_ = base::TimeDelta::FromMilliseconds(timeout_millis);
338 } else {
339 fprintf(stderr, "Invalid timeout parameter\n");
340 return false;
344 if (parsed_command_line.HasSwitch("replay_file")) {
345 base::FilePath replay_path =
346 parsed_command_line.GetSwitchValuePath("replay_file");
347 if (!LoadReplayLog(replay_path, &replay_log_))
348 return false;
351 if (parsed_command_line.HasSwitch("j")) {
352 int parallellism = 0;
353 bool parsed = base::StringToInt(
354 parsed_command_line.GetSwitchValueASCII("j"),
355 &parallellism);
356 if (parsed && parallellism > 0) {
357 parallellism_ = parallellism;
358 } else {
359 fprintf(stderr, "Invalid parallellism parameter\n");
363 if (parsed_command_line.GetArgs().size() == 1) {
364 ReplayLogEntry entry;
365 entry.start_time = base::TimeDelta();
366 #if defined(OS_WIN)
367 entry.domain_name = base::UTF16ToASCII(parsed_command_line.GetArgs()[0]);
368 #else
369 entry.domain_name = parsed_command_line.GetArgs()[0];
370 #endif
371 replay_log_.push_back(entry);
372 } else if (parsed_command_line.GetArgs().size() != 0) {
373 return false;
375 return print_config_ || print_hosts_ || !replay_log_.empty();
378 void GDig::Start() {
379 if (nameserver_.address().size() > 0) {
380 DnsConfig dns_config;
381 dns_config.attempts = 1;
382 dns_config.nameservers.push_back(nameserver_);
383 OnDnsConfig(dns_config);
384 } else {
385 dns_config_service_ = DnsConfigService::CreateSystemService();
386 dns_config_service_->ReadConfig(base::Bind(&GDig::OnDnsConfig,
387 base::Unretained(this)));
388 timeout_closure_.Reset(base::Bind(&GDig::OnTimeout,
389 base::Unretained(this)));
390 base::MessageLoop::current()->PostDelayedTask(
391 FROM_HERE, timeout_closure_.callback(), config_timeout_);
395 void GDig::Finish(Result result) {
396 DCHECK_NE(RESULT_PENDING, result);
397 result_ = result;
398 if (base::MessageLoop::current())
399 base::MessageLoop::current()->Quit();
402 void GDig::OnDnsConfig(const DnsConfig& dns_config_const) {
403 timeout_closure_.Cancel();
404 DCHECK(dns_config_const.IsValid());
405 DnsConfig dns_config = dns_config_const;
407 if (timeout_.InMilliseconds() > 0)
408 dns_config.timeout = timeout_;
409 if (print_config_) {
410 printf("# Dns Configuration\n"
411 "%s", DnsConfigToString(dns_config).c_str());
413 if (print_hosts_) {
414 printf("# Host Database\n"
415 "%s", DnsHostsToString(dns_config.hosts).c_str());
418 if (replay_log_.empty()) {
419 Finish(RESULT_OK);
420 return;
423 scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
424 dns_client->SetConfig(dns_config);
425 HostResolver::Options options;
426 options.max_concurrent_resolves = parallellism_;
427 options.max_retry_attempts = 1u;
428 scoped_ptr<HostResolverImpl> resolver(
429 new HostResolverImpl(options, log_.get()));
430 resolver->SetDnsClient(dns_client.Pass());
431 resolver_ = resolver.Pass();
433 start_time_ = base::Time::Now();
435 ReplayNextEntry();
438 void GDig::ReplayNextEntry() {
439 DCHECK_LT(replay_log_index_, replay_log_.size());
441 base::TimeDelta time_since_start = base::Time::Now() - start_time_;
442 while (replay_log_index_ < replay_log_.size()) {
443 const ReplayLogEntry& entry = replay_log_[replay_log_index_];
444 if (time_since_start < entry.start_time) {
445 // Delay call to next time and return.
446 base::MessageLoop::current()->PostDelayedTask(
447 FROM_HERE,
448 base::Bind(&GDig::ReplayNextEntry, base::Unretained(this)),
449 entry.start_time - time_since_start);
450 return;
453 HostResolver::RequestInfo info(HostPortPair(entry.domain_name.c_str(), 80));
454 AddressList* addrlist = new AddressList();
455 unsigned current_index = replay_log_index_;
456 CompletionCallback callback = base::Bind(&GDig::OnResolveComplete,
457 base::Unretained(this),
458 current_index,
459 base::Owned(addrlist),
460 time_since_start);
461 ++active_resolves_;
462 ++replay_log_index_;
463 int ret = resolver_->Resolve(
464 info,
465 DEFAULT_PRIORITY,
466 addrlist,
467 callback,
468 NULL,
469 BoundNetLog::Make(log_.get(), net::NetLog::SOURCE_NONE));
470 if (ret != ERR_IO_PENDING)
471 callback.Run(ret);
475 void GDig::OnResolveComplete(unsigned entry_index,
476 AddressList* address_list,
477 base::TimeDelta resolve_start_time,
478 int val) {
479 DCHECK_GT(active_resolves_, 0);
480 DCHECK(address_list);
481 DCHECK_LT(entry_index, replay_log_.size());
482 --active_resolves_;
483 base::TimeDelta resolve_end_time = base::Time::Now() - start_time_;
484 base::TimeDelta resolve_time = resolve_end_time - resolve_start_time;
485 printf("%u %d %d %s %d ",
486 entry_index,
487 static_cast<int>(resolve_end_time.InMilliseconds()),
488 static_cast<int>(resolve_time.InMilliseconds()),
489 replay_log_[entry_index].domain_name.c_str(), val);
490 if (val != OK) {
491 std::string error_string = ErrorToString(val);
492 printf("%s", error_string.c_str());
493 } else {
494 for (size_t i = 0; i < address_list->size(); ++i) {
495 if (i != 0)
496 printf(" ");
497 printf("%s", (*address_list)[i].ToStringWithoutPort().c_str());
500 printf("\n");
501 if (active_resolves_ == 0 && replay_log_index_ >= replay_log_.size())
502 Finish(RESULT_OK);
505 void GDig::OnTimeout() {
506 fprintf(stderr, "Timed out waiting to load the dns config\n");
507 Finish(RESULT_NO_CONFIG);
510 } // empty namespace
512 } // namespace net
514 int main(int argc, const char* argv[]) {
515 net::GDig dig;
516 return dig.Main(argc, argv);