Home | History | Annotate | Download | only in dump_syms
      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