1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browser_watcher/postmortem_minidump_writer.h"
#include <windows.h> // NOLINT
#include <dbghelp.h>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/win/scoped_handle.h"
#include "components/browser_watcher/stability_report.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/crashpad/crashpad/snapshot/minidump/process_snapshot_minidump.h"
#include "third_party/crashpad/crashpad/util/file/file_reader.h"
#include "third_party/crashpad/crashpad/util/misc/uuid.h"
namespace browser_watcher {
using crashpad::UUID;
const char kProductName[] = "some-product";
const char kExpectedProductName[] = "some-product_Postmortem";
const char kVersion[] = "51.0.2704.106";
const char kChannel[] = "some-channel";
const char kPlatform[] = "some-platform";
class WritePostmortemDumpTest : public testing::Test {
public:
void SetUp() override {
testing::Test::SetUp();
expected_client_id_.InitializeWithNew();
expected_report_id_.InitializeWithNew();
// Create a stability report.
// TODO(manzagop): flesh out the report once proto is more detailed.
ProcessState* process_state = expected_report_.add_process_states();
CodeModule* module = process_state->add_modules();
module->set_base_address(1024);
module->set_code_file("some_code_file.dll");
// Write the minidump.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
minidump_path_ = temp_dir_.GetPath().AppendASCII("minidump.dmp");
}
bool WriteDump() {
base::win::ScopedHandle file_handle(::CreateFile(
minidump_path_.value().c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, nullptr));
if (!file_handle.IsValid())
return false;
MinidumpInfo minidump_info;
minidump_info.client_id = expected_client_id_;
minidump_info.report_id = expected_report_id_;
minidump_info.product_name = kProductName;
minidump_info.version_number = kVersion;
minidump_info.channel_name = kChannel;
minidump_info.platform = kPlatform;
return WritePostmortemDump(file_handle.Get(), expected_report_,
minidump_info);
}
const base::FilePath& minidump_path() { return minidump_path_; }
const UUID& expected_client_id() { return expected_client_id_; }
const UUID& expected_report_id() { return expected_report_id_; }
const StabilityReport& expected_report() { return expected_report_; }
private:
base::ScopedTempDir temp_dir_;
base::FilePath minidump_path_;
UUID expected_client_id_;
UUID expected_report_id_;
StabilityReport expected_report_;
};
TEST_F(WritePostmortemDumpTest, ValidateStabilityReportTest) {
ASSERT_TRUE(WriteDump());
// Read back the minidump to extract the proto.
// TODO(manzagop): rely on crashpad for reading the proto once crashpad
// supports it (https://crashpad.chromium.org/bug/10).
base::ScopedFILE minidump_file;
minidump_file.reset(base::OpenFile(minidump_path(), "rb"));
ASSERT_TRUE(minidump_file.get());
MINIDUMP_HEADER header = {};
ASSERT_EQ(1U, fread(&header, sizeof(header), 1, minidump_file.get()));
ASSERT_EQ(static_cast<ULONG32>(MINIDUMP_SIGNATURE), header.Signature);
ASSERT_EQ(2U, header.NumberOfStreams);
RVA directory_rva = header.StreamDirectoryRva;
MINIDUMP_DIRECTORY directory = {};
ASSERT_EQ(0, fseek(minidump_file.get(), directory_rva, SEEK_SET));
ASSERT_EQ(1U, fread(&directory, sizeof(directory), 1, minidump_file.get()));
ASSERT_EQ(0x4B6B0002U, directory.StreamType);
RVA report_rva = directory.Location.Rva;
ULONG32 report_size_bytes = directory.Location.DataSize;
std::string recovered_serialized_report;
recovered_serialized_report.resize(report_size_bytes);
ASSERT_EQ(0, fseek(minidump_file.get(), report_rva, SEEK_SET));
ASSERT_EQ(report_size_bytes, fread(&recovered_serialized_report.at(0), 1,
report_size_bytes, minidump_file.get()));
// Validate the recovered report.
std::string expected_serialized_report;
expected_report().SerializeToString(&expected_serialized_report);
ASSERT_EQ(expected_serialized_report, recovered_serialized_report);
StabilityReport recovered_report;
ASSERT_TRUE(recovered_report.ParseFromString(recovered_serialized_report));
}
TEST_F(WritePostmortemDumpTest, CrashpadCanReadTest) {
ASSERT_TRUE(WriteDump());
// Validate crashpad can read the produced minidump.
crashpad::FileReader minidump_file_reader;
ASSERT_TRUE(minidump_file_reader.Open(minidump_path()));
crashpad::ProcessSnapshotMinidump minidump_process_snapshot;
ASSERT_TRUE(minidump_process_snapshot.Initialize(&minidump_file_reader));
// Validate the crashpadinfo.
UUID client_id;
minidump_process_snapshot.ClientID(&client_id);
ASSERT_EQ(expected_client_id(), client_id);
UUID report_id;
minidump_process_snapshot.ReportID(&report_id);
ASSERT_EQ(expected_report_id(), report_id);
std::map<std::string, std::string> parameters =
minidump_process_snapshot.AnnotationsSimpleMap();
auto it = parameters.find("prod");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kExpectedProductName, it->second);
it = parameters.find("ver");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kVersion, it->second);
it = parameters.find("channel");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kChannel, it->second);
it = parameters.find("plat");
ASSERT_NE(parameters.end(), it);
ASSERT_EQ(kPlatform, it->second);
}
} // namespace browser_watcher
|