Coverage for /Syzygy/trace/agent_logger/agent_logger_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
66.0%1752650.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::agent_logger::LoggerApp class which implements
  16    :  // the LoggerApp RPC interface.
  17    :  
  18    :  #include "syzygy/trace/agent_logger/agent_logger_app.h"
  19    :  
  20    :  #include "base/bind.h"
  21    :  #include "base/environment.h"
  22    :  #include "base/path_service.h"
  23    :  #include "base/rand_util.h"
  24    :  #include "base/files/file_util.h"
  25    :  #include "base/process/kill.h"
  26    :  #include "base/process/launch.h"
  27    :  #include "base/process/process.h"
  28    :  #include "base/strings/string_util.h"
  29    :  #include "base/strings/stringprintf.h"
  30    :  #include "base/strings/utf_string_conversions.h"
  31    :  #include "base/win/scoped_handle.h"
  32    :  #include "syzygy/common/rpc/helpers.h"
  33    :  #include "syzygy/trace/agent_logger/agent_logger.h"
  34    :  #include "syzygy/trace/agent_logger/agent_logger_rpc_impl.h"
  35    :  #include "syzygy/trace/common/service_util.h"
  36    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  37    :  #include "syzygy/trace/rpc/logger_rpc.h"
  38    :  
  39    :  namespace trace {
  40    :  namespace agent_logger {
  41    :  
  42    :  namespace {
  43    :  
  44    :  using ::common::rpc::GetInstanceString;
  45    :  
  46    :  // The usage string for the logger app.
  47    :  const char kUsageFormatStr[] =
  48    :      "Usage: %ls [options] ACTION [-- command]\n"
  49    :      "  Supported actions:\n"
  50    :      "    start  Run a new logger instance in the foreground (blocking). You\n"
  51    :      "           may optionally specify an external command which will be\n"
  52    :      "           run behind the logger. The logger will return once the\n"
  53    :      "           external command has terminated or the logger is externally\n"
  54    :      "           stopped. If no command is specified, Ctrl-C or an invocation\n"
  55    :      "           of the stop action will stop the logger.\n"
  56    :      "    spawn  Run a new logger instance in the background (non-blocking).\n"
  57    :      "    stop   Stop a separately running logger instance.\n"
  58    :      "\n"
  59    :      "  Options:\n"
  60    :      "    --append             Append to (instead of truncating) the output\n"
  61    :      "                         file. This option is valid for the start and\n"
  62    :      "                         spawn actions.\n"
  63    :      "    --instance-id=ID     A unique (up to 16 character) ID to identify\n"
  64    :      "                         the logger instance.\n"
  65    :      "    --minidump-dir=PATH  The directory path in which minidumps, if any,\n"
  66    :      "                         should be generated.\n"
  67    :      "    --output-file=PATH   The file path to which logs should be written.\n"
  68    :      "                         This may be stdout (the default), stderr or a\n"
  69    :      "                         file path. This option is valid for the start\n"
  70    :      "                         and spawn actions.\n"
  71    :      "    --unique-instance-id Automatically generate a unique ID for the\n"
  72    :      "                         logger instance.\n";
  73    :  
  74    :  // Names for kernel objects used to synchronize with a logger singleton.
  75    :  const wchar_t kLoggerMutexRoot[] = L"syzygy-logger-mutex";
  76    :  const wchar_t kLoggerStartEventRoot[] = L"syzygy-logger-started";
  77    :  const wchar_t kLoggerStopEventRoot[] = L"syzygy-logger-stopped";
  78    :  
  79    :  // A static location to which the current instance id can be saved. We
  80    :  // persist it here so that OnConsoleCtrl can have access to the instance
  81    :  // id when it is invoked on the signal handler thread.
  82    :  wchar_t saved_instance_id[LoggerApp::kMaxInstanceIdLength + 1] = { 0 };
  83    :  
  84    :  // Send a stop request via RPC to the logger instance given by @p instance_id.
  85    :  // If the process is remote and |handle| is specified, returns a handle to it.
  86    :  bool SendStopRequest(const base::StringPiece16& instance_id,
  87  E :                       base::win::ScopedHandle* handle) {
  88  E :    std::wstring protocol(kLoggerRpcProtocol);
  89  E :    std::wstring endpoint(GetInstanceString(kLoggerRpcEndpointRoot, instance_id));
  90    :  
  91  E :    LOG(INFO) << "Stopping logging service instance at '"
  92    :              << endpoint << "' via " << protocol << '.';
  93    :  
  94  E :    handle_t binding = NULL;
  95  E :    if (!::common::rpc::CreateRpcBinding(protocol, endpoint, &binding)) {
  96  i :      LOG(ERROR) << "Failed to connect to logging service.";
  97  i :      return false;
  98    :    }
  99    :  
 100    :    // Get the process ID, and a handle to that process.
 101  E :    auto result = ::common::rpc::InvokeRpc(LoggerClient_GetProcessId, binding);
 102  E :    if (!result.succeeded() || result.result == 0) {
 103  i :      LOG(ERROR) << "Failed to get logging service process id.";
 104  i :      return false;
 105    :    }
 106  E :    if (handle != nullptr && result.result != ::GetCurrentProcessId())
 107  E :      handle->Set(::OpenProcess(SYNCHRONIZE, FALSE, result.result));
 108    :  
 109  E :    if (!::common::rpc::InvokeRpc(LoggerClient_Stop, binding).succeeded()) {
 110  i :      LOG(ERROR) << "Failed to stop logging service.";
 111  i :      return false;
 112    :    }
 113    :  
 114  E :    LOG(INFO) << "Logging service shutdown has been requested.";
 115    :  
 116  E :    return true;
 117  E :  }
 118    :  
 119    :  // Handler function to be called on exit signals (Ctrl-C, TERM, etc...).
 120  i :  BOOL WINAPI OnConsoleCtrl(DWORD ctrl_type) {
 121  i :    if (ctrl_type != CTRL_LOGOFF_EVENT) {
 122  i :      SendStopRequest(saved_instance_id, nullptr);
 123  i :      return TRUE;
 124    :    }
 125  i :    return FALSE;
 126  i :  }
 127    :  
 128    :  // A helper function to signal an event. This is passable as a callback to
 129    :  // a Logger instance to be called on logger start/stop.
 130  E :  bool SignalEvent(HANDLE event_handle, trace::common::Service* /* logger */) {
 131  E :    DCHECK_NE(INVALID_HANDLE_VALUE, event_handle);
 132  E :    if (!::SetEvent(event_handle))
 133  i :      return false;
 134  E :    return true;
 135  E :  }
 136    :  
 137    :  // A helper function which sets the Syzygy RPC instance id environment variable
 138    :  // then runs a given command line to completion.
 139    :  bool RunApp(const base::CommandLine& command_line,
 140    :              const std::wstring& instance_id,
 141    :              HANDLE interruption_event,
 142  E :              int* exit_code) {
 143  E :    DCHECK(exit_code != NULL);
 144  E :    std::unique_ptr<base::Environment> env(base::Environment::Create());
 145  E :    CHECK(env != NULL);
 146    :  
 147    :    // Put |instance_id| as the first value.
 148  E :    std::string env_var;
 149  E :    env->GetVar(kSyzygyRpcInstanceIdEnvVar, &env_var);
 150  E :    env->SetVar(kSyzygyRpcInstanceIdEnvVar,
 151    :                base::WideToUTF8(instance_id) + ";" + env_var);
 152    :  
 153  E :    LOG(INFO) << "Launching '" << command_line.GetProgram().value() << "'.";
 154  E :    VLOG(1) << "Command Line: " << command_line.GetCommandLineString();
 155    :  
 156  E :    *exit_code = 0;
 157    :  
 158    :    // Launch a new process in the background.
 159  E :    base::LaunchOptions options;
 160  E :    options.start_hidden = false;
 161  E :    base::Process process = base::LaunchProcess(command_line, options);
 162  E :    if (!process.IsValid()) {
 163  i :      LOG(ERROR)
 164    :          << "Failed to launch '" << command_line.GetProgram().value() << "'.";
 165  i :      return false;
 166    :    }
 167    :  
 168  E :    HANDLE objects[] = {process.Handle(), interruption_event};
 169  E :    DWORD num_objects = arraysize(objects);
 170  E :    switch (::WaitForMultipleObjects(num_objects, objects, FALSE, INFINITE)) {
 171    :      case WAIT_OBJECT_0 + 0: {
 172    :        // The client process has finished.
 173    :        DWORD temp_exit_code;
 174  E :        ::GetExitCodeProcess(process.Handle(), &temp_exit_code);
 175  E :        *exit_code = temp_exit_code;
 176  E :        process.Close();
 177  E :        return true;
 178    :      }
 179    :  
 180    :      case WAIT_OBJECT_0 + 1: {
 181    :        // The logger has been shutdown. Kill the client process.
 182  i :        process.Terminate(1, true);
 183  i :        process.Close();
 184  i :        *exit_code = 1;
 185  i :        return true;
 186    :      }
 187    :    }
 188    :  
 189    :    // If we get here then an error has occurred (since the timeout is infinite).
 190  i :    DWORD error = ::GetLastError();
 191  i :    LOG(ERROR) << "Error waiting for shutdown event " << ::common::LogWe(error)
 192    :               << ".";
 193  i :    return false;
 194  E :  }
 195    :  
 196    :  }  // namespace
 197    :  
 198    :  // Keywords appearing on the command-line
 199    :  const wchar_t LoggerApp::kSpawn[] = L"spawn";
 200    :  const wchar_t LoggerApp::kStart[] = L"start";
 201    :  const wchar_t LoggerApp::kStatus[] = L"status";
 202    :  const wchar_t LoggerApp::kStop[] = L"stop";
 203    :  const char LoggerApp::kInstanceId[] = "instance-id";
 204    :  const char LoggerApp::kUniqueInstanceId[] = "unique-instance-id";
 205    :  const char LoggerApp::kOutputFile[] = "output-file";
 206    :  const char LoggerApp::kMiniDumpDir[] = "minidump-dir";
 207    :  const char LoggerApp::kAppend[] = "append";
 208    :  const wchar_t LoggerApp::kStdOut[] = L"stdout";
 209    :  const wchar_t LoggerApp::kStdErr[] = L"stderr";
 210    :  
 211    :  // A table mapping action keywords to their handler implementations.
 212    :  const LoggerApp::ActionTableEntry LoggerApp::kActionTable[] = {
 213  E :      { LoggerApp::kSpawn, &LoggerApp::Spawn },
 214  E :      { LoggerApp::kStart, &LoggerApp::Start },
 215  E :      { LoggerApp::kStatus, &LoggerApp::Status },
 216  E :      { LoggerApp::kStop, &LoggerApp::Stop },
 217  E :  };
 218    :  
 219    :  LoggerApp::LoggerApp()
 220  E :      : ::application::AppImplBase("AgentLogger"),
 221  E :        logger_command_line_(base::CommandLine::NO_PROGRAM),
 222  E :        action_handler_(NULL),
 223  E :        append_(false) {
 224  E :  }
 225    :  
 226  E :  LoggerApp::~LoggerApp() {
 227  E :  }
 228    :  
 229  E :  bool LoggerApp::ParseCommandLine(const base::CommandLine* command_line) {
 230  E :    DCHECK(command_line != NULL);
 231    :  
 232  E :    if (!trace::common::SplitCommandLine(
 233    :            command_line,
 234    :            &logger_command_line_,
 235    :            &app_command_line_)) {
 236  i :      LOG(ERROR) << "Failed to split command_line into logger and app parts.";
 237  i :      return false;
 238    :    }
 239    :  
 240    :    // Save the command-line in case we need to spawn.
 241  E :    command_line = &logger_command_line_;
 242    :  
 243  E :    if (command_line->HasSwitch(kInstanceId) &&
 244    :        command_line->HasSwitch(kUniqueInstanceId)) {
 245  i :      return Usage(command_line,
 246    :                   base::StringPrintf("--%s and --%s are mutually exclusive.",
 247    :                                      kInstanceId,
 248    :                                      kUniqueInstanceId));
 249    :    }
 250    :  
 251    :    // Parse the instance id.
 252  E :    instance_id_ = command_line->GetSwitchValueNative(kInstanceId);
 253  E :    if (instance_id_.length() > kMaxInstanceIdLength) {
 254  i :      return Usage(command_line,
 255    :                   base::StringPrintf("The instance id '%ls' is too long. "
 256    :                                      "The max length is %d characters.",
 257    :                                      instance_id_.c_str(),
 258    :                                      kMaxInstanceIdLength));
 259    :    }
 260    :  
 261    :    // Save the output file parameter.
 262  E :    output_file_path_ = command_line->GetSwitchValuePath(kOutputFile);
 263    :  
 264    :    // Save the minidump-dir parameter.
 265  E :    mini_dump_dir_ = command_line->GetSwitchValuePath(kMiniDumpDir);
 266  E :    if (mini_dump_dir_.empty()) {
 267  E :      CHECK(PathService::Get(base::DIR_CURRENT, &mini_dump_dir_));
 268  E :    } else {
 269  E :      mini_dump_dir_ = base::MakeAbsoluteFilePath(mini_dump_dir_);
 270  E :      if (mini_dump_dir_.empty())
 271  i :        return Usage(command_line, "The minidump-dir parameter is invalid.");
 272    :  
 273  E :      if (!base::DirectoryExists(mini_dump_dir_) &&
 274    :          !base::CreateDirectory(mini_dump_dir_)) {
 275  i :        LOG(ERROR) << "Failed to create minidump-dir "
 276    :                   << mini_dump_dir_.value();
 277    :      }
 278    :    }
 279    :  
 280    :    // Make sure there's exactly one action.
 281  E :    if (command_line->GetArgs().size() != 1) {
 282  E :      return Usage(command_line,
 283    :                   "Exactly 1 action is expected on the command line.");
 284    :    }
 285    :  
 286    :    // Check for the append flag.
 287  E :    append_ = command_line->HasSwitch(kAppend);
 288    :  
 289    :    // Parse the action.
 290  E :    action_ = command_line->GetArgs()[0];
 291  E :    const ActionTableEntry* entry = FindActionHandler(action_);
 292  E :    if (entry == NULL) {
 293  E :      return Usage(
 294    :          command_line,
 295    :          base::StringPrintf("Unrecognized action: %s.", action_.c_str()));
 296    :    }
 297    :  
 298  E :    if (command_line->HasSwitch(kUniqueInstanceId)) {
 299  E :      DWORD process_id = ::GetCurrentProcessId();
 300  E :      DWORD timestamp = ::GetTickCount();
 301  E :      base::SStringPrintf(&instance_id_, L"%08x%08x", process_id, timestamp);
 302  E :      DCHECK_EQ(kMaxInstanceIdLength, instance_id_.length());
 303    :    }
 304    :  
 305  E :    LOG(INFO) << "Using logger instance ID: '" << instance_id_ << "'.";
 306  E :    LOG(INFO) << "Writing minidumps to: " << mini_dump_dir_.value();
 307    :  
 308    :    // Setup the action handler.
 309  E :    DCHECK(entry->handler != NULL);
 310  E :    action_handler_ = entry->handler;
 311    :  
 312  E :    return true;
 313  E :  }
 314    :  
 315  E :  int LoggerApp::Run() {
 316  E :    DCHECK(action_handler_ != NULL);
 317  E :    if (!(this->*action_handler_)())
 318  i :      return 1;
 319  E :    return 0;
 320  E :  }
 321    :  
 322    :  // A helper function to find the handler method for a given action.
 323    :  const LoggerApp::ActionTableEntry* LoggerApp::FindActionHandler(
 324  E :      const base::StringPiece16& action) {
 325  E :    for (size_t i = 0; i < arraysize(kActionTable); ++i) {
 326  E :      if (::_wcsicmp(kActionTable[i].action, action.data()) == 0)
 327  E :        return &kActionTable[i];
 328  E :    }
 329  E :    return NULL;
 330  E :  }
 331    :  
 332  E :  bool LoggerApp::Start() {
 333  E :    std::wstring logger_name(
 334    :        GetInstanceString(kLoggerRpcEndpointRoot, instance_id_));
 335    :  
 336    :    // Acquire the logger mutex.
 337  E :    base::win::ScopedHandle mutex;
 338  E :    std::wstring mutex_name(GetInstanceString(kLoggerMutexRoot, instance_id_));
 339  E :    if (!trace::common::AcquireMutex(mutex_name, &mutex))
 340  i :      return false;
 341    :  
 342    :    // Setup the start event.
 343  E :    base::win::ScopedHandle start_event;
 344  E :    std::wstring start_event_name(
 345    :        GetInstanceString(kLoggerStartEventRoot, instance_id_));
 346  E :    if (!trace::common::InitEvent(start_event_name, &start_event)) {
 347  i :      LOG(ERROR) << "Unable to init start event for '" << logger_name << "'.";
 348  i :      return false;
 349    :    }
 350    :  
 351    :    // Setup the stop event.
 352  E :    base::win::ScopedHandle stop_event;
 353  E :    std::wstring stop_event_name(
 354    :        GetInstanceString(kLoggerStopEventRoot, instance_id_));
 355  E :    if (!trace::common::InitEvent(stop_event_name, &stop_event)) {
 356  i :      LOG(ERROR) << "Unable to init stop event for '" << logger_name << "'.";
 357  i :      return false;
 358    :    }
 359    :  
 360    :    // Setup an anonymous event to notify us if the logger has been
 361    :    // asynchronously asked to shutdown.
 362  E :    base::win::ScopedHandle interrupt_event;
 363  E :    if (!trace::common::InitEvent(L"", &interrupt_event)) {
 364  i :      LOG(ERROR) << "Unable to init interrupt event for '" << logger_name << "'.";
 365  i :      return false;
 366    :    }
 367    :  
 368    :    // Get the log file output_file.
 369  E :    FILE* output_file = NULL;
 370  E :    bool must_close_output_file = false;
 371  E :    base::ScopedFILE auto_close;
 372  E :    if (!OpenOutputFile(&output_file, &must_close_output_file)) {
 373  i :      LOG(ERROR) << "Unable to open '" << output_file_path_.value() << "'.";
 374  i :      return false;
 375    :    }
 376    :  
 377    :    // Setup auto_close as appropriate.
 378  E :    if (must_close_output_file)
 379  E :      auto_close.reset(output_file);
 380    :  
 381    :    // Initialize the logger instance.
 382  E :    AgentLogger logger;
 383  E :    logger.set_destination(output_file);
 384  E :    logger.set_minidump_dir(mini_dump_dir_);
 385  E :    logger.set_instance_id(instance_id_);
 386  E :    logger.set_started_callback(
 387    :        base::Bind(&SignalEvent, start_event.Get()));
 388  E :    logger.set_stopped_callback(
 389    :        base::Bind(&SignalEvent, stop_event.Get()));
 390  E :    logger.set_interrupted_callback(
 391    :        base::Bind(&SignalEvent, interrupt_event.Get()));
 392    :  
 393    :    // Save the instance_id for the Ctrl-C handler.
 394  E :    ::wcsncpy_s(saved_instance_id,
 395    :                arraysize(saved_instance_id),
 396    :                instance_id_.c_str(),
 397    :                _TRUNCATE);
 398    :  
 399    :    // Register the handler for Ctrl-C.
 400  E :    if (!SetConsoleCtrlHandler(&OnConsoleCtrl, TRUE)) {
 401  i :      DWORD error = ::GetLastError();
 402  i :      LOG(ERROR) << "Failed to register shutdown handler: "
 403    :                 << ::common::LogWe(error) << ".";
 404  i :      return false;
 405    :    }
 406    :  
 407    :    // Start the logger.
 408  E :    RpcLoggerInstanceManager instance_manager(&logger);
 409  E :    if (!logger.Start()) {
 410  i :      LOG(ERROR) << "Failed to start '" << logger_name << "'.";
 411  i :      return false;
 412    :    }
 413    :  
 414  E :    bool error = false;
 415    :  
 416    :    // Run the logger, either standalone or as the parent of some application.
 417  E :    trace::common::ScopedConsoleCtrlHandler ctrl_handler;
 418  E :    if (app_command_line_.get() != NULL) {
 419    :      // We have a command to run, so launch that command and when it finishes
 420    :      // stop the logger.
 421  E :      int exit_code = 0;
 422    :      if (!RunApp(*app_command_line_, instance_id_, interrupt_event.Get(),
 423  E :                  &exit_code) ||
 424    :          exit_code != 0) {
 425  i :        error = true;
 426    :      }
 427  E :      ignore_result(logger.Stop());
 428  E :    } else {
 429    :      // There is no command to wait for, so just register the control handler
 430    :      // (we stop the logger if this fails) and then let the logger run until
 431    :      // the control handler stops it or someone externally stops it using the
 432    :      // stop command.
 433  E :      if (!ctrl_handler.Init(&OnConsoleCtrl)) {
 434  i :        ignore_result(logger.Stop());
 435  i :        error = true;
 436    :      }
 437    :    }
 438    :  
 439    :    // Run the logger to completion.
 440  E :    if (!logger.Join()) {
 441  i :      LOG(ERROR) << "Failed running to completion '" << logger_name << "'.";
 442  i :      error = true;
 443    :    }
 444    :  
 445    :    // And we're done.
 446  E :    return !error;
 447  E :  }
 448    :  
 449  i :  bool LoggerApp::Status() {
 450    :    // TODO(rogerm): Implement me.
 451  i :    return false;
 452  i :  }
 453    :  
 454  i :  bool LoggerApp::Spawn() {
 455  i :    std::wstring logger_name(
 456    :        GetInstanceString(kLoggerRpcEndpointRoot, instance_id_));
 457    :  
 458  i :    LOG(INFO) << "Launching background logging service '" << logger_name << "'.";
 459    :  
 460    :    // Get the path to ourselves.
 461  i :    base::FilePath self_path;
 462  i :    PathService::Get(base::FILE_EXE, &self_path);
 463    :  
 464    :    // Build a command line for starting a new instance of the logger.
 465  i :    base::CommandLine new_command_line(self_path);
 466  i :    new_command_line.AppendArg("start");
 467    :  
 468    :    // Copy over any other switches.
 469    :    base::CommandLine::SwitchMap::const_iterator it =
 470  i :        logger_command_line_.GetSwitches().begin();
 471  i :    for (; it != logger_command_line_.GetSwitches().end(); ++it)
 472  i :      new_command_line.AppendSwitchNative(it->first, it->second);
 473    :  
 474    :    // Launch a new process in the background.
 475  i :    base::LaunchOptions options;
 476  i :    options.start_hidden = true;
 477  i :    base::Process process = base::LaunchProcess(new_command_line, options);
 478  i :    if (!process.IsValid()) {
 479  i :      LOG(ERROR) << "Failed to launch process.";
 480  i :      return false;
 481    :    }
 482    :  
 483    :    // Setup the start event.
 484  i :    base::win::ScopedHandle start_event;
 485  i :    std::wstring start_event_name(
 486    :        GetInstanceString(kLoggerStartEventRoot, instance_id_));
 487  i :    if (!trace::common::InitEvent(start_event_name, &start_event)) {
 488  i :      LOG(ERROR) << "Unable to init start event for '" << logger_name << "'.";
 489  i :      return false;
 490    :    }
 491    :  
 492    :    // We wait on both the start event and the process, as if the process fails
 493    :    // for any reason, it'll exit and its handle will become signaled.
 494  i :    HANDLE handles[] = {start_event.Get(), process.Handle()};
 495    :    if (::WaitForMultipleObjects(arraysize(handles),
 496    :                                 handles,
 497    :                                 FALSE,
 498  i :                                 INFINITE) != WAIT_OBJECT_0) {
 499  i :      LOG(ERROR) << "The logger '" << logger_name << "' exited in error.";
 500  i :      return false;
 501    :    }
 502    :  
 503  i :    LOG(INFO) << "Background logger '" << logger_name << "' is running.";
 504    :  
 505  i :    return true;
 506  i :  }
 507    :  
 508  E :  bool LoggerApp::Stop() {
 509  E :    std::wstring logger_name(
 510    :        GetInstanceString(kLoggerRpcEndpointRoot, instance_id_));
 511    :  
 512    :    // Setup the stop event.
 513  E :    base::win::ScopedHandle stop_event;
 514  E :    std::wstring stop_event_name(
 515    :        GetInstanceString(kLoggerStopEventRoot, instance_id_));
 516  E :    if (!trace::common::InitEvent(stop_event_name, &stop_event)) {
 517  i :      LOG(ERROR) << "Unable to init stop event for '" << logger_name << "'.";
 518  i :      return false;
 519    :    }
 520    :  
 521    :    // Send the stop request.
 522  E :    base::win::ScopedHandle process;
 523  E :    if (!SendStopRequest(instance_id_, &process))
 524  i :      return false;
 525    :  
 526    :    // Wait for the RPC event that indicates an orderly shutdown.
 527  E :    if (::WaitForSingleObject(stop_event.Get(), INFINITE) != WAIT_OBJECT_0) {
 528  i :      LOG(ERROR) << "Timed out waiting for '" << logger_name << "' to stop.";
 529  i :      return false;
 530    :    }
 531    :  
 532    :    // Wait on the process itself terminating.
 533  E :    if (process.IsValid()) {
 534  E :      if (::WaitForSingleObject(process.Get(), INFINITE) != WAIT_OBJECT_0) {
 535  i :        LOG(ERROR) << "Timed out waiting for logger process to terminate.";
 536  i :        return false;
 537    :      }
 538    :    }
 539    :  
 540  E :    LOG(INFO) << "The logger instance has stopped.";
 541    :  
 542  E :    return true;
 543  E :  }
 544    :  
 545    :  // Helper to resolve @p path to an open file. This will set @p must_close
 546    :  // to true if @path denotes a newly opened file, and false if it denotes
 547    :  // stderr or stdout.
 548  E :  bool LoggerApp::OpenOutputFile(FILE** output_file, bool* must_close) {
 549  E :    DCHECK(output_file != NULL);
 550  E :    DCHECK(must_close != NULL);
 551    :  
 552  E :    *output_file = NULL;
 553  E :    *must_close = false;
 554    :  
 555    :    // Check for stdout.
 556  E :    if (output_file_path_.empty() ||
 557    :        ::_wcsnicmp(output_file_path_.value().c_str(),
 558    :                    kStdOut,
 559    :                    arraysize(kStdOut)) == 0) {
 560  E :      *output_file = stdout;
 561  E :      return true;
 562    :    }
 563    :  
 564    :    // Check for stderr.
 565    :    if (::_wcsnicmp(output_file_path_.value().c_str(),
 566    :                    kStdErr,
 567  E :                    arraysize(kStdErr)) == 0) {
 568  i :      *output_file = stderr;
 569  i :      return true;
 570    :    }
 571    :  
 572    :    // Setup the write mode.
 573  E :    const char* mode = "wb";
 574  E :    if (append_)
 575  i :      mode = "ab";
 576    :  
 577    :    // Create a new file, which the caller is responsible for closing.
 578  E :    *output_file = base::OpenFile(output_file_path_, mode);
 579  E :    if (*output_file == NULL)
 580  i :      return false;
 581    :  
 582  E :    *must_close = true;
 583  E :    return true;
 584  E :  }
 585    :  
 586    :  // Print the usage/help text, plus an optional @p message.
 587    :  bool LoggerApp::Usage(const base::CommandLine* command_line,
 588  E :                        const base::StringPiece& message) const {
 589  E :    if (!message.empty()) {
 590  E :      ::fwrite(message.data(), 1, message.length(), err());
 591  E :      ::fprintf(err(), "\n\n");
 592    :    }
 593    :  
 594  E :    ::fprintf(err(),
 595    :              kUsageFormatStr,
 596    :              command_line->GetProgram().BaseName().value().c_str());
 597    :  
 598  E :    return false;
 599  E :  }
 600    :  
 601    :  }  // namespace agent_logger
 602    :  }  // namespace trace

Coverage information generated Fri Jul 29 11:00:21 2016.