Skip to content

Commit

Permalink
[pdb] Introduce MsfBuilder for laying out PDB files.
Browse files Browse the repository at this point in the history
Reviewed by: ruiu
Differential Revision: https://reviews.llvm.org/D22308

llvm-svn: 275611
  • Loading branch information
Zachary Turner committed Jul 15, 2016
1 parent 511f2e5 commit f52a899
Show file tree
Hide file tree
Showing 9 changed files with 854 additions and 17 deletions.
136 changes: 136 additions & 0 deletions llvm/include/llvm/DebugInfo/PDB/Raw/MsfBuilder.h
@@ -0,0 +1,136 @@
//===- MSFBuilder.h - MSF Directory & Metadata Builder ----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_PDB_RAW_MSFBUILDER_H
#define LLVM_DEBUGINFO_PDB_RAW_MSFBUILDER_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"

#include "llvm/DebugInfo/PDB/Raw/MsfCommon.h"
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"

#include "llvm/Support/Allocator.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"

#include <utility>
#include <vector>

namespace llvm {
namespace pdb {
class MsfBuilder {
public:
/// \brief Create a new `MsfBuilder`.
///
/// \param BlockSize The internal block size used by the PDB file. See
/// isValidBlockSize() for a list of valid block sizes.
///
/// \param MinBlockCount Causes the builder to reserve up front space for
/// at least `MinBlockCount` blocks. This is useful when using `MsfBuilder`
/// to read an existing PDB that you want to write back out later. The
/// original PDB file's SuperBlock contains the exact number of blocks used
/// by the file, so is a good hint as to how many blocks the new PDB file
/// will contain. Furthermore, it is actually necessary in this case. To
/// preserve stability of the file's layout, it is helpful to try to keep
/// all streams mapped to their original block numbers. To ensure that this
/// is possible, space for all blocks must be allocated beforehand so that
/// streams can be assigned to them.
///
/// \param CanGrow If true, any operation which results in an attempt to
/// locate a free block when all available blocks have been exhausted will
/// allocate a new block, thereby growing the size of the final PDB file.
/// When false, any such attempt will result in an error. This is especially
/// useful in testing scenarios when you know your test isn't going to do
/// anything to increase the size of the file, so having an Error returned if
/// it were to happen would catch a programming error
///
/// \returns an llvm::Error representing whether the operation succeeded or
/// failed. Currently the only way this can fail is if an invalid block size
/// is specified, or `MinBlockCount` does not leave enough room for the
/// mandatory reserved blocks required by an MSF file.
static Expected<MsfBuilder> create(BumpPtrAllocator &Allocator,
uint32_t BlockSize,
uint32_t MinBlockCount = 0,
bool CanGrow = true);

/// Request the block map to be at a specific block address. This is useful
/// when editing a PDB and you want the layout to be as stable as possible.
Error setBlockMapAddr(uint32_t Addr);

/// Add a stream to the MSF file with the given size, occupying the given
/// list of blocks. This is useful when reading a PDB file and you want a
/// particular stream to occupy the original set of blocks. If the given
/// blocks are already allocated, or if the number of blocks specified is
/// incorrect for the given stream size, this function will return an Error.
Error addStream(uint32_t Size, ArrayRef<uint32_t> Blocks);

/// Add a stream to the MSF file with the given size, occupying any available
/// blocks that the builder decides to use. This is useful when building a
/// new PDB file from scratch and you don't care what blocks a stream occupies
/// but you just want it to work.
Error addStream(uint32_t Size);

/// Update the size of an existing stream. This will allocate or deallocate
/// blocks as needed to match the requested size. This can fail if `CanGrow`
/// was set to false when initializing the `MsfBuilder`.
Error setStreamSize(uint32_t Idx, uint32_t Size);

/// Get the total number of streams in the MSF layout. This should return 1
/// for every call to `addStream`.
uint32_t getNumStreams() const;

/// Get the size of a stream by index.
uint32_t getStreamSize(uint32_t StreamIdx) const;

/// Get the list of blocks allocated to a particular stream.
ArrayRef<uint32_t> getStreamBlocks(uint32_t StreamIdx) const;

/// Get the total number of blocks that will be allocated to actual data in
/// this MSF file.
uint32_t getNumUsedBlocks() const;

/// Get the total number of blocks that exist in the MSF file but are not
/// allocated to any valid data.
uint32_t getNumFreeBlocks() const;

/// Get the total number of blocks in the MSF file. In practice this is equal
/// to `getNumUsedBlocks() + getNumFreeBlocks()`.
uint32_t getTotalBlockCount() const;

/// Check whether a particular block is allocated or free.
bool isBlockFree(uint32_t Idx) const;

/// Finalize the layout and build the headers and structures that describe the
/// MSF layout and can be written directly to the MSF file.
Expected<msf::Layout> build();

private:
MsfBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow,
BumpPtrAllocator &Allocator);

Error allocateBlocks(uint32_t NumBlocks, MutableArrayRef<uint32_t> Blocks);
uint32_t computeDirectoryByteSize() const;

typedef std::vector<uint32_t> BlockList;

BumpPtrAllocator &Allocator;

bool IsGrowable;
uint32_t BlockSize;
uint32_t MininumBlocks;
uint32_t BlockMapAddr;
BitVector FreeBlocks;
std::vector<uint32_t> DirectoryBlocks;
std::vector<std::pair<uint32_t, BlockList>> StreamData;
};
}
}

