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 38namespace tools { 39namespace windows { 40namespace dump_syms { 41 42namespace { 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. 46const 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 64void 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. 82bool 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 97void 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 153void 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 166class 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 183TEST_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