Coverage for /Syzygy/kasko/reporter.cc

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

Coverage information generated Thu Mar 26 16:15:41 2015.