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

Coverage information generated Thu Mar 14 11:53:36 2013.