// 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 #include #include #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)); UUID client_id; process_snapshot.ClientID(&client_id); EXPECT_EQ(UUID(), client_id); EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty()); } // 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(writer->SeekGet()); uint32_t string_size = static_cast(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& dictionary) { std::vector 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(writer->SeekGet()); const uint32_t simple_string_dictionary_entries = static_cast(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 = static_cast( 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& strings) { std::vector rvas; for (const std::string& string : strings) { rvas.push_back(WriteString(writer, string)); } location->Rva = static_cast(writer->SeekGet()); const uint32_t string_list_entries = static_cast(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 = static_cast(sizeof(string_list_entries) + rvas.size() * sizeof(RVA)); } TEST(ProcessSnapshotMinidump, ClientID) { StringFile string_file; MINIDUMP_HEADER header = {}; EXPECT_TRUE(string_file.Write(&header, sizeof(header))); UUID client_id; ASSERT_TRUE( client_id.InitializeFromString("0001f4a9-d00d-5155-0a55-c0ffeec0ffee")); MinidumpCrashpadInfo crashpad_info = {}; crashpad_info.version = MinidumpCrashpadInfo::kVersion; crashpad_info.client_id = client_id; MINIDUMP_DIRECTORY crashpad_info_directory = {}; crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; crashpad_info_directory.Location.Rva = static_cast(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(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)); UUID actual_client_id; process_snapshot.ClientID(&actual_client_id); EXPECT_EQ(client_id, actual_client_id); EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty()); } 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 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(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(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)); UUID client_id; process_snapshot.ClientID(&client_id); EXPECT_EQ(UUID(), client_id); 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(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 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(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 dictionary_2; dictionary_2["fakemodule"] = "yes"; WriteMinidumpSimpleStringDictionary( &crashpad_module_2.simple_annotations, &string_file, dictionary_2); std::vector 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(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(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(string_file.SeekGet()); EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info))); header.StreamDirectoryRva = static_cast(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 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