Coverage for /Syzygy/kasko/reporter.cc

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

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