Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -61,6 +61,10 @@ add_llvm_tool_subdirectory(llvm-go) +if(MSVC) + add_llvm_tool_subdirectory(llvm-pdbdump) +endif() + if(NOT CYGWIN AND LLVM_ENABLE_PIC) add_llvm_tool_subdirectory(lto) add_llvm_tool_subdirectory(llvm-lto) Index: tools/LLVMBuild.txt =================================================================== --- tools/LLVMBuild.txt +++ tools/LLVMBuild.txt @@ -16,7 +16,7 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = bugpoint llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link llvm-lto llvm-mc llvm-nm llvm-objdump llvm-profdata llvm-rtdyld llvm-size macho-dump opt llvm-mcmarkup verify-uselistorder dsymutil +subdirectories = bugpoint llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link llvm-lto llvm-mc llvm-nm llvm-objdump llvm-pdbdump llvm-profdata llvm-rtdyld llvm-size macho-dump opt llvm-mcmarkup verify-uselistorder dsymutil [component_0] type = Group Index: tools/llvm-pdbdump/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-pdbdump/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +set(MSVC_DIA_SDK_DIR "$ENV{VSINSTALLDIR}DIA SDK") +include_directories(${MSVC_DIA_SDK_DIR}/include) +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + link_directories(${MSVC_DIA_SDK_DIR}/lib/amd64) +else() + link_directories(${MSVC_DIA_SDK_DIR}/lib) +endif() + +add_llvm_tool(llvm-pdbdump + llvm-pdbdump.cpp + ) +target_link_libraries(llvm-pdbdump diaguids) Index: tools/llvm-pdbdump/COMExtras.h =================================================================== --- /dev/null +++ tools/llvm-pdbdump/COMExtras.h @@ -0,0 +1,286 @@ +//===- COMExtras.h - Helper files for COM operations -------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_PDBDUMP_COMEXTRAS_H +#define LLVM_TOOLS_LLVM_PDBDUMP_COMEXTRAS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" + +#include + +namespace llvm { + +template struct function_traits; + +#if LLVM_HAS_VARIADIC_TEMPLATES +template +struct function_traits : public function_traits {}; + +template +struct function_traits { + using args_tuple = std::tuple; +}; +#else + +// For the sake of COM, we only need a 3 argument version and a 5 argument +// version. We could provide 1, 2, 4, and other length of argument lists if +// this were intended to be more generic. Alternatively, this will "just work" +// if VS2012 support is dropped and we can use the variadic template case +// exclusively. +template +struct function_traits { + using args_tuple = std::tuple; +}; + +template +struct function_traits { + using args_tuple = std::tuple; +}; +#endif + +template struct function_arg { + // Writing function_arg as a separate class that accesses the tuple from + // function_traits is necessary due to what appears to be a bug in MSVC. + // If you write a nested class inside function_traits like this: + // template + // struct Argument + // { + // typedef typename + // std::tuple_element>::type type; + // }; + // MSVC encounters a parsing error. + typedef + typename std::tuple_element::type + type; +}; + +template struct remove_double_pointer {}; +template struct remove_double_pointer { typedef T type; }; + +namespace sys { +namespace windows { + +/// A helper class for allowing the use of COM enumerators in range-based +/// for loops. +/// +/// A common idiom in the COM world is to have an enumerator interface, say +/// IMyEnumerator. It's responsible for enumerating over some child data type, +/// say IChildType. You do the enumeration by calling IMyEnumerator::Next() +/// one of whose arguments will be an IChildType**. Eventually Next() fails, +/// indicating that there are no more items. +/// +/// com_iterator represents a single point-in-time of this iteration. It is +/// used by ComEnumerator to support iterating in this fashion via range-based +/// for loops and other common C++ paradigms. +template class com_iterator { + using FunctionTraits = function_traits; + typedef typename function_arg::type FuncArgType; + // FuncArgType is now something like ISomeCOMInterface **. Remove both + // pointers, so we can make a CComPtr out of it. + typedef typename remove_double_pointer::type EnumDataType; + + CComPtr EnumeratorObject; + CComPtr CurrentItem; + +public: + typedef CComPtr value_type; + typedef std::size_t difference_type; + typedef value_type *pointer_type; + typedef value_type &reference_type; + typedef std::forward_iterator_tag iterator_category; + + explicit com_iterator(CComPtr Enumerator, + CComPtr Current) + : EnumeratorObject(Enumerator), CurrentItem(Current) {} + com_iterator() {} + + com_iterator &operator++() { + // EnumeratorObject->Next() expects CurrentItem to be NULL. + CurrentItem.Release(); + ULONG Count = 0; + HRESULT hr = EnumeratorObject->Next(1, &CurrentItem, &Count); + if (FAILED(hr) || Count == 0) + *this = com_iterator(); + + return *this; + } + + value_type operator*() { return CurrentItem; } + + bool operator==(const com_iterator &other) const { + return (EnumeratorObject == other.EnumeratorObject) && + (CurrentItem == other.CurrentItem); + } + + bool operator!=(const com_iterator &other) const { return !(*this == other); } + + com_iterator &operator=(const com_iterator &other) { + EnumeratorObject = other.EnumeratorObject; + CurrentItem = other.CurrentItem; + return *this; + } +}; + +/// ComEnumerator implements the interfaced required for C++ to allow its use +/// in range-based for loops. In particular, a begin() and end() method. +/// These methods simply construct and return an appropriate ComIterator +/// instance. +template class com_enumerator { + typedef function_traits FunctionTraits; + typedef typename function_arg::type FuncArgType; + typedef typename remove_double_pointer::type EnumDataType; + + CComPtr EnumeratorObject; + +public: + com_enumerator(CComPtr Enumerator) + : EnumeratorObject(Enumerator) {} + + com_iterator begin() { + if (!EnumeratorObject) + return end(); + + EnumeratorObject->Reset(); + ULONG Count = 0; + CComPtr FirstItem; + HRESULT hr = EnumeratorObject->Next(1, &FirstItem, &Count); + return (FAILED(hr) || Count == 0) ? end() + : com_iterator( + EnumeratorObject, FirstItem); + } + + com_iterator end() { + return com_iterator(); + } +}; + +/// A helper class for allowing the use of COM record enumerators in range- +/// based for loops. +/// +/// A record enumerator is almost the same as a regular enumerator, except +/// that it returns raw byte-data instead of interfaces to other COM objects. +/// As a result, the enumerator's Next() method has a slightly different +/// signature, and an iterator dereferences to an ArrayRef instead of a +/// CComPtr. +template class com_data_record_iterator { +public: + typedef llvm::ArrayRef value_type; + typedef std::size_t difference_type; + typedef value_type *pointer_type; + typedef value_type &reference_type; + typedef std::forward_iterator_tag iterator_category; + + explicit com_data_record_iterator(CComPtr enumerator) + : Enumerator(enumerator), CurrentRecord(0) { + // Make sure we start at the beginning. If there are no records, + // immediately set ourselves equal to end(). + if (enumerator) + enumerator->Reset(); + + if (!ReadNextRecord()) + *this = com_data_record_iterator(); + } + com_data_record_iterator() {} + + com_data_record_iterator &operator++() { + ++CurrentRecord; + // If we can't read any more records, either because there are no more + // or because we encountered an error, we should compare equal to end. + if (!ReadNextRecord()) + *this = com_data_record_iterator(); + return *this; + } + + value_type operator*() { + return llvm::ArrayRef(RecordData.begin(), RecordData.end()); + } + + bool operator==(const com_data_record_iterator &other) const { + return (Enumerator == other.Enumerator) && + (CurrentRecord == other.CurrentRecord); + } + + bool operator!=(const com_data_record_iterator &other) const { + return !(*this == other); + } + +private: + bool ReadNextRecord() { + RecordData.clear(); + ULONG Count = 0; + DWORD RequiredBufferSize; + HRESULT hr = Enumerator->Next(1, 0, &RequiredBufferSize, nullptr, &Count); + if (hr == S_OK) { + RecordData.resize(RequiredBufferSize); + DWORD BytesRead = 0; + hr = Enumerator->Next(1, RequiredBufferSize, &BytesRead, + RecordData.data(), &Count); + } + if (hr != S_OK) + RecordData.clear(); + return (hr == S_OK); + } + + CComPtr Enumerator; + uint32_t CurrentRecord; + llvm::SmallVector RecordData; +}; + +/// Similar to ComEnumerator, com_data_record_enumerator implements the range +/// interface for ComDataRecordIterators. +template class com_data_record_enumerator { +public: + com_data_record_enumerator(CComPtr enumerator) + : Enumerator(enumerator) {} + + com_data_record_iterator begin() { + return com_data_record_iterator(Enumerator); + } + + com_data_record_iterator end() { + LONG NumElts = 0; + HRESULT hr = Enumerator->get_Count(&NumElts); + return (FAILED(hr)) ? com_data_record_iterator(Enumerator) + : com_data_record_iterator(); + } + +private: + CComPtr Enumerator; +}; + +/// com_enumerator is a simple helper function to allow the enumerator +/// class's type to be inferred automatically. +/// This allows you to write simply: +/// for (auto item : com_enumerator(MyEnumerator)) { +/// } +template +com_enumerator +make_com_enumerator(CComPtr Enumerator) { + return com_enumerator(Enumerator); +} + +/// com_data_record_enumerator is a simple helper function to allow the +/// enumerator class's type to be inferred automatically. +/// This allows you to write simply: +/// for (auto item : com_data_record_enumerator(MyEnumerator)) { +/// } +//============================================================================= +template +com_data_record_enumerator +make_com_data_record_enumerator(CComPtr Enumerator) { + return com_data_record_enumerator(Enumerator); +} + +} // namespace windows +} // namespace sys +} // namespace llvm + +#endif Index: tools/llvm-pdbdump/LLVMBuild.txt =================================================================== --- /dev/null +++ tools/llvm-pdbdump/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./tools/llvm-pdbdump/LLVMBuild.txt -----------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-pdbdump +parent = Tools +required_libraries = + Index: tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- /dev/null +++ tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -0,0 +1,157 @@ +//===- llvm-pdbdump.cpp - Dump debug info from a PDB file -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Dumps debug information present in PDB files. This utility makes use of +// the Microsoft Windows SDK, so will not compile or run on non-Windows +// platforms. +// +//===----------------------------------------------------------------------===// + +#define NTDDI_VERSION NTDDI_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#define WINVER _WIN32_WINNT_VISTA +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include +#include +#include + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" + +#include "COMExtras.h" + +using namespace llvm; +using namespace llvm::sys::windows; + +namespace opts { +cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::OneOrMore); + +// -streams, -s +cl::opt Streams("streams", cl::desc("Display data stream information")); +cl::alias StreamsShort("s", cl::desc("Alias for --streams"), + cl::aliasopt(Streams)); + +// stream-data, -S +cl::opt StreamData("stream-data", + cl::desc("Dumps stream record data as bytes")); +cl::alias StreamDataShort("S", cl::desc("Alias for --stream-data"), + cl::aliasopt(StreamData)); +} + +namespace { +bool BSTRToUTF8(BSTR String16, std::string &String8) { + UINT ByteLength = ::SysStringByteLen(String16); + char *Bytes = reinterpret_cast(String16); + String8.clear(); + return llvm::convertUTF16ToUTF8String(ArrayRef(Bytes, ByteLength), + String8); +} +} + +static void dumpDataStreams(IDiaSession *session) { + CComPtr DebugStreams = nullptr; + if (FAILED(session->getEnumDebugStreams(&DebugStreams))) + return; + + LONG Count = 0; + if (FAILED(DebugStreams->get_Count(&Count))) + return; + outs() << "Data Streams [count=" << Count << "]\n"; + + std::string Name8; + + for (auto Stream : make_com_enumerator(DebugStreams)) { + BSTR Name16; + if (FAILED(Stream->get_name(&Name16))) + continue; + if (BSTRToUTF8(Name16, Name8)) + outs() << " " << Name8; + ::SysFreeString(Name16); + if (FAILED(Stream->get_Count(&Count))) { + outs() << "\n"; + continue; + } + + outs() << " [" << Count << " records]\n"; + if (opts::StreamData) { + int RecordIndex = 0; + for (auto StreamRecord : make_com_data_record_enumerator(Stream)) { + outs() << " Record " << RecordIndex << " [" << StreamRecord.size() + << " bytes]"; + for (uint8_t byte : StreamRecord) { + outs() << " " << llvm::format_hex_no_prefix(byte, 2, true); + } + outs() << "\n"; + ++RecordIndex; + } + } + } + outs().flush(); +} + +static void dumpInput(StringRef Path) { + SmallVector Path16String; + llvm::convertUTF8ToUTF16String(Path, Path16String); + wchar_t *Path16 = reinterpret_cast(Path16String.data()); + CComPtr source; + HRESULT hr = + ::CoCreateInstance(CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IDiaDataSource), (void **)&source); + if (FAILED(hr)) + return; + if (FAILED(source->loadDataFromPdb(Path16))) + return; + CComPtr session; + if (FAILED(source->openSession(&session))) + return; + if (opts::Streams || opts::StreamData) { + dumpDataStreams(session); + } +} + +int main(int argc_, const char *argv_[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc_, argv_); + + SmallVector argv; + llvm::SpecificBumpPtrAllocator ArgAllocator; + std::error_code EC = llvm::sys::Process::GetArgumentVector( + argv, llvm::makeArrayRef(argv_, argc_), ArgAllocator); + if (EC) { + llvm::errs() << "error: couldn't get arguments: " << EC.message() << '\n'; + return 1; + } + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n"); + + CoInitializeEx(nullptr, COINIT_MULTITHREADED); + + std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), + dumpInput); + + CoUninitialize(); + return 0; +}