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