• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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