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