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) \ No newline at end of file Index: tools/llvm-pdbdump/COMExtras.h =================================================================== --- /dev/null +++ tools/llvm-pdbdump/COMExtras.h @@ -0,0 +1,264 @@ +//===- 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" + +template struct function_traits; + +template +struct function_traits : public function_traits {}; + +template struct function_traits { + using return_type = R; + + using args_tuple = std::tuple; +}; + +template +struct function_traits + : public function_traits {}; + +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; }; + +//============================================================================= +// class ComIterator<> +// +// 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() +// to get a pointer to a pointer to the child type. Eventually it fails, +// and that means you're at the end. +// +// ComIterator 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 ComIterator { +private: + 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; + +public: + explicit ComIterator(CComPtr Enumerator, + CComPtr Current) + : EnumeratorObject(Enumerator), CurrentItem(Current) {} + ComIterator() {} + + ComIterator &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 = ComIterator(); + + return *this; + } + + CComPtr operator*() { return CurrentItem; } + + bool operator==(const ComIterator &other) const { + return (EnumeratorObject == other.EnumeratorObject) && + (CurrentItem == other.CurrentItem); + } + + bool operator!=(const ComIterator &other) const { return !(*this == other); } + + ComIterator &operator=(const ComIterator &other) { + EnumeratorObject = other.EnumeratorObject; + CurrentItem = other.CurrentItem; + return *this; + } + +private: + CComPtr EnumeratorObject; + CComPtr CurrentItem; +}; + +//============================================================================= +// class ComEnumerator<> +// +// ComEnumerator is the top-level "range class" used to support iteration via +// range-based for loops. It simply provides a begin() and end() method which +// return appropriately constructed ComIterator<> classes. +//============================================================================= +template class ComEnumerator { +private: + typedef function_traits FunctionTraits; + typedef typename function_arg::type FuncArgType; + typedef typename remove_double_pointer::type EnumDataType; + +public: + ComEnumerator(CComPtr Enumerator) + : EnumeratorObject(Enumerator) {} + + ComIterator 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() + : ComIterator(EnumeratorObject, FirstItem); + } + + ComIterator end() { + return ComIterator(); + } + +private: + CComPtr EnumeratorObject; +}; + +//============================================================================= +// class ComDataRecordIterator<> +// +// Similar to ComIterator<>, but uses a +//============================================================================= +template class ComDataRecordIterator { +public: + explicit ComDataRecordIterator(CComPtr enumerator, + uint32_t currentRecord) + : Enumerator(enumerator), CurrentRecord(currentRecord) {} + ComDataRecordIterator() {} + + ComDataRecordIterator &operator++() { + // Release the current item so that Enumerator->Next() is happy. + ++CurrentRecord; + ReadNextRecord(); + return *this; + } + + llvm::ArrayRef operator*() { + if (CurrentRecord == 0) + ReadNextRecord(); + + return llvm::ArrayRef(RecordData.begin(), RecordData.end()); + } + + bool operator==(const ComDataRecordIterator &other) const { + return (Enumerator == other.Enumerator) && + (CurrentRecord == other.CurrentRecord); + } + + bool operator!=(const ComDataRecordIterator &other) const { + return !(*this == other); + } + +private: + void 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_FALSE) { + // This is the end of the enumeration. + RecordData.clear(); + } + } + + CComPtr Enumerator; + uint32_t CurrentRecord; + llvm::SmallVector RecordData; +}; + +//============================================================================= +// class ComEnumerator<> +// +// ComEnumerator is the top-level "range class" used to support iteration via +// range-based for loops. It simply provides a begin() and end() method which +// return appropriately constructed ComIterator<> classes. +//============================================================================= +template class ComDataRecordEnumerator { +public: + ComDataRecordEnumerator(CComPtr enumerator) + : Enumerator(enumerator) {} + + ComDataRecordIterator begin() { + if (Enumerator) + Enumerator->Reset(); + return ComDataRecordIterator(Enumerator, 0); + } + + ComDataRecordIterator end() { + LONG NumElts = 0; + HRESULT hr = Enumerator->get_Count(&NumElts); + return (FAILED(hr)) + ? ComDataRecordIterator(Enumerator, 0) + : ComDataRecordIterator(Enumerator, NumElts); + } + +private: + CComPtr Enumerator; +}; + +//============================================================================= +// function com_enumerator<> +// +// com_enumerator puts together all the magic C++ incantations to deduce all +// necessary types (enumerator, child type) automatically. You need only write +// for (auto item : com_enumerator(MyEnumerator)) +// { +// } +//============================================================================= +template +ComEnumerator +com_enumerator(CComPtr Enumerator) { + return ComEnumerator(Enumerator); +} + +//============================================================================= +// function com_data_record_enumerator<> +// +// com_data_record_enumerator returns a ComDataRecordEnumerator appropriately +// parameterized so that it can be used in a range-based for loop. You need +// only write +// for (auto record : com_data_record_enumerator(MyEnumerator)) +// { +// } +//============================================================================= +template +ComDataRecordEnumerator +com_data_record_enumerator(CComPtr Enumerator) { + return ComDataRecordEnumerator(Enumerator); +} +#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,152 @@ +//===- 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/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; + +namespace llvm { +namespace sys { +namespace windows { +extern std::error_code UTF8ToUTF16(StringRef utf8, + SmallVectorImpl &utf16); +extern std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, + SmallVectorImpl &utf8); +} +} +} + +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)); +} + +static void dumpDataStreams(IDiaSession *session) { + CComPtr DebugStreams = nullptr; + if (session->getEnumDebugStreams(&DebugStreams) == S_OK) { + LONG Count = 0; + if (FAILED(DebugStreams->get_Count(&Count))) + return; + outs() << "Data Streams [count=" << Count << "]\n"; + + llvm::SmallString<32> Name8; + + for (auto Stream : com_enumerator(DebugStreams)) { + BSTR Name16; + if (Stream->get_name(&Name16) != S_OK) + continue; + if (!llvm::sys::windows::UTF16ToUTF8(Name16, SysStringLen(Name16), Name8)) + outs() << " " << Name8; + ::SysFreeString(Name16); + if (SUCCEEDED(Stream->get_Count(&Count))) { + outs() << " [" << Count << " records]\n"; + if (opts::StreamData) { + int RecordIndex = 0; + for (auto StreamRecord : com_data_record_enumerator(Stream)) { + outs() << " Record " << RecordIndex << " [" + << StreamRecord.size() << " bytes]"; + for (size_t i = 0; i < StreamRecord.size(); ++i) { + outs() << " " + << llvm::format_hex(StreamRecord[i], 2, true, false); + } + outs() << "\n"; + ++RecordIndex; + } + } + } else + outs() << "\n"; + } + } + outs().flush(); +} + +static void dumpInput(StringRef Path) { + SmallVector path_utf16; + std::error_code EC = llvm::sys::windows::UTF8ToUTF16(Path, path_utf16); + CComPtr source; + HRESULT hr = + ::CoCreateInstance(CLSID_DiaSource, nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IDiaDataSource), (void **)&source); + if (FAILED(hr)) + return; + if (FAILED(source->loadDataFromPdb(path_utf16.data()))) + 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; +}