Coverage for /Syzygy/pdb/pdb_util.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
78.3%3504470.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    :  #include "syzygy/pdb/pdb_util.h"
  15    :  
  16    :  #include <algorithm>
  17    :  #include <string>
  18    :  
  19    :  #include "base/strings/stringprintf.h"
  20    :  #include "syzygy/pdb/pdb_byte_stream.h"
  21    :  #include "syzygy/pdb/pdb_file.h"
  22    :  #include "syzygy/pdb/pdb_reader.h"
  23    :  #include "syzygy/pdb/pdb_writer.h"
  24    :  
  25    :  namespace pdb {
  26    :  
  27    :  namespace {
  28    :  
  29    :  // Sets the stream associated with a given entry in the DBI DBG header.
  30    :  // Gets the index at position @p index_offset of the DBI DBG header. If invalid,
  31    :  // adds a new stream to the PDB and updates the index to point to it. If a valid
  32    :  // stream already exists, replaces it with the new @p stream.
  33    :  // @param index_offset the offset of the int16 stream index within the DBI DBG
  34    :  //     header.
  35    :  // @param stream the stream to be associated with the given DBI DBG entry.
  36    :  // @param pdb_file the PDB file to be updated.
  37    :  bool SetDbiDbgStream(size_t index_offset,
  38    :                       PdbStream* stream,
  39  E :                       PdbFile* pdb_file) {
  40  E :    DCHECK(pdb_file != NULL);
  41    :  
  42  E :    if (!EnsureStreamWritable(kDbiStream, pdb_file)) {
  43  i :      LOG(ERROR) << "Failed to make DBI stream writable.";
  44  i :      return false;
  45    :    }
  46    :  
  47  E :    scoped_refptr<PdbStream> dbi_reader(pdb_file->GetStream(kDbiStream));
  48  E :    scoped_refptr<WritablePdbStream> dbi_writer(dbi_reader->GetWritableStream());
  49  E :    DCHECK(dbi_writer.get() != NULL);
  50    :  
  51    :    // Read the DBI header.
  52  E :    DbiHeader dbi_header = {};
  53  E :    if (!dbi_reader->Seek(0) || !dbi_reader->Read(&dbi_header, 1)) {
  54  i :      LOG(ERROR) << "Failed to read DBI header.";
  55  i :      return false;
  56    :    }
  57    :  
  58    :    // Get the stream index at the provided offset.
  59  E :    uint32 dbi_dbg_offset = GetDbiDbgHeaderOffset(dbi_header);
  60  E :    int16 existing_index = -1;
  61    :    if (!dbi_reader->Seek(dbi_dbg_offset + index_offset) ||
  62  E :        !dbi_reader->Read(&existing_index, 1)) {
  63  i :      LOG(ERROR) << "Failed to read stream index at offset " << dbi_dbg_offset
  64    :                 << " of DBI DBG header.";
  65  i :      return false;
  66    :    }
  67    :  
  68    :    // If the stream is an invalid index, we create a new one.
  69  E :    int16 new_index = existing_index;
  70    :    if (existing_index < 0 ||
  71  E :        existing_index >= static_cast<int16>(pdb_file->StreamCount())) {
  72  E :      new_index = static_cast<int16>(pdb_file->AppendStream(stream));
  73  E :    } else {
  74  i :      pdb_file->ReplaceStream(new_index, stream);
  75    :    }
  76    :  
  77    :    // Update the index in the header if we need to.
  78  E :    if (new_index != existing_index) {
  79  E :      dbi_writer->set_pos(dbi_dbg_offset + index_offset);
  80  E :      if (!dbi_writer->Write(new_index)) {
  81  i :        LOG(ERROR) << "Failed to write stream index at offset " << dbi_dbg_offset
  82    :                   << " of DBI DBG header.";
  83  i :        return false;
  84    :      }
  85    :    }
  86    :  
  87  E :    return true;
  88  E :  }
  89    :  
  90    :  bool SetOmapStream(size_t dbi_dbg_index_offset,
  91    :                     const std::vector<OMAP>& omap_list,
  92  E :                     PdbFile* pdb_file) {
  93  E :    DCHECK(pdb_file != NULL);
  94    :  
  95  E :    scoped_refptr<PdbByteStream> stream = new PdbByteStream();
  96  E :    if (!omap_list.empty()) {
  97    :      if (!stream->Init(reinterpret_cast<const uint8*>(&omap_list.at(0)),
  98  E :                        omap_list.size() * sizeof(OMAP))) {
  99  i :        LOG(ERROR) << "Failed to initialize OMAP stream.";
 100  i :        return false;
 101    :      }
 102    :    }
 103    :  
 104  E :    return SetDbiDbgStream(dbi_dbg_index_offset, stream.get(), pdb_file);
 105  E :  }
 106    :  
 107    :  // Calculates the size of the hash table required to represent a named stream
 108    :  // hash containing the given number of entries. This has been determined from
 109    :  // inspection of the output produced by pdbstr.exe.
 110  E :  size_t GetNamedStreamsHashTableSize(size_t entries) {
 111    :    // This produces the sequence of sizes: 6, 10, 14, 20, 28, 38, 52, etc.
 112  E :    size_t size = 3;
 113  E :    while (true) {
 114  E :      size_t threshold = 2 * size / 3;
 115  E :      if (entries <= threshold)
 116  E :        return size;
 117  E :      size = 2 * (threshold + 1);
 118  E :    }
 119  E :  }
 120    :  
 121    :  // Sets the bit at the given index, with collision semantics. Returns the index
 122    :  // of the bit that was set. Note that there must be at least one bit that is not
 123    :  // already set, otherwise this will loop forever.
 124    :  size_t SetNamedStreamsHashTableBit(
 125  E :      size_t index, size_t max, PdbBitSet* bitset) {
 126  E :    DCHECK(bitset != NULL);
 127    :  
 128  E :    while (bitset->IsSet(index)) {
 129  E :      ++index;
 130  E :      if (index == max)
 131  i :        index = 0;
 132  E :    }
 133    :  
 134  E :    bitset->Set(index);
 135  E :    return index;
 136  E :  }
 137    :  
 138    :  // A small struct that is used for storing information regarding the name-stream
 139    :  // map, and the associated hash table. The (string table offset, stream id)
 140    :  // pairs need to be output in order of the hash value, which is the default sort
 141    :  // order of this object.
 142    :  struct NamedStreamInfo {
 143  E :    NamedStreamInfo(NameStreamMap::const_iterator it,
 144    :                    uint32 offset,
 145    :                    uint32 bucket)
 146    :        : it(it), offset(offset), bucket(bucket) {
 147  E :    }
 148    :  
 149  E :    bool operator<(const NamedStreamInfo& rhs) const {
 150  E :      return bucket < rhs.bucket;
 151  E :    }
 152    :  
 153    :    // An iterator into the (name, stream_id) map.
 154    :    NameStreamMap::const_iterator it;
 155    :    // The offset of the name in the string table.
 156    :    uint32 offset;
 157    :    // The bucket that this name occupies in the hash map, after collision
 158    :    // resolution. This is the sort key.
 159    :    uint32 bucket;
 160    :  };
 161    :  
 162    :  }  // namespace
 163    :  
 164  E :  bool PdbBitSet::Read(PdbStream* stream) {
 165  E :    DCHECK(stream != NULL);
 166  E :    uint32 size = 0;
 167  E :    if (!stream->Read(&size, 1)) {
 168  E :      LOG(ERROR) << "Failed to read bitset size.";
 169  E :      return false;
 170    :    }
 171  E :    if (!stream->Read(&bits_, size)) {
 172  i :      LOG(ERROR) << "Failed to read bitset bits.";
 173  i :      return false;
 174    :    }
 175    :  
 176  E :    return true;
 177  E :  }
 178    :  
 179  E :  bool PdbBitSet::Write(WritablePdbStream* stream, bool with_size) {
 180  E :    DCHECK(stream != NULL);
 181  E :    if (with_size && !stream->Write(static_cast<uint32>(bits_.size()))) {
 182  i :      LOG(ERROR) << "Failed to write bitset size.";
 183  i :      return false;
 184    :    }
 185  E :    if (bits_.empty())
 186  E :      return true;
 187  E :    if (!stream->Write(bits_.size(), &bits_[0])) {
 188  i :      LOG(ERROR) << "Failed to write bitset bits.";
 189  i :      return false;
 190    :    }
 191    :  
 192  E :    return true;
 193  E :  }
 194    :  
 195  E :  void PdbBitSet::Resize(size_t bits) {
 196  E :    bits_.resize((bits + 31) / 32);
 197  E :  }
 198    :  
 199  E :  void PdbBitSet::Set(size_t bit) {
 200  E :    size_t index = bit / 32;
 201  E :    if (index >= bits_.size())
 202  i :      return;
 203  E :    bits_[index] |= (1 << (bit % 32));
 204  E :  }
 205    :  
 206  E :  void PdbBitSet::Clear(size_t bit) {
 207  E :    size_t index = bit / 32;
 208  E :    if (index >= bits_.size())
 209  i :      return;
 210  E :    bits_[index] &= ~(1 << (bit % 32));
 211  E :  }
 212    :  
 213  E :  void PdbBitSet::Toggle(size_t bit) {
 214  E :    size_t index = bit / 32;
 215  E :    if (index >= bits_.size())
 216  i :      return;
 217  E :    bits_[index] ^= (1 << (bit % 32));
 218  E :  }
 219    :  
 220  E :  bool PdbBitSet::IsSet(size_t bit) const {
 221  E :    size_t index = bit / 32;
 222  E :    if (index >= bits_.size())
 223  i :      return false;
 224    :  
 225  E :    return (bits_[index] & (1 << (bit % 32))) != 0;
 226  E :  }
 227    :  
 228  E :  bool PdbBitSet::IsEmpty() const {
 229  E :    return bits_.empty();
 230  E :  }
 231    :  
 232  E :  uint16 HashString(const base::StringPiece& string) {
 233  E :    size_t length = string.size();
 234  E :    const char* data = string.data();
 235    :  
 236  E :    uint32 hash = 0;
 237  E :    while (length >= 4) {
 238  E :      hash ^= *reinterpret_cast<const uint32*>(data);
 239  E :      data += 4;
 240  E :      length -= 4;
 241  E :    }
 242    :  
 243  E :    if (length >= 2) {
 244  E :      hash ^= *reinterpret_cast<const uint16*>(data);
 245  E :      data += 2;
 246  E :      length -= 2;
 247    :    }
 248    :  
 249  E :    if (length >= 1)
 250  E :      hash ^= *data;
 251    :  
 252  E :    hash |= 0x20202020;
 253  E :    hash ^= hash >> 11;
 254  E :    hash ^= hash >> 16;
 255    :  
 256  E :    return hash & 0xFFFF;
 257  E :  }
 258    :  
 259  E :  bool ReadString(PdbStream* stream, std::string* out) {
 260  E :    DCHECK(stream != NULL);
 261  E :    DCHECK(out != NULL);
 262    :  
 263  E :    std::string result;
 264  E :    size_t start_pos = stream->pos();
 265  E :    const size_t kBufferSize = 1024;
 266    :    char buffer[kBufferSize];
 267    :  
 268  E :    size_t read_count = 0;
 269  E :    while (stream->Read(buffer, kBufferSize, &read_count) || read_count > 0) {
 270  E :      for (size_t i = 0; i < read_count; ++i) {
 271  E :        if (buffer[i] == '\0') {
 272  E :          out->swap(result);
 273  E :          stream->Seek(start_pos + out->size() + 1);
 274  E :          return true;
 275    :        }
 276  E :        result.push_back(buffer[i]);
 277  E :      }
 278  i :    }
 279    :  
 280  E :    return false;
 281  E :  }
 282    :  
 283  E :  bool ReadStringAt(PdbStream* stream, size_t pos, std::string* out) {
 284  E :    size_t save = stream->pos();
 285  E :    bool read = stream->Seek(pos) && ReadString(stream, out);
 286  E :    stream->Seek(save);
 287  E :    return read;
 288  E :  }
 289    :  
 290  E :  uint32 GetDbiDbgHeaderOffset(const DbiHeader& dbi_header) {
 291  E :    uint32 offset = sizeof(DbiHeader);
 292  E :    offset += dbi_header.gp_modi_size;
 293  E :    offset += dbi_header.section_contribution_size;
 294  E :    offset += dbi_header.section_map_size;
 295  E :    offset += dbi_header.file_info_size;
 296  E :    offset += dbi_header.ts_map_size;
 297  E :    offset += dbi_header.ec_info_size;  // Unexpected, but necessary.
 298  E :    return offset;
 299  E :  }
 300    :  
 301  E :  bool EnsureStreamWritable(uint32 index, PdbFile* pdb_file) {
 302  E :    DCHECK(pdb_file != NULL);
 303    :  
 304    :    // Bail if the index is to a non-existent stream.
 305  E :    if (index >= pdb_file->StreamCount()) {
 306  E :      LOG(ERROR) << "Invalid PDB stream index.";
 307  E :      return false;
 308    :    }
 309    :  
 310    :    // Get the reader. If it doesn't actually exist, create a new one.
 311  E :    scoped_refptr<PdbStream> reader(pdb_file->GetStream(index));
 312  E :    if (reader.get() == NULL)
 313  E :      reader = new PdbByteStream();
 314    :  
 315    :    // Try and get a writer.
 316  E :    scoped_refptr<WritablePdbStream> writer(reader->GetWritableStream());
 317  E :    if (writer.get() == NULL) {
 318    :      // If not possible, copy the existing reader to a PdbByteStream which will
 319    :      // be able to give us a writer.
 320  E :      scoped_refptr<PdbByteStream> new_stream = new PdbByteStream();
 321  E :      if (!new_stream->Init(reader.get())) {
 322  i :        LOG(ERROR) << "Failed to initialize writable stream.";
 323  i :        return false;
 324    :      }
 325  E :      reader = new_stream.get();
 326  E :    }
 327    :  
 328    :    DCHECK_NE(static_cast<WritablePdbStream*>(nullptr),
 329  E :              reader->GetWritableStream());
 330    :  
 331    :    // Be sure to replace the stream at this index with the new one. This is a
 332    :    // no-op if the stream hasn't changed.
 333  E :    pdb_file->ReplaceStream(index, reader.get());
 334    :  
 335  E :    return true;
 336  E :  }
 337    :  
 338    :  bool SetOmapToStream(const std::vector<OMAP>& omap_to_list,
 339  E :                       PdbFile* pdb_file) {
 340  E :    DCHECK(pdb_file != NULL);
 341    :    return SetOmapStream(offsetof(DbiDbgHeader, omap_to_src),
 342    :                         omap_to_list,
 343  E :                         pdb_file);
 344  E :  }
 345    :  
 346    :  bool SetOmapFromStream(const std::vector<OMAP>& omap_from_list,
 347  E :                         PdbFile* pdb_file) {
 348  E :    DCHECK(pdb_file != NULL);
 349    :    return SetOmapStream(offsetof(DbiDbgHeader, omap_from_src),
 350    :                         omap_from_list,
 351  E :                         pdb_file);
 352  E :  }
 353    :  
 354  E :  bool SetGuid(const GUID& guid, PdbFile* pdb_file) {
 355  E :    DCHECK(pdb_file != NULL);
 356    :  
 357    :    // Make sure the Pdb header and the Dbi streams are writable.
 358  E :    if (!EnsureStreamWritable(kPdbHeaderInfoStream, pdb_file)) {
 359  i :      LOG(ERROR) << "Failed to make PDB Header Info stream writable.";
 360  i :      return false;
 361    :    }
 362  E :    if (!EnsureStreamWritable(kDbiStream, pdb_file)) {
 363  i :      LOG(ERROR) << "Failed to make DBI Info stream writable.";
 364  i :      return false;
 365    :    }
 366    :  
 367    :    // Get the reader and writer for the header info stream.
 368  E :    scoped_refptr<PdbStream> reader(pdb_file->GetStream(kPdbHeaderInfoStream));
 369  E :    scoped_refptr<WritablePdbStream> writer(reader->GetWritableStream());
 370  E :    DCHECK(writer.get() != NULL);
 371    :  
 372    :    // Read the header.
 373  E :    PdbInfoHeader70 info_header = {};
 374  E :    if (!reader->Seek(0) || !reader->Read(&info_header, 1)) {
 375  E :      LOG(ERROR) << "Failed to read PdbInfoHeader70.";
 376  E :      return false;
 377    :    }
 378    :  
 379    :    // Update it.
 380  E :    info_header.timestamp = static_cast<uint32>(time(NULL));
 381  E :    info_header.pdb_age = 1;  // Reset age to 1, as this is a new generation.
 382  E :    info_header.signature = guid;
 383    :  
 384    :    // And write it back.
 385  E :    writer->set_pos(0);
 386  E :    if (!writer->Write(info_header)) {
 387  i :      LOG(ERROR) << "Failed to write PdbInfoHeader70.";
 388  i :      return false;
 389    :    }
 390    :  
 391    :    // Now update the age in the DBI stream to match the age we set above.
 392  E :    reader = pdb_file->GetStream(kDbiStream);
 393  E :    if (reader.get() == NULL) {
 394  i :      LOG(ERROR) << "No DBI stream in PDB.";
 395  i :      return false;
 396    :    }
 397  E :    writer = reader->GetWritableStream();
 398    :  
 399  E :    DCHECK(writer.get() != NULL);
 400    :  
 401    :    // Read the header.
 402  E :    DbiHeader dbi_header = {};
 403  E :    if (!reader->Seek(0) || !reader->Read(&dbi_header, 1)) {
 404  E :      LOG(ERROR) << "Failed to read DbiHeader.";
 405  E :      return false;
 406    :    }
 407    :  
 408  E :    dbi_header.age = 1;
 409  E :    writer->set_pos(0);
 410  E :    if (!writer->Write(dbi_header)) {
 411  i :      LOG(ERROR) << "Failed to write DbiHeader";
 412  i :      return false;
 413    :    }
 414    :  
 415  E :    return true;
 416  E :  }
 417    :  
 418    :  bool ReadPdbHeader(const base::FilePath& pdb_path,
 419  E :                     PdbInfoHeader70* pdb_header) {
 420  E :    DCHECK(!pdb_path.empty());
 421  E :    DCHECK(pdb_header != NULL);
 422    :  
 423  E :    PdbReader pdb_reader;
 424  E :    PdbFile pdb_file;
 425  E :    if (!pdb_reader.Read(pdb_path, &pdb_file)) {
 426  E :      LOG(ERROR) << "Unable to process PDB file: " << pdb_path.value();
 427  E :      return false;
 428    :    }
 429    :  
 430  E :    PdbStream* header_stream = pdb_file.GetStream(kPdbHeaderInfoStream).get();
 431  E :    if (header_stream == NULL) {
 432  i :      LOG(ERROR) << "PDB file contains no header stream: " << pdb_path.value();
 433  i :      return false;
 434    :    }
 435    :  
 436  E :    if (!header_stream->Read(pdb_header, 1)) {
 437  i :      LOG(ERROR) << "Failure reading PDB header: " << pdb_path.value();
 438  i :      return false;
 439    :    }
 440    :  
 441    :    // We only know how to deal with PDB files of the current version.
 442  E :    if (pdb_header->version != kPdbCurrentVersion) {
 443  i :      LOG(ERROR) << "PDB header has unsupported version (got "
 444    :                 << pdb_header->version << ", expected " << kPdbCurrentVersion
 445    :                 << ").";
 446  i :      return false;
 447    :    }
 448    :  
 449  E :    return true;
 450  E :  }
 451    :  
 452    :  bool ReadHeaderInfoStream(const PdbFile& pdb_file,
 453    :                            PdbInfoHeader70* pdb_header,
 454  E :                            NameStreamMap* name_stream_map) {
 455  E :    DCHECK(pdb_header != NULL);
 456  E :    DCHECK(name_stream_map != NULL);
 457    :  
 458    :    // Get the stream reader.
 459  E :    if (kPdbHeaderInfoStream >= pdb_file.StreamCount()) {
 460  E :      LOG(ERROR) << "No header info stream found.";
 461  E :      return false;
 462    :    }
 463    :    scoped_refptr<PdbStream> header_reader(
 464  E :        pdb_file.GetStream(kPdbHeaderInfoStream));
 465  E :    if (header_reader.get() == NULL) {
 466  i :      LOG(ERROR) << "No header info stream found.";
 467  i :      return false;
 468    :    }
 469    :  
 470    :    // Read the header.
 471    :    if (!pdb::ReadHeaderInfoStream(header_reader.get(), pdb_header,
 472  E :                                   name_stream_map)) {
 473  i :      LOG(ERROR) << "Failed to read header info stream.";
 474  i :      return false;
 475    :    }
 476    :  
 477  E :    return true;
 478  E :  }
 479    :  
 480    :  bool ReadHeaderInfoStream(PdbStream* pdb_stream,
 481    :                            PdbInfoHeader70* pdb_header,
 482  E :                            NameStreamMap* name_stream_map) {
 483  E :    VLOG(1) << "Header Info Stream size: " << pdb_stream->length();
 484    :  
 485  E :    if (!pdb_stream->Seek(0)) {
 486  i :      LOG(ERROR) << "Unable to seek to start of PDB header info stream.";
 487  i :      return false;
 488    :    }
 489    :  
 490    :    // The header stream starts with the fixed-size header pdb_header record.
 491  E :    if (!pdb_stream->Read(pdb_header, 1)) {
 492  E :      LOG(ERROR) << "Unable to read PDB pdb_header header.";
 493  E :      return false;
 494    :    }
 495    :  
 496  E :    uint32 string_len = 0;
 497  E :    if (!pdb_stream->Read(&string_len, 1)) {
 498  E :      LOG(ERROR) << "Unable to read string table length.";
 499  E :      return false;
 500    :    }
 501    :  
 502    :    // The fixed-size record is followed by information on named streams, which
 503    :    // is essentially a string->id mapping. This starts with the strings
 504    :    // themselves, which have been observed to be a packed run of zero-terminated
 505    :    // strings.
 506    :    // We store the start of the string list, as the string positions we read
 507    :    // later are relative to that position.
 508  E :    size_t string_start = pdb_stream->pos();
 509    :  
 510    :    // Seek past the strings.
 511  E :    if (!pdb_stream->Seek(string_start + string_len)) {
 512  i :      LOG(ERROR) << "Unable to seek past string list";
 513  i :      return false;
 514    :    }
 515    :  
 516    :    // Next there's a pair of integers. The first one of those is the number of
 517    :    // items in the string->id mapping. The purpose of the second one is not
 518    :    // clear, but has been observed as larger or equal to the first one.
 519  E :    uint32 size = 0;
 520  E :    uint32 max = 0;
 521  E :    if (!pdb_stream->Read(&size, 1) || !pdb_stream->Read(&max, 1)) {
 522  i :      LOG(ERROR) << "Unable to read name pdb_stream size/max";
 523  i :      return false;
 524    :    }
 525  E :    DCHECK(max >= size);
 526    :  
 527    :    // After the counts, there's a pair of bitsets. Each bitset has a 32 bit
 528    :    // length, followed by that number of 32 bit words that contain the bits.
 529    :    // The purpose of those is again not clear, though the first set will have
 530    :    // "size" bits of the bits in the range 0-max set.
 531  E :    PdbBitSet used;
 532  E :    PdbBitSet deleted;
 533  E :    if (!used.Read(pdb_stream) || !deleted.Read(pdb_stream)) {
 534  i :      LOG(ERROR) << "Unable to read name pdb_stream bitsets.";
 535  i :      return false;
 536    :    }
 537    :  
 538    :  #ifndef NDEBUG
 539    :    // The first bitset has "size" bits set of the first "max" bits.
 540  E :    size_t set_bits = 0;
 541  E :    for (size_t i = 0; i < max; ++i) {
 542  E :      if (used.IsSet(i))
 543  E :        ++set_bits;
 544  E :    }
 545    :  
 546    :    // The second bitset has always been observed to be empty.
 547  E :    DCHECK(deleted.IsEmpty());
 548    :  
 549  E :    DCHECK_EQ(size, set_bits);
 550    :  #endif
 551    :  
 552    :    // Read the mapping proper, this is simply a run of {string offset, id} pairs.
 553  E :    for (size_t i = 0; i < size; ++i) {
 554  E :      uint32 str_offs = 0;
 555  E :      uint32 stream_no = 0;
 556    :      // Read the offset and pdb_stream number.
 557  E :      if (!pdb_stream->Read(&str_offs, 1) || !pdb_stream->Read(&stream_no, 1)) {
 558  i :        LOG(ERROR) << "Unable to read pdb_stream data.";
 559  i :        return false;
 560    :      }
 561    :  
 562    :      // Read the string itself from the table.
 563  E :      std::string name;
 564  E :      if (!ReadStringAt(pdb_stream, string_start + str_offs, &name)) {
 565  i :        LOG(ERROR) << "Failed to read pdb_stream name.";
 566  i :        return false;
 567    :      }
 568    :  
 569  E :      (*name_stream_map)[name] = stream_no;
 570  E :    }
 571    :  
 572  E :    return true;
 573  E :  }
 574    :  
 575    :  bool WriteHeaderInfoStream(const PdbInfoHeader70& header,
 576    :                             const NameStreamMap& name_stream_map,
 577  E :                             PdbFile* pdb_file) {
 578  E :    DCHECK(pdb_file != NULL);
 579    :  
 580  E :    if (!pdb::EnsureStreamWritable(pdb::kPdbHeaderInfoStream, pdb_file)) {
 581  i :      LOG(ERROR) << "Failed to make PDB Header Info stream writable.";
 582  i :      return false;
 583    :    }
 584    :  
 585    :    // Get the stream reader.
 586    :    scoped_refptr<PdbStream> header_reader(
 587  E :        pdb_file->GetStream(pdb::kPdbHeaderInfoStream));
 588  E :    DCHECK(header_reader.get() != NULL);
 589    :  
 590    :    // Get the stream writer.
 591    :    scoped_refptr<WritablePdbStream> header_writer(
 592  E :        header_reader->GetWritableStream());
 593  E :    DCHECK(header_writer.get() != NULL);
 594    :  
 595    :    // Write the new header.
 596  E :    if (!WriteHeaderInfoStream(header, name_stream_map, header_writer.get())) {
 597  i :      LOG(ERROR) << "Failed to write PDB Header Info stream.";
 598  i :      return false;
 599    :    }
 600    :  
 601  E :    return true;
 602  E :  }
 603    :  
 604    :  bool WriteHeaderInfoStream(const PdbInfoHeader70& pdb_header,
 605    :                             const NameStreamMap& name_stream_map,
 606  E :                             WritablePdbStream* pdb_stream) {
 607  E :    DCHECK(pdb_stream != NULL);
 608    :  
 609  E :    if (!pdb_stream->Write(pdb_header)) {
 610  i :      LOG(ERROR) << "Failed to write PDB header.";
 611  i :      return false;
 612    :    }
 613    :  
 614    :    // Calculate the hash table entry count and size.
 615  E :    uint32 string_count = name_stream_map.size();
 616  E :    uint32 table_size = GetNamedStreamsHashTableSize(string_count);
 617    :  
 618    :    // Initialize the 'used' bitset.
 619  E :    PdbBitSet used;
 620  E :    used.Resize(table_size);
 621    :  
 622    :    // Get the string table length. We also calculate hashes for each name,
 623    :    // populating the 'used' bitset and determining the order in which to output
 624    :    // the stream names.
 625  E :    std::vector<NamedStreamInfo> name_infos;
 626  E :    name_infos.reserve(string_count);
 627  E :    uint32 string_length = 0;
 628  E :    NameStreamMap::const_iterator name_it = name_stream_map.begin();
 629  E :    for (; name_it != name_stream_map.end(); ++name_it) {
 630  E :      uint16 hash = HashString(name_it->first);
 631  E :      size_t bucket = hash % table_size;
 632  E :      bucket = SetNamedStreamsHashTableBit(bucket, table_size, &used);
 633  E :      name_infos.push_back(NamedStreamInfo(name_it, 0, bucket));
 634  E :      string_length += name_it->first.size() + 1;  // Include the trailing zero.
 635  E :    }
 636    :  
 637    :    // Sort the strings in the order in which they will be output.
 638  E :    std::sort(name_infos.begin(), name_infos.end());
 639    :  
 640    :    // Dump the string table.
 641  E :    if (!pdb_stream->Write(string_length)) {
 642  i :      LOG(ERROR) << "Failed to write stream name table length.";
 643  i :      return false;
 644    :    }
 645  E :    string_length = 0;
 646  E :    for (size_t i = 0; i < name_infos.size(); ++i) {
 647  E :      name_infos[i].offset = string_length;
 648  E :      name_it = name_infos[i].it;
 649  E :      string_length += name_it->first.size() + 1;
 650  E :      if (!pdb_stream->Write(name_it->first.size() + 1, name_it->first.c_str())) {
 651  i :        LOG(ERROR) << "Failed to write stream name.";
 652  i :        return false;
 653    :      }
 654  E :    }
 655    :  
 656    :    // Write the string hash table size.
 657  E :    if (!pdb_stream->Write(string_count) || !pdb_stream->Write(table_size)) {
 658  i :      LOG(ERROR) << "Failed to write string table size.";
 659  i :      return false;
 660    :    }
 661    :  
 662    :    // Write the 'used' bitset.
 663  E :    if (!used.Write(pdb_stream, true)) {
 664  i :      LOG(ERROR) << "Failed to write 'used' bitset.";
 665  i :      return false;
 666    :    }
 667    :  
 668    :    // The 'deleted' bitset is always empty.
 669  E :    PdbBitSet deleted;
 670  E :    DCHECK(deleted.IsEmpty());
 671  E :    if (!deleted.Write(pdb_stream, true)) {
 672  i :      LOG(ERROR) << "Failed to write 'deleted' bitset.";
 673  i :      return false;
 674    :    }
 675    :  
 676    :    // Now output the actual mapping, a run of (string table offset, stream id)
 677    :    // pairs. We output these in order of hash table buckets of each name,
 678    :    // mimicking the output produced by pdbstr.exe.
 679  E :    for (size_t i = 0; i < name_infos.size(); ++i) {
 680    :      if (!pdb_stream->Write(name_infos[i].offset) ||
 681  E :          !pdb_stream->Write(name_infos[i].it->second)) {
 682  i :        LOG(ERROR) << "Failed to write stream name mapping.";
 683  i :        return false;
 684    :      }
 685  E :    }
 686    :  
 687    :    // The run of pairs must be terminated with a single NULL entry.
 688  E :    if (!pdb_stream->Write(static_cast<uint32>(0))) {
 689  i :      LOG(ERROR) << "Failed to write terminating NULL.";
 690  i :      return false;
 691    :    }
 692    :  
 693  E :    return true;
 694  E :  }
 695    :  
 696    :  bool ReadStringTable(PdbStream* stream,
 697    :                       const char* table_name,
 698    :                       size_t table_start,
 699    :                       size_t table_end,
 700  E :                       OffsetStringMap* string_map) {
 701  E :    DCHECK(stream != NULL);
 702  E :    DCHECK(table_name != NULL);
 703  E :    DCHECK(string_map != NULL);
 704    :  
 705  E :    uint32 string_table_signature = 0;
 706  E :    uint32 string_table_version = 0;
 707    :  
 708    :    if (!stream->Seek(table_start) ||
 709    :        !stream->Read(&string_table_signature, 1) ||
 710  E :        !stream->Read(&string_table_version, 1)) {
 711  i :      LOG(ERROR) << "Unable to seek to " << table_name << " stream.";
 712  i :      return false;
 713    :    }
 714    :  
 715    :    if (string_table_signature != kPdbStringTableSignature ||
 716  E :        string_table_version != kPdbStringTableVersion) {
 717  i :      LOG(ERROR) << "Unexpected " << table_name << " header. Expected "
 718    :                 << "signature/version "
 719    :                 << base::StringPrintf("0x%08X", kPdbStringTableSignature) << "/"
 720    :                 << kPdbStringTableVersion << ", read "
 721    :                 << base::StringPrintf("0x%08X", string_table_signature) << "/"
 722    :                 << string_table_version << ".";
 723  i :      return false;
 724    :    }
 725    :  
 726  E :    size_t string_table_size = 0;
 727  E :    if (!stream->Read(&string_table_size, 1)) {
 728  i :      LOG(ERROR) << "Unable to read the size of the " << table_name << " string "
 729    :                 << "table.";
 730  i :      return false;
 731    :    }
 732    :  
 733  E :    size_t string_table_start = stream->pos();
 734  E :    size_t offset_table_start = stream->pos() + string_table_size;
 735    :  
 736    :    // Skip the string table and seek to the offset table.
 737  E :    if (!stream->Seek(offset_table_start)) {
 738  i :      LOG(ERROR) << "Unable to seek to the " << table_name << " offset table.";
 739  i :      return false;
 740    :    }
 741    :  
 742  E :    size_t entries_count = 0;
 743  E :    if (!stream->Read(&entries_count, 1)) {
 744  i :      LOG(ERROR) << "Unable to read the number of entries in the " << table_name
 745    :                 << " offset table.";
 746  i :      return false;
 747    :    }
 748    :  
 749    :    // Some of the offsets present in the offset table have the value 0, which
 750    :    // refers to an empty string present at the beginning of the string table.
 751  E :    for (size_t i = 0; i < entries_count; ++i) {
 752  E :      size_t string_offset = 0;
 753  E :      std::string temp_string;
 754    :      if (!stream->Read(&string_offset, 1) ||
 755    :          !ReadStringAt(stream, string_table_start + string_offset,
 756  E :                        &temp_string)) {
 757  i :        LOG(ERROR) << "Unable to read the " << table_name << " name table.";
 758  i :        return false;
 759    :      }
 760    :  
 761  E :      string_map->insert(std::make_pair(string_offset, temp_string));
 762  E :    }
 763    :  
 764  E :    uint32 string_count = 0;
 765    :    // Sometimes the string_count field matches the number of non-empty strings
 766    :    // in the string_map and sometimes it doesn't.
 767    :    // TODO(sebmarchand) : understand what's this value once the compiland streams
 768    :    //     are deciphered.
 769  E :    if (!stream->Read(&string_count, 1)) {
 770  i :      LOG(ERROR) << "Unable to read the number of files present in the "
 771    :                 << table_name << " stream.";
 772  i :      return false;
 773    :    }
 774    :  
 775  E :    if (stream->pos() != table_end) {
 776  i :      LOG(ERROR) << table_name << " stream is not valid.";
 777  i :      return false;
 778    :    }
 779    :  
 780  E :    return true;
 781  E :  }
 782    :  
 783    :  bool LoadNamedStreamFromPdbFile(
 784    :      const base::StringPiece& stream_name,
 785    :      PdbFile* pdb_file,
 786  E :      scoped_refptr<PdbStream>* stream) {
 787  E :    DCHECK(pdb_file != NULL);
 788  E :    DCHECK(stream != NULL);
 789  E :    DCHECK(stream->get() == NULL);
 790    :  
 791    :    // Get the PDB header and try to get the named stream ID from it.
 792  E :    pdb::PdbInfoHeader70 pdb_header = {0};
 793  E :    pdb::NameStreamMap name_stream_map;
 794    :    if (!ReadHeaderInfoStream(
 795    :            pdb_file->GetStream(pdb::kPdbHeaderInfoStream).get(), &pdb_header,
 796  E :            &name_stream_map)) {
 797  i :      LOG(ERROR) << "Failed to read header info stream.";
 798  i :      return false;
 799    :    }
 800    :  
 801    :    // The stream with the given name does not exist.
 802    :    pdb::NameStreamMap::const_iterator name_it = name_stream_map.find(
 803  E :        stream_name.as_string());
 804  E :    if (name_it == name_stream_map.end())
 805  E :      return true;
 806    :  
 807    :    // Get the named stream and ensure that it's not empty.
 808  E :    *stream = pdb_file->GetStream(name_it->second);
 809  E :    if (stream->get() == NULL) {
 810  i :      LOG(ERROR) << "Failed to read the \"" << stream_name.as_string()
 811    :                 << "\" stream from the PDB.";
 812  i :      return false;
 813    :    }
 814    :  
 815  E :    return true;
 816  E :  }
 817    :  
 818    :  }  // namespace pdb

Coverage information generated Thu Jan 14 17:40:38 2016.