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/strings/string16.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "chrome/common/chrome_switches.h" 23 #include "ui/base/ui_base_paths.h" 24 25 namespace diagnostics { 26 27 // This is a minimalistic interface to wrap the platform console. 28 class SimpleConsole { 29 public: 30 enum Color { 31 DEFAULT, 32 RED, 33 GREEN, 34 }; 35 36 virtual ~SimpleConsole() {} 37 38 // Init must be called before using any other method. If it returns 39 // false there will be no console output. 40 virtual bool Init() = 0; 41 42 // Writes a string to the console with the current color. 43 virtual bool Write(const base::string16& text) = 0; 44 45 // Called when the program is about to exit. 46 virtual void OnQuit() = 0; 47 48 // Sets the foreground text color. 49 virtual bool SetColor(Color color) = 0; 50 51 // Create an appropriate SimpleConsole instance. May return NULL if there is 52 // no implementation for the current platform. 53 static SimpleConsole* Create(); 54 }; 55 56 #if defined(OS_WIN) 57 namespace { 58 59 // Wrapper for the windows console operating in high-level IO mode. 60 class WinConsole : public SimpleConsole { 61 public: 62 // The ctor allocates a console. This avoids having to ask the user to start 63 // chrome from a command prompt. 64 WinConsole() 65 : std_out_(INVALID_HANDLE_VALUE), 66 std_in_(INVALID_HANDLE_VALUE) { 67 ::AllocConsole(); 68 } 69 70 virtual ~WinConsole() { 71 ::FreeConsole(); 72 } 73 74 virtual bool Init() { 75 return SetIOHandles(); 76 } 77 78 virtual bool Write(const base::string16& 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 void OnQuit() { 86 // Block here so the user can see the results. 87 SetColor(SimpleConsole::DEFAULT); 88 Write(L"Press [enter] to continue\n"); 89 wchar_t buf[256]; 90 DWORD read = arraysize(buf); 91 ::ReadConsoleW(std_in_, buf, read, &read, NULL); 92 } 93 94 // Sets the foreground and background color. 95 virtual bool SetColor(Color color) { 96 uint16 color_combo = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | 97 FOREGROUND_INTENSITY; 98 switch (color) { 99 case RED: 100 color_combo = FOREGROUND_RED | FOREGROUND_INTENSITY; 101 break; 102 case GREEN: 103 color_combo = FOREGROUND_GREEN | FOREGROUND_INTENSITY; 104 break; 105 case DEFAULT: 106 break; 107 default: 108 NOTREACHED(); 109 } 110 return (TRUE == ::SetConsoleTextAttribute(std_out_, color_combo)); 111 } 112 113 private: 114 bool SetIOHandles() { 115 std_out_ = ::GetStdHandle(STD_OUTPUT_HANDLE); 116 std_in_ = ::GetStdHandle(STD_INPUT_HANDLE); 117 return ((std_out_ != INVALID_HANDLE_VALUE) && 118 (std_in_ != INVALID_HANDLE_VALUE)); 119 } 120 121 // The input and output handles to the screen. They seem to be 122 // implemented as pipes but they have non-documented protocol. 123 HANDLE std_out_; 124 HANDLE std_in_; 125 126 DISALLOW_COPY_AND_ASSIGN(WinConsole); 127 }; 128 129 } // namespace 130 131 SimpleConsole* SimpleConsole::Create() { return new WinConsole(); } 132 133 #elif defined(OS_POSIX) 134 namespace { 135 136 class PosixConsole : public SimpleConsole { 137 public: 138 PosixConsole() : use_color_(false) {} 139 140 virtual bool Init() OVERRIDE { 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 base::string16& text) OVERRIDE { 148 // We're assuming that the terminal is using UTF-8 encoding. 149 printf("%s", base::UTF16ToUTF8(text).c_str()); 150 return true; 151 } 152 153 virtual void OnQuit() OVERRIDE { 154 // The "press enter to continue" prompt isn't very unixy, so only do that on 155 // Windows. 156 } 157 158 virtual bool SetColor(Color color) OVERRIDE { 159 if (!use_color_) 160 return false; 161 162 const char* code = "\033[m"; 163 switch (color) { 164 case RED: 165 code = "\033[1;31m"; 166 break; 167 case GREEN: 168 code = "\033[1;32m"; 169 break; 170 case DEFAULT: 171 break; 172 default: 173 NOTREACHED(); 174 } 175 printf("%s", code); 176 return true; 177 } 178 179 private: 180 bool use_color_; 181 182 DISALLOW_COPY_AND_ASSIGN(PosixConsole); 183 }; 184 185 } // namespace 186 187 SimpleConsole* SimpleConsole::Create() { return new PosixConsole(); } 188 189 #else // !defined(OS_WIN) && !defined(OS_POSIX) 190 SimpleConsole* SimpleConsole::Create() { return NULL; } 191 #endif 192 193 /////////////////////////////////////////////////////////// 194 // DiagnosticsWriter 195 196 DiagnosticsWriter::DiagnosticsWriter(FormatType format) 197 : failures_(0), format_(format) { 198 // Only create consoles for non-log output. 199 if (format_ != LOG) { 200 console_.reset(SimpleConsole::Create()); 201 console_->Init(); 202 } 203 } 204 205 DiagnosticsWriter::~DiagnosticsWriter() { 206 if (console_.get()) 207 console_->OnQuit(); 208 } 209 210 bool DiagnosticsWriter::WriteInfoLine(const std::string& info_text) { 211 if (format_ == LOG) { 212 LOG(WARNING) << info_text; 213 return true; 214 } else { 215 if (console_.get()) { 216 console_->SetColor(SimpleConsole::DEFAULT); 217 console_->Write(base::UTF8ToUTF16(info_text + "\n")); 218 } 219 } 220 return true; 221 } 222 223 void DiagnosticsWriter::OnTestFinished(int index, DiagnosticsModel* model) { 224 const DiagnosticsModel::TestInfo& test_info = model->GetTest(index); 225 bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult()); 226 WriteResult(success, 227 test_info.GetName(), 228 test_info.GetTitle(), 229 test_info.GetOutcomeCode(), 230 test_info.GetAdditionalInfo()); 231 } 232 233 void DiagnosticsWriter::OnAllTestsDone(DiagnosticsModel* model) { 234 WriteInfoLine( 235 base::StringPrintf("Finished %d tests.", model->GetTestRunCount())); 236 } 237 238 void DiagnosticsWriter::OnRecoveryFinished(int index, DiagnosticsModel* model) { 239 const DiagnosticsModel::TestInfo& test_info = model->GetTest(index); 240 WriteInfoLine("Finished Recovery for: " + test_info.GetTitle()); 241 } 242 243 void DiagnosticsWriter::OnAllRecoveryDone(DiagnosticsModel* model) { 244 WriteInfoLine("Finished All Recovery operations."); 245 } 246 247 bool DiagnosticsWriter::WriteResult(bool success, 248 const std::string& id, 249 const std::string& name, 250 int outcome_code, 251 const std::string& extra) { 252 std::string result; 253 SimpleConsole::Color color; 254 255 if (success) { 256 result = "[PASS] "; 257 color = SimpleConsole::GREEN; 258 } else { 259 color = SimpleConsole::RED; 260 result = "[FAIL] "; 261 failures_++; 262 } 263 264 if (format_ != LOG) { 265 if (console_.get()) { 266 console_->SetColor(color); 267 console_->Write(base::ASCIIToUTF16(result)); 268 } 269 if (format_ == MACHINE) { 270 return WriteInfoLine(base::StringPrintf( 271 "%03d %s (%s)", outcome_code, id.c_str(), extra.c_str())); 272 } else { 273 return WriteInfoLine(name + "\n " + extra + "\n"); 274 } 275 } else { 276 if (!success) { 277 // For log output, we only care about the tests that failed. 278 return WriteInfoLine(base::StringPrintf("%s%03d %s (%s)", 279 result.c_str(), 280 outcome_code, 281 id.c_str(), 282 extra.c_str())); 283 } 284 } 285 return true; 286 } 287 288 } // namespace diagnostics 289