Coverage for /Syzygy/trace/logger/logger_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
55.6%1592860.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  //
  15    :  // This file defines the trace::logger::LoggerApp class which implements the
  16    :  // LoggerApp RPC interface.
  17    :  
  18    :  #include "syzygy/trace/logger/logger_app.h"
  19    :  
  20    :  #include "base/bind.h"
  21    :  #include "base/environment.h"
  22    :  #include "base/file_util.h"
  23    :  #include "base/path_service.h"
  24    :  #include "base/process.h"
  25    :  #include "base/process_util.h"
  26    :  #include "base/string_util.h"
  27    :  #include "base/stringprintf.h"
  28    :  #include "base/utf_string_conversions.h"
  29    :  #include "base/win/scoped_handle.h"
  30    :  #include "syzygy/trace/logger/logger.h"
  31    :  #include "syzygy/trace/logger/logger_rpc_impl.h"
  32    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  33    :  #include "syzygy/trace/rpc/logger_rpc.h"
  34    :  #include "syzygy/trace/rpc/rpc_helpers.h"
  35    :  
  36    :  namespace trace {
  37    :  namespace logger {
  38    :  
  39    :  namespace {
  40    :  
  41    :  using trace::client::GetInstanceString;
  42    :  
  43    :  // The usage string for the logger app.
  44    :  const char kUsageFormatStr[] =
  45    :      "Usage: %ls [options] ACTION [-- command]\n"
  46    :      "  Supported actions:\n"
  47    :      "    start  Run a new logger instance in the foreground (blocking). You\n"
  48    :      "           may optionally specify an external command which will be\n"
  49    :      "           run behind the logger. The logger will return once the\n"
  50    :      "           external command has terminated or the logger is externally\n"
  51    :      "           stopped. If no command is specified, Ctrl-C or an invocation\n"
  52    :      "           of the stop action will stop the logger.\n"
  53    :      "    spawn  Run a new logger instance in the background (non-blocking).\n"
  54    :      "    stop   Stop a separately running logger instance.\n"
  55    :      "  Options:\n"
  56    :      "    --instance-id=ID     A unique (up to 16 character) ID to identify\n"
  57    :      "                         the logger instance.\n"
  58    :      "    --output-file=PATH>  The file path to which logs should be written.\n"
  59    :      "                         This may be stdout (the default), stderr or a\n"
  60    :      "                         file path. This option is valid for the start\n"
  61    :      "                         and spawn actions.\n"
  62    :      "    --append             Append to (instead of truncating) the output\n"
  63    :      "                         file. This option is valid for the start and\n"
  64    :      "                         spawn actions.\n";
  65    :  
  66    :  // Names for kernel objects used to synchronize with a logger singleton.
  67    :  const wchar_t kLoggerMutexRoot[] = L"syzygy-logger-mutex";
  68    :  const wchar_t kLoggerStartEventRoot[] = L"syzygy-logger-started";
  69    :  const wchar_t kLoggerStopEventRoot[] = L"syzygy-logger-stopped";
  70    :  
  71    :  // A static location to which the current instance id can be saved. We
  72    :  // persist it here so that OnConsoleCtrl can have access to the instance
  73    :  // id when it is invoked on the signal handler thread.
  74    :  wchar_t saved_instance_id[16] = { 0 };
  75    :  const size_t kMaxInstanceIdLength = arraysize(saved_instance_id) - 1;
  76    :  
  77    :  // Send a stop request via RPC to the logger instance given by @p instance_id.
  78  i :  bool SendStopRequest(const base::StringPiece16& instance_id) {
  79  i :    std::wstring protocol(kLoggerRpcProtocol);
  80  i :    std::wstring endpoint(GetInstanceString(kLoggerRpcEndpointRoot, instance_id));
  81    :  
  82  i :    LOG(INFO) << "Stopping logging service instance at '"
  83    :              << endpoint << "' via " << protocol << '.';
  84    :  
  85  i :    handle_t binding = NULL;
  86  i :    if (!trace::client::CreateRpcBinding(protocol, endpoint, &binding)) {
  87  i :      LOG(ERROR) << "Failed to connect to logging service.";
  88  i :      return false;
  89    :    }
  90    :  
  91  i :    if (!trace::client::InvokeRpc(LoggerClient_Stop, binding).succeeded()) {
  92  i :      LOG(ERROR) << "Failed to stop logging service.";
  93  i :      return false;
  94    :    }
  95    :  
  96  i :    LOG(INFO) << "Logging service shutdown has been requested.";
  97    :  
  98  i :    return true;
  99  i :  }
 100    :  
 101    :  // Handler function to be called on exit signals (Ctrl-C, TERM, etc...).
 102  i :  BOOL WINAPI OnConsoleCtrl(DWORD ctrl_type) {
 103  i :    if (ctrl_type != CTRL_LOGOFF_EVENT) {
 104  i :      SendStopRequest(saved_instance_id);
 105  i :      return TRUE;
 106    :    }
 107  i :    return FALSE;
 108  i :  }
 109    :  
 110    :  // A helper class to manage a console handler for Control-C.
 111    :  // TODO(rogerm): Move this to a shared location (perhaps in common, next to
 112    :  //     the application classes?).
 113    :  class ScopedConsoleCtrlHandler {
 114    :   public:
 115  E :    ScopedConsoleCtrlHandler() : handler_(NULL) {
 116  E :    }
 117    :  
 118  E :    ~ScopedConsoleCtrlHandler() {
 119  E :      if (handler_ != NULL) {
 120  i :        ignore_result(::SetConsoleCtrlHandler(handler_, FALSE));
 121  i :        handler_ = NULL;
 122    :      }
 123  E :    }
 124    :  
 125  i :    bool Init(PHANDLER_ROUTINE handler) {
 126  i :      DCHECK(handler != NULL);
 127  i :      DCHECK(handler_ == NULL);
 128    :  
 129  i :      if (!::SetConsoleCtrlHandler(handler, TRUE)) {
 130  i :        DWORD err = ::GetLastError();
 131  i :        LOG(ERROR) << "Failed to register console control handler: "
 132    :                   << com::LogWe(err) << ".";
 133  i :        return false;
 134    :      }
 135    :  
 136  i :      handler_ = handler;
 137  i :      return true;
 138  i :    }
 139    :  
 140    :   protected:
 141    :    PHANDLER_ROUTINE handler_;
 142    :  };
 143    :  
 144    :  // Helper function to acquire a named mutex.
 145    :  // TODO(rogerm): Move this to a shared location (perhaps in common, next to
 146    :  //     the application classes?).
 147    :  bool AcquireMutex(const std::wstring& mutex_name,
 148  E :                    base::win::ScopedHandle* mutex) {
 149  E :    DCHECK(mutex != NULL);
 150  E :    DCHECK(!mutex->IsValid());
 151    :  
 152    :    base::win::ScopedHandle tmp_mutex(
 153  E :        ::CreateMutex(NULL, FALSE, mutex_name.c_str()));
 154  E :    if (!tmp_mutex.IsValid()) {
 155  i :      DWORD error = ::GetLastError();
 156  i :      LOG(ERROR) << "Failed to create mutex: " << com::LogWe(error) << ".";
 157  i :      return false;
 158    :    }
 159  E :    const DWORD kOneSecondInMs = 1000;
 160    :  
 161  E :    switch (::WaitForSingleObject(tmp_mutex, kOneSecondInMs)) {
 162    :      case WAIT_ABANDONED:
 163  i :        LOG(WARNING) << "Orphaned service mutex found!";
 164    :        // Fall through...
 165    :  
 166    :      case WAIT_OBJECT_0:
 167  E :        VLOG(1) << "Service mutex acquired.";
 168  E :        mutex->Set(tmp_mutex.Take());
 169  E :        return true;
 170    :  
 171    :      case WAIT_TIMEOUT:
 172  i :        LOG(ERROR) << "A synonymous instance of the logger is already running.";
 173  i :        break;
 174    :  
 175    :      default: {
 176  i :        DWORD error = ::GetLastError();
 177  i :        LOG(ERROR) << "Failed to acquire mutex: " << com::LogWe(error) << ".";
 178    :        break;
 179    :      }
 180    :    }
 181  i :    return false;
 182  E :  }
 183    :  
 184    :  // Helper function to initialize a named event.
 185    :  // TODO(rogerm): Move this to a shared location (perhaps in common, next to
 186    :  //     the application classes?).
 187    :  bool InitEvent(const std::wstring& event_name,
 188  E :                 base::win::ScopedHandle* handle) {
 189  E :    DCHECK(handle != NULL);
 190  E :    DCHECK(!handle->IsValid());
 191  E :    handle->Set(::CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
 192  E :    if (!handle->IsValid())
 193  i :      return false;
 194  E :    return true;
 195  E :  }
 196    :  
 197    :  // A helper function to signal an event. This is passable as a callback to
 198    :  // a Logger instance to be called on logger start/stop.
 199  E :  bool SignalEvent(HANDLE event_handle, Logger* /* logger */) {
 200  E :    DCHECK_NE(INVALID_HANDLE_VALUE, event_handle);
 201  E :    if (!::SetEvent(event_handle))
 202  i :      return false;
 203  E :    return true;
 204  E :  }
 205    :  
 206    :  // A helper to split a command line into two command lines. The split will
 207    :  // occur after the first non-switch parameter. The logger command line will
 208    :  // be populated by the switches and arguments up to and including the first
 209    :  // non-switch parameter. All remaining arguments and switches will be added
 210    :  // to the app command line. This function understands the "--" marker
 211    :  // which is used to allow switches to appear after the first non-switch
 212    :  // argument (otherwise CommandLine will sort the entire command line before
 213    :  // we get a chance to inspect it.).
 214    :  bool SplitCommandLine(const CommandLine* orig_command_line,
 215    :                        CommandLine* logger_command_line,
 216  E :                        scoped_ptr<CommandLine>* app_command_line) {
 217  E :    DCHECK(orig_command_line != NULL);
 218  E :    DCHECK(!orig_command_line->argv().empty());
 219  E :    DCHECK(logger_command_line != NULL);
 220  E :    DCHECK(app_command_line != NULL);
 221    :  
 222    :    // Copy the initial parts of the command-line, up to and including the
 223    :    // first non-switch argument (which should be the "action"), into a
 224    :    // string vector for the logger command line.
 225  E :    CommandLine::StringVector logger_argv;
 226    :    CommandLine::StringVector::const_iterator it =
 227  E :        orig_command_line->argv().begin();
 228  E :    logger_argv.push_back(*(it++));  // Always copy the program.
 229  E :    for (; it != orig_command_line->argv().end(); ++it) {
 230  E :      logger_argv.push_back(*it);
 231  E :      if ((*it)[0] != L'-') {
 232  E :        ++it;
 233  E :        break;
 234    :      }
 235  E :    }
 236    :  
 237    :    // Strip out the (optional) sentinel which marks the split between the
 238    :    // two command-lines.
 239  E :    if (it != orig_command_line->argv().end() && *it == L"--")
 240  E :      ++it;
 241    :  
 242    :    // Copy the rest of the command-line arguments into a string vector for the
 243    :    // app command line.
 244  E :    CommandLine::StringVector app_argv;
 245  E :    for (; it != orig_command_line->argv().end(); ++it) {
 246  E :      app_argv.push_back(*it);
 247  E :    }
 248    :  
 249    :    // Initialize the output command lines with the new arguments.
 250  E :    logger_command_line->InitFromArgv(logger_argv);
 251  E :    if (!app_argv.empty())
 252  E :      app_command_line->reset(new CommandLine(app_argv));
 253    :  
 254  E :    return true;
 255  E :  }
 256    :  
 257    :  // A helper function which sets the syzygy RPC instance id environment variable
 258    :  // then runs a given command line to completion.
 259    :  bool RunApp(const CommandLine& command_line,
 260    :              const std::wstring& instance_id,
 261  E :              int* exit_code) {
 262  E :    DCHECK(exit_code != NULL);
 263  E :    scoped_ptr<base::Environment> env(base::Environment::Create());
 264  E :    CHECK(env != NULL);
 265  E :    env->SetVar(kSyzygyRpcInstanceIdEnvVar, WideToUTF8(instance_id));
 266    :  
 267  E :    LOG(INFO) << "Launching '" << command_line.GetProgram().value() << "'.";
 268  E :    VLOG(1) << "Command Line: " << command_line.GetCommandLineString();
 269    :  
 270    :    // Launch a new process in the background.
 271    :    base::ProcessHandle process_handle;
 272  E :    base::LaunchOptions options;
 273  E :    options.start_hidden = false;
 274  E :    if (!base::LaunchProcess(command_line, options, &process_handle)) {
 275  i :      LOG(ERROR)
 276    :          << "Failed to launch '" << command_line.GetProgram().value() << "'.";
 277  i :      return false;
 278    :    }
 279    :  
 280    :    // Wait for and return the processes exit code.
 281    :    // Note that this closes the process handle.
 282  E :    if (!base::WaitForExitCode(process_handle, exit_code)) {
 283  i :      LOG(ERROR) << "Failed to get exit code.";
 284  i :      return false;
 285    :    }
 286    :  
 287  E :    return true;
 288  E :  }
 289    :  
 290    :  }  // namespace
 291    :  
 292    :  // Keywords appearing on the command-line
 293    :  const wchar_t LoggerApp::kSpawn[] = L"spawn";
 294    :  const wchar_t LoggerApp::kStart[] = L"start";
 295    :  const wchar_t LoggerApp::kStatus[] = L"status";
 296    :  const wchar_t LoggerApp::kStop[] = L"stop";
 297    :  const char LoggerApp::kInstanceId[] = "instance-id";
 298    :  const char LoggerApp::kOutputFile[] = "output-file";
 299    :  const char LoggerApp::kAppend[] = "append";
 300    :  const wchar_t LoggerApp::kStdOut[] = L"stdout";
 301    :  const wchar_t LoggerApp::kStdErr[] = L"stderr";
 302    :  
 303    :  // A table mapping action keywords to their handler implementations.
 304  E :  const LoggerApp::ActionTableEntry LoggerApp::kActionTable[] = {
 305  E :      { LoggerApp::kSpawn, &LoggerApp::Spawn },
 306  E :      { LoggerApp::kStart, &LoggerApp::Start },
 307  E :      { LoggerApp::kStatus, &LoggerApp::Status },
 308  E :      { LoggerApp::kStop, &LoggerApp::Stop },
 309  E :  };
 310    :  
 311    :  LoggerApp::LoggerApp()
 312    :      : common::AppImplBase("Logger"),
 313    :        logger_command_line_(CommandLine::NO_PROGRAM),
 314    :        action_handler_(NULL),
 315  E :        append_(false) {
 316  E :  }
 317    :  
 318  E :  LoggerApp::~LoggerApp() {
 319  E :  }
 320    :  
 321  E :  bool LoggerApp::ParseCommandLine(const CommandLine* command_line) {
 322  E :    DCHECK(command_line != NULL);
 323    :  
 324    :    if (!SplitCommandLine(command_line,
 325    :                          &logger_command_line_,
 326  E :                          &app_command_line_)) {
 327  i :      LOG(ERROR) << "Failed to split command_line into logger and app parts.";
 328  i :      return false;
 329    :    }
 330    :  
 331    :    // Save the command-line in case we need to spawn.
 332  E :    command_line = &logger_command_line_;
 333    :  
 334    :    // Parse the instance id.
 335  E :    instance_id_ = command_line->GetSwitchValueNative(kInstanceId);
 336  E :    if (instance_id_.size() > kMaxInstanceIdLength) {
 337    :      return Usage(command_line,
 338    :                   base::StringPrintf("The instance id '%ls' is too long. "
 339    :                                      "The max length is %d characters.",
 340    :                                      instance_id_.c_str(),
 341  i :                                      kMaxInstanceIdLength));
 342    :    }
 343    :  
 344    :    // Save the output file parameter.
 345  E :    output_file_path_ = command_line->GetSwitchValuePath(kOutputFile);
 346    :  
 347    :    // Make sure there's exactly one action.
 348  E :    if (command_line->GetArgs().size() != 1) {
 349    :      return Usage(command_line,
 350  E :                   "Exactly 1 action is expected on the command line.");
 351    :    }
 352    :  
 353    :    // Check for the append flag.
 354  E :    append_ = command_line->HasSwitch(kAppend);
 355    :  
 356    :    // Parse the action.
 357  E :    action_ = command_line->GetArgs()[0];
 358  E :    const ActionTableEntry* entry = FindActionHandler(action_);
 359  E :    if (entry == NULL) {
 360    :      return Usage(
 361    :          command_line,
 362  E :          base::StringPrintf("Unrecognized action: %s.", action_.c_str()));
 363    :    }
 364    :  
 365    :    // Setup the action handler.
 366  E :    DCHECK(entry->handler != NULL);
 367  E :    action_handler_ = entry->handler;
 368    :  
 369  E :    return true;
 370  E :  }
 371    :  
 372  E :  int LoggerApp::Run() {
 373  E :    DCHECK(action_handler_ != NULL);
 374  E :    if (!(this->*action_handler_)())
 375  i :      return 1;
 376  E :    return 0;
 377  E :  }
 378    :  
 379    :  // A helper function to find the handler method for a given action.
 380    :  const LoggerApp::ActionTableEntry* LoggerApp::FindActionHandler(
 381  E :      const base::StringPiece16& action) {
 382  E :    const ActionTableEntry* const begin  = &kActionTable[0];
 383  E :    const ActionTableEntry* const end = begin + arraysize(kActionTable);
 384    :    ActionTableEntryCompare compare_func;
 385    :  
 386    :    // Make sure that the array is sorted.
 387  E :    DCHECK(std::is_sorted(begin, end, compare_func));
 388    :  
 389    :    const ActionTableEntry* entry =
 390  E :        std::lower_bound(begin, end, action, compare_func);
 391  E :    if (entry == end)
 392  E :      return NULL;
 393    :  
 394  E :    return entry;
 395  E :  }
 396    :  
 397  E :  bool LoggerApp::Start() {
 398    :    std::wstring logger_name(
 399  E :        GetInstanceString(kLoggerRpcEndpointRoot, instance_id_));
 400    :  
 401    :    // Acquire the logger mutex.
 402  E :    base::win::ScopedHandle mutex;
 403  E :    std::wstring mutex_name(GetInstanceString(kLoggerMutexRoot, instance_id_));
 404  E :    if (!AcquireMutex(mutex_name, &mutex))
 405  i :      return false;
 406    :  
 407    :    // Setup the start event.
 408  E :    base::win::ScopedHandle start_event;
 409    :    std::wstring start_event_name(
 410  E :        GetInstanceString(kLoggerStartEventRoot, instance_id_));
 411  E :    if (!InitEvent(start_event_name, &start_event)) {
 412  i :      LOG(ERROR) << "Unable to init start event for '" << logger_name << "'.";
 413  i :      return false;
 414    :    }
 415    :  
 416    :    // Setup the stop event.
 417  E :    base::win::ScopedHandle stop_event;
 418    :    std::wstring stop_event_name(
 419  E :        GetInstanceString(kLoggerStopEventRoot, instance_id_));
 420  E :    if (!InitEvent(stop_event_name, &stop_event)) {
 421  i :      LOG(ERROR) << "Unable to init stop event for '" << logger_name << "'.";
 422  i :      return false;
 423    :    }
 424    :  
 425    :    // Get the log file output_file.
 426  E :    FILE* output_file = NULL;
 427  E :    bool must_close_output_file = false;
 428  E :    file_util::ScopedFILE auto_close;
 429  E :    if (!OpenOutputFile(&output_file, &must_close_output_file)) {
 430  i :      LOG(ERROR) << "Unable to open '" << output_file_path_.value() << "'.";
 431  i :      return false;
 432    :    }
 433    :  
 434    :    // Setup auto_close as appropriate.
 435  E :    if (must_close_output_file)
 436  i :      auto_close.reset(output_file);
 437    :  
 438    :    // Initialize the logger instance.
 439  E :    Logger logger;
 440  E :    logger.set_destination(output_file);
 441  E :    logger.set_instance_id(instance_id_);
 442    :    logger.set_logger_started_callback(
 443  E :        base::Bind(&SignalEvent, start_event.Get()));
 444    :    logger.set_logger_stopped_callback(
 445  E :        base::Bind(&SignalEvent, stop_event.Get()));
 446    :  
 447    :    // Save the instance_id for the Ctrl-C handler.
 448    :    ::wcsncpy_s(saved_instance_id,
 449    :                arraysize(saved_instance_id),
 450    :                instance_id_.c_str(),
 451  E :                -1);
 452    :  
 453    :    // Register the handler for Ctrl-C.
 454  E :    if (!SetConsoleCtrlHandler(&OnConsoleCtrl, TRUE)) {
 455  i :      DWORD error = ::GetLastError();
 456  i :      LOG(ERROR) << "Failed to register shutdown handler: "
 457    :                 << com::LogWe(error) << ".";
 458  i :      return false;
 459    :    }
 460    :  
 461    :    // Start the logger.
 462  E :    RpcLoggerInstanceManager instance_manager(&logger);
 463  E :    if (!logger.Start()) {
 464  i :      LOG(ERROR) << "Failed to start '" << logger_name << "'.";
 465  i :      return false;
 466    :    }
 467    :  
 468  E :    bool error = false;
 469    :  
 470    :    // Run the logger, either standalone or as the parent of some application.
 471  E :    ScopedConsoleCtrlHandler ctrl_handler;
 472  E :    if (app_command_line_.get() != NULL) {
 473    :      // We have a command to run, so launch that command and when it finishes
 474    :      // stop the logger.
 475  E :      int exit_code = 0;
 476    :      if (!RunApp(*app_command_line_, instance_id_, &exit_code) ||
 477  E :          exit_code != 0) {
 478  i :        error = true;
 479    :      }
 480  E :      ignore_result(logger.Stop());
 481  E :    } else {
 482    :      // There is no command to wait for, so just register the control handler
 483    :      // (we stop the logger if this fails) and then let the logger run until
 484    :      // the control handler stops it or someone externally stops it using the
 485    :      // stop command.
 486  i :      if (!ctrl_handler.Init(&OnConsoleCtrl)) {
 487  i :        ignore_result(logger.Stop());
 488  i :        error = true;
 489    :      }
 490    :    }
 491    :  
 492    :    // Run the logger to completion.
 493  E :    if (!logger.RunToCompletion()) {
 494  i :      LOG(ERROR) << "Failed running to completion '" << logger_name << "'.";
 495  i :      error = true;
 496    :    }
 497    :  
 498    :    // And we're done.
 499  E :    return !error;
 500  E :  }
 501    :  
 502  i :  bool LoggerApp::Status() {
 503    :    // TODO(rogerm): Implement me.
 504  i :    return false;
 505  i :  }
 506    :  
 507  i :  bool LoggerApp::Spawn() {
 508    :    std::wstring logger_name(
 509  i :        GetInstanceString(kLoggerRpcEndpointRoot, instance_id_));
 510    :  
 511  i :    LOG(INFO) << "Launching background logging service '" << logger_name << "'.";
 512    :  
 513    :    // Get the path to ourselves.
 514  i :    FilePath self_path;
 515  i :    PathService::Get(base::FILE_EXE, &self_path);
 516    :  
 517    :    // Build a command line for starting a new instance of the logger.
 518  i :    CommandLine new_command_line(self_path);
 519  i :    new_command_line.AppendArg("start");
 520    :  
 521    :    // Copy over any other switches.
 522    :    CommandLine::SwitchMap::const_iterator it =
 523  i :        logger_command_line_.GetSwitches().begin();
 524  i :    for (; it != logger_command_line_.GetSwitches().end(); ++it)
 525  i :      new_command_line.AppendSwitchNative(it->first, it->second);
 526    :  
 527    :    // Launch a new process in the background.
 528    :    base::ProcessHandle service_process;
 529  i :    base::LaunchOptions options;
 530  i :    options.start_hidden = true;
 531  i :    if (!base::LaunchProcess(new_command_line, options, &service_process)) {
 532  i :      LOG(ERROR) << "Failed to launch process.";
 533  i :      return false;
 534    :    }
 535  i :    DCHECK_NE(base::kNullProcessHandle, service_process);
 536    :  
 537    :    // Setup the start event.
 538  i :    base::win::ScopedHandle start_event;
 539    :    std::wstring start_event_name(
 540  i :        GetInstanceString(kLoggerStartEventRoot, instance_id_));
 541  i :    if (!InitEvent(start_event_name, &start_event)) {
 542  i :      LOG(ERROR) << "Unable to init start event for '" << logger_name << "'.";
 543  i :      return false;
 544    :    }
 545    :  
 546    :    // We wait on both the start event and the process, as if the process fails
 547    :    // for any reason, it'll exit and its handle will become signaled.
 548  i :    HANDLE handles[] = { start_event, service_process };
 549    :    if (::WaitForMultipleObjects(arraysize(handles),
 550    :                                 handles,
 551    :                                 FALSE,
 552  i :                                 INFINITE) != WAIT_OBJECT_0) {
 553  i :      LOG(ERROR) << "The logger '" << logger_name << "' exited in error.";
 554  i :      return false;
 555    :    }
 556    :  
 557  i :    LOG(INFO) << "Background logger '" << logger_name << "' is running.";
 558    :  
 559  i :    return true;
 560  i :  }
 561    :  
 562  i :  bool LoggerApp::Stop() {
 563    :    std::wstring logger_name(
 564  i :        GetInstanceString(kLoggerRpcEndpointRoot, instance_id_));
 565    :  
 566    :    // Setup the stop event.
 567  i :    base::win::ScopedHandle stop_event;
 568    :    std::wstring stop_event_name(
 569  i :        GetInstanceString(kLoggerStopEventRoot, instance_id_));
 570  i :    if (!InitEvent(stop_event_name, &stop_event)) {
 571  i :      LOG(ERROR) << "Unable to init stop event for '" << logger_name << "'.";
 572  i :      return false;
 573    :    }
 574    :  
 575    :    // Send the stop request.
 576  i :    if (!SendStopRequest(instance_id_))
 577  i :      return false;
 578    :  
 579    :    // We wait on both the RPC event and the process, as if the process fails for
 580    :    // any reason, it'll exit and its handle will become signaled.
 581  i :    if (::WaitForSingleObject(stop_event, INFINITE) != WAIT_OBJECT_0) {
 582  i :      LOG(ERROR) << "Timed out waiting for '" << logger_name << "' to stop.";
 583  i :      return false;
 584    :    }
 585    :  
 586  i :    LOG(INFO) << "The logger instance has stopped.";
 587    :  
 588  i :    return true;
 589  i :  }
 590    :  
 591    :  // Helper to resolve @p path to an open file. This will set @p must_close
 592    :  // to true if @path denotes a newly opened file, and false if it denotes
 593    :  // stderr or stdout.
 594  E :  bool LoggerApp::OpenOutputFile(FILE** output_file, bool* must_close) {
 595  E :    DCHECK(output_file != NULL);
 596  E :    DCHECK(must_close != NULL);
 597    :  
 598  E :    *output_file = NULL;
 599  E :    *must_close = false;
 600    :  
 601    :    // Check for stdout.
 602    :    if (output_file_path_.empty() ||
 603    :        ::_wcsnicmp(output_file_path_.value().c_str(),
 604    :                    kStdOut,
 605  E :                    arraysize(kStdOut)) == 0) {
 606  E :      *output_file = stdout;
 607  E :      return true;
 608    :    }
 609    :  
 610    :    // Check for stderr.
 611    :    if (::_wcsnicmp(output_file_path_.value().c_str(),
 612    :                    kStdErr,
 613  i :                    arraysize(kStdErr)) == 0) {
 614  i :      *output_file = stderr;
 615  i :      return true;
 616    :    }
 617    :  
 618    :    // Setup the write mode.
 619  i :    const char* mode = "wb";
 620  i :    if (append_)
 621  i :      mode = "ab";
 622    :  
 623    :    // Create a new file, which the caller is responsible for closing.
 624  i :    *output_file = file_util::OpenFile(output_file_path_, mode);
 625  i :    if (*output_file == NULL)
 626  i :      return false;
 627    :  
 628  i :    *must_close = true;
 629  i :    return true;
 630  E :  }
 631    :  
 632    :  // Print the usage/help text, plus an optional @p message.
 633    :  bool LoggerApp::Usage(const CommandLine* command_line,
 634  E :                        const base::StringPiece& message) const {
 635  E :    if (!message.empty()) {
 636  E :      ::fwrite(message.data(), 1, message.length(), err());
 637  E :      ::fprintf(err(), "\n\n");
 638    :    }
 639    :  
 640    :    ::fprintf(err(),
 641    :              kUsageFormatStr,
 642  E :              command_line->GetProgram().BaseName().value().c_str());
 643    :  
 644  E :    return false;
 645  E :  }
 646    :  
 647    :  }  // namespace logger
 648    :  }  // namespace trace

Coverage information generated Thu Mar 14 11:53:36 2013.