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 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. 65 WinConsole() 66 : std_out_(INVALID_HANDLE_VALUE), 67 std_in_(INVALID_HANDLE_VALUE) { 68 } 69 70 virtual ~WinConsole() { 71 ::FreeConsole(); 72 } 73 74 virtual bool Init() { 75 ::AllocConsole(); 76 return SetIOHandles(); 77 } 78 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. 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. 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: 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 131 SimpleConsole* SimpleConsole::Create() { 132 return new WinConsole(); 133 } 134 135 #elif defined(OS_POSIX) 136 137 class PosixConsole : public SimpleConsole { 138 public: 139 PosixConsole() : use_color_(false) { } 140 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 148 virtual bool Write(const std::wstring& text) { 149 printf("%s", base::SysWideToNativeMB(text).c_str()); 150 return true; 151 } 152 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 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 190 SimpleConsole* SimpleConsole::Create() { 191 return new PosixConsole(); 192 } 193 194 #else // !defined(OS_WIN) && !defined(OS_POSIX) 195 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. 208 explicit TestWriter(SimpleConsole* console) 209 : console_(console), 210 failures_(0) { 211 } 212 213 // How many tests reported failure. 214 int failures() { return failures_; } 215 216 // Write an informational line of text in white over black. 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|. 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 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: 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. 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. 290 virtual void OnProgress(int id, int percent, DiagnosticsModel* model) { 291 } 292 293 virtual void OnSkipped(int id, DiagnosticsModel* model) { 294 // TODO(cpu): display skipped tests. 295 } 296 297 virtual void OnFinished(int id, DiagnosticsModel* model) { 298 // As each test completes we output the results. 299 ShowResult(model->GetTest(id)); 300 } 301 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: 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 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