Home | History | Annotate | Download | only in diagnostics
      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