1 // Copyright 2013 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_writer.h" 6 7 #include "build/build_config.h" 8 9 #if defined(OS_POSIX) 10 #include <stdio.h> 11 #include <unistd.h> 12 #endif 13 14 #include <string> 15 16 #include "base/basictypes.h" 17 #include "base/command_line.h" 18 #include "base/logging.h" 19 #include "base/logging.h" 20 #include "base/strings/string16.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "chrome/common/chrome_switches.h" 24 #include "ui/base/resource/resource_bundle.h" 25 #include "ui/base/ui_base_paths.h" 26 27 namespace diagnostics { 28 29 // This is a minimalistic interface to wrap the platform console. 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 will 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 base::string16& text) = 0; 46 47 // Called when the program is about to exit. 48 virtual void OnQuit() = 0; 49 50 // Sets the foreground text 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 namespace { 60 61 // Wrapper for the windows console operating in high-level IO mode. 62 class WinConsole : public SimpleConsole { 63 public: 64 // The ctor allocates a console. This avoids having to ask the user to start 65 // chrome from a command prompt. 66 WinConsole() 67 : std_out_(INVALID_HANDLE_VALUE), 68 std_in_(INVALID_HANDLE_VALUE) { 69 ::AllocConsole(); 70 } 71 72 virtual ~WinConsole() { 73 ::FreeConsole(); 74 } 75 76 virtual bool Init() { 77 return SetIOHandles(); 78 } 79 80 virtual bool Write(const base::string16& txt) { 81 DWORD sz = txt.size(); 82 return (TRUE == ::WriteConsoleW(std_out_, txt.c_str(), sz, &sz, NULL)); 83 } 84 85 // Reads a string from the console. Internally it is limited to 256 86 // characters. 87 virtual void OnQuit() { 88 // Block here so the user can see the results. 89 SetColor(SimpleConsole::DEFAULT); 90 Write(L"Press [enter] to continue\n"); 91 wchar_t buf[256]; 92 DWORD read = arraysize(buf); 93 ::ReadConsoleW(std_in_, buf, read, &read, NULL); 94 } 95 96 // Sets the foreground and background color. 97 virtual bool SetColor(Color color) { 98 uint16 color_combo = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 99 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 } // namespace 132 133 SimpleConsole* SimpleConsole::Create() { return new WinConsole(); } 134 135 #elif defined(OS_POSIX) 136 namespace { 137 138 class PosixConsole : public SimpleConsole { 139 public: 140 PosixConsole() : use_color_(false) {} 141 142 virtual bool Init() OVERRIDE { 143 // Technically, we should also check the terminal capabilities before using 144 // color, but in practice this is unlikely to be an issue. 145 use_color_ = isatty(STDOUT_FILENO); 146 return true; 147 } 148 149 virtual bool Write(const base::string16& text) OVERRIDE { 150 // We're assuming that the terminal is using UTF-8 encoding. 151 printf("%s", base::UTF16ToUTF8(text).c_str()); 152 return true; 153 } 154 155 virtual void OnQuit() OVERRIDE { 156 // The "press enter to continue" prompt isn't very unixy, so only do that on 157 // Windows. 158 } 159 160 virtual bool SetColor(Color color) OVERRIDE { 161 if (!use_color_) 162 return false; 163 164 const char* code = "\033[m"; 165 switch (color) { 166 case RED: 167 code = "\033[1;31m"; 168 break; 169 case GREEN: 170 code = "\033[1;32m"; 171 break; 172 case DEFAULT: 173 break; 174 default: 175 NOTREACHED(); 176 } 177 printf("%s", code); 178 return true; 179 } 180 181 private: 182 bool use_color_; 183 184 DISALLOW_COPY_AND_ASSIGN(PosixConsole); 185 }; 186 187 } // namespace 188 189 SimpleConsole* SimpleConsole::Create() { return new PosixConsole(); } 190 191 #else // !defined(OS_WIN) && !defined(OS_POSIX) 192 SimpleConsole* SimpleConsole::Create() { return NULL; } 193 #endif 194 195 /////////////////////////////////////////////////////////// 196 // DiagnosticsWriter 197 198 DiagnosticsWriter::DiagnosticsWriter(FormatType format) 199 : failures_(0), format_(format) { 200 // Only create consoles for non-log output. 201 if (format_ != LOG) { 202 console_.reset(SimpleConsole::Create()); 203 console_->Init(); 204 } 205 } 206 207 DiagnosticsWriter::~DiagnosticsWriter() { 208 if (console_.get()) 209 console_->OnQuit(); 210 } 211 212 bool DiagnosticsWriter::WriteInfoLine(const std::string& info_text) { 213 if (format_ == LOG) { 214 LOG(WARNING) << info_text; 215 return true; 216 } else { 217 if (console_.get()) { 218 console_->SetColor(SimpleConsole::DEFAULT); 219 console_->Write(base::UTF8ToUTF16(info_text + "\n")); 220 } 221 } 222 return true; 223 } 224 225 void DiagnosticsWriter::OnTestFinished(int index, DiagnosticsModel* model) { 226 const DiagnosticsModel::TestInfo& test_info = model->GetTest(index); 227 bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult()); 228 WriteResult(success, 229 test_info.GetName(), 230 test_info.GetTitle(), 231 test_info.GetOutcomeCode(), 232 test_info.GetAdditionalInfo()); 233 } 234 235 void DiagnosticsWriter::OnAllTestsDone(DiagnosticsModel* model) { 236 WriteInfoLine( 237 base::StringPrintf("Finished %d tests.", model->GetTestRunCount())); 238 } 239 240 void DiagnosticsWriter::OnRecoveryFinished(int index, DiagnosticsModel* model) { 241 const DiagnosticsModel::TestInfo& test_info = model->GetTest(index); 242 WriteInfoLine("Finished Recovery for: " + test_info.GetTitle()); 243 } 244 245 void DiagnosticsWriter::OnAllRecoveryDone(DiagnosticsModel* model) { 246 WriteInfoLine("Finished All Recovery operations."); 247 } 248 249 bool DiagnosticsWriter::WriteResult(bool success, 250 const std::string& id, 251 const std::string& name, 252 int outcome_code, 253 const std::string& extra) { 254 std::string result; 255 SimpleConsole::Color color; 256 257 if (success) { 258 result = "[PASS] "; 259 color = SimpleConsole::GREEN; 260 } else { 261 color = SimpleConsole::RED; 262 result = "[FAIL] "; 263 failures_++; 264 } 265 266 if (format_ != LOG) { 267 if (console_.get()) { 268 console_->SetColor(color); 269 console_->Write(base::ASCIIToUTF16(result)); 270 } 271 if (format_ == MACHINE) { 272 return WriteInfoLine(base::StringPrintf( 273 "%03d %s (%s)", outcome_code, id.c_str(), extra.c_str())); 274 } else { 275 return WriteInfoLine(name + "\n " + extra + "\n"); 276 } 277 } else { 278 if (!success) { 279 // For log output, we only care about the tests that failed. 280 return WriteInfoLine(base::StringPrintf("%s%03d %s (%s)", 281 result.c_str(), 282 outcome_code, 283 id.c_str(), 284 extra.c_str())); 285 } 286 } 287 return true; 288 } 289 290 } // namespace diagnostics 291