diagnostics_main.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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.
4
5#include "chrome/browser/diagnostics/diagnostics_main.h"
6
7#if defined(OS_POSIX)
8#include <stdio.h>
9#include <unistd.h>
10#endif
11
12#include <iostream>
13
14#include "app/app_paths.h"
15#include "base/basictypes.h"
16#include "base/command_line.h"
17#include "base/i18n/icu_util.h"
18#include "base/string_util.h"
19#include "base/sys_string_conversions.h"
20#include "base/time.h"
21#include "base/utf_string_conversions.h"
22#include "chrome/browser/diagnostics/diagnostics_model.h"
23#include "chrome/common/chrome_paths.h"
24
25#if defined(OS_WIN)
26#include "base/win_util.h"
27#endif
28
29namespace {
30// This is a minimalistic interface to wrap the platform console.  This will be
31// eventually replaced by a view that can be subclassed for each platform and
32// that the approved look and feel.
33class SimpleConsole {
34 public:
35  enum Color {
36    DEFAULT,
37    RED,
38    GREEN,
39  };
40
41  virtual ~SimpleConsole() { }
42
43  // Init must be called before using any other method. If it returns
44  // false there would be no console output.
45  virtual bool Init() = 0;
46
47  // Writes a string to the console with the current color.
48  virtual bool Write(const std::wstring& text) = 0;
49
50  // Reads a string from the console. Internally it may be limited to 256
51  // characters.
52  virtual bool Read(std::wstring* txt) = 0;
53
54  // Sets the foreground and background color.
55  virtual bool SetColor(Color color) = 0;
56
57  // Create an appropriate SimpleConsole instance.  May return NULL if there is
58  // no implementation for the current platform.
59  static SimpleConsole* Create();
60};
61
62#if defined(OS_WIN)
63// Wrapper for the windows console operating in high-level IO mode.
64class WinConsole : public SimpleConsole {
65 public:
66  // The ctor allocates a console always. This avoids having to ask
67  // the user to start chrome from a command prompt.
68  WinConsole()
69      : std_out_(INVALID_HANDLE_VALUE),
70        std_in_(INVALID_HANDLE_VALUE)  {
71  }
72
73  virtual ~WinConsole() {
74    ::FreeConsole();
75  }
76
77  virtual bool Init() {
78    ::AllocConsole();
79    return SetIOHandles();
80  }
81
82  virtual bool Write(const std::wstring& txt) {
83    DWORD sz = txt.size();
84    return (TRUE == ::WriteConsoleW(std_out_, txt.c_str(), sz, &sz, NULL));
85  }
86
87  // Reads a string from the console. Internally it is limited to 256
88  // characters.
89  virtual bool Read(std::wstring* txt) {
90    wchar_t buf[256];
91    DWORD read = sizeof(buf) - sizeof(buf[0]);
92    if (!::ReadConsoleW(std_in_, buf, read, &read, NULL))
93      return false;
94    // Note that |read| is in bytes.
95    txt->assign(buf, read/2);
96    return true;
97  }
98
99  // Sets the foreground and background color.
100  virtual bool SetColor(Color color) {
101    uint16 color_combo =
102        FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY;
103    switch (color) {
104      case RED:
105        color_combo = FOREGROUND_RED|FOREGROUND_INTENSITY;
106        break;
107      case GREEN:
108        color_combo = FOREGROUND_GREEN|FOREGROUND_INTENSITY;
109        break;
110      case DEFAULT:
111        break;
112      default:
113        NOTREACHED();
114    }
115    return (TRUE == ::SetConsoleTextAttribute(std_out_, color_combo));
116  }
117
118 private:
119  bool SetIOHandles() {
120    std_out_ = ::GetStdHandle(STD_OUTPUT_HANDLE);
121    std_in_ = ::GetStdHandle(STD_INPUT_HANDLE);
122    return ((std_out_ != INVALID_HANDLE_VALUE) &&
123            (std_in_ != INVALID_HANDLE_VALUE));
124  }
125
126  // The input and output handles to the screen. They seem to be
127  // implemented as pipes but they have non-documented protocol.
128  HANDLE std_out_;
129  HANDLE std_in_;
130
131  DISALLOW_COPY_AND_ASSIGN(WinConsole);
132};
133
134SimpleConsole* SimpleConsole::Create() {
135  return new WinConsole();
136}
137
138#elif defined(OS_POSIX)
139
140class PosixConsole : public SimpleConsole {
141 public:
142  PosixConsole() { }
143
144  virtual bool Init() {
145    // Technically, we should also check the terminal capabilities before using
146    // color, but in practice this is unlikely to be an issue.
147    use_color_ = isatty(STDOUT_FILENO);
148    return true;
149  }
150
151  virtual bool Write(const std::wstring& text) {
152    printf("%s", base::SysWideToNativeMB(text).c_str());
153    return true;
154  }
155
156  virtual bool Read(std::wstring* txt) {
157    std::string input;
158    if (!std::getline(std::cin, input)) {
159      std::cin.clear();
160      return false;
161    }
162    *txt = UTF8ToWide(input);
163    return true;
164  }
165
166  virtual bool SetColor(Color color) {
167    if (!use_color_)
168      return false;
169
170    const char* code = "\033[m";
171    switch (color) {
172      case RED:
173        code = "\033[1;31m";
174        break;
175      case GREEN:
176        code = "\033[1;32m";
177        break;
178      case DEFAULT:
179        break;
180      default:
181        NOTREACHED();
182    }
183    printf("%s", code);
184    return true;
185  }
186
187 private:
188  bool use_color_;
189
190  DISALLOW_COPY_AND_ASSIGN(PosixConsole);
191};
192
193SimpleConsole* SimpleConsole::Create() {
194  return new PosixConsole();
195}
196
197#else  // !defined(OS_WIN) && !defined(OS_POSIX)
198
199SimpleConsole* SimpleConsole::Create() {
200  return NULL;
201}
202#endif
203
204// This class wraps a SimpleConsole for the specific use case of
205// writing the results of the diagnostic tests.
206// TODO(cpu) figure out the localization strategy.
207class TestWriter {
208 public:
209  // The |console| must be valid and properly initialized. This
210  // class does not own it.
211  explicit TestWriter(SimpleConsole* console) : console_(console) {
212  }
213
214  // Write an informational line of text in white over black.
215  bool WriteInfoText(const std::wstring& txt) {
216    console_->SetColor(SimpleConsole::DEFAULT);
217    return console_->Write(txt);
218  }
219
220  // Write a result block. It consist of two lines. The first line
221  // has [PASS] or [FAIL] with |name| and the second line has
222  // the text in |extra|.
223  bool WriteResult(bool success, const std::wstring& name,
224                   const std::wstring& extra) {
225    if (success) {
226      console_->SetColor(SimpleConsole::GREEN);
227      console_->Write(L"[PASS] ");
228    } else {
229      console_->SetColor(SimpleConsole::RED);
230      console_->Write(L"[FAIL] ");
231    }
232    WriteInfoText(name + L"\n");
233    std::wstring second_line(L"   ");
234    second_line.append(extra);
235    return WriteInfoText(second_line + L"\n\n");
236  }
237
238 private:
239
240  SimpleConsole* console_;
241
242  DISALLOW_COPY_AND_ASSIGN(TestWriter);
243};
244
245std::wstring PrintableUSCurrentTime() {
246  base::Time::Exploded exploded = {0};
247  base::Time::Now().UTCExplode(&exploded);
248  return StringPrintf(L"%d:%d:%d.%d:%d:%d",
249      exploded.year, exploded.month, exploded.day_of_month,
250      exploded.hour, exploded.minute, exploded.second);
251}
252
253// This class is a basic test controller. In this design the view (TestWriter)
254// and the model (DiagnosticsModel) do not talk to each other directly but they
255// are mediated by the controller. This has a name: 'passive view'.
256// More info at http://martinfowler.com/eaaDev/PassiveScreen.html
257class TestController : public DiagnosticsModel::Observer {
258 public:
259  explicit TestController(TestWriter* writer) : writer_(writer) {
260  }
261
262  // Run all the diagnostics of |model| and invoke the view as the model
263  // callbacks arrive.
264  void Run(DiagnosticsModel* model) {
265    std::wstring title(L"Chrome Diagnostics Mode (");
266    writer_->WriteInfoText(title.append(PrintableUSCurrentTime()) + L")\n");
267    if (!model) {
268      writer_->WriteResult(false, L"Diagnostics start", L"model is null");
269      return;
270    }
271    bool icu_result = icu_util::Initialize();
272    if (!icu_result) {
273      writer_->WriteResult(false, L"Diagnostics start", L"ICU failure");
274      return;
275    }
276    int count = model->GetTestAvailableCount();
277    writer_->WriteInfoText(StringPrintf(L"%d available test(s)\n\n", count));
278    model->RunAll(this);
279  }
280
281  // Next four are overriden from DiagnosticsModel::Observer
282  virtual void OnProgress(int id, int percent, DiagnosticsModel* model) {
283  }
284
285  virtual void OnSkipped(int id, DiagnosticsModel* model) {
286    // TODO(cpu): display skipped tests.
287  }
288
289  virtual void OnFinished(int id, DiagnosticsModel* model) {
290    // As each test completes we output the results.
291    ShowResult(model->GetTest(id));
292  }
293
294  virtual void OnDoneAll(DiagnosticsModel* model) {
295    writer_->WriteInfoText(L"DONE\n\n");
296  }
297
298 private:
299  void ShowResult(DiagnosticsModel::TestInfo& test_info) {
300    bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult());
301    writer_->WriteResult(success, UTF16ToWide(test_info.GetTitle()),
302                         UTF16ToWide(test_info.GetAdditionalInfo()));
303  }
304
305  DiagnosticsModel* model_;
306  TestWriter* writer_;
307
308  DISALLOW_COPY_AND_ASSIGN(TestController);
309};
310}  // namespace
311
312// This entry point is called from ChromeMain() when very few things
313// have been initialized. To wit:
314// -(win)   Breakpad
315// -(macOS) base::EnableTerminationOnHeapCorruption()
316// -(macOS) base::EnableTerminationOnOutOfMemory()
317// -(all)   RegisterInvalidParamHandler()
318// -(all)   base::AtExitManager::AtExitManager()
319// -(macOS) base::ScopedNSAutoreleasePool
320// -(posix) Singleton<base::GlobalDescriptors>::Set(kPrimaryIPCChannel)
321// -(linux) Singleton<base::GlobalDescriptors>::Set(kCrashDumpSignal)
322// -(posix) setlocale(LC_ALL,..)
323// -(all)   CommandLine::Init();
324
325int DiagnosticsMain(const CommandLine& command_line) {
326  // If we can't initialize the console exit right away.
327  SimpleConsole* console = SimpleConsole::Create();
328  if (!console || !console->Init())
329    return 1;
330
331  // We need to have the path providers registered. They both
332  // return void so there is no early error signal that we can use.
333  app::RegisterPathProvider();
334  chrome::RegisterPathProvider();
335
336  TestWriter writer(console);
337  DiagnosticsModel* model = MakeDiagnosticsModel(command_line);
338  TestController controller(&writer);
339
340  // Run all the diagnostic tests.
341  controller.Run(model);
342  delete model;
343
344  // The "press enter to continue" prompt isn't very unixy, so only do that on
345  // Windows.
346#if defined(OS_WIN)
347  // Block here so the user can see the results.
348  writer.WriteInfoText(L"Press [enter] to continue\n");
349  std::wstring txt;
350  console->Read(&txt);
351#endif
352  delete console;
353  return 0;
354}
355