1 // Copyright 2003 Google Inc. All rights reserved. 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google Inc. nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include <Windows.h> 30 #include <shellapi.h> 31 32 #include <string> 33 #include <utility> 34 35 #include "breakpad_googletest_includes.h" 36 37 namespace tools { 38 namespace windows { 39 namespace dump_syms { 40 41 namespace { 42 43 // Root names of PDB and dumped symbol files to be regression tested. These are 44 // specified in complexity of the resulting dumped symbol files. 45 const wchar_t* kRootNames[] = { 46 // A PDB file with no OMAP data. 47 L"dump_syms_regtest", 48 // A PDB file with OMAP data for an image that has been function-level 49 // reordered. 50 L"omap_reorder_funcs", 51 // A PDB file with OMAP data for an image that had new content injected, all 52 // of it with source data. 53 L"omap_stretched_filled", 54 // A PDB file with OMAP data for an image that had new content injected, but 55 // without source data. 56 L"omap_stretched", 57 // A PDB file with OMAP data for an image that has been basic block reordered. 58 L"omap_reorder_bbs", 59 // A 64bit PDB file with no OMAP data. 60 L"dump_syms_regtest64", 61 }; 62 63 const wchar_t* kPEOnlyRootNames[] = { 64 L"pe_only_symbol_test", 65 }; 66 TrimLastComponent(const std::wstring & path,std::wstring * trimmed,std::wstring * component)67 void TrimLastComponent(const std::wstring& path, 68 std::wstring* trimmed, 69 std::wstring* component) { 70 size_t len = path.size(); 71 while (len > 0 && path[len - 1] != '\\') 72 --len; 73 74 if (component != NULL) 75 component->assign(path.c_str() + len, path.c_str() + path.size()); 76 77 while (len > 0 && path[len - 1] == '\\') 78 --len; 79 80 if (trimmed != NULL) 81 trimmed->assign(path.c_str(), len); 82 } 83 84 // Get the directory of the current executable. GetSelfDirectory(std::wstring * self_dir)85 bool GetSelfDirectory(std::wstring* self_dir) { 86 std::wstring command_line = GetCommandLineW(); 87 88 int num_args = 0; 89 wchar_t** args = NULL; 90 args = ::CommandLineToArgvW(command_line.c_str(), &num_args); 91 if (args == NULL) 92 return false; 93 94 *self_dir = args[0]; 95 TrimLastComponent(*self_dir, self_dir, NULL); 96 97 return true; 98 } 99 RunCommand(const std::wstring & command_line,std::string * stdout_string)100 void RunCommand(const std::wstring& command_line, 101 std::string* stdout_string) { 102 // Create a PIPE for the child process stdout. 103 HANDLE child_stdout_read = 0; 104 HANDLE child_stdout_write = 0; 105 SECURITY_ATTRIBUTES sec_attr_stdout = {}; 106 sec_attr_stdout.nLength = sizeof(sec_attr_stdout); 107 sec_attr_stdout.bInheritHandle = TRUE; 108 ASSERT_TRUE(::CreatePipe(&child_stdout_read, &child_stdout_write, 109 &sec_attr_stdout, 0)); 110 ASSERT_TRUE(::SetHandleInformation(child_stdout_read, HANDLE_FLAG_INHERIT, 111 0)); 112 113 // Create a PIPE for the child process stdin. 114 HANDLE child_stdin_read = 0; 115 HANDLE child_stdin_write = 0; 116 SECURITY_ATTRIBUTES sec_attr_stdin = {}; 117 sec_attr_stdin.nLength = sizeof(sec_attr_stdin); 118 sec_attr_stdin.bInheritHandle = TRUE; 119 ASSERT_TRUE(::CreatePipe(&child_stdin_read, &child_stdin_write, 120 &sec_attr_stdin, 0)); 121 ASSERT_TRUE(::SetHandleInformation(child_stdin_write, HANDLE_FLAG_INHERIT, 122 0)); 123 124 // Startup the child. 125 STARTUPINFO startup_info = {}; 126 PROCESS_INFORMATION process_info = {}; 127 startup_info.cb = sizeof(STARTUPINFO); 128 startup_info.hStdError = NULL; 129 startup_info.hStdInput = child_stdin_read; 130 startup_info.hStdOutput = child_stdout_write; 131 startup_info.dwFlags = STARTF_USESTDHANDLES; 132 ASSERT_TRUE(::CreateProcessW(NULL, (LPWSTR)command_line.c_str(), NULL, NULL, 133 TRUE, 0, NULL, NULL, 134 &startup_info, &process_info)); 135 136 // Collect the output. 137 ASSERT_TRUE(::CloseHandle(child_stdout_write)); 138 char buffer[4096] = {}; 139 DWORD bytes_read = 0; 140 while (::ReadFile(child_stdout_read, buffer, sizeof(buffer), &bytes_read, 141 NULL) && bytes_read > 0) { 142 stdout_string->append(buffer, bytes_read); 143 } 144 145 // Wait for the process to finish. 146 ::WaitForSingleObject(process_info.hProcess, INFINITE); 147 148 // Shut down all of our handles. 149 ASSERT_TRUE(::CloseHandle(process_info.hThread)); 150 ASSERT_TRUE(::CloseHandle(process_info.hProcess)); 151 ASSERT_TRUE(::CloseHandle(child_stdin_write)); 152 ASSERT_TRUE(::CloseHandle(child_stdin_read)); 153 ASSERT_TRUE(::CloseHandle(child_stdout_read)); 154 } 155 GetFileContents(const std::wstring & path,std::string * content)156 void GetFileContents(const std::wstring& path, std::string* content) { 157 FILE* f = ::_wfopen(path.c_str(), L"rb"); 158 ASSERT_TRUE(f != NULL); 159 160 char buffer[4096] = {}; 161 while (true) { 162 size_t bytes_read = ::fread(buffer, 1, sizeof(buffer), f); 163 if (bytes_read == 0) 164 break; 165 content->append(buffer, bytes_read); 166 } 167 } 168 169 class DumpSymsRegressionTest : public testing::TestWithParam<const wchar_t *> { 170 public: SetUp()171 virtual void SetUp() { 172 std::wstring self_dir; 173 ASSERT_TRUE(GetSelfDirectory(&self_dir)); 174 dump_syms_exe = self_dir + L"\\dump_syms.exe"; 175 176 TrimLastComponent(self_dir, &testdata_dir, NULL); 177 testdata_dir += L"\\testdata"; 178 } 179 180 std::wstring dump_syms_exe; 181 std::wstring testdata_dir; 182 }; 183 184 class DumpSymsPEOnlyRegressionTest : public testing::TestWithParam<const wchar_t *> { 185 public: SetUp()186 virtual void SetUp() { 187 std::wstring self_dir; 188 ASSERT_TRUE(GetSelfDirectory(&self_dir)); 189 dump_syms_exe = self_dir + L"\\dump_syms.exe"; 190 191 TrimLastComponent(self_dir, &testdata_dir, NULL); 192 testdata_dir += L"\\testdata"; 193 } 194 195 std::wstring dump_syms_exe; 196 std::wstring testdata_dir; 197 }; 198 199 } //namespace 200 TEST_P(DumpSymsRegressionTest,EnsureDumpedSymbolsMatch)201 TEST_P(DumpSymsRegressionTest, EnsureDumpedSymbolsMatch) { 202 const wchar_t* root_name = GetParam(); 203 std::wstring root_path = testdata_dir + L"\\" + root_name; 204 205 std::wstring sym_path = root_path + L".sym"; 206 std::string expected_symbols; 207 ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols)); 208 209 std::wstring pdb_path = root_path + L".pdb"; 210 std::wstring command_line = L"\"" + dump_syms_exe + L"\" \"" + 211 pdb_path + L"\""; 212 std::string symbols; 213 ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols)); 214 215 EXPECT_EQ(expected_symbols, symbols); 216 } 217 218 INSTANTIATE_TEST_CASE_P(DumpSyms, DumpSymsRegressionTest, 219 testing::ValuesIn(kRootNames)); 220 TEST_P(DumpSymsPEOnlyRegressionTest,EnsurePEOnlyDumpedSymbolsMatch)221 TEST_P(DumpSymsPEOnlyRegressionTest, EnsurePEOnlyDumpedSymbolsMatch) { 222 const wchar_t* root_name = GetParam(); 223 std::wstring root_path = testdata_dir + L"\\" + root_name; 224 225 std::wstring sym_path = root_path + L".sym"; 226 std::string expected_symbols; 227 ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols)); 228 229 std::wstring dll_path = root_path + L".dll"; 230 std::wstring command_line = L"\"" + dump_syms_exe + L"\" --pe \"" + 231 dll_path + L"\""; 232 std::string symbols; 233 ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols)); 234 235 EXPECT_EQ(expected_symbols, symbols); 236 } 237 238 INSTANTIATE_TEST_CASE_P(PEOnlyDumpSyms, DumpSymsPEOnlyRegressionTest, 239 testing::ValuesIn(kPEOnlyRootNames)); 240 241 242 } // namespace dump_syms 243 } // namespace windows 244 } // namespace tools 245