From: Zachary Turner Date: Tue, 27 Jan 2015 20:46:21 +0000 (+0000) Subject: Add llvm-pdbdump to tools. X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=c1592bca1e85596abc3dc0a67e2815fa0f84748d;p=oota-llvm.git Add llvm-pdbdump to tools. llvm-pdbdump is a tool which can be used to dump the contents of Microsoft-generated PDB files. It makes use of the Microsoft DIA SDK, which is a COM based library designed specifically for this purpose. The initial commit of this tool dumps the raw bytes from PDB data streams. Future commits will dump more semantic information such as types, symbols, source files, etc similar to the types of information accessible via llvm-dwarfdump. Reviewed by: Aaron Ballman, Reid Kleckner, Chandler Carruth Differential Revision: http://reviews.llvm.org/D7153 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@227241 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 7e9938119f5..07b0bf90717 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -61,6 +61,10 @@ add_llvm_tool_subdirectory(yaml2obj) 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) diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index 53d4ffc28dc..e7b81bbf53f 100644 --- a/tools/LLVMBuild.txt +++ b/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 diff --git a/tools/llvm-pdbdump/CMakeLists.txt b/tools/llvm-pdbdump/CMakeLists.txt new file mode 100644 index 00000000000..bc1c0f8c39c --- /dev/null +++ b/tools/llvm-pdbdump/CMakeLists.txt @@ -0,0 +1,26 @@ +set(MSVC_DIA_SDK_DIR "$ENV{VSINSTALLDIR}DIA SDK") + +# Due to a bug in MSVC 2013's installation software, it is possible +# for MSVC 2013 to write the DIA SDK into the Visual Studio 2012 +# install directory. If this happens, the installation is corrupt +# and there's nothing we can do. It happens with enough frequency +# though that we should handle it. We do so by simply checking that +# the DIA SDK folder exists. Should this happen you will need to +# uninstall VS 2012 and then re-install VS 2013. +if (IS_DIRECTORY ${MSVC_DIA_SDK_DIR}) + set(LLVM_LINK_COMPONENTS + Support + ) + + 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) +endif() diff --git a/tools/llvm-pdbdump/COMExtras.h b/tools/llvm-pdbdump/COMExtras.h new file mode 100644 index 00000000000..7a48ee5887d --- /dev/null +++ b/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::ptrdiff_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::ptrdiff_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 diff --git a/tools/llvm-pdbdump/LLVMBuild.txt b/tools/llvm-pdbdump/LLVMBuild.txt new file mode 100644 index 00000000000..29d40debd6b --- /dev/null +++ b/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 = + diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp new file mode 100644 index 00000000000..a29f85395d8 --- /dev/null +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -0,0 +1,155 @@ +//===- 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); + +cl::opt Streams("streams", cl::desc("Display data stream information")); +cl::alias StreamsShort("s", cl::desc("Alias for --streams"), + cl::aliasopt(Streams)); + +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; +}