dump_syms_unittest.cc
1 // Copyright 2003 Google LLC 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 LLC 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 #ifdef HAVE_CONFIG_H 30 #include <config.h> // Must come first 31 #endif 32 33 #include <Windows.h> 34 #include <shellapi.h> 35 36 #include <string> 37 #include <utility> 38 39 #include "breakpad_googletest_includes.h" 40 41 namespace tools { 42 namespace windows { 43 namespace dump_syms { 44 45 namespace { 46 47 // Root names of PDB and dumped symbol files to be regression tested. These are 48 // specified in complexity of the resulting dumped symbol files. 49 const wchar_t* kRootNames[] = { 50 // A PDB file with no OMAP data. 51 L"dump_syms_regtest", 52 // A PDB file with OMAP data for an image that has been function-level 53 // reordered. 54 L"omap_reorder_funcs", 55 // A PDB file with OMAP data for an image that had new content injected, all 56 // of it with source data. 57 L"omap_stretched_filled", 58 // A PDB file with OMAP data for an image that had new content injected, but 59 // without source data. 60 L"omap_stretched", 61 // A PDB file with OMAP data for an image that has been basic block reordered. 62 L"omap_reorder_bbs", 63 // A 64bit PDB file with no OMAP data. 64 L"dump_syms_regtest64", 65 }; 66 67 const wchar_t* kPEOnlyRootNames[] = { 68 L"pe_only_symbol_test", 69 }; 70 71 void TrimLastComponent(const std::wstring& path, 72 std::wstring* trimmed, 73 std::wstring* component) { 74 size_t len = path.size(); 75 while (len > 0 && path[len - 1] != '\\') 76 --len; 77 78 if (component != NULL) 79 component->assign(path.c_str() + len, path.c_str() + path.size()); 80 81 while (len > 0 && path[len - 1] == '\\') 82 --len; 83 84 if (trimmed != NULL) 85 trimmed->assign(path.c_str(), len); 86 } 87 88 // Get the directory of the current executable. 89 bool GetSelfDirectory(std::wstring* self_dir) { 90 std::wstring command_line = GetCommandLineW(); 91 92 int num_args = 0; 93 wchar_t** args = NULL; 94 args = ::CommandLineToArgvW(command_line.c_str(), &num_args); 95 if (args == NULL) 96 return false; 97 98 *self_dir = args[0]; 99 TrimLastComponent(*self_dir, self_dir, NULL); 100 101 return true; 102 } 103 104 void RunCommand(const std::wstring& command_line, 105 std::string* stdout_string) { 106 // Create a PIPE for the child process stdout. 107 HANDLE child_stdout_read = 0; 108 HANDLE child_stdout_write = 0; 109 SECURITY_ATTRIBUTES sec_attr_stdout = {}; 110 sec_attr_stdout.nLength = sizeof(sec_attr_stdout); 111 sec_attr_stdout.bInheritHandle = TRUE; 112 ASSERT_TRUE(::CreatePipe(&child_stdout_read, &child_stdout_write, 113 &sec_attr_stdout, 0)); 114 ASSERT_TRUE(::SetHandleInformation(child_stdout_read, HANDLE_FLAG_INHERIT, 115 0)); 116 117 // Create a PIPE for the child process stdin. 118 HANDLE child_stdin_read = 0; 119 HANDLE child_stdin_write = 0; 120 SECURITY_ATTRIBUTES sec_attr_stdin = {}; 121 sec_attr_stdin.nLength = sizeof(sec_attr_stdin); 122 sec_attr_stdin.bInheritHandle = TRUE; 123 ASSERT_TRUE(::CreatePipe(&child_stdin_read, &child_stdin_write, 124 &sec_attr_stdin, 0)); 125 ASSERT_TRUE(::SetHandleInformation(child_stdin_write, HANDLE_FLAG_INHERIT, 126 0)); 127 128 // Startup the child. 129 STARTUPINFO startup_info = {}; 130 PROCESS_INFORMATION process_info = {}; 131 startup_info.cb = sizeof(STARTUPINFO); 132 startup_info.hStdError = NULL; 133 startup_info.hStdInput = child_stdin_read; 134 startup_info.hStdOutput = child_stdout_write; 135 startup_info.dwFlags = STARTF_USESTDHANDLES; 136 ASSERT_TRUE(::CreateProcessW(NULL, (LPWSTR)command_line.c_str(), NULL, NULL, 137 TRUE, 0, NULL, NULL, 138 &startup_info, &process_info)); 139 140 // Collect the output. 141 ASSERT_TRUE(::CloseHandle(child_stdout_write)); 142 char buffer[4096] = {}; 143 DWORD bytes_read = 0; 144 while (::ReadFile(child_stdout_read, buffer, sizeof(buffer), &bytes_read, 145 NULL) && bytes_read > 0) { 146 stdout_string->append(buffer, bytes_read); 147 } 148 149 // Wait for the process to finish. 150 ::WaitForSingleObject(process_info.hProcess, INFINITE); 151 152 // Shut down all of our handles. 153 ASSERT_TRUE(::CloseHandle(process_info.hThread)); 154 ASSERT_TRUE(::CloseHandle(process_info.hProcess)); 155 ASSERT_TRUE(::CloseHandle(child_stdin_write)); 156 ASSERT_TRUE(::CloseHandle(child_stdin_read)); 157 ASSERT_TRUE(::CloseHandle(child_stdout_read)); 158 } 159 160 void GetFileContents(const std::wstring& path, std::string* content) { 161 FILE* f = ::_wfopen(path.c_str(), L"rb"); 162 ASSERT_TRUE(f != NULL); 163 164 char buffer[4096] = {}; 165 while (true) { 166 size_t bytes_read = ::fread(buffer, 1, sizeof(buffer), f); 167 if (bytes_read == 0) 168 break; 169 content->append(buffer, bytes_read); 170 } 171 } 172 173 class DumpSymsRegressionTest : public testing::TestWithParam<const wchar_t*> { 174 public: 175 virtual void SetUp() { 176 std::wstring self_dir; 177 ASSERT_TRUE(GetSelfDirectory(&self_dir)); 178 dump_syms_exe = self_dir + L"\\dump_syms.exe"; 179 180 TrimLastComponent(self_dir, &testdata_dir, NULL); 181 testdata_dir += L"\\testdata"; 182 } 183 184 std::wstring dump_syms_exe; 185 std::wstring testdata_dir; 186 }; 187 188 class DumpSymsPEOnlyRegressionTest : public testing::TestWithParam<const wchar_t*> { 189 public: 190 virtual void SetUp() { 191 std::wstring self_dir; 192 ASSERT_TRUE(GetSelfDirectory(&self_dir)); 193 dump_syms_exe = self_dir + L"\\dump_syms.exe"; 194 195 TrimLastComponent(self_dir, &testdata_dir, NULL); 196 testdata_dir += L"\\testdata"; 197 } 198 199 std::wstring dump_syms_exe; 200 std::wstring testdata_dir; 201 }; 202 203 } //namespace 204 205 TEST_P(DumpSymsRegressionTest, EnsureDumpedSymbolsMatch) { 206 const wchar_t* root_name = GetParam(); 207 std::wstring root_path = testdata_dir + L"\\" + root_name; 208 209 std::wstring sym_path = root_path + L".sym"; 210 std::string expected_symbols; 211 ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols)); 212 213 std::wstring pdb_path = root_path + L".pdb"; 214 std::wstring command_line = L"\"" + dump_syms_exe + L"\" \"" + 215 pdb_path + L"\""; 216 std::string symbols; 217 ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols)); 218 219 EXPECT_EQ(expected_symbols, symbols); 220 } 221 222 INSTANTIATE_TEST_SUITE_P(DumpSyms, DumpSymsRegressionTest, 223 testing::ValuesIn(kRootNames)); 224 225 TEST_P(DumpSymsPEOnlyRegressionTest, EnsurePEOnlyDumpedSymbolsMatch) { 226 const wchar_t* root_name = GetParam(); 227 std::wstring root_path = testdata_dir + L"\\" + root_name; 228 229 std::wstring sym_path = root_path + L".sym"; 230 std::string expected_symbols; 231 ASSERT_NO_FATAL_FAILURE(GetFileContents(sym_path, &expected_symbols)); 232 233 std::wstring dll_path = root_path + L".dll"; 234 std::wstring command_line = L"\"" + dump_syms_exe + L"\" --pe \"" + 235 dll_path + L"\""; 236 std::string symbols; 237 ASSERT_NO_FATAL_FAILURE(RunCommand(command_line, &symbols)); 238 239 EXPECT_EQ(expected_symbols, symbols); 240 } 241 242 INSTANTIATE_TEST_SUITE_P(PEOnlyDumpSyms, DumpSymsPEOnlyRegressionTest, 243 testing::ValuesIn(kPEOnlyRootNames)); 244 245 246 } // namespace dump_syms 247 } // namespace windows 248 } // namespace tools