mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-31 01:43:03 +08:00
445c0eae7c
ProcessSnapshotMinidump. ModuleSnapshotMinidump is currently only capable of reading module annotations, in both vector and simple-dictionary forms. It does not read any other module information from minidump files. These annotations are all that are necessary to be able to upload Crashpad-produced minidumps to Breakpad crash processor servers, because Breakpad accepts them as HTTP POST parameters, while Crashpad places them in the minidump file itself. TEST=snapshot_test ProcessSnapshotMinidump.Modules BUG=crashpad:10 R=rsesek@chromium.org Review URL: https://codereview.chromium.org/972383002
286 lines
11 KiB
C++
286 lines
11 KiB
C++
// Copyright 2015 The Crashpad Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "snapshot/minidump/process_snapshot_minidump.h"
|
|
|
|
#include <string.h>
|
|
#include <windows.h>
|
|
#include <dbghelp.h>
|
|
|
|
#include "base/memory/scoped_ptr.h"
|
|
#include "gtest/gtest.h"
|
|
#include "snapshot/module_snapshot.h"
|
|
#include "util/file/string_file.h"
|
|
|
|
namespace crashpad {
|
|
namespace test {
|
|
namespace {
|
|
|
|
TEST(ProcessSnapshotMinidump, EmptyFile) {
|
|
StringFile string_file;
|
|
ProcessSnapshotMinidump process_snapshot;
|
|
|
|
EXPECT_FALSE(process_snapshot.Initialize(&string_file));
|
|
}
|
|
|
|
TEST(ProcessSnapshotMinidump, InvalidSignatureAndVersion) {
|
|
StringFile string_file;
|
|
|
|
MINIDUMP_HEADER header = {};
|
|
|
|
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
|
|
|
|
ProcessSnapshotMinidump process_snapshot;
|
|
EXPECT_FALSE(process_snapshot.Initialize(&string_file));
|
|
}
|
|
|
|
TEST(ProcessSnapshotMinidump, Empty) {
|
|
StringFile string_file;
|
|
|
|
MINIDUMP_HEADER header = {};
|
|
header.Signature = MINIDUMP_SIGNATURE;
|
|
header.Version = MINIDUMP_VERSION;
|
|
|
|
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
|
|
|
|
ProcessSnapshotMinidump process_snapshot;
|
|
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
|
|
}
|
|
|
|
// Writes |string| to |writer| as a MinidumpUTF8String, and returns the file
|
|
// offst of the beginning of the string.
|
|
RVA WriteString(FileWriterInterface* writer, const std::string& string) {
|
|
RVA rva = static_cast<RVA>(writer->SeekGet());
|
|
|
|
uint32_t string_size = string.size();
|
|
EXPECT_TRUE(writer->Write(&string_size, sizeof(string_size)));
|
|
|
|
// Include the trailing NUL character.
|
|
EXPECT_TRUE(writer->Write(string.c_str(), string.size() + 1));
|
|
|
|
return rva;
|
|
}
|
|
|
|
// Writes |dictionary| to |writer| as a MinidumpSimpleStringDictionary, and
|
|
// populates |location| with a location descriptor identifying what was written.
|
|
void WriteMinidumpSimpleStringDictionary(
|
|
MINIDUMP_LOCATION_DESCRIPTOR* location,
|
|
FileWriterInterface* writer,
|
|
const std::map<std::string, std::string>& dictionary) {
|
|
std::vector<MinidumpSimpleStringDictionaryEntry> entries;
|
|
for (const auto& it : dictionary) {
|
|
MinidumpSimpleStringDictionaryEntry entry;
|
|
entry.key = WriteString(writer, it.first);
|
|
entry.value = WriteString(writer, it.second);
|
|
entries.push_back(entry);
|
|
}
|
|
|
|
location->Rva = static_cast<RVA>(writer->SeekGet());
|
|
|
|
const uint32_t simple_string_dictionary_entries = entries.size();
|
|
EXPECT_TRUE(writer->Write(&simple_string_dictionary_entries,
|
|
sizeof(simple_string_dictionary_entries)));
|
|
for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {
|
|
EXPECT_TRUE(writer->Write(&entry, sizeof(entry)));
|
|
}
|
|
|
|
location->DataSize =
|
|
sizeof(simple_string_dictionary_entries) +
|
|
entries.size() * sizeof(MinidumpSimpleStringDictionaryEntry);
|
|
}
|
|
|
|
// Writes |strings| to |writer| as a MinidumpRVAList referencing
|
|
// MinidumpUTF8String objects, and populates |location| with a location
|
|
// descriptor identifying what was written.
|
|
void WriteMinidumpStringList(MINIDUMP_LOCATION_DESCRIPTOR* location,
|
|
FileWriterInterface* writer,
|
|
const std::vector<std::string>& strings) {
|
|
std::vector<RVA> rvas;
|
|
for (const std::string& string : strings) {
|
|
rvas.push_back(WriteString(writer, string));
|
|
}
|
|
|
|
location->Rva = static_cast<RVA>(writer->SeekGet());
|
|
|
|
const uint32_t string_list_entries = rvas.size();
|
|
EXPECT_TRUE(writer->Write(&string_list_entries, sizeof(string_list_entries)));
|
|
for (RVA rva : rvas) {
|
|
EXPECT_TRUE(writer->Write(&rva, sizeof(rva)));
|
|
}
|
|
|
|
location->DataSize = sizeof(string_list_entries) + rvas.size() * sizeof(RVA);
|
|
}
|
|
|
|
TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) {
|
|
StringFile string_file;
|
|
|
|
MINIDUMP_HEADER header = {};
|
|
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
|
|
|
|
MinidumpCrashpadInfo crashpad_info = {};
|
|
crashpad_info.version = MinidumpCrashpadInfo::kVersion;
|
|
|
|
std::map<std::string, std::string> dictionary;
|
|
dictionary["the first key"] = "THE FIRST VALUE EVER!";
|
|
dictionary["2key"] = "a lowly second value";
|
|
WriteMinidumpSimpleStringDictionary(
|
|
&crashpad_info.simple_annotations, &string_file, dictionary);
|
|
|
|
MINIDUMP_DIRECTORY crashpad_info_directory = {};
|
|
crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
|
|
crashpad_info_directory.Location.Rva =
|
|
static_cast<RVA>(string_file.SeekGet());
|
|
EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));
|
|
crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);
|
|
|
|
header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
|
|
EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
|
|
sizeof(crashpad_info_directory)));
|
|
|
|
header.Signature = MINIDUMP_SIGNATURE;
|
|
header.Version = MINIDUMP_VERSION;
|
|
header.NumberOfStreams = 1;
|
|
EXPECT_TRUE(string_file.SeekSet(0));
|
|
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
|
|
|
|
ProcessSnapshotMinidump process_snapshot;
|
|
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
|
|
|
|
const auto annotations_simple_map = process_snapshot.AnnotationsSimpleMap();
|
|
EXPECT_EQ(dictionary, annotations_simple_map);
|
|
}
|
|
|
|
TEST(ProcessSnapshotMinidump, Modules) {
|
|
StringFile string_file;
|
|
|
|
MINIDUMP_HEADER header = {};
|
|
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
|
|
|
|
MINIDUMP_MODULE minidump_module = {};
|
|
uint32_t minidump_module_count = 3;
|
|
|
|
MINIDUMP_DIRECTORY minidump_module_list_directory = {};
|
|
minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList;
|
|
minidump_module_list_directory.Location.DataSize =
|
|
sizeof(MINIDUMP_MODULE_LIST) +
|
|
minidump_module_count * sizeof(MINIDUMP_MODULE);
|
|
minidump_module_list_directory.Location.Rva =
|
|
static_cast<RVA>(string_file.SeekGet());
|
|
|
|
EXPECT_TRUE(
|
|
string_file.Write(&minidump_module_count, sizeof(minidump_module_count)));
|
|
for (uint32_t minidump_module_index = 0;
|
|
minidump_module_index < minidump_module_count;
|
|
++minidump_module_index) {
|
|
EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module)));
|
|
}
|
|
|
|
MinidumpModuleCrashpadInfo crashpad_module_0 = {};
|
|
crashpad_module_0.version = MinidumpModuleCrashpadInfo::kVersion;
|
|
std::map<std::string, std::string> dictionary_0;
|
|
dictionary_0["ptype"] = "browser";
|
|
dictionary_0["pid"] = "12345";
|
|
WriteMinidumpSimpleStringDictionary(
|
|
&crashpad_module_0.simple_annotations, &string_file, dictionary_0);
|
|
|
|
MinidumpModuleCrashpadInfoLink crashpad_module_0_link = {};
|
|
crashpad_module_0_link.minidump_module_list_index = 0;
|
|
crashpad_module_0_link.location.DataSize = sizeof(crashpad_module_0);
|
|
crashpad_module_0_link.location.Rva = static_cast<RVA>(string_file.SeekGet());
|
|
EXPECT_TRUE(string_file.Write(&crashpad_module_0, sizeof(crashpad_module_0)));
|
|
|
|
MinidumpModuleCrashpadInfo crashpad_module_2 = {};
|
|
crashpad_module_2.version = MinidumpModuleCrashpadInfo::kVersion;
|
|
std::map<std::string, std::string> dictionary_2;
|
|
dictionary_2["fakemodule"] = "yes";
|
|
WriteMinidumpSimpleStringDictionary(
|
|
&crashpad_module_2.simple_annotations, &string_file, dictionary_2);
|
|
|
|
std::vector<std::string> list_annotations_2;
|
|
list_annotations_2.push_back("first string");
|
|
list_annotations_2.push_back("last string");
|
|
WriteMinidumpStringList(
|
|
&crashpad_module_2.list_annotations, &string_file, list_annotations_2);
|
|
|
|
MinidumpModuleCrashpadInfoLink crashpad_module_2_link = {};
|
|
crashpad_module_2_link.minidump_module_list_index = 2;
|
|
crashpad_module_2_link.location.DataSize = sizeof(crashpad_module_2);
|
|
crashpad_module_2_link.location.Rva = static_cast<RVA>(string_file.SeekGet());
|
|
EXPECT_TRUE(string_file.Write(&crashpad_module_2, sizeof(crashpad_module_2)));
|
|
|
|
MinidumpCrashpadInfo crashpad_info = {};
|
|
crashpad_info.version = MinidumpCrashpadInfo::kVersion;
|
|
|
|
uint32_t crashpad_module_count = 2;
|
|
|
|
crashpad_info.module_list.DataSize =
|
|
sizeof(MinidumpModuleCrashpadInfoList) +
|
|
crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink);
|
|
crashpad_info.module_list.Rva = static_cast<RVA>(string_file.SeekGet());
|
|
|
|
EXPECT_TRUE(
|
|
string_file.Write(&crashpad_module_count, sizeof(crashpad_module_count)));
|
|
EXPECT_TRUE(string_file.Write(&crashpad_module_0_link,
|
|
sizeof(crashpad_module_0_link)));
|
|
EXPECT_TRUE(string_file.Write(&crashpad_module_2_link,
|
|
sizeof(crashpad_module_2_link)));
|
|
|
|
MINIDUMP_DIRECTORY crashpad_info_directory = {};
|
|
crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;
|
|
crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);
|
|
crashpad_info_directory.Location.Rva =
|
|
static_cast<RVA>(string_file.SeekGet());
|
|
EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));
|
|
|
|
header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
|
|
EXPECT_TRUE(string_file.Write(&minidump_module_list_directory,
|
|
sizeof(minidump_module_list_directory)));
|
|
EXPECT_TRUE(string_file.Write(&crashpad_info_directory,
|
|
sizeof(crashpad_info_directory)));
|
|
|
|
header.Signature = MINIDUMP_SIGNATURE;
|
|
header.Version = MINIDUMP_VERSION;
|
|
header.NumberOfStreams = 2;
|
|
EXPECT_TRUE(string_file.SeekSet(0));
|
|
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
|
|
|
|
ProcessSnapshotMinidump process_snapshot;
|
|
EXPECT_TRUE(process_snapshot.Initialize(&string_file));
|
|
|
|
std::vector<const ModuleSnapshot*> modules = process_snapshot.Modules();
|
|
ASSERT_EQ(minidump_module_count, modules.size());
|
|
|
|
auto annotations_simple_map = modules[0]->AnnotationsSimpleMap();
|
|
EXPECT_EQ(dictionary_0, annotations_simple_map);
|
|
|
|
auto annotations_vector = modules[0]->AnnotationsVector();
|
|
EXPECT_TRUE(annotations_vector.empty());
|
|
|
|
annotations_simple_map = modules[1]->AnnotationsSimpleMap();
|
|
EXPECT_TRUE(annotations_simple_map.empty());
|
|
|
|
annotations_vector = modules[1]->AnnotationsVector();
|
|
EXPECT_TRUE(annotations_vector.empty());
|
|
|
|
annotations_simple_map = modules[2]->AnnotationsSimpleMap();
|
|
EXPECT_EQ(dictionary_2, annotations_simple_map);
|
|
|
|
annotations_vector = modules[2]->AnnotationsVector();
|
|
EXPECT_EQ(list_annotations_2, annotations_vector);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace test
|
|
} // namespace crashpad
|