mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +08:00
6278690abe
sed -i '' -E -e 's/Copyright (.+) The Crashpad Authors\. All rights reserved\.$/Copyright \1 The Crashpad Authors/' $(git grep -El 'Copyright (.+) The Crashpad Authors\. All rights reserved\.$') Bug: chromium:1098010 Change-Id: I8d6138469ddbe3d281a5d83f64cf918ec2491611 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3878262 Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Mark Mentovai <mark@chromium.org>
320 lines
12 KiB
C++
320 lines
12 KiB
C++
// Copyright 2022 The Crashpad Authors
|
|
//
|
|
// 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 "minidump/minidump_thread_name_list_writer.h"
|
|
|
|
#include <iterator>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "base/compiler_specific.h"
|
|
#include "base/format_macros.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "gtest/gtest.h"
|
|
#include "minidump/minidump_file_writer.h"
|
|
#include "minidump/minidump_system_info_writer.h"
|
|
#include "minidump/test/minidump_file_writer_test_util.h"
|
|
#include "minidump/test/minidump_string_writer_test_util.h"
|
|
#include "minidump/test/minidump_writable_test_util.h"
|
|
#include "test/gtest_death.h"
|
|
#include "util/file/string_file.h"
|
|
|
|
namespace crashpad {
|
|
namespace test {
|
|
namespace {
|
|
|
|
// This returns the MINIDUMP_THREAD_NAME_LIST stream in |thread_name_list|.
|
|
void GetThreadNameListStream(
|
|
const std::string& file_contents,
|
|
const MINIDUMP_THREAD_NAME_LIST** thread_name_list) {
|
|
constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
|
|
const uint32_t kExpectedStreams = 1;
|
|
const size_t kThreadNameListStreamOffset =
|
|
kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY);
|
|
const size_t kThreadNameListOffset =
|
|
kThreadNameListStreamOffset + sizeof(MINIDUMP_THREAD_NAME_LIST);
|
|
|
|
ASSERT_GE(file_contents.size(), kThreadNameListOffset);
|
|
|
|
const MINIDUMP_DIRECTORY* directory;
|
|
const MINIDUMP_HEADER* header =
|
|
MinidumpHeaderAtStart(file_contents, &directory);
|
|
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));
|
|
ASSERT_TRUE(directory);
|
|
|
|
ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeThreadNameList);
|
|
EXPECT_EQ(directory[0].Location.Rva, kThreadNameListStreamOffset);
|
|
|
|
*thread_name_list =
|
|
MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>(
|
|
file_contents, directory[0].Location);
|
|
ASSERT_TRUE(thread_name_list);
|
|
}
|
|
|
|
TEST(MinidumpThreadNameListWriter, EmptyThreadNameList) {
|
|
MinidumpFileWriter minidump_file_writer;
|
|
auto thread_name_list_writer =
|
|
std::make_unique<MinidumpThreadNameListWriter>();
|
|
|
|
ASSERT_TRUE(
|
|
minidump_file_writer.AddStream(std::move(thread_name_list_writer)));
|
|
|
|
StringFile string_file;
|
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
|
|
|
ASSERT_EQ(string_file.string().size(),
|
|
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
|
sizeof(MINIDUMP_THREAD_NAME_LIST));
|
|
|
|
const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
GetThreadNameListStream(string_file.string(), &thread_name_list));
|
|
|
|
EXPECT_EQ(thread_name_list->NumberOfThreadNames, 0u);
|
|
}
|
|
|
|
// The MINIDUMP_THREAD_NAMEs |expected| and |observed| are compared against
|
|
// each other using Google Test assertions.
|
|
void ExpectThreadName(const MINIDUMP_THREAD_NAME* expected,
|
|
const MINIDUMP_THREAD_NAME* observed,
|
|
const std::string& file_contents,
|
|
const std::string& expected_thread_name) {
|
|
// Copy RvaOfThreadName into a local variable because
|
|
// |MINIDUMP_THREAD_NAME::RvaOfThreadName| requires 8-byte alignment but the
|
|
// struct itself is 4-byte algined.
|
|
const auto rva_of_thread_name = [&observed] {
|
|
RVA64 data = 0;
|
|
memcpy(&data, &observed->RvaOfThreadName, sizeof(RVA64));
|
|
return data;
|
|
}();
|
|
|
|
EXPECT_EQ(observed->ThreadId, expected->ThreadId);
|
|
EXPECT_NE(rva_of_thread_name, 0u);
|
|
const std::string observed_thread_name = base::UTF16ToUTF8(
|
|
MinidumpStringAtRVAAsString(file_contents, rva_of_thread_name));
|
|
EXPECT_EQ(observed_thread_name, expected_thread_name);
|
|
}
|
|
|
|
TEST(MinidumpThreadNameListWriter, OneThread) {
|
|
MinidumpFileWriter minidump_file_writer;
|
|
auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();
|
|
|
|
constexpr uint32_t kThreadID = 0x11111111;
|
|
const std::string kThreadName = "ariadne";
|
|
|
|
auto thread_name_list_writer =
|
|
std::make_unique<MinidumpThreadNameListWriter>();
|
|
auto thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();
|
|
thread_name_writer->SetThreadId(kThreadID);
|
|
thread_name_writer->SetThreadName(kThreadName);
|
|
thread_name_list_writer->AddThreadName(std::move(thread_name_writer));
|
|
|
|
ASSERT_TRUE(
|
|
minidump_file_writer.AddStream(std::move(thread_name_list_writer)));
|
|
|
|
StringFile string_file;
|
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
|
|
|
ASSERT_GT(string_file.string().size(),
|
|
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
|
sizeof(MINIDUMP_THREAD_NAME_LIST) +
|
|
1 * sizeof(MINIDUMP_THREAD_NAME));
|
|
|
|
const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
GetThreadNameListStream(string_file.string(), &thread_name_list));
|
|
|
|
EXPECT_EQ(thread_name_list->NumberOfThreadNames, 1u);
|
|
|
|
MINIDUMP_THREAD_NAME expected = {};
|
|
expected.ThreadId = kThreadID;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,
|
|
&thread_name_list->ThreadNames[0],
|
|
string_file.string(),
|
|
kThreadName));
|
|
}
|
|
|
|
TEST(MinidumpThreadNameListWriter, OneThreadWithLeadingPadding) {
|
|
MinidumpFileWriter minidump_file_writer;
|
|
|
|
// Add a stream before the MINIDUMP_THREAD_NAME_LIST to ensure the thread name
|
|
// MINIDUMP_STRING requires leading padding to align to a 4-byte boundary.
|
|
auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();
|
|
system_info_writer->SetCSDVersion("");
|
|
ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));
|
|
|
|
auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();
|
|
|
|
constexpr uint32_t kThreadID = 0x11111111;
|
|
const std::string kThreadName = "ariadne";
|
|
|
|
auto thread_name_list_writer =
|
|
std::make_unique<MinidumpThreadNameListWriter>();
|
|
auto thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();
|
|
thread_name_writer->SetThreadId(kThreadID);
|
|
thread_name_writer->SetThreadName(kThreadName);
|
|
thread_name_list_writer->AddThreadName(std::move(thread_name_writer));
|
|
|
|
ASSERT_TRUE(
|
|
minidump_file_writer.AddStream(std::move(thread_name_list_writer)));
|
|
|
|
StringFile string_file;
|
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
|
|
|
ASSERT_GT(string_file.string().size(),
|
|
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
|
sizeof(MINIDUMP_THREAD_NAME_LIST) +
|
|
1 * sizeof(MINIDUMP_THREAD_NAME));
|
|
|
|
const uint32_t kExpectedStreams = 2;
|
|
const MINIDUMP_DIRECTORY* directory;
|
|
const MINIDUMP_HEADER* header =
|
|
MinidumpHeaderAtStart(string_file.string(), &directory);
|
|
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));
|
|
ASSERT_TRUE(directory);
|
|
|
|
ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);
|
|
ASSERT_EQ(directory[1].StreamType, kMinidumpStreamTypeThreadNameList);
|
|
|
|
const MINIDUMP_THREAD_NAME_LIST* thread_name_list =
|
|
MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>(
|
|
string_file.string(), directory[1].Location);
|
|
ASSERT_TRUE(thread_name_list);
|
|
|
|
EXPECT_EQ(thread_name_list->NumberOfThreadNames, 1u);
|
|
|
|
MINIDUMP_THREAD_NAME expected = {};
|
|
expected.ThreadId = kThreadID;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,
|
|
&thread_name_list->ThreadNames[0],
|
|
string_file.string(),
|
|
kThreadName));
|
|
}
|
|
|
|
TEST(MinidumpThreadNameListWriter, TwoThreads_DifferentNames) {
|
|
MinidumpFileWriter minidump_file_writer;
|
|
auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();
|
|
|
|
constexpr uint32_t kFirstThreadID = 0x11111111;
|
|
const std::string kFirstThreadName = "ariadne";
|
|
|
|
constexpr uint32_t kSecondThreadID = 0x22222222;
|
|
const std::string kSecondThreadName = "theseus";
|
|
|
|
auto thread_name_list_writer =
|
|
std::make_unique<MinidumpThreadNameListWriter>();
|
|
auto first_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();
|
|
first_thread_name_writer->SetThreadId(kFirstThreadID);
|
|
first_thread_name_writer->SetThreadName(kFirstThreadName);
|
|
thread_name_list_writer->AddThreadName(std::move(first_thread_name_writer));
|
|
|
|
auto second_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();
|
|
second_thread_name_writer->SetThreadId(kSecondThreadID);
|
|
second_thread_name_writer->SetThreadName(kSecondThreadName);
|
|
thread_name_list_writer->AddThreadName(std::move(second_thread_name_writer));
|
|
|
|
ASSERT_TRUE(
|
|
minidump_file_writer.AddStream(std::move(thread_name_list_writer)));
|
|
|
|
StringFile string_file;
|
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
|
|
|
ASSERT_GT(string_file.string().size(),
|
|
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
|
sizeof(MINIDUMP_THREAD_NAME_LIST) +
|
|
2 * sizeof(MINIDUMP_THREAD_NAME));
|
|
|
|
const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
GetThreadNameListStream(string_file.string(), &thread_name_list));
|
|
|
|
EXPECT_EQ(thread_name_list->NumberOfThreadNames, 2u);
|
|
|
|
MINIDUMP_THREAD_NAME expected = {};
|
|
expected.ThreadId = kFirstThreadID;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,
|
|
&thread_name_list->ThreadNames[0],
|
|
string_file.string(),
|
|
kFirstThreadName));
|
|
|
|
expected.ThreadId = kSecondThreadID;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,
|
|
&thread_name_list->ThreadNames[1],
|
|
string_file.string(),
|
|
kSecondThreadName));
|
|
}
|
|
|
|
TEST(MinidumpThreadNameListWriter, TwoThreads_SameNames) {
|
|
MinidumpFileWriter minidump_file_writer;
|
|
auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();
|
|
|
|
constexpr uint32_t kFirstThreadID = 0x11111111;
|
|
const std::string kThreadName = "ariadne";
|
|
|
|
constexpr uint32_t kSecondThreadID = 0x22222222;
|
|
|
|
auto thread_name_list_writer =
|
|
std::make_unique<MinidumpThreadNameListWriter>();
|
|
auto first_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();
|
|
first_thread_name_writer->SetThreadId(kFirstThreadID);
|
|
first_thread_name_writer->SetThreadName(kThreadName);
|
|
thread_name_list_writer->AddThreadName(std::move(first_thread_name_writer));
|
|
|
|
auto second_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();
|
|
second_thread_name_writer->SetThreadId(kSecondThreadID);
|
|
second_thread_name_writer->SetThreadName(kThreadName);
|
|
thread_name_list_writer->AddThreadName(std::move(second_thread_name_writer));
|
|
|
|
ASSERT_TRUE(
|
|
minidump_file_writer.AddStream(std::move(thread_name_list_writer)));
|
|
|
|
StringFile string_file;
|
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
|
|
|
ASSERT_GT(string_file.string().size(),
|
|
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
|
sizeof(MINIDUMP_THREAD_NAME_LIST) +
|
|
2 * sizeof(MINIDUMP_THREAD_NAME));
|
|
|
|
const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;
|
|
ASSERT_NO_FATAL_FAILURE(
|
|
GetThreadNameListStream(string_file.string(), &thread_name_list));
|
|
|
|
EXPECT_EQ(thread_name_list->NumberOfThreadNames, 2u);
|
|
|
|
MINIDUMP_THREAD_NAME expected = {};
|
|
expected.ThreadId = kFirstThreadID;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,
|
|
&thread_name_list->ThreadNames[0],
|
|
string_file.string(),
|
|
kThreadName));
|
|
|
|
expected.ThreadId = kSecondThreadID;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,
|
|
&thread_name_list->ThreadNames[1],
|
|
string_file.string(),
|
|
kThreadName));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace test
|
|
} // namespace crashpad
|