1 // Copyright 2003 Google Inc. All rights reserved.
\r
3 // Redistribution and use in source and binary forms, with or without
\r
4 // modification, are permitted provided that the following conditions are
\r
7 // * Redistributions of source code must retain the above copyright
\r
8 // notice, this list of conditions and the following disclaimer.
\r
9 // * Redistributions in binary form must reproduce the above
\r
10 // copyright notice, this list of conditions and the following disclaimer
\r
11 // in the documentation and/or other materials provided with the
\r
13 // * Neither the name of Google Inc. nor the names of its
\r
14 // contributors may be used to endorse or promote products derived from
\r
15 // this software without specific prior written permission.
\r
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
\r
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
\r
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
\r
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
\r
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
\r
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
\r
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
\r
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
\r
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
\r
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
\r
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
29 #include <Windows.h>
\r
30 #include <shellapi.h>
\r
35 #include "gmock/gmock.h"
\r
36 #include "gtest/gtest.h"
\r
40 namespace dump_syms {
\r
44 // Root names of PDB and dumped symbol files to be regression tested. These are
\r
45 // specified in complexity of the resulting dumped symbol files.
\r
46 const wchar_t* kRootNames[] = {
\r
47 // A PDB file with no OMAP data.
\r
48 L"dump_syms_regtest",
\r
49 // A PDB file with OMAP data for an image that has been function-level
\r
51 L"omap_reorder_funcs",
\r
52 // A PDB file with OMAP data for an image that had new content injected, all
\r
53 // of it with source data.
\r
54 L"omap_stretched_filled",
\r
55 // A PDB file with OMAP data for an image that had new content injected, but
\r
56 // without source data.
\r
58 // A PDB file with OMAP data for an image that has been basic block reordered.
\r
59 L"omap_reorder_bbs",
\r
60 // A 64bit PDB file with no OMAP data.
\r
61 L"dump_syms_regtest64",
\r
64 void TrimLastComponent(const std::wstring& path,
\r
65 std::wstring* trimmed,
\r
66 std::wstring* component) {
\r
67 size_t len = path.size();
\r
68 while (len > 0 && path[len - 1] != '\\')
\r
71 if (component != NULL)
\r
72 component->assign(path.c_str() + len, path.c_str() + path.size());
\r
74 while (len > 0 && path[len - 1] == '\\')
\r
77 if (trimmed != NULL)
\r
78 trimmed->assign(path.c_str(), len);
\r
81 // Get the directory of the current executable.
\r
82 bool GetSelfDirectory(std::wstring* self_dir) {
\r
83 std::wstring command_line = GetCommandLineW();
\r
86 wchar_t** args = NULL;
\r
87 args = ::CommandLineToArgvW(command_line.c_str(), &num_args);
\r
91 *self_dir = args[0];
\r
92 TrimLastComponent(*self_dir, self_dir, NULL);
\r
97 void RunCommand(const std::wstring& command_line,
\r
98 std::string* stdout_string) {
\r
99 // Create a PIPE for the child process stdout.
\r
100 HANDLE child_stdout_read = 0;
\r
101 HANDLE child_stdout_write = 0;
\r
102 SECURITY_ATTRIBUTES sec_attr_stdout = {};
\r
103 sec_attr_stdout.nLength = sizeof(sec_attr_stdout);
\r
104 sec_attr_stdout.bInheritHandle = TRUE;
\r
105 ASSERT_TRUE(::CreatePipe(&child_stdout_read, &child_stdout_write,
\r
106 &sec_attr_stdout, 0));
\r
107 ASSERT_TRUE(::SetHandleInformation(child_stdout_read, HANDLE_FLAG_INHERIT,
\r
110 // Create a PIPE for the child process stdin.
\r
111 HANDLE child_stdin_read = 0;
\r
112 HANDLE child_stdin_write = 0;
\r
113 SECURITY_ATTRIBUTES sec_attr_stdin = {};
\r
114 sec_attr_stdin.nLength = sizeof(sec_attr_stdin);
\r
115 sec_attr_stdin.bInheritHandle = TRUE;
\r
116 ASSERT_TRUE(::CreatePipe(&child_stdin_read, &child_stdin_write,
\r
117 &sec_attr_stdin, 0));
\r
118 ASSERT_TRUE(::SetHandleInformation(child_stdin_write, HANDLE_FLAG_INHERIT,
\r
121 // Startup the child.
\r
122 STARTUPINFO startup_info = {};
\r
123 PROCESS_INFORMATION process_info = {};
\r
124 startup_info.cb = sizeof(STARTUPINFO);
\r
125 startup_info.hStdError = child_stdout_write;
\r
126 startup_info.hStdInput = child_stdin_read;
\r
127 startup_info.hStdOutput = child_stdout_write;
\r
128 startup_info.dwFlags = STARTF_USESTDHANDLES;
\r
129 ASSERT_TRUE(::CreateProcessW(NULL, (LPWSTR)command_line.c_str(), NULL, NULL,
\r
130 TRUE, 0, NULL, NULL,
\r
131 &startup_info, &process_info));
\r
133 // Collect the output.
\r
134 ASSERT_TRUE(::CloseHandle(child_stdout_write));
\r
135 char buffer[4096] = {};
\r
136 DWORD bytes_read = 0;
\r
137 while (::ReadFile(child_stdout_read, buffer, sizeof(buffer), &bytes_read,
\r
138 NULL) && bytes_read > 0) {
\r
139 stdout_string->append(buffer, bytes_read);
\r
142 // Wait for the process to finish.
\r
143 ::WaitForSingleObject(process_info.hProcess, INFINITE);
\r
145 // Shut down all of our handles.
\r
146 ASSERT_TRUE(::CloseHandle(process_info.hThread));
\r
147 ASSERT_TRUE(::CloseHandle(process_info.hProcess));
\r
148 ASSERT_TRUE(::CloseHandle(child_stdin_write));
\r
149 ASSERT_TRUE(::CloseHandle(child_stdin_read));
\r
150 ASSERT_TRUE(::CloseHandle(child_stdout_read));
\r
153 void GetFileContents(const std::wstring& path, std::string* content) {
\r
154 FILE* f = ::_wfopen(path.c_str(), L"rb");
\r
155 ASSERT_TRUE(f != NULL);
\r
157 char buffer[4096] = {};
\r
159 size_t bytes_read = ::fread(buffer, 1, sizeof(buffer), f);
\r
160 if (bytes_read == 0)
\r
162 content->append(buffer, bytes_read);
\r
166 class DumpSymsRegressionTest : public testing::Test {
\r
168 virtual void SetUp() {
\r
169 std::wstring self_dir;
\r
170 ASSERT_TRUE(GetSelfDirectory(&self_dir));
\r
171 dump_syms_exe = self_dir + L"\\dump_syms.exe";
\r
173 TrimLastComponent(self_dir, &testdata_dir, NULL);
\r
174 testdata_dir += L"\\testdata";
\r
177 std::wstring dump_syms_exe;
\r
178 std::wstring testdata_dir;
\r
183 TEST_F(DumpSymsRegressionTest, EnsureDumpedSymbolsMatch) {
\r
184 for (size_t i = 0; i < sizeof(kRootNames) / sizeof(kRootNames[0]); ++i) {
\r
185 const wchar_t* root_name = kRootNames[i];
\r
186 std::wstring root_path = testdata_dir + L"\\" + root_name;
\r
188 std::wstring sym_path = root_path + L".sym";
\r
189 std::string expected_symbols;
\r
190 ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols));
\r
192 std::wstring pdb_path = root_path + L".pdb";
\r
193 std::wstring command_line = L"\"" + dump_syms_exe + L"\" \"" +
\r
195 std::string symbols;
\r
196 ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols));
\r
198 EXPECT_EQ(expected_symbols, symbols);
\r
202 } // namespace dump_syms
\r
203 } // namespace windows
\r