Coverage for /Syzygy/pdb/pdb_writer.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
82.9%1702050.C++source

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/logging.h"
  18    :  #include "syzygy/pdb/pdb_constants.h"
  19    :  #include "syzygy/pdb/pdb_data.h"
  20    :  
  21    :  namespace pdb {
  22    :  
  23    :  namespace {
  24    :  
  25    :  const uint32 kZeroBuffer[kPdbPageSize] = { 0 };
  26    :  
  27    :  // A byte-based bitmap for keeping track of free pages in an MSF/PDB file.
  28    :  // TODO(chrisha): Promote this to its own file and unittest it when we make
  29    :  //     a library for MSF-specific stuff.
  30    :  class FreePageBitMap {
  31    :   public:
  32  E :    FreePageBitMap() : page_count_(0) {
  33  E :    }
  34    :  
  35  E :    void SetPageCount(uint32 page_count) {
  36  E :      page_count_ = page_count;
  37  E :      data_.resize((page_count + 7) / 8);
  38    :  
  39    :      // Double check our invariant.
  40  E :      DCHECK_LE(page_count, data_.size() * 8);
  41  E :      DCHECK_LE(page_count / 8, data_.size());
  42  E :    }
  43    :  
  44  E :    void SetBit(uint32 page_index, bool free) {
  45  E :      DCHECK_LT(page_index, page_count_);
  46    :  
  47  E :      uint32 byte = page_index / 8;
  48  E :      uint32 bit = page_index % 8;
  49  E :      uint8 bitmask = 1 << bit;
  50  E :      DCHECK_LT(byte, data_.size());
  51    :  
  52  E :      if (free) {
  53  E :        data_[byte] |= bitmask;
  54  E :      } else {
  55  i :        data_[byte] &= ~bitmask;
  56    :      }
  57  E :    }
  58    :  
  59  E :    void SetFree(uint32 page_index) { SetBit(page_index, true); }
  60    :    void SetUsed(uint32 page_index) { SetBit(page_index, false); }
  61    :  
  62    :    // TODO(chrisha): Make this an invariant of the class and move the logic
  63    :    //     to SetPageCount. This involves both clearing and setting bits in that
  64    :    //     case.
  65  E :    void Finalize() {
  66  E :      uint32 bits_left = data_.size() * 8 - page_count_;
  67  E :      DCHECK_LE(bits_left, 7u);
  68    :  
  69    :      // This leaves the top |bits_left| bits set.
  70  E :      uint8 bitmask = ~(0xFF >> bits_left);
  71    :  
  72    :      // Mark any bits as free beyond those specifically allocated.
  73  E :      data_.back() |= bitmask;
  74  E :    }
  75    :  
  76  E :    const std::vector<uint8>& data() const { return data_; }
  77    :  
  78    :   private:
  79    :    std::vector<uint8> data_;
  80    :    uint32 page_count_;
  81    :  };
  82    :  
  83    :  // A light-weight wrapper that allows a previously allocated buffer to be read
  84    :  // as a PdbStream.
  85    :  class ReadOnlyPdbStream : public PdbStream {
  86    :   public:
  87  E :    ReadOnlyPdbStream(const void* data, size_t bytes)
  88    :        : PdbStream(bytes), data_(data) {
  89  E :    }
  90    :  
  91    :    virtual bool ReadBytes(
  92  E :        void* dest, size_t count, size_t* bytes_read) OVERRIDE {
  93  E :      DCHECK(dest != NULL);
  94  E :      DCHECK(bytes_read != NULL);
  95    :  
  96  E :      bool result = true;
  97  E :      size_t bytes_to_read = count;
  98  E :      size_t bytes_left = length() - pos();
  99  E :      if (bytes_left < bytes_to_read) {
 100  i :        bytes_to_read = bytes_left;
 101  i :        result = false;
 102    :      }
 103    :  
 104    :      ::memcpy(dest, reinterpret_cast<const uint8*>(data_) + pos(),
 105  E :               bytes_to_read);
 106  E :      Seek(pos() + bytes_to_read);
 107    :  
 108  E :      *bytes_read = bytes_to_read;
 109  E :      return result;
 110  E :    }
 111    :  
 112    :   private:
 113    :    const void* data_;
 114    :  };
 115    :  
 116    :  // Appends a page to the provided file, adding the written page ID to the vector
 117    :  // of @p pages_written, and incrementing the total @P page_count. This will
 118    :  // occasionally cause more than one single page to be written to the output,
 119    :  // thus advancing @p page_count by more than one (when reserving pages for the
 120    :  // free page map). It is expected that @data be kPdbPageSize in length.
 121    :  // @pre the file is expected to be positioned at @p *page_count * kPdbPageSize
 122    :  //     when entering this routine
 123    :  // @post the file will be positioned at @p *page_count * kPdbPageSiz when
 124    :  //     exiting this routine.
 125    :  bool AppendPage(const void* data,
 126    :                  std::vector<uint32>* pages_written,
 127    :                  uint32* page_count,
 128  E :                  FILE* file) {
 129  E :    DCHECK(data != NULL);
 130  E :    DCHECK(pages_written != NULL);
 131  E :    DCHECK(page_count != NULL);
 132  E :    DCHECK(file != NULL);
 133    :  
 134  E :    uint32 local_page_count = *page_count;
 135    :  
 136    :    // The file is written sequentially, so it will already be pointing to
 137    :    // the appropriate spot.
 138    :    DCHECK_EQ(local_page_count * kPdbPageSize,
 139  E :              static_cast<uint32>(::ftell(file)));
 140    :  
 141    :    // If we're due to allocate pages for the free page map, then do so.
 142  E :    if (((*page_count) % kPdbPageSize) == 1) {
 143    :      if (::fwrite(kZeroBuffer, 1, kPdbPageSize, file) != kPdbPageSize ||
 144  E :          ::fwrite(kZeroBuffer, 1, kPdbPageSize, file) != kPdbPageSize) {
 145  i :        LOG(ERROR) << "Failed to allocate free page map pages.";
 146  i :        return false;
 147    :      }
 148  E :      local_page_count += 2;
 149    :    }
 150    :  
 151    :    // Write the page itself.
 152  E :    if (::fwrite(data, 1, kPdbPageSize, file) != kPdbPageSize) {
 153  i :      LOG(ERROR) << "Failed to write page " << *page_count << ".";
 154  i :      return false;
 155    :    }
 156  E :    pages_written->push_back(local_page_count);
 157  E :    ++local_page_count;
 158    :  
 159    :    DCHECK_EQ(local_page_count * kPdbPageSize,
 160  E :              static_cast<uint32>(::ftell(file)));
 161    :  
 162  E :    *page_count = local_page_count;
 163  E :    return true;
 164  E :  }
 165    :  
 166  E :  bool WriteFreePageBitMap(const FreePageBitMap& free, FILE* file) {
 167  E :    DCHECK(file != NULL);
 168    :  
 169  E :    const uint8* data = free.data().data();
 170  E :    size_t bytes_left = free.data().size();
 171  E :    size_t page_index = 1;
 172  E :    size_t bytes_to_write = kPdbPageSize;
 173  E :    while (true) {
 174  E :      if (::fseek(file, page_index * kPdbPageSize, SEEK_SET) != 0) {
 175  i :        LOG(ERROR) << "Failed to seek to page " << page_index << ".";
 176  i :        return false;
 177    :      }
 178    :  
 179  E :      bytes_to_write = kPdbPageSize;
 180  E :      if (bytes_left < bytes_to_write)
 181  E :        bytes_to_write = bytes_left;
 182    :  
 183  E :      if (::fwrite(data, 1, bytes_to_write, file) != bytes_to_write) {
 184  i :        LOG(ERROR) << "Failed to write page " << page_index
 185    :                   << " of free page map.";
 186  i :        return false;
 187    :      }
 188    :  
 189  E :      bytes_left -= bytes_to_write;
 190  E :      if (bytes_left == 0)
 191  E :        break;
 192    :  
 193  E :      data += bytes_to_write;
 194  E :      page_index += kPdbPageSize;
 195  E :    }
 196    :  
 197    :    // Was the last write partial? If so, we need to flush out the rest of the
 198    :    // free page map with ones (0xFF bytes).
 199  E :    if (bytes_to_write < kPdbPageSize) {
 200    :      // Create a vector of bytes with all the bits set.
 201  E :      std::vector<uint8> ones(kPdbPageSize - bytes_to_write, 0xFF);
 202  E :      if (::fwrite(ones.data(), 1, ones.size(), file) != ones.size()) {
 203  i :        LOG(ERROR) << "Failed to pad page " << page_index
 204    :                   << " of free page map.";
 205  i :        return false;
 206    :      }
 207  E :    }
 208    :  
 209  E :    return true;
 210  E :  }
 211    :  
 212    :  }  // namespace
 213    :  
 214  E :  PdbWriter::PdbWriter() {
 215  E :  }
 216    :  
 217  E :  PdbWriter::~PdbWriter() {
 218  E :  }
 219    :  
 220  E :  bool PdbWriter::Write(const base::FilePath& pdb_path, const PdbFile& pdb_file) {
 221  E :    file_.reset(file_util::OpenFile(pdb_path, "wb"));
 222  E :    if (!file_.get()) {
 223  i :      LOG(ERROR) << "Failed to create '" << pdb_path.value() << "'.";
 224  i :      return false;
 225    :    }
 226    :  
 227    :    // Initialize the directory with stream count and lengths.
 228  E :    std::vector<uint32> directory;
 229  E :    directory.push_back(pdb_file.StreamCount());
 230  E :    for (size_t i = 0; i < pdb_file.StreamCount(); ++i) {
 231    :      // Null streams have an implicit zero length.
 232  E :      PdbStream* stream = pdb_file.GetStream(i);
 233  E :      if (stream == NULL)
 234  E :        directory.push_back(0);
 235  E :      else
 236  E :        directory.push_back(stream->length());
 237  E :    }
 238    :  
 239    :    // Reserve space for the header page, the two free page map pages, and a
 240    :    // fourth empty page. The fourth empty page doesn't appear to be strictly
 241    :    // necessary but MSF/PDB files produced by MS tools always contain it.
 242  E :    uint32 page_count = 4;
 243  E :    for (uint32 i = 0; i < page_count; ++i) {
 244  E :      if (::fwrite(kZeroBuffer, 1, kPdbPageSize, file_.get()) != kPdbPageSize) {
 245  i :        LOG(ERROR) << "Failed to allocate preamble page.";
 246  i :        return false;
 247    :      }
 248  E :    }
 249    :  
 250    :    // Append all the streams after the preamble and build the directory while
 251    :    // we're at it. We keep track of which pages host stream 0 for some free page
 252    :    // map bookkeeping later on.
 253  E :    size_t stream0_start = directory.size();
 254  E :    size_t stream0_end = 0;
 255  E :    for (size_t i = 0; i < pdb_file.StreamCount(); ++i) {
 256  E :      if (i == 1)
 257  E :        stream0_end = directory.size();
 258    :  
 259    :      // Null streams are treated as empty streams.
 260  E :      PdbStream* stream = pdb_file.GetStream(i);
 261  E :      if (stream == NULL || stream->length() == 0)
 262  E :        continue;
 263    :  
 264    :      // Write the stream, updating the directory and page index. This routine
 265    :      // takes care of making room for the free page map pages.
 266  E :      if (!AppendStream(stream, &directory, &page_count)) {
 267  i :        LOG(ERROR) << "Failed to write stream " << i << ".";
 268  i :        return false;
 269    :      }
 270  E :    }
 271  E :    DCHECK_LE(stream0_start, stream0_end);
 272    :  
 273    :    // Write the directory, and keep track of the pages it is written to.
 274  E :    std::vector<uint32> directory_pages;
 275    :    scoped_refptr<PdbStream> directory_stream(new ReadOnlyPdbStream(
 276  E :        directory.data(), sizeof(directory[0]) * directory.size()));
 277  E :    if (!AppendStream(directory_stream.get(), &directory_pages, &page_count)) {
 278  i :      LOG(ERROR) << "Failed to write directory.";
 279  i :      return false;
 280    :    }
 281    :  
 282    :    // Write the root directory, and keep track of the pages it is written to.
 283    :    // These will in turn go into the header root directory pointers.
 284  E :    std::vector<uint32> root_directory_pages;
 285    :    scoped_refptr<PdbStream> root_directory_stream(new ReadOnlyPdbStream(
 286    :        directory_pages.data(),
 287  E :        sizeof(directory_pages[0]) * directory_pages.size()));
 288    :    if (!AppendStream(root_directory_stream.get(), &root_directory_pages,
 289  E :                      &page_count)) {
 290  i :      LOG(ERROR) << "Failed to write root directory.";
 291  i :      return false;
 292    :    }
 293    :  
 294    :    // Write the header.
 295    :    if (!WriteHeader(root_directory_pages,
 296    :                     sizeof(directory[0]) * directory.size(),
 297  E :                     page_count)) {
 298  i :      LOG(ERROR) << "Failed to write PDB header.";
 299  i :      return false;
 300    :    }
 301    :  
 302    :    // Initialize the free page bit map. The pages corresponding to stream 0 are
 303    :    // always marked as free, as well as page 3 which we allocated in the
 304    :    // preamble.
 305  E :    FreePageBitMap free;
 306  E :    free.SetPageCount(page_count);
 307  E :    free.SetFree(3);
 308  E :    for (size_t i = stream0_start; i < stream0_end; ++i)
 309  E :      free.SetFree(directory[i]);
 310  E :    free.Finalize();
 311    :  
 312  E :    if (!WriteFreePageBitMap(free, file_.get())) {
 313  i :      LOG(ERROR) << "Failed to write free page bitmap.";
 314  i :      return false;
 315    :    }
 316    :  
 317    :    // On success we want the file to be closed right away.
 318  E :    file_.reset();
 319    :  
 320  E :    return true;
 321  E :  }
 322    :  
 323    :  bool PdbWriter::AppendStream(PdbStream* stream,
 324    :                               std::vector<uint32>* pages_written,
 325  E :                               uint32* page_count) {
 326  E :    DCHECK(stream != NULL);
 327  E :    DCHECK(pages_written != NULL);
 328  E :    DCHECK(page_count != NULL);
 329    :  
 330    :  #ifndef NDEBUG
 331  E :    size_t old_pages_written_count = pages_written->size();
 332  E :    size_t old_page_count = *page_count;
 333    :  #endif
 334    :  
 335    :    // Write the stream page by page.
 336  E :    stream->Seek(0);
 337  E :    uint8 buffer[kPdbPageSize] = { 0 };
 338  E :    size_t bytes_left = stream->length();
 339  E :    while (bytes_left) {
 340  E :      size_t bytes_to_read = sizeof(buffer);
 341  E :      if (bytes_to_read > bytes_left) {
 342  E :        bytes_to_read = bytes_left;
 343    :  
 344    :        // If we're only reading a partial buffer then pad the end of it with
 345    :        // zeros.
 346  E :        ::memset(buffer + bytes_to_read, 0, sizeof(buffer) - bytes_to_read);
 347    :      }
 348    :  
 349    :      // Read the buffer from the stream.
 350  E :      size_t bytes_read = 0;
 351    :      if (!stream->ReadBytes(buffer, bytes_to_read, &bytes_read) ||
 352  E :          bytes_read != bytes_to_read) {
 353  i :        size_t offset = stream->length() - bytes_left;
 354  i :        LOG(ERROR) << "Failed to read " << bytes_to_read << " bytes at offset "
 355    :                   << offset << " of PDB stream.";
 356  i :        return false;
 357    :      }
 358    :  
 359  E :      if (!AppendPage(buffer, pages_written, page_count, file_.get()))
 360  i :        return false;
 361    :  
 362  E :      bytes_left -= bytes_read;
 363  E :    }
 364  E :    DCHECK_EQ(0u, bytes_left);
 365    :  
 366    :  #ifndef NDEBUG
 367    :    size_t expected_pages_written = (stream->length() + kPdbPageSize - 1) /
 368  E :        kPdbPageSize;
 369    :    DCHECK_EQ(old_pages_written_count + expected_pages_written,
 370  E :              pages_written->size());
 371    :    // We can't say anything about |page_count| as AppendPage occasionally snags
 372    :    // extra pages for the free page map.
 373    :  #endif
 374    :  
 375  E :    return true;
 376  E :  }
 377    :  
 378    :  bool PdbWriter::WriteHeader(const std::vector<uint32>& root_directory_pages,
 379    :                              size_t directory_size,
 380  E :                              uint32 page_count) {
 381  E :    VLOG(1) << "Writing MSF Header ...";
 382    :  
 383  E :    PdbHeader header = { 0 };
 384    :  
 385    :    // Make sure the root directory pointers won't overflow.
 386  E :    if (root_directory_pages.size() > arraysize(header.root_pages)) {
 387  E :      LOG(ERROR) << "Too many root directory pages for header ("
 388    :                 << root_directory_pages.size() << " > "
 389    :                 << arraysize(header.root_pages) << ").";
 390  E :      return false;
 391    :    }
 392    :  
 393    :    // Seek to the beginning of the file so we can stamp in the header.
 394  E :    if (::fseek(file_.get(), 0, SEEK_SET) != 0) {
 395  i :      LOG(ERROR) << "Seek failed while writing header.";
 396  i :      return false;
 397    :    }
 398    :  
 399    :    ::memcpy(header.magic_string, kPdbHeaderMagicString,
 400  E :             sizeof(kPdbHeaderMagicString));
 401  E :    header.page_size = kPdbPageSize;
 402  E :    header.free_page_map = 1;
 403  E :    header.num_pages = page_count;
 404  E :    header.directory_size = directory_size;
 405  E :    header.reserved = 0;
 406    :    ::memcpy(header.root_pages,
 407    :             root_directory_pages.data(),
 408  E :             sizeof(root_directory_pages[0]) * root_directory_pages.size());
 409    :  
 410  E :    if (::fwrite(&header, sizeof(header), 1, file_.get()) != 1) {
 411  i :      LOG(ERROR) << "Failed to write header.";
 412  i :      return false;
 413    :    }
 414    :  
 415  E :    return true;
 416  E :  }
 417    :  
 418    :  }  // namespace pdb

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