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