diff options
Diffstat (limited to 'externals/breakpad/src/tools/windows/dump_syms/dump_syms_unittest.cc')
-rw-r--r-- | externals/breakpad/src/tools/windows/dump_syms/dump_syms_unittest.cc | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/externals/breakpad/src/tools/windows/dump_syms/dump_syms_unittest.cc b/externals/breakpad/src/tools/windows/dump_syms/dump_syms_unittest.cc new file mode 100644 index 0000000000..73c48a2f40 --- /dev/null +++ b/externals/breakpad/src/tools/windows/dump_syms/dump_syms_unittest.cc @@ -0,0 +1,248 @@ +// Copyright 2003 Google LLC +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifdef HAVE_CONFIG_H +#include <config.h> // Must come first +#endif + +#include <Windows.h> +#include <shellapi.h> + +#include <string> +#include <utility> + +#include "breakpad_googletest_includes.h" + +namespace tools { +namespace windows { +namespace dump_syms { + +namespace { + +// Root names of PDB and dumped symbol files to be regression tested. These are +// specified in complexity of the resulting dumped symbol files. +const wchar_t* kRootNames[] = { + // A PDB file with no OMAP data. + L"dump_syms_regtest", + // A PDB file with OMAP data for an image that has been function-level + // reordered. + L"omap_reorder_funcs", + // A PDB file with OMAP data for an image that had new content injected, all + // of it with source data. + L"omap_stretched_filled", + // A PDB file with OMAP data for an image that had new content injected, but + // without source data. + L"omap_stretched", + // A PDB file with OMAP data for an image that has been basic block reordered. + L"omap_reorder_bbs", + // A 64bit PDB file with no OMAP data. + L"dump_syms_regtest64", +}; + +const wchar_t* kPEOnlyRootNames[] = { + L"pe_only_symbol_test", +}; + +void TrimLastComponent(const std::wstring& path, + std::wstring* trimmed, + std::wstring* component) { + size_t len = path.size(); + while (len > 0 && path[len - 1] != '\\') + --len; + + if (component != NULL) + component->assign(path.c_str() + len, path.c_str() + path.size()); + + while (len > 0 && path[len - 1] == '\\') + --len; + + if (trimmed != NULL) + trimmed->assign(path.c_str(), len); +} + +// Get the directory of the current executable. +bool GetSelfDirectory(std::wstring* self_dir) { + std::wstring command_line = GetCommandLineW(); + + int num_args = 0; + wchar_t** args = NULL; + args = ::CommandLineToArgvW(command_line.c_str(), &num_args); + if (args == NULL) + return false; + + *self_dir = args[0]; + TrimLastComponent(*self_dir, self_dir, NULL); + + return true; +} + +void RunCommand(const std::wstring& command_line, + std::string* stdout_string) { + // Create a PIPE for the child process stdout. + HANDLE child_stdout_read = 0; + HANDLE child_stdout_write = 0; + SECURITY_ATTRIBUTES sec_attr_stdout = {}; + sec_attr_stdout.nLength = sizeof(sec_attr_stdout); + sec_attr_stdout.bInheritHandle = TRUE; + ASSERT_TRUE(::CreatePipe(&child_stdout_read, &child_stdout_write, + &sec_attr_stdout, 0)); + ASSERT_TRUE(::SetHandleInformation(child_stdout_read, HANDLE_FLAG_INHERIT, + 0)); + + // Create a PIPE for the child process stdin. + HANDLE child_stdin_read = 0; + HANDLE child_stdin_write = 0; + SECURITY_ATTRIBUTES sec_attr_stdin = {}; + sec_attr_stdin.nLength = sizeof(sec_attr_stdin); + sec_attr_stdin.bInheritHandle = TRUE; + ASSERT_TRUE(::CreatePipe(&child_stdin_read, &child_stdin_write, + &sec_attr_stdin, 0)); + ASSERT_TRUE(::SetHandleInformation(child_stdin_write, HANDLE_FLAG_INHERIT, + 0)); + + // Startup the child. + STARTUPINFO startup_info = {}; + PROCESS_INFORMATION process_info = {}; + startup_info.cb = sizeof(STARTUPINFO); + startup_info.hStdError = NULL; + startup_info.hStdInput = child_stdin_read; + startup_info.hStdOutput = child_stdout_write; + startup_info.dwFlags = STARTF_USESTDHANDLES; + ASSERT_TRUE(::CreateProcessW(NULL, (LPWSTR)command_line.c_str(), NULL, NULL, + TRUE, 0, NULL, NULL, + &startup_info, &process_info)); + + // Collect the output. + ASSERT_TRUE(::CloseHandle(child_stdout_write)); + char buffer[4096] = {}; + DWORD bytes_read = 0; + while (::ReadFile(child_stdout_read, buffer, sizeof(buffer), &bytes_read, + NULL) && bytes_read > 0) { + stdout_string->append(buffer, bytes_read); + } + + // Wait for the process to finish. + ::WaitForSingleObject(process_info.hProcess, INFINITE); + + // Shut down all of our handles. + ASSERT_TRUE(::CloseHandle(process_info.hThread)); + ASSERT_TRUE(::CloseHandle(process_info.hProcess)); + ASSERT_TRUE(::CloseHandle(child_stdin_write)); + ASSERT_TRUE(::CloseHandle(child_stdin_read)); + ASSERT_TRUE(::CloseHandle(child_stdout_read)); +} + +void GetFileContents(const std::wstring& path, std::string* content) { + FILE* f = ::_wfopen(path.c_str(), L"rb"); + ASSERT_TRUE(f != NULL); + + char buffer[4096] = {}; + while (true) { + size_t bytes_read = ::fread(buffer, 1, sizeof(buffer), f); + if (bytes_read == 0) + break; + content->append(buffer, bytes_read); + } +} + +class DumpSymsRegressionTest : public testing::TestWithParam<const wchar_t*> { + public: + virtual void SetUp() { + std::wstring self_dir; + ASSERT_TRUE(GetSelfDirectory(&self_dir)); + dump_syms_exe = self_dir + L"\\dump_syms.exe"; + + TrimLastComponent(self_dir, &testdata_dir, NULL); + testdata_dir += L"\\testdata"; + } + + std::wstring dump_syms_exe; + std::wstring testdata_dir; +}; + +class DumpSymsPEOnlyRegressionTest : public testing::TestWithParam<const wchar_t*> { +public: + virtual void SetUp() { + std::wstring self_dir; + ASSERT_TRUE(GetSelfDirectory(&self_dir)); + dump_syms_exe = self_dir + L"\\dump_syms.exe"; + + TrimLastComponent(self_dir, &testdata_dir, NULL); + testdata_dir += L"\\testdata"; + } + + std::wstring dump_syms_exe; + std::wstring testdata_dir; +}; + +} //namespace + +TEST_P(DumpSymsRegressionTest, EnsureDumpedSymbolsMatch) { + const wchar_t* root_name = GetParam(); + std::wstring root_path = testdata_dir + L"\\" + root_name; + + std::wstring sym_path = root_path + L".sym"; + std::string expected_symbols; + ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols)); + + std::wstring pdb_path = root_path + L".pdb"; + std::wstring command_line = L"\"" + dump_syms_exe + L"\" \"" + + pdb_path + L"\""; + std::string symbols; + ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols)); + + EXPECT_EQ(expected_symbols, symbols); +} + +INSTANTIATE_TEST_SUITE_P(DumpSyms, DumpSymsRegressionTest, + testing::ValuesIn(kRootNames)); + +TEST_P(DumpSymsPEOnlyRegressionTest, EnsurePEOnlyDumpedSymbolsMatch) { + const wchar_t* root_name = GetParam(); + std::wstring root_path = testdata_dir + L"\\" + root_name; + + std::wstring sym_path = root_path + L".sym"; + std::string expected_symbols; + ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols)); + + std::wstring dll_path = root_path + L".dll"; + std::wstring command_line = L"\"" + dump_syms_exe + L"\" --pe \"" + + dll_path + L"\""; + std::string symbols; + ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols)); + + EXPECT_EQ(expected_symbols, symbols); +} + +INSTANTIATE_TEST_SUITE_P(PEOnlyDumpSyms, DumpSymsPEOnlyRegressionTest, + testing::ValuesIn(kPEOnlyRootNames)); + + +} // namespace dump_syms +} // namespace windows +} // namespace tools |