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