Coverage for /Syzygy/kasko/reporter.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
78.3%65830.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 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    :  #include "syzygy/kasko/reporter.h"
  16    :  
  17    :  #include <stdint.h>
  18    :  
  19    :  #include <map>
  20    :  #include <string>
  21    :  
  22    :  #include "base/bind.h"
  23    :  #include "base/bind_helpers.h"
  24    :  #include "base/callback.h"
  25    :  #include "base/logging.h"
  26    :  #include "base/files/file_util.h"
  27    :  #include "base/process/process.h"
  28    :  #include "base/strings/utf_string_conversions.h"
  29    :  #include "base/threading/platform_thread.h"
  30    :  #include "base/time/time.h"
  31    :  #include "syzygy/kasko/http_agent_impl.h"
  32    :  #include "syzygy/kasko/minidump.h"
  33    :  #include "syzygy/kasko/minidump_request.h"
  34    :  #include "syzygy/kasko/service.h"
  35    :  #include "syzygy/kasko/upload.h"
  36    :  #include "syzygy/kasko/upload_thread.h"
  37    :  #include "syzygy/kasko/version.h"
  38    :  #include "syzygy/kasko/waitable_timer.h"
  39    :  #include "syzygy/kasko/waitable_timer_impl.h"
  40    :  
  41    :  namespace kasko {
  42    :  
  43    :  namespace {
  44    :  
  45    :  // The RPC protocol used for receiving dump requests.
  46    :  const base::char16* const kRpcProtocol = L"ncalrpc";
  47    :  
  48    :  // The subdirectory where minidumps are generated.
  49    :  const base::char16* const kTemporarySubdir = L"Temporary";
  50    :  
  51    :  // Uploads a crash report containing the minidump at |minidump_path| and
  52    :  // |crash_keys| to |upload_url|. Returns true if successful.
  53    :  bool UploadCrashReport(
  54    :      const Reporter::OnUploadCallback& on_upload_callback,
  55    :      const base::string16& upload_url,
  56    :      const base::FilePath& minidump_path,
  57  E :      const std::map<base::string16, base::string16>& crash_keys) {
  58  E :    std::string dump_contents;
  59  E :    if (!base::ReadFileToString(minidump_path, &dump_contents)) {
  60  i :      LOG(ERROR) << "Failed to read the minidump file at "
  61    :                 << minidump_path.value();
  62  i :      return false;
  63    :    }
  64    :  
  65    :    HttpAgentImpl http_agent(
  66  E :        L"Kasko", base::ASCIIToUTF16(KASKO_VERSION_STRING));
  67  E :    base::string16 remote_dump_id;
  68  E :    uint16_t response_code = 0;
  69  E :    std::map<base::string16, base::string16> augmented_crash_keys(crash_keys);
  70    :    augmented_crash_keys[Reporter::kKaskoUploadedByVersion] =
  71  E :        base::ASCIIToUTF16(KASKO_VERSION_STRING);
  72    :    if (!SendHttpUpload(&http_agent, upload_url, augmented_crash_keys,
  73    :                        dump_contents, Reporter::kMinidumpUploadFilePart,
  74  E :                        &remote_dump_id, &response_code)) {
  75  E :      LOG(ERROR) << "Failed to upload the minidump file to " << upload_url;
  76  E :      return false;
  77  E :    } else if (!on_upload_callback.is_null()) {
  78  E :      on_upload_callback.Run(remote_dump_id, minidump_path, crash_keys);
  79    :    }
  80    :  
  81  E :    return true;
  82  E :  }
  83    :  
  84    :  // Moves |minidump_path| and |crash_keys_path| to |permanent_failure_directory|.
  85    :  // The destination filenames have the filename from |minidump_path| and the
  86    :  // extensions Reporter::kPermanentFailureMinidumpExtension and
  87    :  // Reporter::kPermanentFailureCrashKeysExtension.
  88    :  void HandlePermanentFailure(const base::FilePath& permanent_failure_directory,
  89    :                              const base::FilePath& minidump_path,
  90  E :                              const base::FilePath& crash_keys_path) {
  91    :    base::FilePath minidump_target = permanent_failure_directory.Append(
  92    :        minidump_path.BaseName().ReplaceExtension(
  93  E :            Reporter::kPermanentFailureMinidumpExtension));
  94    :  
  95    :    // Note that we take the filename from the minidump file, in order to
  96    :    // guarantee that the two files have matching names.
  97    :    base::FilePath crash_keys_target = permanent_failure_directory.Append(
  98    :        minidump_path.BaseName().ReplaceExtension(
  99  E :            Reporter::kPermanentFailureCrashKeysExtension));
 100    :  
 101  E :    if (!base::CreateDirectory(permanent_failure_directory)) {
 102  i :      LOG(ERROR) << "Failed to create directory at "
 103    :                 << permanent_failure_directory.value();
 104  E :    } else if (!base::Move(minidump_path, minidump_target)) {
 105  i :      LOG(ERROR) << "Failed to move " << minidump_path.value() << " to "
 106    :                 << minidump_target.value();
 107  E :    } else if (!base::Move(crash_keys_path, crash_keys_target)) {
 108  i :      LOG(ERROR) << "Failed to move " << crash_keys_path.value() << " to "
 109    :                 << crash_keys_target.value();
 110    :    }
 111  E :  }
 112    :  
 113    :  void GenerateReport(const base::FilePath& temporary_directory,
 114    :                      ReportRepository* report_repository,
 115    :                      base::ProcessHandle client_process,
 116    :                      base::PlatformThreadId thread_id,
 117  E :                      const MinidumpRequest& request) {
 118  E :    if (!base::CreateDirectory(temporary_directory)) {
 119  i :      LOG(ERROR) << "Failed to create dump destination directory: "
 120    :                 << temporary_directory.value();
 121  i :      return;
 122    :    }
 123    :  
 124  E :    base::FilePath dump_file;
 125  E :    if (!base::CreateTemporaryFileInDir(temporary_directory, &dump_file)) {
 126  i :      LOG(ERROR) << "Failed to create a temporary dump file.";
 127  i :      return;
 128    :    }
 129    :  
 130  E :    if (!GenerateMinidump(dump_file, client_process, thread_id, request)) {
 131  i :      LOG(ERROR) << "Minidump generation failed.";
 132  i :      base::DeleteFile(dump_file, false);
 133  i :      return;
 134    :    }
 135    :  
 136  E :    std::map<base::string16, base::string16> crash_keys;
 137  E :    for (auto& crash_key : request.crash_keys) {
 138  E :      crash_keys[crash_key.first] = crash_key.second;
 139  E :    }
 140    :  
 141    :    crash_keys[Reporter::kKaskoGeneratedByVersion] =
 142  E :        base::ASCIIToUTF16(KASKO_VERSION_STRING);
 143    :  
 144  E :    report_repository->StoreReport(dump_file, crash_keys);
 145  E :  }
 146    :  
 147    :  // Implements kasko::Service to capture minidumps and store them in a
 148    :  // ReportRepository.
 149    :  class ServiceImpl : public Service {
 150    :   public:
 151    :    ServiceImpl(const base::FilePath& temporary_directory,
 152    :                ReportRepository* report_repository,
 153    :                UploadThread* upload_thread)
 154    :        : temporary_directory_(temporary_directory),
 155    :          report_repository_(report_repository),
 156  E :          upload_thread_(upload_thread) {}
 157    :  
 158  E :    ~ServiceImpl() override {}
 159    :  
 160    :    // Service implementation.
 161    :    void SendDiagnosticReport(base::ProcessId client_process_id,
 162    :                              base::PlatformThreadId thread_id,
 163  E :                              const MinidumpRequest& request) override {
 164    :      base::win::ScopedHandle client_process(
 165    :          ::OpenProcess(GetRequiredAccessForMinidumpType(request.type), FALSE,
 166  E :                        client_process_id));
 167  E :      if (client_process.IsValid()) {
 168    :        GenerateReport(temporary_directory_, report_repository_,
 169  E :                       client_process.Get(), thread_id, request);
 170    :      }
 171  E :      upload_thread_->UploadOneNowAsync();
 172  E :    }
 173    :  
 174    :   private:
 175    :    base::FilePath temporary_directory_;
 176    :    ReportRepository* report_repository_;
 177    :    UploadThread* upload_thread_;
 178    :  
 179    :    DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
 180    :  };
 181    :  
 182    :  }  // namespace
 183    :  
 184    :  const base::char16* const Reporter::kPermanentFailureCrashKeysExtension =
 185    :      L".kys";
 186    :  const base::char16* const Reporter::kPermanentFailureMinidumpExtension =
 187    :      L".dmp";
 188    :  const base::char16* const Reporter::kMinidumpUploadFilePart =
 189    :      L"upload_file_minidump";
 190    :  const base::char16* const Reporter::kKaskoGeneratedByVersion =
 191    :      L"kasko-generated-by-version";
 192    :  const base::char16* const Reporter::kKaskoUploadedByVersion =
 193    :      L"kasko-uploaded-by-version";
 194    :  
 195    :  // static
 196    :  scoped_ptr<Reporter> Reporter::Create(
 197    :      const base::string16& endpoint_name,
 198    :      const base::string16& url,
 199    :      const base::FilePath& data_directory,
 200    :      const base::FilePath& permanent_failure_directory,
 201    :      const base::TimeDelta& upload_interval,
 202    :      const base::TimeDelta& retry_interval,
 203  E :      const OnUploadCallback& on_upload_callback) {
 204    :    scoped_ptr<WaitableTimer> waitable_timer(
 205  E :        WaitableTimerImpl::Create(upload_interval));
 206  E :    if (!waitable_timer) {
 207  i :      LOG(ERROR) << "Failed to create a timer for the upload process.";
 208  i :      return scoped_ptr<Reporter>();
 209    :    }
 210    :  
 211    :    scoped_ptr<ReportRepository> report_repository(new ReportRepository(
 212    :        data_directory, retry_interval, base::Bind(&base::Time::Now),
 213    :        base::Bind(&UploadCrashReport, on_upload_callback, url),
 214  E :        base::Bind(&HandlePermanentFailure, permanent_failure_directory)));
 215    :  
 216    :    // It's safe to pass an Unretained reference to |report_repository| because
 217    :    // the Reporter instance will shut down |upload_thread| before destroying
 218    :    // |report_repository|.
 219    :    scoped_ptr<UploadThread> upload_thread = UploadThread::Create(
 220    :        data_directory, waitable_timer.Pass(),
 221    :        base::Bind(base::IgnoreResult(&ReportRepository::UploadPendingReport),
 222  E :                   base::Unretained(report_repository.get())));
 223    :  
 224  E :    if (!upload_thread) {
 225  i :      LOG(ERROR) << "Failed to initialize background upload process.";
 226  i :      return scoped_ptr<Reporter>();
 227    :    }
 228    :  
 229    :    scoped_ptr<Reporter> instance(
 230    :        new Reporter(report_repository.Pass(), upload_thread.Pass(),
 231  E :                     endpoint_name, data_directory.Append(kTemporarySubdir)));
 232  E :    if (!instance->service_bridge_.Run()) {
 233  i :      LOG(ERROR) << "Failed to start the Kasko RPC service using protocol "
 234    :                 << kRpcProtocol << " and endpoint name " << endpoint_name << ".";
 235  i :      return scoped_ptr<Reporter>();
 236    :    }
 237    :  
 238  E :    instance->upload_thread_->Start();
 239    :  
 240  E :    return instance.Pass();
 241  E :  }
 242    :  
 243  E :  Reporter::~Reporter() {}
 244    :  
 245    :  void Reporter::SendReportForProcess(base::ProcessHandle process_handle,
 246    :                                      base::PlatformThreadId thread_id,
 247  E :                                      MinidumpRequest request) {
 248    :    GenerateReport(temporary_minidump_directory_, report_repository_.get(),
 249  E :                   process_handle, thread_id, request);
 250  E :    upload_thread_->UploadOneNowAsync();
 251  E :  }
 252    :  
 253    :  // static
 254  E :  void Reporter::Shutdown(scoped_ptr<Reporter> instance) {
 255  E :    instance->upload_thread_->Stop();  // Non-blocking.
 256  E :    instance->service_bridge_.Stop();  // Blocking.
 257  E :    instance->upload_thread_->Join();  // Blocking.
 258  E :  }
 259    :  
 260    :  Reporter::Reporter(scoped_ptr<ReportRepository> report_repository,
 261    :                     scoped_ptr<UploadThread> upload_thread,
 262    :                     const base::string16& endpoint_name,
 263    :                     const base::FilePath& temporary_minidump_directory)
 264    :      : report_repository_(report_repository.Pass()),
 265    :        upload_thread_(upload_thread.Pass()),
 266    :        temporary_minidump_directory_(temporary_minidump_directory),
 267    :        service_bridge_(
 268    :            kRpcProtocol,
 269    :            endpoint_name,
 270    :            make_scoped_ptr(new ServiceImpl(temporary_minidump_directory_,
 271    :                                            report_repository_.get(),
 272  E :                                            upload_thread_.get()))) {
 273  E :  }
 274    :  
 275    :  }  // namespace kasko

Coverage information generated Thu Jan 14 17:40:38 2016.