Coverage for /Syzygy/pdb/pdb_writer.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
81.8%1301590.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 Google Inc.
   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    :  }  // namespace
  28    :  
  29  E :  PdbWriter::PdbWriter() {
  30  E :  }
  31    :  
  32  E :  PdbWriter::~PdbWriter() {
  33  E :  }
  34    :  
  35  E :  bool PdbWriter::Write(const FilePath& pdb_path, const PdbFile& pdb_file) {
  36  E :    file_.reset(file_util::OpenFile(pdb_path, "wb"));
  37  E :    if (!file_.get()) {
  38  i :      LOG(ERROR) << "Failed to create '" << pdb_path.value() << "'.";
  39  i :      return false;
  40    :    }
  41    :  
  42  E :    uint32 total_bytes = 0;
  43    :  
  44    :    // Reserve space for the header and free page map.
  45    :    // TODO(rogerm): The free page map is a kludge. This should be sized to
  46    :    // correspond to the file instead of just one page. It should be relocated
  47    :    // to the end and sized properly.
  48  E :    if (fseek(file_.get(), kPdbPageSize * 3, SEEK_SET) != 0) {
  49  i :      LOG(ERROR) << "Failed to reserve header and free page map.";
  50  i :      return false;
  51    :    }
  52  E :    total_bytes += kPdbPageSize * 3;
  53    :  
  54    :    // Append all the streams after the header.
  55  E :    StreamInfoList stream_info_list;
  56  E :    for (size_t i = 0; i < pdb_file.StreamCount(); ++i) {
  57  E :      PdbStream* stream = pdb_file.GetStream(i);
  58  E :      if (stream == NULL) {
  59    :        // A null stream is treated as an empty one.
  60  E :        StreamInfo info = { total_bytes, 0 };
  61  E :        stream_info_list.push_back(info);
  62  E :      } else {
  63    :        // Save the offset and length for later.
  64  E :        StreamInfo info = { total_bytes, stream->length() };
  65  E :        stream_info_list.push_back(info);
  66    :  
  67  E :        uint32 bytes_written = 0;
  68  E :        if (!AppendStream(stream, &bytes_written))
  69  i :          return false;
  70    :  
  71  E :        total_bytes += bytes_written;
  72  E :        DCHECK_EQ(0U, total_bytes % kPdbPageSize);
  73    :      }
  74  E :    }
  75    :  
  76    :    // Map out the directory: i.e., pages on which the streams have been written.
  77  E :    uint32 dir_size = 0;
  78  E :    uint32 dir_page = total_bytes / kPdbPageSize;
  79  E :    uint32 bytes_written = 0;
  80  E :    if (!WriteDirectory(stream_info_list, &dir_size, &bytes_written))
  81  i :      return false;
  82  E :    total_bytes += bytes_written;
  83    :  
  84    :    // Map out the directory roots: i.e., pages on which the directory has been
  85    :    // written.
  86  E :    uint32 dir_root_size = 0;
  87  E :    uint32 dir_root_page = (total_bytes / kPdbPageSize);
  88  E :    if (!WriteDirectoryPages(dir_size, dir_page, &dir_root_size, &bytes_written))
  89  i :      return false;
  90  E :    total_bytes += bytes_written;
  91    :  
  92    :    // Fill in the MSF header.
  93  E :    if (!WriteHeader(total_bytes, dir_size, dir_root_size, dir_root_page))
  94  i :      return false;
  95    :  
  96    :    // On success we want the file to be closed right away.
  97  E :    file_.reset();
  98  E :    return true;
  99  E :  }
 100    :  
 101    :  // Write an unsigned 32 bit value to the output file.
 102    :  bool PdbWriter::WriteUint32(const char* func,
 103    :                              const char* desc,
 104  E :                              uint32 value) {
 105  E :    DCHECK(func != NULL);
 106  E :    DCHECK(desc != NULL);
 107    :  
 108  E :    if (fwrite(&value, sizeof(value), 1, file_.get()) != 1) {
 109  i :      LOG(ERROR) << func << ": Error writing " << desc;
 110  i :      return false;
 111    :    }
 112    :  
 113  E :    return true;
 114  E :  }
 115    :  
 116    :  bool PdbWriter::PadToPageBoundary(const char* func,
 117    :                                    uint32 offset,
 118  E :                                    uint32* padding) {
 119  E :    DCHECK(padding != NULL);
 120    :  
 121  E :    *padding = (kPdbPageSize - (offset % kPdbPageSize)) % kPdbPageSize;
 122  E :    if (fwrite(kZeroBuffer, 1, *padding, file_.get()) != *padding) {
 123  i :      LOG(ERROR) << func << ": Error padding page";
 124  i :      return false;
 125    :    }
 126    :  
 127  E :    return true;
 128  E :  }
 129    :  
 130  E :  bool PdbWriter::AppendStream(PdbStream* stream, uint32* bytes_written) {
 131  E :    DCHECK(bytes_written != NULL);
 132    :  
 133    :    // Append the contents of source to output file.
 134  E :    stream->Seek(0);
 135    :    uint8 buffer[1 << 16];
 136  E :    while (true) {
 137  E :      size_t bytes_read = 0;
 138  E :      if (!stream->ReadBytes(buffer, sizeof(buffer), &bytes_read)) {
 139  i :        LOG(ERROR) << "Error reading from pdb stream";
 140  i :        return false;
 141  E :      } else if (bytes_read == 0) {
 142  E :        break;
 143    :      }
 144    :  
 145  E :      if (fwrite(buffer, 1, bytes_read, file_.get()) != bytes_read) {
 146  i :        LOG(ERROR) << "Error appending pdb stream to file";
 147  i :        return false;
 148    :      }
 149  E :    }
 150    :  
 151    :    // Pad to the end of the current page boundary.
 152  E :    uint32 padding = 0;
 153  E :    if (!PadToPageBoundary("AppendStream", stream->length(), &padding))
 154  i :      return false;
 155    :  
 156  E :    *bytes_written = stream->length() + padding;
 157  E :    DCHECK_EQ(0U, (*bytes_written) % kPdbPageSize);
 158    :  
 159  E :    return true;
 160  E :  }
 161    :  
 162    :  bool PdbWriter::WriteDirectory(const StreamInfoList& stream_info_list,
 163    :                                 uint32* dir_size,
 164  E :                                 uint32* bytes_written) {
 165    :    static const char* func = "WriteDirectory";
 166    :  
 167  E :    VLOG(1) << "Writing directory ...";
 168  E :    DCHECK(dir_size != NULL);
 169  E :    DCHECK(bytes_written != NULL);
 170    :  
 171    :  
 172    :    // The directory format is:
 173    :    //    num_streams   (32-bit)
 174    :    //    + stream_length (32-bit) for each stream in num_streams
 175    :    //    + page_offset   (32-bit) for each page in each stream in num_streams
 176    :  
 177  E :    uint32 byte_count = 0;
 178    :  
 179    :    // Write the number of streams.
 180  E :    if (!WriteUint32(func, "stream count", stream_info_list.size()))
 181  i :      return false;
 182  E :    byte_count += sizeof(uint32);
 183    :  
 184    :    // Write the length of each stream.
 185  E :    for (StreamInfoList::const_iterator iter = stream_info_list.begin();
 186  E :         iter != stream_info_list.end(); ++iter) {
 187  E :      if (!WriteUint32(func, "stream length", iter->length))
 188  i :        return false;
 189  E :      byte_count += sizeof(uint32);
 190  E :    }
 191    :  
 192    :    // Write the page numbers for each page in each stream.
 193  E :    for (StreamInfoList::const_iterator iter = stream_info_list.begin();
 194  E :         iter != stream_info_list.end(); ++iter) {
 195  E :      DCHECK_EQ(0U, iter->offset % kPdbPageSize);
 196  E :      for (uint32 length = 0, page_number = iter->offset / kPdbPageSize;
 197  E :           length < iter->length;
 198  E :           length += kPdbPageSize, ++page_number) {
 199  E :        if (!WriteUint32(func, "page offset", page_number))
 200  i :          return false;
 201  E :        byte_count += sizeof(uint32);
 202  E :      }
 203  E :    }
 204    :  
 205    :    // Pad the directory to the next page boundary.
 206  E :    uint32 padding = 0;
 207  E :    if (!PadToPageBoundary(func, byte_count, &padding))
 208  i :      return false;
 209    :  
 210    :    // Return the output values
 211  E :    (*dir_size) = byte_count;
 212  E :    (*bytes_written) = byte_count + padding;
 213    :  
 214  E :    DCHECK_EQ(0U, (*bytes_written) % kPdbPageSize);
 215  E :    return true;
 216  E :  }
 217    :  
 218    :  bool PdbWriter::WriteDirectoryPages(uint32 dir_size,
 219    :                                      uint32 dir_page,
 220    :                                      uint32* dir_pages_size,
 221  E :                                      uint32* bytes_written) {
 222    :    static const char* func = "WriteDirectoryPages";
 223    :  
 224  E :    VLOG(1) << "Writing directory roots...";
 225  E :    DCHECK(dir_pages_size != NULL);
 226  E :    DCHECK(bytes_written != NULL);
 227    :  
 228    :    // Write all page offsets that are used in the directory.
 229  E :    uint32 byte_count = 0;
 230  E :    for (uint32 page_offset = 0; page_offset < dir_size;
 231  E :         page_offset += kPdbPageSize, ++dir_page) {
 232  E :      if (!WriteUint32(func, "page offset", dir_page))
 233  i :        return false;
 234  E :      byte_count += sizeof(uint32);
 235  E :    }
 236    :  
 237    :    // Pad to a page boundary.
 238  E :    uint32 padding = 0;
 239  E :    if (!PadToPageBoundary(func, byte_count, &padding))
 240  i :      return false;
 241    :  
 242  E :    (*dir_pages_size) = byte_count;
 243  E :    (*bytes_written) = byte_count + padding;
 244    :  
 245  E :    DCHECK_EQ(0U, (*bytes_written) % kPdbPageSize);
 246  E :    return true;
 247  E :  }
 248    :  
 249    :  bool PdbWriter::WriteHeader(uint32 file_size,
 250    :                              uint32 dir_size,
 251    :                              uint32 dir_root_size,
 252  E :                              uint32 dir_root_page) {
 253  E :    VLOG(1) << "Writing MSF Header ...";
 254  E :    DCHECK_EQ(0U, file_size % kPdbPageSize);
 255    :  
 256    :    // Make sure the root pages list won't overflow.
 257  E :    if ((dir_root_size + kPdbPageSize - 1) / kPdbPageSize > kPdbMaxDirPages) {
 258  i :      LOG(ERROR) << "Too many directory root pages";
 259  i :      return false;
 260    :    }
 261    :  
 262  E :    if (fseek(file_.get(), 0, SEEK_SET) != 0) {
 263  i :      LOG(ERROR) << "Seek failed when writing header";
 264  i :      return false;
 265    :    }
 266    :  
 267  E :    PdbHeader header = { 0 };
 268    :    memcpy(header.magic_string, kPdbHeaderMagicString,
 269  E :           sizeof(kPdbHeaderMagicString));
 270  E :    header.page_size = kPdbPageSize;
 271  E :    header.free_page_map = 1;
 272  E :    header.num_pages = file_size / kPdbPageSize;
 273  E :    header.directory_size = dir_size;
 274  E :    header.reserved = 0;
 275    :  
 276  E :    for (uint32 page_offset = 0; page_offset < dir_root_size;
 277  E :         page_offset += kPdbPageSize, ++dir_root_page) {
 278  E :      header.root_pages[page_offset / kPdbPageSize] = dir_root_page;
 279  E :    }
 280    :  
 281  E :    if (fwrite(&header, sizeof(header), 1, file_.get()) != 1) {
 282  i :      LOG(ERROR) << "Failed writing header";
 283  i :      return false;
 284    :    }
 285    :  
 286  E :    return true;
 287  E :  }
 288    :  
 289    :  }  // namespace pdb

Coverage information generated Thu Sep 06 11:30:46 2012.