#endif
83 changes: 83 additions & 0 deletions llvm/include/llvm/DebugInfo/PDB/Raw/MsfCommon.h
@@ -0,0 +1,83 @@
//===- MsfCommon.h - Common types and functions for MSF files ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_PDB_RAW_MSFCOMMON_H
#define LLVM_DEBUGINFO_PDB_RAW_MSFCOMMON_H

#include "llvm/ADT/ArrayRef.h"

#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MathExtras.h"

#include <vector>

namespace llvm {
namespace pdb {
namespace msf {
static const char Magic[] = {'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
't', ' ', 'C', '/', 'C', '+', '+', ' ',
'M', 'S', 'F', ' ', '7', '.', '0', '0',
'\r', '\n', '\x1a', 'D', 'S', '\0', '\0', '\0'};

// The superblock is overlaid at the beginning of the file (offset 0).
// It starts with a magic header and is followed by information which
// describes the layout of the file system.
struct SuperBlock {
char MagicBytes[sizeof(Magic)];
// The file system is split into a variable number of fixed size elements.
// These elements are referred to as blocks. The size of a block may vary
// from system to system.
support::ulittle32_t BlockSize;
// This field's purpose is not yet known.
support::ulittle32_t Unknown0;
// This contains the number of blocks resident in the file system. In
// practice, NumBlocks * BlockSize is equivalent to the size of the PDB
// file.
support::ulittle32_t NumBlocks;
// This contains the number of bytes which make up the directory.
support::ulittle32_t NumDirectoryBytes;
// This field's purpose is not yet known.
support::ulittle32_t Unknown1;
// This contains the block # of the block map.
support::ulittle32_t BlockMapAddr;
};

struct Layout {
SuperBlock *SB;
ArrayRef<support::ulittle32_t> DirectoryBlocks;
ArrayRef<support::ulittle32_t> StreamSizes;
std::vector<ArrayRef<support::ulittle32_t>> StreamMap;
};

inline bool isValidBlockSize(uint32_t Size) {
switch (Size) {
case 512:
case 1024:
case 2048:
case 4096:
return true;
}
return false;
}

inline uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) {
return alignTo(NumBytes, BlockSize) / BlockSize;
}

inline uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) {
return BlockNumber * BlockSize;
}

Error validateSuperBlock(const SuperBlock &SB);
}
}
}

#endif
2 changes: 2 additions & 0 deletions llvm/lib/DebugInfo/PDB/CMakeLists.txt
Expand Up @@ -38,6 +38,8 @@ add_pdb_impl_folder(Raw
Raw/MappedBlockStream.cpp
Raw/ModInfo.cpp
Raw/ModStream.cpp
Raw/MsfBuilder.cpp
Raw/MsfCommon.cpp
Raw/NameHashTable.cpp
Raw/NameMap.cpp
Raw/PDBFile.cpp
Expand Down

0 comments on commit f52a899

Please sign in to comment.