Coverage for /Syzygy/pdb/pdb_writer_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
97.3%1441480.C++test

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/pdb/pdb_writer.h"
  16    :  
  17    :  #include "base/command_line.h"
  18    :  #include "base/file_util.h"
  19    :  #include "base/process_util.h"
  20    :  #include "base/utf_string_conversions.h"
  21    :  #include "base/files/scoped_temp_dir.h"
  22    :  #include "gmock/gmock.h"
  23    :  #include "gtest/gtest.h"
  24    :  #include "syzygy/core/unittest_util.h"
  25    :  #include "syzygy/pdb/pdb_constants.h"
  26    :  #include "syzygy/pdb/pdb_data.h"
  27    :  #include "syzygy/pdb/pdb_reader.h"
  28    :  #include "syzygy/pdb/pdb_util.h"
  29    :  #include "syzygy/pdb/unittest_util.h"
  30    :  #include "syzygy/pe/unittest_util.h"
  31    :  
  32    :  namespace pdb {
  33    :  
  34    :  namespace {
  35    :  
  36    :  uint32 GetNumPages(uint32 num_bytes) {
  37    :    return (num_bytes + pdb::kPdbPageSize - 1) / pdb::kPdbPageSize;
  38    :  }
  39    :  
  40    :  class TestPdbWriter : public PdbWriter {
  41    :   public:
  42  E :    TestPdbWriter() {
  43  E :      file_.reset(file_util::CreateAndOpenTemporaryFile(&path_));
  44  E :      EXPECT_TRUE(file_.get() != NULL);
  45  E :    }
  46    :  
  47  E :    ~TestPdbWriter() {
  48  E :      if (file_.get()) {
  49  i :        fclose(file_.get());
  50  i :        file_.reset();
  51    :      }
  52  E :      file_util::Delete(path_, false);
  53  E :    }
  54    :  
  55  E :    file_util::ScopedFILE& file() { return file_; }
  56    :  
  57    :    using PdbWriter::AppendStream;
  58    :    using PdbWriter::WriteHeader;
  59    :  
  60    :    base::FilePath path_;
  61    :  };
  62    :  
  63    :  class TestPdbStream : public PdbStream {
  64    :   public:
  65  E :    TestPdbStream(uint32 length, uint32 mask)
  66    :        : PdbStream(length), data_(length) {
  67  E :      uint32* data = reinterpret_cast<uint32*>(data_.data());
  68    :  
  69    :      // Just to make sure the data is non-repeating (so we can distinguish if it
  70    :      // has been correctly written or not) fill it with integers encoding their
  71    :      // own position in the stream.
  72  E :      for (size_t i = 0; i < data_.size() / sizeof(data[0]); ++i)
  73  E :        data[i] = i | mask;
  74  E :    }
  75    :  
  76  E :    bool ReadBytes(void* dest, size_t count, size_t* bytes_read) {
  77  E :      DCHECK(bytes_read != NULL);
  78    :  
  79  E :      if (pos() == length()) {
  80  i :        *bytes_read = 0;
  81  i :        return true;
  82    :      }
  83    :  
  84  E :      count = std::min(count, length() - pos());
  85  E :      ::memcpy(dest, data_.data() + pos(), count);
  86  E :      Seek(pos() + count);
  87  E :      *bytes_read = count;
  88    :  
  89  E :      return true;
  90  E :    }
  91    :  
  92  E :    const std::vector<uint8> data() const { return data_; }
  93    :  
  94    :   private:
  95    :    std::vector<uint8> data_;
  96    :  };
  97    :  
  98    :  void EnsurePdbContentsAreIdentical(const PdbFile& pdb_file,
  99  E :                                     const PdbFile& pdb_file_read) {
 100  E :    ASSERT_EQ(pdb_file.StreamCount(), pdb_file_read.StreamCount());
 101    :  
 102  E :    for (size_t i = 0; i < pdb_file.StreamCount(); ++i) {
 103  E :      PdbStream* stream = pdb_file.GetStream(i);
 104  E :      PdbStream* stream_read = pdb_file_read.GetStream(i);
 105    :  
 106  E :      ASSERT_TRUE(stream != NULL);
 107  E :      ASSERT_TRUE(stream_read != NULL);
 108    :  
 109  E :      ASSERT_EQ(stream->length(), stream_read->length());
 110    :  
 111  E :      std::vector<uint8> data;
 112  E :      std::vector<uint8> data_read;
 113  E :      ASSERT_TRUE(stream->Seek(0));
 114  E :      ASSERT_TRUE(stream_read->Seek(0));
 115  E :      ASSERT_TRUE(stream->Read(&data, stream->length()));
 116  E :      ASSERT_TRUE(stream_read->Read(&data_read, stream_read->length()));
 117    :  
 118    :      // We don't use ContainerEq because upon failure this generates a
 119    :      // ridiculously long and useless error message. We don't use memcmp because
 120    :      // it doesn't given any context as to where the failure occurs.
 121  E :      for (size_t j = 0; j < data.size(); ++j)
 122  E :        ASSERT_EQ(data[j], data_read[j]);
 123  E :    }
 124  E :  }
 125    :  
 126    :  }  // namespace
 127    :  
 128    :  using pdb::kPdbHeaderMagicString;
 129    :  using pdb::kPdbPageSize;
 130    :  using pdb::PdbHeader;
 131    :  using pdb::PdbReader;
 132    :  
 133  E :  TEST(PdbWriterTest, AppendStream) {
 134  E :    TestPdbWriter writer;
 135    :  
 136  E :    testing::ScopedTempFile temp_file;
 137  E :    writer.file().reset(file_util::OpenFile(temp_file.path(), "wb"));
 138  E :    ASSERT_TRUE(writer.file().get() != NULL);
 139    :  
 140    :    scoped_refptr<PdbStream> stream(
 141  E :        new TestPdbStream(4 * kPdbPageSize, 0));
 142    :  
 143    :    // Test writing a stream that will force allocation of the free page map
 144    :    // pages.
 145  E :    std::vector<uint32> pages_written;
 146  E :    uint32 page_count = 0;
 147  E :    EXPECT_TRUE(writer.AppendStream(stream.get(), &pages_written, &page_count));
 148  E :    writer.file().reset();
 149    :  
 150    :    // We expect pages_written to contain 4 pages, like the stream. However, we
 151    :    // expect page_count to have 2 more pages for the free page map.
 152  E :    uint32 expected_pages_written[] = { 0, 3, 4, 5 };
 153    :    EXPECT_THAT(pages_written,
 154  E :                ::testing::ElementsAreArray(expected_pages_written));
 155  E :    EXPECT_EQ(page_count, 6);
 156    :  
 157    :    // Build the expected stream contents. Two blank pages should have been
 158    :    // reserved by the append stream routine.
 159  E :    stream->Seek(0);
 160  E :    std::vector<uint8> expected_contents(6 * kPdbPageSize);
 161  E :    ASSERT_TRUE(stream->Read(expected_contents.data(), kPdbPageSize));
 162    :    ASSERT_TRUE(stream->Read(expected_contents.data() + 3 * kPdbPageSize,
 163  E :                             3 * kPdbPageSize));
 164    :  
 165  E :    std::vector<uint8> contents(6 * kPdbPageSize);
 166    :    ASSERT_EQ(contents.size(),
 167    :              file_util::ReadFile(temp_file.path(),
 168    :                                  reinterpret_cast<char*>(contents.data()),
 169  E :                                  contents.size()));
 170    :  
 171  E :    EXPECT_THAT(contents, ::testing::ContainerEq(expected_contents));
 172  E :  }
 173    :  
 174  E :  TEST(PdbWriterTest, WriteHeader) {
 175  E :    TestPdbWriter writer;
 176    :  
 177  E :    testing::ScopedTempFile temp_file;
 178  E :    writer.file().reset(file_util::OpenFile(temp_file.path(), "wb"));
 179  E :    ASSERT_TRUE(writer.file().get() != NULL);
 180    :  
 181  E :    std::vector<uint32> root_directory_pages(kPdbMaxDirPages + 10, 1);
 182    :  
 183    :    // Try to write a root directorty that's too big and expect this to fail.
 184  E :    EXPECT_FALSE(writer.WriteHeader(root_directory_pages, 67 * 4, 438));
 185    :  
 186    :    // Now write a reasonable root directory size.
 187  E :    root_directory_pages.resize(1);
 188  E :    EXPECT_TRUE(writer.WriteHeader(root_directory_pages, 67 * 4, 438));
 189  E :    writer.file().reset();
 190    :  
 191    :    // Build the expected stream contents. Two blank pages should have been
 192    :    // reserved by the append stream routine.
 193  E :    std::vector<uint8> expected_contents(sizeof(PdbHeader));
 194  E :    PdbHeader* header = reinterpret_cast<PdbHeader*>(expected_contents.data());
 195    :    ::memcpy(header->magic_string, kPdbHeaderMagicString,
 196  E :             kPdbHeaderMagicStringSize);
 197  E :    header->page_size = kPdbPageSize;
 198  E :    header->free_page_map = 1;
 199  E :    header->num_pages = 438;
 200  E :    header->directory_size = 67 * 4;
 201  E :    header->root_pages[0] = 1;
 202    :  
 203  E :    std::vector<uint8> contents(sizeof(PdbHeader));
 204    :    ASSERT_EQ(contents.size(),
 205    :              file_util::ReadFile(temp_file.path(),
 206    :                                  reinterpret_cast<char*>(contents.data()),
 207  E :                                  contents.size()));
 208    :  
 209  E :    EXPECT_THAT(contents, ::testing::ContainerEq(expected_contents));
 210  E :  }
 211    :  
 212  E :  TEST(PdbWriterTest, WritePdbFile) {
 213  E :    PdbFile pdb_file;
 214  E :    for (uint32 i = 0; i < 4; ++i)
 215  E :      pdb_file.AppendStream(new TestPdbStream(1 << (8 + i), (i << 24)));
 216    :  
 217    :    // Test that we can create a pdb file and then read it successfully.
 218  E :    testing::ScopedTempFile file;
 219    :    {
 220    :      // Create a scope so that the file gets closed.
 221  E :      TestPdbWriter writer;
 222  E :      EXPECT_TRUE(writer.Write(file.path(), pdb_file));
 223  E :    }
 224    :  
 225  E :    PdbFile pdb_file_read;
 226  E :    PdbReader reader;
 227  E :    EXPECT_TRUE(reader.Read(file.path(), &pdb_file_read));
 228    :  
 229    :    ASSERT_NO_FATAL_FAILURE(
 230  E :        EnsurePdbContentsAreIdentical(pdb_file, pdb_file_read));
 231  E :  }
 232    :  
 233  E :  TEST(PdbWriterTest, PdbStrCompatible) {
 234    :    base::FilePath test_dll_pdb =
 235  E :        testing::GetSrcRelativePath(testing::kTestPdbFilePath);
 236    :  
 237  E :    PdbFile file;
 238  E :    PdbReader reader;
 239  E :    ASSERT_TRUE(reader.Read(test_dll_pdb, &file));
 240    :  
 241    :    // We need at least 8 MB of data in the DLL to ensure that the free page map
 242    :    // requires a second page. We manually add data to it until we get to that
 243    :    // point.
 244  E :    int64 test_dll_pdb_length = 0;
 245  E :    ASSERT_TRUE(file_util::GetFileSize(test_dll_pdb, &test_dll_pdb_length));
 246  E :    while (test_dll_pdb_length < 9 * 1024 * 1024) {
 247  E :      file.AppendStream(new TestPdbStream(1024 * 1024, file.StreamCount()));
 248  E :      test_dll_pdb_length += 1024 * 1024;
 249  E :    }
 250    :  
 251    :    // Write the Syzygy modified PDB to disk.
 252  E :    base::ScopedTempDir temp_dir;
 253  E :    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 254  E :    base::FilePath pdb_path = temp_dir.path().Append(testing::kTestDllPdbName);
 255  E :    PdbWriter writer;
 256  E :    ASSERT_TRUE(writer.Write(pdb_path, file));
 257    :  
 258    :    // Write a new stream to disk.
 259  E :    base::FilePath stream_path = temp_dir.path().AppendASCII("new_stream.dat");
 260    :    scoped_refptr<TestPdbStream> new_stream(
 261  E :        new TestPdbStream(1024 * 1024, 0xff));
 262    :    {
 263    :      file_util::ScopedFILE stream_file(file_util::OpenFile(
 264  E :          stream_path, "wb"));
 265  E :      ASSERT_TRUE(stream_file.get() != NULL);
 266    :      ASSERT_EQ(new_stream->data().size(),
 267    :                ::fwrite(new_stream->data().data(),
 268    :                         sizeof(new_stream->data()[0]),
 269    :                         new_stream->data().size(),
 270  E :                         stream_file.get()));
 271  E :    }
 272    :  
 273    :    // Get the path to pdbstr.exe, which we redistribute in third_party.
 274    :    base::FilePath pdbstr_path =
 275  E :        testing::GetSrcRelativePath(testing::kPdbStrPath);
 276    :  
 277    :    // Create the arguments to pdbstr.
 278  E :    std::string pdb_arg = ::WideToUTF8(pdb_path.value());
 279  E :    pdb_arg.insert(0, "-p:");
 280  E :    std::string stream_arg = ::WideToUTF8(stream_path.value());
 281  E :    stream_arg.insert(0, "-i:");
 282    :  
 283    :    // Add a new stream to the PDB in place. This should produce no output.
 284    :    {
 285  E :      CommandLine cmd(pdbstr_path);
 286  E :      cmd.AppendArg(pdb_arg);
 287  E :      cmd.AppendArg(stream_arg);
 288  E :      cmd.AppendArg("-w");
 289  E :      cmd.AppendArg("-s:nonexistent-stream-name");
 290    :  
 291  E :      std::string output;
 292  E :      ASSERT_TRUE(base::GetAppOutput(cmd, &output));
 293  E :      ASSERT_TRUE(output.empty());
 294  E :    }
 295    :  
 296    :    // Read the pdbstr modified PDB.
 297  E :    PdbFile file_read;
 298  E :    ASSERT_TRUE(reader.Read(pdb_path, &file_read));
 299    :  
 300    :    // Add the new stream to the original PDB.
 301  E :    file.AppendStream(new_stream.get());
 302    :  
 303    :    // Clear stream 0 (the previous directory) and stream 1 (the PDB header
 304    :    // stream). These can vary but be functionally equivalent. We only care about
 305    :    // the actual content streams, which are the rest of them.
 306  E :    scoped_refptr<PdbStream> empty_stream(new TestPdbStream(0, 0));
 307  E :    file.ReplaceStream(0, empty_stream.get());
 308  E :    file.ReplaceStream(1, empty_stream.get());
 309  E :    file_read.ReplaceStream(0, empty_stream.get());
 310  E :    file_read.ReplaceStream(1, empty_stream.get());
 311    :  
 312    :    // Ensure that the two PDBs are identical.
 313    :    ASSERT_NO_FATAL_FAILURE(
 314  E :        EnsurePdbContentsAreIdentical(file, file_read));
 315  E :  }
 316    :  
 317    :  }  // namespace pdb

Coverage information generated Thu Jul 04 09:34:53 2013.