Enables compositing support for webview.
[chromium-blink-merge.git] / net / tools / gdig / gdig.cc
blob5e209c269cd6eb79fb8b0d525960af25bdfcc72e
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/bind.h"
10 #include "base/cancelable_callback.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop.h"
15 #include "base/string_number_conversions.h"
16 #include "base/string_split.h"
17 #include "base/string_util.h"
18 #include "base/stringprintf.h"
19 #include "base/time.h"
20 #include "net/base/address_list.h"
21 #include "net/base/host_cache.h"
22 #include "net/base/host_resolver_impl.h"
23 #include "net/base/ip_endpoint.h"
24 #include "net/base/net_errors.h"
25 #include "net/base/net_log.h"
26 #include "net/base/net_util.h"
27 #include "net/dns/dns_client.h"
28 #include "net/dns/dns_config_service.h"
29 #include "net/dns/dns_protocol.h"
30 #include "net/tools/gdig/file_net_log.h"
32 #if defined(OS_MACOSX)
33 #include "base/mac/scoped_nsautorelease_pool.h"
34 #endif
36 namespace net {
38 namespace {
40 bool StringToIPEndPoint(const std::string& ip_address_and_port,
41 IPEndPoint* ip_end_point) {
42 DCHECK(ip_end_point);
44 std::string ip;
45 int port;
46 if (!ParseHostAndPort(ip_address_and_port, &ip, &port))
47 return false;
48 if (port == -1)
49 port = dns_protocol::kDefaultPort;
51 net::IPAddressNumber ip_number;
52 if (!net::ParseIPLiteralToNumber(ip, &ip_number))
53 return false;
55 *ip_end_point = net::IPEndPoint(ip_number, port);
56 return true;
59 // Convert DnsConfig to human readable text omitting the hosts member.
60 std::string DnsConfigToString(const DnsConfig& dns_config) {
61 std::string output;
62 output.append("search ");
63 for (size_t i = 0; i < dns_config.search.size(); ++i) {
64 output.append(dns_config.search[i] + " ");
66 output.append("\n");
68 for (size_t i = 0; i < dns_config.nameservers.size(); ++i) {
69 output.append("nameserver ");
70 output.append(dns_config.nameservers[i].ToString()).append("\n");
73 base::StringAppendF(&output, "options ndots:%d\n", dns_config.ndots);
74 base::StringAppendF(&output, "options timeout:%d\n",
75 static_cast<int>(dns_config.timeout.InMilliseconds()));
76 base::StringAppendF(&output, "options attempts:%d\n", dns_config.attempts);
77 if (dns_config.rotate)
78 output.append("options rotate\n");
79 if (dns_config.edns0)
80 output.append("options edns0\n");
81 return output;
84 // Convert DnsConfig hosts member to a human readable text.
85 std::string DnsHostsToString(const DnsHosts& dns_hosts) {
86 std::string output;
87 for (DnsHosts::const_iterator i = dns_hosts.begin();
88 i != dns_hosts.end();
89 ++i) {
90 const DnsHostsKey& key = i->first;
91 std::string host_name = key.first;
92 output.append(IPEndPoint(i->second, -1).ToStringWithoutPort());
93 output.append(" ").append(host_name).append("\n");
95 return output;
98 struct ReplayLogEntry {
99 base::TimeDelta start_time;
100 std::string domain_name;
103 typedef std::vector<ReplayLogEntry> ReplayLog;
105 // Loads and parses a replay log file and fills |replay_log| with a structured
106 // representation. Returns whether the operation was successful. If not, the
107 // contents of |replay_log| are undefined.
109 // The replay log is a text file where each line contains
111 // timestamp_in_milliseconds domain_name
113 // The timestamp_in_milliseconds needs to be an integral delta from start of
114 // resolution and is in milliseconds. domain_name is the name to be resolved.
116 // The file should be sorted by timestamp in ascending time.
117 bool LoadReplayLog(const FilePath& file_path, ReplayLog* replay_log) {
118 std::string original_replay_log_contents;
119 if (!file_util::ReadFileToString(file_path, &original_replay_log_contents)) {
120 fprintf(stderr, "Unable to open replay file %s\n",
121 file_path.MaybeAsASCII().c_str());
122 return false;
125 // Strip out \r characters for Windows files. This isn't as efficient as a
126 // smarter line splitter, but this particular use does not need to target
127 // efficiency.
128 std::string replay_log_contents;
129 RemoveChars(original_replay_log_contents, "\r", &replay_log_contents);
131 std::vector<std::string> lines;
132 base::SplitString(replay_log_contents, '\n', &lines);
133 base::TimeDelta previous_delta;
134 bool bad_parse = false;
135 for (size_t i = 0; i < lines.size(); ++i) {
136 if (lines[i].empty())
137 continue;
138 std::vector<std::string> time_and_name;
139 base::SplitString(lines[i], ' ', &time_and_name);
140 if (time_and_name.size() != 2) {
141 fprintf(
142 stderr,
143 "[%s %zu] replay log should have format 'timestamp domain_name\\n'\n",
144 file_path.MaybeAsASCII().c_str(),
145 i + 1);
146 bad_parse = true;
147 continue;
150 int64 delta_in_milliseconds;
151 if (!base::StringToInt64(time_and_name[0], &delta_in_milliseconds)) {
152 fprintf(
153 stderr,
154 "[%s %zu] replay log should have format 'timestamp domain_name\\n'\n",
155 file_path.MaybeAsASCII().c_str(),
156 i + 1);
157 bad_parse = true;
158 continue;
161 base::TimeDelta delta =
162 base::TimeDelta::FromMilliseconds(delta_in_milliseconds);
163 if (delta < previous_delta) {
164 fprintf(
165 stderr,
166 "[%s %zu] replay log should be sorted by time\n",
167 file_path.MaybeAsASCII().c_str(),
168 i + 1);
169 bad_parse = true;
170 continue;
173 previous_delta = delta;
174 ReplayLogEntry entry;
175 entry.start_time = delta;
176 entry.domain_name = time_and_name[1];
177 replay_log->push_back(entry);
179 return !bad_parse;
182 class GDig {
183 public:
184 GDig();
186 enum Result {
187 RESULT_NO_RESOLVE = -3,
188 RESULT_NO_CONFIG = -2,
189 RESULT_WRONG_USAGE = -1,
190 RESULT_OK = 0,
191 RESULT_PENDING = 1,
194 Result Main(int argc, const char* argv[]);
196 private:
197 bool ParseCommandLine(int argc, const char* argv[]);
199 void Start();
200 void Finish(Result);
202 void OnDnsConfig(const DnsConfig& dns_config_const);
203 void OnResolveComplete(size_t index, AddressList* address_list,
204 base::TimeDelta time_since_start, int val);
205 void OnTimeout();
206 void ReplayNextEntry();
208 base::TimeDelta config_timeout_;
209 bool print_config_;
210 bool print_hosts_;
211 net::IPEndPoint nameserver_;
212 base::TimeDelta timeout_;
213 int parallellism_;
214 ReplayLog replay_log_;
215 size_t replay_log_index_;
216 base::Time start_time_;
217 int active_resolves_;
218 Result result_;
220 base::CancelableClosure timeout_closure_;
221 scoped_ptr<DnsConfigService> dns_config_service_;
222 scoped_ptr<FileNetLog> log_;
223 scoped_ptr<HostResolver> resolver_;
226 GDig::GDig()
227 : config_timeout_(base::TimeDelta::FromSeconds(5)),
228 print_config_(false),
229 print_hosts_(false),
230 parallellism_(6),
231 replay_log_index_(0u),
232 active_resolves_(0) {
235 GDig::Result GDig::Main(int argc, const char* argv[]) {
236 if (!ParseCommandLine(argc, argv)) {
237 fprintf(stderr,
238 "usage: %s [--net_log[=<basic|no_bytes|all>]]"
239 " [--print_config] [--print_hosts]"
240 " [--nameserver=<ip_address[:port]>]"
241 " [--timeout=<milliseconds>]"
242 " [--config_timeout=<seconds>]"
243 " [--j=<parallel resolves>]"
244 " [--replay_file=<path>]"
245 " [domain_name]\n",
246 argv[0]);
247 return RESULT_WRONG_USAGE;
250 #if defined(OS_MACOSX)
251 // Without this there will be a mem leak on osx.
252 base::mac::ScopedNSAutoreleasePool scoped_pool;
253 #endif
255 base::AtExitManager exit_manager;
256 MessageLoopForIO loop;
258 result_ = RESULT_PENDING;
259 Start();
260 if (result_ == RESULT_PENDING)
261 MessageLoop::current()->Run();
263 // Destroy it while MessageLoopForIO is alive.
264 dns_config_service_.reset();
265 return result_;
268 bool GDig::ParseCommandLine(int argc, const char* argv[]) {
269 CommandLine::Init(argc, argv);
270 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
272 if (parsed_command_line.HasSwitch("config_timeout")) {
273 int timeout_seconds = 0;
274 bool parsed = base::StringToInt(
275 parsed_command_line.GetSwitchValueASCII("config_timeout"),
276 &timeout_seconds);
277 if (parsed && timeout_seconds > 0) {
278 config_timeout_ = base::TimeDelta::FromSeconds(timeout_seconds);
279 } else {
280 fprintf(stderr, "Invalid config_timeout parameter\n");
281 return false;
285 if (parsed_command_line.HasSwitch("net_log")) {
286 std::string log_param = parsed_command_line.GetSwitchValueASCII("net_log");
287 NetLog::LogLevel level = NetLog::LOG_ALL_BUT_BYTES;
289 if (log_param.length() > 0) {
290 std::map<std::string, NetLog::LogLevel> log_levels;
291 log_levels["all"] = NetLog::LOG_ALL;
292 log_levels["no_bytes"] = NetLog::LOG_ALL_BUT_BYTES;
293 log_levels["basic"] = NetLog::LOG_BASIC;
295 if (log_levels.find(log_param) != log_levels.end()) {
296 level = log_levels[log_param];
297 } else {
298 fprintf(stderr, "Invalid net_log parameter\n");
299 return false;
302 log_.reset(new FileNetLog(stderr, level));
305 print_config_ = parsed_command_line.HasSwitch("print_config");
306 print_hosts_ = parsed_command_line.HasSwitch("print_hosts");
308 if (parsed_command_line.HasSwitch("nameserver")) {
309 std::string nameserver =
310 parsed_command_line.GetSwitchValueASCII("nameserver");
311 if (!StringToIPEndPoint(nameserver, &nameserver_)) {
312 fprintf(stderr,
313 "Cannot parse the namerserver string into an IPEndPoint\n");
314 return false;
318 if (parsed_command_line.HasSwitch("timeout")) {
319 int timeout_millis = 0;
320 bool parsed = base::StringToInt(
321 parsed_command_line.GetSwitchValueASCII("timeout"),
322 &timeout_millis);
323 if (parsed && timeout_millis > 0) {
324 timeout_ = base::TimeDelta::FromMilliseconds(timeout_millis);
325 } else {
326 fprintf(stderr, "Invalid timeout parameter\n");
327 return false;
331 if (parsed_command_line.HasSwitch("replay_file")) {
332 FilePath replay_path =
333 parsed_command_line.GetSwitchValuePath("replay_file");
334 if (!LoadReplayLog(replay_path, &replay_log_))
335 return false;
338 if (parsed_command_line.HasSwitch("j")) {
339 int parallellism = 0;
340 bool parsed = base::StringToInt(
341 parsed_command_line.GetSwitchValueASCII("j"),
342 &parallellism);
343 if (parsed && parallellism > 0) {
344 parallellism_ = parallellism;
345 } else {
346 fprintf(stderr, "Invalid parallellism parameter\n");
350 if (parsed_command_line.GetArgs().size() == 1) {
351 ReplayLogEntry entry;
352 entry.start_time = base::TimeDelta();
353 #if defined(OS_WIN)
354 entry.domain_name = WideToASCII(parsed_command_line.GetArgs()[0]);
355 #else
356 entry.domain_name = parsed_command_line.GetArgs()[0];
357 #endif
358 replay_log_.push_back(entry);
359 } else if (parsed_command_line.GetArgs().size() != 0) {
360 return false;
362 return print_config_ || print_hosts_ || !replay_log_.empty();
365 void GDig::Start() {
366 if (nameserver_.address().size() > 0) {
367 DnsConfig dns_config;
368 dns_config.attempts = 1;
369 dns_config.nameservers.push_back(nameserver_);
370 OnDnsConfig(dns_config);
371 } else {
372 dns_config_service_ = DnsConfigService::CreateSystemService();
373 dns_config_service_->ReadConfig(base::Bind(&GDig::OnDnsConfig,
374 base::Unretained(this)));
375 timeout_closure_.Reset(base::Bind(&GDig::OnTimeout,
376 base::Unretained(this)));
377 MessageLoop::current()->PostDelayedTask(
378 FROM_HERE,
379 timeout_closure_.callback(),
380 config_timeout_);
384 void GDig::Finish(Result result) {
385 DCHECK_NE(RESULT_PENDING, result);
386 result_ = result;
387 if (MessageLoop::current())
388 MessageLoop::current()->Quit();
391 void GDig::OnDnsConfig(const DnsConfig& dns_config_const) {
392 timeout_closure_.Cancel();
393 DCHECK(dns_config_const.IsValid());
394 DnsConfig dns_config = dns_config_const;
396 if (timeout_.InMilliseconds() > 0)
397 dns_config.timeout = timeout_;
398 if (print_config_) {
399 printf("# Dns Configuration\n"
400 "%s", DnsConfigToString(dns_config).c_str());
402 if (print_hosts_) {
403 printf("# Host Database\n"
404 "%s", DnsHostsToString(dns_config.hosts).c_str());
407 if (replay_log_.empty()) {
408 Finish(RESULT_OK);
409 return;
412 scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
413 dns_client->SetConfig(dns_config);
414 scoped_ptr<HostResolverImpl> resolver(
415 new HostResolverImpl(
416 HostCache::CreateDefaultCache(),
417 PrioritizedDispatcher::Limits(NUM_PRIORITIES, parallellism_),
418 HostResolverImpl::ProcTaskParams(NULL, 1),
419 log_.get()));
420 resolver->SetDnsClient(dns_client.Pass());
421 resolver_ = resolver.Pass();
423 start_time_ = base::Time::Now();
425 ReplayNextEntry();
428 void GDig::ReplayNextEntry() {
429 DCHECK_LT(replay_log_index_, replay_log_.size());
431 base::TimeDelta time_since_start = base::Time::Now() - start_time_;
432 while (replay_log_index_ < replay_log_.size()) {
433 const ReplayLogEntry& entry = replay_log_[replay_log_index_];
434 if (time_since_start < entry.start_time) {
435 // Delay call to next time and return.
436 MessageLoop::current()->PostDelayedTask(
437 FROM_HERE,
438 base::Bind(&GDig::ReplayNextEntry, base::Unretained(this)),
439 entry.start_time - time_since_start);
440 return;
443 HostResolver::RequestInfo info(HostPortPair(entry.domain_name.c_str(), 80));
444 AddressList* addrlist = new AddressList();
445 size_t current_index = replay_log_index_;
446 CompletionCallback callback = base::Bind(&GDig::OnResolveComplete,
447 base::Unretained(this),
448 current_index,
449 base::Owned(addrlist),
450 time_since_start);
451 ++active_resolves_;
452 ++replay_log_index_;
453 int ret = resolver_->Resolve(
454 info, addrlist, callback, NULL,
455 BoundNetLog::Make(log_.get(), net::NetLog::SOURCE_NONE));
456 if (ret != ERR_IO_PENDING)
457 callback.Run(ret);
461 void GDig::OnResolveComplete(size_t entry_index,
462 AddressList* address_list,
463 base::TimeDelta resolve_start_time,
464 int val) {
465 DCHECK_GT(active_resolves_, 0);
466 DCHECK(address_list);
467 DCHECK_LT(entry_index, replay_log_.size());
468 --active_resolves_;
469 base::TimeDelta resolve_end_time = base::Time::Now() - start_time_;
470 base::TimeDelta resolve_time = resolve_end_time - resolve_start_time;
471 printf("%zu %d %d %s %d ",
472 entry_index,
473 static_cast<int>(resolve_end_time.InMilliseconds()),
474 static_cast<int>(resolve_time.InMilliseconds()),
475 replay_log_[entry_index].domain_name.c_str(), val);
476 if (val != OK) {
477 printf("%s", ErrorToString(val));
478 } else {
479 for (size_t i = 0; i < address_list->size(); ++i) {
480 if (i != 0)
481 printf(" ");
482 printf("%s", (*address_list)[i].ToStringWithoutPort().c_str());
485 printf("\n");
486 if (active_resolves_ == 0 && replay_log_index_ >= replay_log_.size())
487 Finish(RESULT_OK);
490 void GDig::OnTimeout() {
491 fprintf(stderr, "Timed out waiting to load the dns config\n");
492 Finish(RESULT_NO_CONFIG);
495 } // empty namespace
497 } // namespace net
499 int main(int argc, const char* argv[]) {
500 net::GDig dig;
501 return dig.Main(argc, argv);