Source code
Revision control
Copy as Markdown
Other Tools
// Copyright 2003 Google Inc. All rights reserved.↩
//↩
// 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 Inc. 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.↩
↩
#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_CASE_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_CASE_P(PEOnlyDumpSyms, DumpSymsPEOnlyRegressionTest,↩
testing::ValuesIn(kPEOnlyRootNames));↩
↩
↩
} // namespace dump_syms↩
} // namespace windows↩
} // namespace tools↩