Index: lld/trunk/test/CMakeLists.txt =================================================================== --- lld/trunk/test/CMakeLists.txt +++ lld/trunk/test/CMakeLists.txt @@ -29,7 +29,7 @@ list(APPEND LLD_TEST_DEPS FileCheck count not llvm-ar llvm-as llvm-dis llvm-dwarfdump llvm-nm llc llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml - llvm-mc llvm-lib llvm-pdbdump opt + llvm-mc llvm-lib llvm-pdbutil opt ) endif() Index: lld/trunk/test/COFF/pdb-none.test =================================================================== --- lld/trunk/test/COFF/pdb-none.test +++ lld/trunk/test/COFF/pdb-none.test @@ -3,7 +3,7 @@ # RUN: lld-link /debug /debugtype:pdata /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \ # RUN: %t1.obj %t2.obj -# RUN: llvm-pdbdump pdb2yaml -pdb-stream %t.pdb | FileCheck %s +# RUN: llvm-pdbutil pdb2yaml -pdb-stream %t.pdb | FileCheck %s # CHECK: PdbStream: # CHECK-NEXT: Age: 0 Index: lld/trunk/test/COFF/pdb.test =================================================================== --- lld/trunk/test/COFF/pdb.test +++ lld/trunk/test/COFF/pdb.test @@ -3,10 +3,10 @@ # RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \ # RUN: %t1.obj %t2.obj -# RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream \ +# RUN: llvm-pdbutil pdb2yaml -stream-metadata -stream-directory -pdb-stream \ # RUN: -dbi-stream -ipi-stream -tpi-stream %t.pdb | FileCheck %s -# RUN: llvm-pdbdump raw -modules -section-map -section-headers -section-contribs \ +# RUN: llvm-pdbutil raw -modules -section-map -section-headers -section-contribs \ # RUN: -tpi-records %t.pdb | FileCheck -check-prefix RAW %s # CHECK: MSF: Index: llvm/trunk/docs/ReleaseNotes.rst =================================================================== --- llvm/trunk/docs/ReleaseNotes.rst +++ llvm/trunk/docs/ReleaseNotes.rst @@ -50,6 +50,10 @@ structure and type definitions for DWARF, ELF, COFF, WASM, and MachO file formats. +* The tool ``llvm-pdbdump`` has been renamed ``llvm-pdbutil`` to better reflect + its nature as a general purpose PDB manipulation / diagnostics tool that does + more than just dumping contents. + * ... next change ... Index: llvm/trunk/test/CMakeLists.txt =================================================================== --- llvm/trunk/test/CMakeLists.txt +++ llvm/trunk/test/CMakeLists.txt @@ -61,7 +61,7 @@ llvm-nm llvm-objdump llvm-opt-report - llvm-pdbdump + llvm-pdbutil llvm-profdata llvm-ranlib llvm-readobj Index: llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-flags.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-flags.test +++ llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-flags.test @@ -1,7 +1,7 @@ -; RUN: llvm-pdbdump pretty %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=NO_ARGS -; RUN: llvm-pdbdump pretty -types %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=TYPES -; RUN: llvm-pdbdump pretty -compilands %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=COMPILANDS -; RUN: llvm-pdbdump pretty -types -compilands %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=MULTIPLE +; RUN: llvm-pdbutil pretty %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=NO_ARGS +; RUN: llvm-pdbutil pretty -types %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=TYPES +; RUN: llvm-pdbutil pretty -compilands %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=COMPILANDS +; RUN: llvm-pdbutil pretty -types -compilands %p/../Inputs/empty.pdb | FileCheck %s -check-prefix=MULTIPLE ; Check that neither symbols nor compilands are dumped when neither argument specified. ; NO_ARGS: empty.pdb Index: llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-linenumbers.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-linenumbers.test +++ llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-linenumbers.test @@ -1,5 +1,5 @@ -; RUN: llvm-pdbdump pretty -lines %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=LINE_NUMS_FPO %s -; RUN: llvm-pdbdump pretty -lines %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=LINE_NUMS %s +; RUN: llvm-pdbutil pretty -lines %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=LINE_NUMS_FPO %s +; RUN: llvm-pdbutil pretty -lines %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=LINE_NUMS %s ; LINE_NUMS_FPO: llvm\test\debuginfo\pdb\inputs\symbolformat-fpo.cpp ; LINE_NUMS_FPO: Line 5, Address: [0x000011a0 - 0x000011a5] (6 bytes) Index: llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test +++ llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test @@ -1,11 +1,11 @@ -; RUN: llvm-pdbdump pretty -module-syms %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=SYM_FORMAT_FPO %s -; RUN: llvm-pdbdump pretty -module-syms %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=SYM_FORMAT %s -; RUN: llvm-pdbdump pretty -types %p/../Inputs/symbolformat.pdb > %t.types +; RUN: llvm-pdbutil pretty -module-syms %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=SYM_FORMAT_FPO %s +; RUN: llvm-pdbutil pretty -module-syms %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=SYM_FORMAT %s +; RUN: llvm-pdbutil pretty -types %p/../Inputs/symbolformat.pdb > %t.types ; RUN: FileCheck --check-prefix=TYPES_FORMAT %s < %t.types ; RUN: FileCheck --check-prefix=TYPES_1 %s < %t.types ; RUN: FileCheck --check-prefix=TYPES_2 %s < %t.types -; RUN: llvm-pdbdump pretty -types %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=TYPES_FORMAT %s -; RUN: llvm-pdbdump pretty -globals %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=GLOBALS %s +; RUN: llvm-pdbutil pretty -types %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=TYPES_FORMAT %s +; RUN: llvm-pdbutil pretty -globals %p/../Inputs/symbolformat.pdb | FileCheck --check-prefix=GLOBALS %s ; The format is func [0x+ - 0x-] ; SYM_FORMAT_FPO: ---SYMBOLS--- Index: llvm/trunk/test/DebugInfo/PDB/Native/pdb-native-compilands.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/Native/pdb-native-compilands.test +++ llvm/trunk/test/DebugInfo/PDB/Native/pdb-native-compilands.test @@ -1,7 +1,7 @@ ; Test that the native PDB reader can enumerate the compilands. -; RUN: llvm-pdbdump pretty -native -compilands %p/../Inputs/empty.pdb \ +; RUN: llvm-pdbutil pretty -native -compilands %p/../Inputs/empty.pdb \ ; RUN: | FileCheck -check-prefix=EMPTY %s -; RUN: llvm-pdbdump pretty -native -compilands %p/../Inputs/big-read.pdb \ +; RUN: llvm-pdbutil pretty -native -compilands %p/../Inputs/big-read.pdb \ ; RUN: | FileCheck -check-prefix=BIGREAD %s ; Reference output was generated with the DIA reader to ensure that the Index: llvm/trunk/test/DebugInfo/PDB/Native/pdb-native-summary.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/Native/pdb-native-summary.test +++ llvm/trunk/test/DebugInfo/PDB/Native/pdb-native-summary.test @@ -1,5 +1,5 @@ ; Test that the native PDB reader gets the PDB summary correct. -; RUN: llvm-pdbdump pretty -native -color-output=false %p/../Inputs/empty.pdb \ +; RUN: llvm-pdbutil pretty -native -color-output=false %p/../Inputs/empty.pdb \ ; RUN: | FileCheck -check-prefix=EMPTY %s ; Reference output was generated with the DIA reader to ensure that the Index: llvm/trunk/test/DebugInfo/PDB/pdb-longname-truncation.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdb-longname-truncation.test +++ llvm/trunk/test/DebugInfo/PDB/pdb-longname-truncation.test @@ -1,3 +1,3 @@ ; For now just verify that this doesn't cause an error. Later we pdbdump can ; do type lookup, we can verify that the name matches what we expect. -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.pdb %p/Inputs/longname-truncation.yaml +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.pdb %p/Inputs/longname-truncation.yaml Index: llvm/trunk/test/DebugInfo/PDB/pdb-minimal-construct.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdb-minimal-construct.test +++ llvm/trunk/test/DebugInfo/PDB/pdb-minimal-construct.test @@ -6,6 +6,6 @@ ; resulting PDB to go back to yaml, and verify that the resulting yaml ; is identical. -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.pdb %p/Inputs/one-symbol.yaml -; RUN: llvm-pdbdump pdb2yaml -minimal -module-syms -no-file-headers %t.pdb > %t.pdb.yaml +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.pdb %p/Inputs/one-symbol.yaml +; RUN: llvm-pdbutil pdb2yaml -minimal -module-syms -no-file-headers %t.pdb > %t.pdb.yaml ; RUN: diff -b %p/Inputs/one-symbol.yaml %t.pdb.yaml Index: llvm/trunk/test/DebugInfo/PDB/pdb-yaml-symbols.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdb-yaml-symbols.test +++ llvm/trunk/test/DebugInfo/PDB/pdb-yaml-symbols.test @@ -1,4 +1,4 @@ -; RUN: llvm-pdbdump pdb2yaml -module-syms %p/Inputs/empty.pdb \ +; RUN: llvm-pdbutil pdb2yaml -module-syms %p/Inputs/empty.pdb \ ; RUN: | FileCheck -check-prefix=YAML %s Index: llvm/trunk/test/DebugInfo/PDB/pdb-yaml-types.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdb-yaml-types.test +++ llvm/trunk/test/DebugInfo/PDB/pdb-yaml-types.test @@ -1,7 +1,7 @@ -RUN: llvm-pdbdump pdb2yaml -tpi-stream %p/Inputs/big-read.pdb > %t.yaml +RUN: llvm-pdbutil pdb2yaml -tpi-stream %p/Inputs/big-read.pdb > %t.yaml RUN: FileCheck -check-prefix=YAML %s < %t.yaml -RUN: llvm-pdbdump yaml2pdb %t.yaml -pdb %t.pdb -RUN: llvm-pdbdump raw -tpi-records %t.pdb | FileCheck %s --check-prefix=PDB +RUN: llvm-pdbutil yaml2pdb %t.yaml -pdb %t.pdb +RUN: llvm-pdbutil raw -tpi-records %t.pdb | FileCheck %s --check-prefix=PDB Only verify the beginning of the type stream. Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-debug-subsections.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-debug-subsections.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-debug-subsections.test @@ -1,6 +1,6 @@ -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.pdb %p/Inputs/debug-subsections.yaml -; RUN: llvm-pdbdump pdb2yaml -all -no-file-headers %t.pdb | FileCheck --check-prefix=YAML %s -; RUN: llvm-pdbdump raw -subsections=all %t.pdb | FileCheck --check-prefix=RAW %s +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.pdb %p/Inputs/debug-subsections.yaml +; RUN: llvm-pdbutil pdb2yaml -all -no-file-headers %t.pdb | FileCheck --check-prefix=YAML %s +; RUN: llvm-pdbutil raw -subsections=all %t.pdb | FileCheck --check-prefix=RAW %s YAML: Modules: YAML-NEXT: - Module: Foo.obj Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-headers.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-headers.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-headers.test @@ -1,12 +1,12 @@ -; RUN: llvm-pdbdump raw -headers -string-table -tpi-records -tpi-record-bytes -module-syms \ +; RUN: llvm-pdbutil raw -headers -string-table -tpi-records -tpi-record-bytes -module-syms \ ; RUN: -sym-record-bytes -globals -publics -module-files \ ; RUN: -stream-summary -stream-blocks -ipi-records -ipi-record-bytes \ ; RUN: -section-contribs -section-map -section-headers -subsections=all \ ; RUN: -tpi-hash -fpo -page-stats %p/Inputs/empty.pdb | FileCheck -check-prefix=EMPTY %s -; RUN: llvm-pdbdump raw -all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s -; RUN: llvm-pdbdump raw -headers -modules -module-files \ +; RUN: llvm-pdbutil raw -all %p/Inputs/empty.pdb | FileCheck -check-prefix=ALL %s +; RUN: llvm-pdbutil raw -headers -modules -module-files \ ; RUN: %p/Inputs/big-read.pdb | FileCheck -check-prefix=BIG %s -; RUN: not llvm-pdbdump raw -headers %p/Inputs/bad-block-size.pdb 2>&1 | FileCheck -check-prefix=BAD-BLOCK-SIZE %s +; RUN: not llvm-pdbutil raw -headers %p/Inputs/bad-block-size.pdb 2>&1 | FileCheck -check-prefix=BAD-BLOCK-SIZE %s ; EMPTY: FileHeaders { ; EMPTY-NEXT: BlockSize: 4096 Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-merge-ids-and-types.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-merge-ids-and-types.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-merge-ids-and-types.test @@ -1,12 +1,12 @@ -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge-ids-and-types-1.yaml -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge-ids-and-types-2.yaml -; RUN: llvm-pdbdump merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb -; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=TPI-TYPES %s -; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=INTMAIN %s -; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=VOIDMAIN %s -; RUN: llvm-pdbdump raw -ipi-records %t.3.pdb | FileCheck -check-prefix=IPI-TYPES %s -; RUN: llvm-pdbdump raw -ipi-records %t.3.pdb | FileCheck -check-prefix=IPI-NAMES %s -; RUN: llvm-pdbdump raw -ipi-records %t.3.pdb | FileCheck -check-prefix=IPI-UDT %s +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge-ids-and-types-1.yaml +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge-ids-and-types-2.yaml +; RUN: llvm-pdbutil merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb +; RUN: llvm-pdbutil raw -tpi-records %t.3.pdb | FileCheck -check-prefix=TPI-TYPES %s +; RUN: llvm-pdbutil raw -tpi-records %t.3.pdb | FileCheck -check-prefix=INTMAIN %s +; RUN: llvm-pdbutil raw -tpi-records %t.3.pdb | FileCheck -check-prefix=VOIDMAIN %s +; RUN: llvm-pdbutil raw -ipi-records %t.3.pdb | FileCheck -check-prefix=IPI-TYPES %s +; RUN: llvm-pdbutil raw -ipi-records %t.3.pdb | FileCheck -check-prefix=IPI-NAMES %s +; RUN: llvm-pdbutil raw -ipi-records %t.3.pdb | FileCheck -check-prefix=IPI-UDT %s TPI-TYPES: Type Info Stream (TPI) TPI-TYPES: Record count: 9 Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-mergeids.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-mergeids.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-mergeids.test @@ -1,9 +1,9 @@ -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge-ids-1.yaml -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge-ids-2.yaml -; RUN: llvm-pdbdump merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb -; RUN: llvm-pdbdump raw -ipi-records %t.3.pdb | FileCheck -check-prefix=MERGED %s -; RUN: llvm-pdbdump raw -ipi-records %t.3.pdb | FileCheck -check-prefix=SUBSTRS %s -; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=TPI-EMPTY %s +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge-ids-1.yaml +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge-ids-2.yaml +; RUN: llvm-pdbutil merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb +; RUN: llvm-pdbutil raw -ipi-records %t.3.pdb | FileCheck -check-prefix=MERGED %s +; RUN: llvm-pdbutil raw -ipi-records %t.3.pdb | FileCheck -check-prefix=SUBSTRS %s +; RUN: llvm-pdbutil raw -tpi-records %t.3.pdb | FileCheck -check-prefix=TPI-EMPTY %s MERGED: Type Info Stream (IPI) Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-mergetypes.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-mergetypes.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-mergetypes.test @@ -1,8 +1,8 @@ -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge-types-1.yaml -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge-types-2.yaml -; RUN: llvm-pdbdump merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb -; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=MERGED %s -; RUN: llvm-pdbdump raw -tpi-records %t.3.pdb | FileCheck -check-prefix=ARGLIST %s +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.1.pdb %p/Inputs/merge-types-1.yaml +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.2.pdb %p/Inputs/merge-types-2.yaml +; RUN: llvm-pdbutil merge -pdb=%t.3.pdb %t.1.pdb %t.2.pdb +; RUN: llvm-pdbutil raw -tpi-records %t.3.pdb | FileCheck -check-prefix=MERGED %s +; RUN: llvm-pdbutil raw -tpi-records %t.3.pdb | FileCheck -check-prefix=ARGLIST %s MERGED: Type Info Stream (TPI) Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-raw-blocks.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-raw-blocks.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-raw-blocks.test @@ -1,8 +1,8 @@ -; RUN: llvm-pdbdump raw -block-data=0 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK0 %s -; RUN: llvm-pdbdump raw -block-data=0-1 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK01 %s -; RUN: not llvm-pdbdump raw -block-data=0,1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s -; RUN: not llvm-pdbdump raw -block-data=0a1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s -; RUN: not llvm-pdbdump raw -block-data=0- %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s +; RUN: llvm-pdbutil raw -block-data=0 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK0 %s +; RUN: llvm-pdbutil raw -block-data=0-1 %p/Inputs/empty.pdb | FileCheck --check-prefix=BLOCK01 %s +; RUN: not llvm-pdbutil raw -block-data=0,1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s +; RUN: not llvm-pdbutil raw -block-data=0a1 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s +; RUN: not llvm-pdbutil raw -block-data=0- %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=BADSYNTAX %s BLOCK0: Block Data { BLOCK0-NEXT: Block 0 ( Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-raw-stream.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-raw-stream.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-raw-stream.test @@ -1,5 +1,5 @@ -; RUN: llvm-pdbdump raw -stream-data=1 %p/Inputs/empty.pdb | FileCheck --check-prefix=STREAM1 %s -; RUN: not llvm-pdbdump raw -stream-data=100 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=INVALIDSTREAM %s +; RUN: llvm-pdbutil raw -stream-data=1 %p/Inputs/empty.pdb | FileCheck --check-prefix=STREAM1 %s +; RUN: not llvm-pdbutil raw -stream-data=100 %p/Inputs/empty.pdb 2>&1 | FileCheck --check-prefix=INVALIDSTREAM %s STREAM1: Stream Data { STREAM1-NEXT: Stream { Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-readwrite.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-readwrite.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-readwrite.test @@ -1,10 +1,10 @@ -RUN: llvm-pdbdump pdb2yaml -modules -module-files -dbi-stream \ +RUN: llvm-pdbutil pdb2yaml -modules -module-files -dbi-stream \ RUN: -pdb-stream -string-table -tpi-stream -stream-directory \ RUN: -stream-metadata %p/Inputs/empty.pdb > %t.1 -RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1 +RUN: llvm-pdbutil yaml2pdb -pdb=%t.2 %t.1 -RUN: llvm-pdbdump raw -headers -string-table -tpi-records %p/Inputs/empty.pdb | FileCheck %s -RUN: llvm-pdbdump raw -headers -string-table -tpi-records %t.2 | FileCheck %s +RUN: llvm-pdbutil raw -headers -string-table -tpi-records %p/Inputs/empty.pdb | FileCheck %s +RUN: llvm-pdbutil raw -headers -string-table -tpi-records %t.2 | FileCheck %s CHECK: FileHeaders { CHECK-NEXT: BlockSize: 4096 Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-source-names.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-source-names.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-source-names.test @@ -6,11 +6,11 @@ # that differ by one byte, so that at least one of those will only # pass if alignment is implemented correctly. -RUN: llvm-pdbdump yaml2pdb -pdb=%T/source-names-1.pdb %p/Inputs/source-names-1.yaml -RUN: llvm-pdbdump pdb2yaml -module-files %T/source-names-1.pdb \ +RUN: llvm-pdbutil yaml2pdb -pdb=%T/source-names-1.pdb %p/Inputs/source-names-1.yaml +RUN: llvm-pdbutil pdb2yaml -module-files %T/source-names-1.pdb \ RUN: | FileCheck -check-prefix=CHECK1 %s -RUN: llvm-pdbdump yaml2pdb -pdb=%T/source-names-2.pdb %p/Inputs/source-names-2.yaml -RUN: llvm-pdbdump pdb2yaml -module-files %T/source-names-2.pdb \ +RUN: llvm-pdbutil yaml2pdb -pdb=%T/source-names-2.pdb %p/Inputs/source-names-2.yaml +RUN: llvm-pdbutil pdb2yaml -module-files %T/source-names-2.pdb \ RUN: | FileCheck -check-prefix=CHECK2 %s CHECK1: SourceFiles: Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-write.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-write.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-write.test @@ -10,11 +10,11 @@ ; stream metadata, since the layout of the MSF file might be different ; (for example if we don't write the entire stream) ; -; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory \ +; RUN: llvm-pdbutil pdb2yaml -stream-metadata -stream-directory \ ; RUN: -pdb-stream -tpi-stream -module-syms %p/Inputs/empty.pdb > %t.1 -; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1 -; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream \ +; RUN: llvm-pdbutil yaml2pdb -pdb=%t.2 %t.1 +; RUN: llvm-pdbutil pdb2yaml -pdb-stream -tpi-stream \ ; RUN: -module-syms -no-file-headers %p/Inputs/empty.pdb > %t.3 -; RUN: llvm-pdbdump pdb2yaml -pdb-stream -tpi-stream \ +; RUN: llvm-pdbutil pdb2yaml -pdb-stream -tpi-stream \ ; RUN: -module-syms -no-file-headers %t.2 > %t.4 ; RUN: diff %t.3 %t.4 Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-yaml-types.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-yaml-types.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-yaml-types.test @@ -1,4 +1,4 @@ -; RUN: llvm-pdbdump pdb2yaml -tpi-stream %p/Inputs/empty.pdb \ +; RUN: llvm-pdbutil pdb2yaml -tpi-stream %p/Inputs/empty.pdb \ ; RUN: | FileCheck -check-prefix=YAML %s YAML: --- Index: llvm/trunk/test/DebugInfo/PDB/pdbdump-yaml.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/pdbdump-yaml.test +++ llvm/trunk/test/DebugInfo/PDB/pdbdump-yaml.test @@ -1,6 +1,6 @@ -; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -string-table -pdb-stream \ +; RUN: llvm-pdbutil pdb2yaml -stream-metadata -stream-directory -string-table -pdb-stream \ ; RUN: %p/Inputs/empty.pdb | FileCheck -check-prefix=YAML %s -; RUN: llvm-pdbdump pdb2yaml -no-file-headers -stream-metadata -stream-directory -pdb-stream \ +; RUN: llvm-pdbutil pdb2yaml -no-file-headers -stream-metadata -stream-directory -pdb-stream \ ; RUN: %p/Inputs/empty.pdb | FileCheck -check-prefix=NO-HEADERS %s ; YAML: --- Index: llvm/trunk/test/tools/llvm-pdbdump/class-layout.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/class-layout.test +++ llvm/trunk/test/tools/llvm-pdbdump/class-layout.test @@ -1,4 +1,4 @@ -; RUN: llvm-pdbdump pretty -all -class-recurse-depth=1 \ +; RUN: llvm-pdbutil pretty -all -class-recurse-depth=1 \ ; RUN: %p/Inputs/ClassLayoutTest.pdb > %t ; RUN: FileCheck -input-file=%t %s -check-prefix=GLOBALS_TEST ; RUN: FileCheck -input-file=%t %s -check-prefix=MEMBERS_TEST Index: llvm/trunk/test/tools/llvm-pdbdump/complex-padding-graphical.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/complex-padding-graphical.test +++ llvm/trunk/test/tools/llvm-pdbdump/complex-padding-graphical.test @@ -1,4 +1,4 @@ -; RUN: llvm-pdbdump pretty -classes -class-definitions=layout \ +; RUN: llvm-pdbutil pretty -classes -class-definitions=layout \ ; RUN: -include-types=Test %p/Inputs/ComplexPaddingTest.pdb > %t ; RUN: FileCheck -input-file=%t %s -check-prefix=DIRECT_VB_ONLY Index: llvm/trunk/test/tools/llvm-pdbdump/enum-layout.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/enum-layout.test +++ llvm/trunk/test/tools/llvm-pdbdump/enum-layout.test @@ -1,4 +1,4 @@ -; RUN: llvm-pdbdump pretty -types %p/Inputs/ClassLayoutTest.pdb > %t +; RUN: llvm-pdbutil pretty -types %p/Inputs/ClassLayoutTest.pdb > %t ; RUN: FileCheck -input-file=%t %s -check-prefix=GLOBAL_ENUM ; RUN: FileCheck -input-file=%t %s -check-prefix=MEMBER_ENUM Index: llvm/trunk/test/tools/llvm-pdbdump/load-address.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/load-address.test +++ llvm/trunk/test/tools/llvm-pdbdump/load-address.test @@ -1,6 +1,6 @@ -; RUN: llvm-pdbdump pretty -externals %p/Inputs/LoadAddressTest.pdb \ +; RUN: llvm-pdbutil pretty -externals %p/Inputs/LoadAddressTest.pdb \ ; RUN: | FileCheck --check-prefix=RVA %s -; RUN: llvm-pdbdump pretty -externals -load-address=0x40000000 \ +; RUN: llvm-pdbutil pretty -externals -load-address=0x40000000 \ ; RUN: %p/Inputs/LoadAddressTest.pdb | FileCheck --check-prefix=VA %s ; RVA: ---EXTERNALS--- Index: llvm/trunk/test/tools/llvm-pdbdump/raw-stream-data.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/raw-stream-data.test +++ llvm/trunk/test/tools/llvm-pdbdump/raw-stream-data.test @@ -1,8 +1,8 @@ -; RUN: llvm-pdbdump raw -stream-data=8 %p/Inputs/LoadAddressTest.pdb \ +; RUN: llvm-pdbutil raw -stream-data=8 %p/Inputs/LoadAddressTest.pdb \ ; RUN: | FileCheck %s -check-prefix=FULL_STREAM -; RUN: llvm-pdbdump raw -stream-data=8:4 %p/Inputs/LoadAddressTest.pdb \ +; RUN: llvm-pdbutil raw -stream-data=8:4 %p/Inputs/LoadAddressTest.pdb \ ; RUN: | FileCheck %s -check-prefix=OFFSET_STREAM -; RUN: llvm-pdbdump raw -stream-data=8:4@24 %p/Inputs/LoadAddressTest.pdb \ +; RUN: llvm-pdbutil raw -stream-data=8:4@24 %p/Inputs/LoadAddressTest.pdb \ ; RUN: | FileCheck %s -check-prefix=OFFSET_AND_LENGTH FULL_STREAM: Stream Data { Index: llvm/trunk/test/tools/llvm-pdbdump/regex-filter.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/regex-filter.test +++ llvm/trunk/test/tools/llvm-pdbdump/regex-filter.test @@ -1,25 +1,25 @@ -; RUN: llvm-pdbdump pretty -module-syms -globals -types %p/Inputs/FilterTest.pdb \ +; RUN: llvm-pdbutil pretty -module-syms -globals -types %p/Inputs/FilterTest.pdb \ ; RUN: | FileCheck --check-prefix=NO_FILTER %s -; RUN: llvm-pdbdump pretty -types -exclude-types="GlobalTypedef|NestedTypedef" \ +; RUN: llvm-pdbutil pretty -types -exclude-types="GlobalTypedef|NestedTypedef" \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_TYPEDEFS %s -; RUN: llvm-pdbdump pretty -classes -enums %p/Inputs/FilterTest.pdb \ +; RUN: llvm-pdbutil pretty -classes -enums %p/Inputs/FilterTest.pdb \ ; RUN: | FileCheck --check-prefix=EXCLUDE_TYPEDEFS %s -; RUN: llvm-pdbdump pretty -types -exclude-types="GlobalEnum|NestedEnum" \ +; RUN: llvm-pdbutil pretty -types -exclude-types="GlobalEnum|NestedEnum" \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_ENUMS %s -; RUN: llvm-pdbdump pretty -classes -typedefs %p/Inputs/FilterTest.pdb \ +; RUN: llvm-pdbutil pretty -classes -typedefs %p/Inputs/FilterTest.pdb \ ; RUN: | FileCheck --check-prefix=EXCLUDE_ENUMS %s -; RUN: llvm-pdbdump pretty -types -module-syms -globals -exclude-symbols="MemberVar|GlobalVar" \ +; RUN: llvm-pdbutil pretty -types -module-syms -globals -exclude-symbols="MemberVar|GlobalVar" \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_VARS %s -; RUN: llvm-pdbdump pretty -types -exclude-types="FilterTestClass" \ +; RUN: llvm-pdbutil pretty -types -exclude-types="FilterTestClass" \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_WHOLE_CLASS %s -; RUN: llvm-pdbdump pretty -module-syms -globals -exclude-compilands="FilterTest.obj" \ +; RUN: llvm-pdbutil pretty -module-syms -globals -exclude-compilands="FilterTest.obj" \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=EXCLUDE_COMPILAND %s -; RUN: llvm-pdbdump pretty -types -include-types="FilterTestClass" \ +; RUN: llvm-pdbutil pretty -types -include-types="FilterTestClass" \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=INCLUDE_ONLY_TYPES %s -; RUN: llvm-pdbdump pretty -types -module-syms -globals -include-symbols="[[:<:]](IntGlobalVar|DoubleGlobalVar)[[:>:]]" \ +; RUN: llvm-pdbutil pretty -types -module-syms -globals -include-symbols="[[:<:]](IntGlobalVar|DoubleGlobalVar)[[:>:]]" \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=INCLUDE_ONLY_VARS %s ; NO_FILTER: ---TYPES--- Index: llvm/trunk/test/tools/llvm-pdbdump/simple-padding-graphical.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/simple-padding-graphical.test +++ llvm/trunk/test/tools/llvm-pdbdump/simple-padding-graphical.test @@ -1,4 +1,4 @@ -; RUN: llvm-pdbdump pretty -classes -class-definitions=layout \ +; RUN: llvm-pdbutil pretty -classes -class-definitions=layout \ ; RUN: -include-types=SimplePad %p/Inputs/SimplePaddingTest.pdb > %t ; RUN: FileCheck -input-file=%t %s -check-prefix=NO_PADDING Index: llvm/trunk/test/tools/llvm-pdbdump/symbol-filters.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/symbol-filters.test +++ llvm/trunk/test/tools/llvm-pdbdump/symbol-filters.test @@ -1,25 +1,25 @@ -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=data %p/Inputs/FilterTest.pdb \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=data %p/Inputs/FilterTest.pdb \ ; RUN: | FileCheck --check-prefix=ONLY_DATA %s -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=thunks %p/Inputs/FilterTest.pdb \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=thunks %p/Inputs/FilterTest.pdb \ ; RUN: | FileCheck --check-prefix=ONLY_THUNKS %s -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=funcs %p/Inputs/FilterTest.pdb \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=funcs %p/Inputs/FilterTest.pdb \ ; RUN: | FileCheck --check-prefix=ONLY_FUNCS %s -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=funcs -sym-types=data \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=funcs -sym-types=data \ ; RUN: %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=TWO_TYPES %s -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=data \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=data \ ; RUN: -symbol-order=name %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=NAME_SORT_DATA %s -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=data \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=data \ ; RUN: -symbol-order=size %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=SIZE_SORT_DATA %s -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=funcs \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=funcs \ ; RUN: -symbol-order=name %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=NAME_SORT_FUNCS %s -; RUN: llvm-pdbdump pretty -globals -module-syms -sym-types=funcs \ +; RUN: llvm-pdbutil pretty -globals -module-syms -sym-types=funcs \ ; RUN: -symbol-order=size %p/Inputs/FilterTest.pdb | FileCheck --check-prefix=SIZE_SORT_FUNCS %s ; ONLY_DATA-NOT: func Index: llvm/trunk/tools/LLVMBuild.txt =================================================================== --- llvm/trunk/tools/LLVMBuild.txt +++ llvm/trunk/tools/LLVMBuild.txt @@ -40,7 +40,7 @@ llvm-modextract llvm-nm llvm-objdump - llvm-pdbdump + llvm-pdbutil llvm-profdata llvm-rtdyld llvm-size Index: llvm/trunk/tools/llvm-pdbutil/Analyze.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/Analyze.h +++ llvm/trunk/tools/llvm-pdbutil/Analyze.h @@ -0,0 +1,30 @@ +//===- Analyze.h - PDB analysis functions -----------------------*- 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_LLVMPDBDUMP_ANALYSIS_H +#define LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H + +#include "OutputStyle.h" + +namespace llvm { +namespace pdb { +class PDBFile; +class AnalysisStyle : public OutputStyle { +public: + explicit AnalysisStyle(PDBFile &File); + + Error dump() override; + +private: + PDBFile &File; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/Analyze.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/Analyze.cpp +++ llvm/trunk/tools/llvm-pdbutil/Analyze.cpp @@ -0,0 +1,152 @@ +//===- Analyze.cpp - PDB analysis functions ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Analyze.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeDatabase.h" +#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static StringRef getLeafTypeName(TypeLeafKind LT) { + switch (LT) { +#define TYPE_RECORD(ename, value, name) \ + case ename: \ + return #name; +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + default: + break; + } + return "UnknownLeaf"; +} + +namespace { +struct HashLookupVisitor : public TypeVisitorCallbacks { + struct Entry { + TypeIndex TI; + CVType Record; + }; + + explicit HashLookupVisitor(TpiStream &Tpi) : Tpi(Tpi) {} + + Error visitTypeBegin(CVType &Record) override { + uint32_t H = Tpi.getHashValues()[I]; + Record.Hash = H; + TypeIndex TI(I + TypeIndex::FirstNonSimpleIndex); + Lookup[H].push_back(Entry{TI, Record}); + ++I; + return Error::success(); + } + + uint32_t I = 0; + DenseMap> Lookup; + TpiStream &Tpi; +}; +} + +AnalysisStyle::AnalysisStyle(PDBFile &File) : File(File) {} + +Error AnalysisStyle::dump() { + auto Tpi = File.getPDBTpiStream(); + if (!Tpi) + return Tpi.takeError(); + + TypeDatabase TypeDB(Tpi->getNumTypeRecords()); + TypeDatabaseVisitor DBV(TypeDB); + TypeVisitorCallbackPipeline Pipeline; + HashLookupVisitor Hasher(*Tpi); + // Add them to the database + Pipeline.addCallbackToPipeline(DBV); + // Store their hash values + Pipeline.addCallbackToPipeline(Hasher); + + if (auto EC = codeview::visitTypeStream(Tpi->typeArray(), Pipeline)) + return EC; + + auto &Adjusters = Tpi->getHashAdjusters(); + DenseSet AdjusterSet; + for (const auto &Adj : Adjusters) { + assert(AdjusterSet.find(Adj.second) == AdjusterSet.end()); + AdjusterSet.insert(Adj.second); + } + + uint32_t Count = 0; + outs() << "Searching for hash collisions\n"; + for (const auto &H : Hasher.Lookup) { + if (H.second.size() <= 1) + continue; + ++Count; + outs() << formatv("Hash: {0}, Count: {1} records\n", H.first, + H.second.size()); + for (const auto &R : H.second) { + auto Iter = AdjusterSet.find(R.TI.getIndex()); + StringRef Prefix; + if (Iter != AdjusterSet.end()) { + Prefix = "[HEAD]"; + AdjusterSet.erase(Iter); + } + StringRef LeafName = getLeafTypeName(R.Record.Type); + uint32_t TI = R.TI.getIndex(); + StringRef TypeName = TypeDB.getTypeName(R.TI); + outs() << formatv("{0,-6} {1} ({2:x}) {3}\n", Prefix, LeafName, TI, + TypeName); + } + } + + outs() << "\n"; + outs() << "Dumping hash adjustment chains\n"; + for (const auto &A : Tpi->getHashAdjusters()) { + TypeIndex TI(A.second); + StringRef TypeName = TypeDB.getTypeName(TI); + const CVType &HeadRecord = TypeDB.getTypeRecord(TI); + assert(HeadRecord.Hash.hasValue()); + + auto CollisionsIter = Hasher.Lookup.find(*HeadRecord.Hash); + if (CollisionsIter == Hasher.Lookup.end()) + continue; + + const auto &Collisions = CollisionsIter->second; + outs() << TypeName << "\n"; + outs() << formatv(" [HEAD] {0:x} {1} {2}\n", A.second, + getLeafTypeName(HeadRecord.Type), TypeName); + for (const auto &Chain : Collisions) { + if (Chain.TI == TI) + continue; + const CVType &TailRecord = TypeDB.getTypeRecord(Chain.TI); + outs() << formatv(" {0:x} {1} {2}\n", Chain.TI.getIndex(), + getLeafTypeName(TailRecord.Type), + TypeDB.getTypeName(Chain.TI)); + } + } + outs() << formatv("There are {0} orphaned hash adjusters\n", + AdjusterSet.size()); + for (const auto &Adj : AdjusterSet) { + outs() << formatv(" {0}\n", Adj); + } + + uint32_t DistinctHashValues = Hasher.Lookup.size(); + outs() << formatv("{0}/{1} hash collisions", Count, DistinctHashValues); + return Error::success(); +} Index: llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt =================================================================== --- llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt +++ llvm/trunk/tools/llvm-pdbutil/CMakeLists.txt @@ -0,0 +1,34 @@ +set(LLVM_LINK_COMPONENTS + DebugInfoCodeView + DebugInfoMSF + DebugInfoPDB + Object + ObjectYAML + Support + ) + +add_llvm_tool(llvm-pdbutil + Analyze.cpp + CompactTypeDumpVisitor.cpp + Diff.cpp + llvm-pdbutil.cpp + LinePrinter.cpp + LLVMOutputStyle.cpp + PdbYaml.cpp + PrettyBuiltinDumper.cpp + PrettyClassDefinitionDumper.cpp + PrettyClassLayoutGraphicalDumper.cpp + PrettyCompilandDumper.cpp + PrettyEnumDumper.cpp + PrettyExternalSymbolDumper.cpp + PrettyFunctionDumper.cpp + PrettyTypeDumper.cpp + PrettyTypedefDumper.cpp + PrettyVariableDumper.cpp + StreamUtil.cpp + YAMLOutputStyle.cpp + ) + +if(LLVM_USE_SANITIZE_COVERAGE) + add_subdirectory(fuzzer) +endif() Index: llvm/trunk/tools/llvm-pdbutil/CompactTypeDumpVisitor.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/CompactTypeDumpVisitor.h +++ llvm/trunk/tools/llvm-pdbutil/CompactTypeDumpVisitor.h @@ -0,0 +1,49 @@ +//===-- CompactTypeDumpVisitor.h - CodeView type info dumper ----*- 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_CODEVIEW_COMPACTTYPEDUMPVISITOR_H +#define LLVM_DEBUGINFO_CODEVIEW_COMPACTTYPEDUMPVISITOR_H + +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" + +namespace llvm { +class ScopedPrinter; +namespace codeview { +class TypeCollection; +} + +namespace pdb { + +/// Dumper for CodeView type streams found in COFF object files and PDB files. +/// Dumps records on a single line, and ignores member records. +class CompactTypeDumpVisitor : public codeview::TypeVisitorCallbacks { +public: + CompactTypeDumpVisitor(codeview::TypeCollection &Types, ScopedPrinter *W); + CompactTypeDumpVisitor(codeview::TypeCollection &Types, + codeview::TypeIndex FirstTI, ScopedPrinter *W); + + /// Paired begin/end actions for all types. Receives all record data, + /// including the fixed-length record prefix. + Error visitTypeBegin(codeview::CVType &Record) override; + Error visitTypeEnd(codeview::CVType &Record) override; + +private: + ScopedPrinter *W; + + codeview::TypeIndex TI; + uint32_t Offset; + codeview::TypeCollection &Types; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif Index: llvm/trunk/tools/llvm-pdbutil/CompactTypeDumpVisitor.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/CompactTypeDumpVisitor.cpp +++ llvm/trunk/tools/llvm-pdbutil/CompactTypeDumpVisitor.cpp @@ -0,0 +1,59 @@ +//===-- CompactTypeDumpVisitor.cpp - CodeView type info dumper --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompactTypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeDatabase.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static const EnumEntry LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +}; + +static StringRef getLeafName(TypeLeafKind K) { + for (const auto &E : LeafTypeNames) { + if (E.Value == K) + return E.Name; + } + return StringRef(); +} + +CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeCollection &Types, + ScopedPrinter *W) + : CompactTypeDumpVisitor(Types, TypeIndex(TypeIndex::FirstNonSimpleIndex), + W) {} + +CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeCollection &Types, + TypeIndex FirstTI, + ScopedPrinter *W) + : W(W), TI(FirstTI), Offset(0), Types(Types) {} + +Error CompactTypeDumpVisitor::visitTypeBegin(CVType &Record) { + return Error::success(); +} + +Error CompactTypeDumpVisitor::visitTypeEnd(CVType &Record) { + uint32_t I = TI.getIndex(); + StringRef Leaf = getLeafName(Record.Type); + StringRef Name = Types.getTypeName(TI); + W->printString( + llvm::formatv("Index: {0:x} ({1:N} bytes, offset {2:N}) {3} \"{4}\"", I, + Record.length(), Offset, Leaf, Name) + .str()); + + Offset += Record.length(); + TI.setIndex(TI.getIndex() + 1); + + return Error::success(); +} Index: llvm/trunk/tools/llvm-pdbutil/Diff.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/Diff.h +++ llvm/trunk/tools/llvm-pdbutil/Diff.h @@ -0,0 +1,45 @@ +//===- Diff.h - PDB diff utility --------------------------------*- 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_LLVMPDBDUMP_DIFF_H +#define LLVM_TOOLS_LLVMPDBDUMP_DIFF_H + +#include "OutputStyle.h" + +namespace llvm { +namespace pdb { +class PDBFile; +class DiffStyle : public OutputStyle { +public: + explicit DiffStyle(PDBFile &File1, PDBFile &File2); + + Error dump() override; + +private: + Error diffSuperBlock(); + Error diffStreamDirectory(); + Error diffStringTable(); + Error diffFreePageMap(); + Error diffInfoStream(); + Error diffDbiStream(); + Error diffSectionContribs(); + Error diffSectionMap(); + Error diffFpoStream(); + Error diffTpiStream(int Index); + Error diffModuleInfoStream(int Index); + Error diffPublics(); + Error diffGlobals(); + + PDBFile &File1; + PDBFile &File2; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/Diff.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/Diff.cpp +++ llvm/trunk/tools/llvm-pdbutil/Diff.cpp @@ -0,0 +1,537 @@ +//===- Diff.cpp - PDB diff utility ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Diff.h" + +#include "StreamUtil.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/Native/Formatters.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" + +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace llvm { +template <> struct format_provider { + static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream, + StringRef Style) { + switch (Sig) { + case PdbRaw_FeatureSig::MinimalDebugInfo: + Stream << "MinimalDebugInfo"; + break; + case PdbRaw_FeatureSig::NoTypeMerge: + Stream << "NoTypeMerge"; + break; + case PdbRaw_FeatureSig::VC110: + Stream << "VC110"; + break; + case PdbRaw_FeatureSig::VC140: + Stream << "VC140"; + break; + } + } +}; +} + +template using ValueOfRange = llvm::detail::ValueOfRange; + +template +static void set_differences(Range &&R1, Range &&R2, + SmallVectorImpl> *OnlyLeft, + SmallVectorImpl> *OnlyRight, + SmallVectorImpl> *Intersection, + Comp Comparator) { + + std::sort(R1.begin(), R1.end(), Comparator); + std::sort(R2.begin(), R2.end(), Comparator); + + if (OnlyLeft) { + OnlyLeft->reserve(R1.size()); + auto End = std::set_difference(R1.begin(), R1.end(), R2.begin(), R2.end(), + OnlyLeft->begin(), Comparator); + OnlyLeft->set_size(std::distance(OnlyLeft->begin(), End)); + } + if (OnlyRight) { + OnlyLeft->reserve(R2.size()); + auto End = std::set_difference(R2.begin(), R2.end(), R1.begin(), R1.end(), + OnlyRight->begin(), Comparator); + OnlyRight->set_size(std::distance(OnlyRight->begin(), End)); + } + if (Intersection) { + Intersection->reserve(std::min(R1.size(), R2.size())); + auto End = std::set_intersection(R1.begin(), R1.end(), R2.begin(), R2.end(), + Intersection->begin(), Comparator); + Intersection->set_size(std::distance(Intersection->begin(), End)); + } +} + +template +static void +set_differences(Range &&R1, Range &&R2, + SmallVectorImpl> *OnlyLeft, + SmallVectorImpl> *OnlyRight, + SmallVectorImpl> *Intersection = nullptr) { + std::less> Comp; + set_differences(std::forward(R1), std::forward(R2), OnlyLeft, + OnlyRight, Intersection, Comp); +} + +DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2) + : File1(File1), File2(File2) {} + +Error DiffStyle::dump() { + if (auto EC = diffSuperBlock()) + return EC; + + if (auto EC = diffFreePageMap()) + return EC; + + if (auto EC = diffStreamDirectory()) + return EC; + + if (auto EC = diffStringTable()) + return EC; + + if (auto EC = diffInfoStream()) + return EC; + + if (auto EC = diffDbiStream()) + return EC; + + if (auto EC = diffSectionContribs()) + return EC; + + if (auto EC = diffSectionMap()) + return EC; + + if (auto EC = diffFpoStream()) + return EC; + + if (auto EC = diffTpiStream(StreamTPI)) + return EC; + + if (auto EC = diffTpiStream(StreamIPI)) + return EC; + + if (auto EC = diffPublics()) + return EC; + + if (auto EC = diffGlobals()) + return EC; + + return Error::success(); +} + +template +static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1, + T V2) { + if (V1 == V2) { + outs() << formatv(" {0}: No differences detected!\n", Label); + return false; + } + + outs().indent(2) << Label << "\n"; + outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1); + outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2); + return true; +} + +template +static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, + ArrayRef V1, ArrayRef V2) { + if (V1 == V2) { + outs() << formatv(" {0}: No differences detected!\n", Label); + return false; + } + + outs().indent(2) << Label << "\n"; + outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), + make_range(V1.begin(), V1.end())); + outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), + make_range(V2.begin(), V2.end())); + return true; +} + +template +static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2, + T &&OnlyRange1, T &&OnlyRange2, + StringRef Label) { + bool HasDiff = false; + if (!OnlyRange1.empty()) { + HasDiff = true; + outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange1.size(), Label, + File1.getFilePath()); + for (const auto &Item : OnlyRange1) + outs() << formatv(" {0}\n", Label, Item); + } + if (!OnlyRange2.empty()) { + HasDiff = true; + outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange2.size(), + File2.getFilePath()); + for (const auto &Item : OnlyRange2) + outs() << formatv(" {0}\n", Item); + } + return HasDiff; +} + +Error DiffStyle::diffSuperBlock() { + outs() << "MSF Super Block: Searching for differences...\n"; + bool Diffs = false; + + Diffs |= diffAndPrint("Block Size", File1, File2, File1.getBlockSize(), + File2.getBlockSize()); + Diffs |= diffAndPrint("Block Count", File1, File2, File1.getBlockCount(), + File2.getBlockCount()); + Diffs |= diffAndPrint("Unknown 1", File1, File2, File1.getUnknown1(), + File2.getUnknown1()); + + if (opts::diff::Pedantic) { + Diffs |= diffAndPrint("Free Block Map", File1, File2, + File1.getFreeBlockMapBlock(), + File2.getFreeBlockMapBlock()); + Diffs |= diffAndPrint("Directory Size", File1, File2, + File1.getNumDirectoryBytes(), + File2.getNumDirectoryBytes()); + Diffs |= diffAndPrint("Block Map Addr", File1, File2, + File1.getBlockMapOffset(), File2.getBlockMapOffset()); + } + if (!Diffs) + outs() << "MSF Super Block: No differences detected...\n"; + return Error::success(); +} + +Error DiffStyle::diffStreamDirectory() { + SmallVector P; + SmallVector Q; + discoverStreamPurposes(File1, P); + discoverStreamPurposes(File2, Q); + outs() << "Stream Directory: Searching for differences...\n"; + + bool HasDifferences = false; + if (opts::diff::Pedantic) { + size_t Min = std::min(P.size(), Q.size()); + for (size_t I = 0; I < Min; ++I) { + StringRef Names[] = {P[I], Q[I]}; + uint32_t Sizes[] = {File1.getStreamByteSize(I), + File2.getStreamByteSize(I)}; + bool NamesDiffer = Names[0] != Names[1]; + bool SizesDiffer = Sizes[0] != Sizes[1]; + if (NamesDiffer) { + HasDifferences = true; + outs().indent(2) << formatv("Stream {0} - {1}: {2}, {3}: {4}\n", I, + File1.getFilePath(), Names[0], + File2.getFilePath(), Names[1]); + continue; + } + if (SizesDiffer) { + HasDifferences = true; + outs().indent(2) << formatv( + "Stream {0} ({1}): {2}: {3} bytes, {4}: {5} bytes\n", I, Names[0], + File1.getFilePath(), Sizes[0], File2.getFilePath(), Sizes[1]); + continue; + } + } + + ArrayRef MaxNames = (P.size() > Q.size() ? P : Q); + size_t Max = std::max(P.size(), Q.size()); + PDBFile &MaxFile = (P.size() > Q.size() ? File1 : File2); + StringRef MinFileName = + (P.size() < Q.size() ? File1.getFilePath() : File2.getFilePath()); + for (size_t I = Min; I < Max; ++I) { + HasDifferences = true; + StringRef StreamName = MaxNames[I]; + + outs().indent(2) << formatv( + "Stream {0} - {1}: , {2}: Index {3}, {4} bytes\n", + StreamName, MinFileName, MaxFile.getFilePath(), I, + MaxFile.getStreamByteSize(I)); + } + if (!HasDifferences) + outs() << "Stream Directory: No differences detected...\n"; + } else { + auto PI = to_vector<32>(enumerate(P)); + auto QI = to_vector<32>(enumerate(Q)); + + typedef decltype(PI) ContainerType; + typedef typename ContainerType::value_type value_type; + + auto Comparator = [](const value_type &I1, const value_type &I2) { + return I1.value() < I2.value(); + }; + + decltype(PI) OnlyP; + decltype(QI) OnlyQ; + decltype(PI) Common; + + set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator); + + if (!OnlyP.empty()) { + HasDifferences = true; + outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(), + File1.getFilePath()); + for (auto &Item : OnlyP) { + outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), + Item.value()); + } + } + + if (!OnlyQ.empty()) { + HasDifferences = true; + outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n", + OnlyQ.size(), File2.getFilePath()); + for (auto &Item : OnlyQ) { + outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), + Item.value()); + } + } + if (!Common.empty()) { + outs().indent(2) << formatv("Found {0} common streams. Searching for " + "intra-stream differences.\n", + Common.size()); + bool HasCommonDifferences = false; + for (const auto &Left : Common) { + // Left was copied from the first range so its index refers to a stream + // index in the first file. Find the corresponding stream index in the + // second file. + auto Range = + std::equal_range(QI.begin(), QI.end(), Left, + [](const value_type &L, const value_type &R) { + return L.value() < R.value(); + }); + const auto &Right = *Range.first; + assert(Left.value() == Right.value()); + uint32_t LeftSize = File1.getStreamByteSize(Left.index()); + uint32_t RightSize = File2.getStreamByteSize(Right.index()); + if (LeftSize != RightSize) { + HasDifferences = true; + HasCommonDifferences = true; + outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n", + Left.value(), File1.getFilePath(), + LeftSize, File2.getFilePath(), RightSize); + } + } + if (!HasCommonDifferences) + outs().indent(2) << "Common Streams: No differences detected!\n"; + } + if (!HasDifferences) + outs() << "Stream Directory: No differences detected!\n"; + } + + return Error::success(); +} + +Error DiffStyle::diffStringTable() { + auto ExpectedST1 = File1.getStringTable(); + auto ExpectedST2 = File2.getStringTable(); + outs() << "String Table: Searching for differences...\n"; + bool Has1 = !!ExpectedST1; + bool Has2 = !!ExpectedST2; + if (!(Has1 && Has2)) { + // If one has a string table and the other doesn't, we can print less + // output. + if (Has1 != Has2) { + if (Has1) { + outs() << formatv(" {0}: ({1} strings)\n", File1.getFilePath(), + ExpectedST1->getNameCount()); + outs() << formatv(" {0}: (string table not present)\n", + File2.getFilePath()); + } else { + outs() << formatv(" {0}: (string table not present)\n", + File1.getFilePath()); + outs() << formatv(" {0}: ({1})\n", File2.getFilePath(), + ExpectedST2->getNameCount()); + } + } + consumeError(ExpectedST1.takeError()); + consumeError(ExpectedST2.takeError()); + return Error::success(); + } + + bool HasDiff = false; + auto &ST1 = *ExpectedST1; + auto &ST2 = *ExpectedST2; + + if (ST1.getByteSize() != ST2.getByteSize()) { + outs() << " Stream Size\n"; + outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(), + ST1.getByteSize()); + outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(), + ST2.getByteSize()); + outs() << formatv(" Difference: {0} bytes\n", + AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize())); + HasDiff = true; + } + HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(), + ST1.getHashVersion()); + HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(), + ST1.getSignature()); + + // Both have a valid string table, dive in and compare individual strings. + + auto IdList1 = ST1.name_ids(); + auto IdList2 = ST2.name_ids(); + if (opts::diff::Pedantic) { + // In pedantic mode, we compare index by index (i.e. the strings are in the + // same order + // in both tables. + uint32_t Max = std::max(IdList1.size(), IdList2.size()); + for (uint32_t I = 0; I < Max; ++I) { + Optional Id1, Id2; + StringRef S1, S2; + if (I < IdList1.size()) { + Id1 = IdList1[I]; + if (auto Result = ST1.getStringForID(*Id1)) + S1 = *Result; + else + return Result.takeError(); + } + if (I < IdList2.size()) { + Id2 = IdList2[I]; + if (auto Result = ST2.getStringForID(*Id2)) + S2 = *Result; + else + return Result.takeError(); + } + if (Id1 == Id2 && S1 == S2) + continue; + + std::string OutId1 = + Id1 ? formatv("{0}", *Id1).str() : "(index not present)"; + std::string OutId2 = + Id2 ? formatv("{0}", *Id2).str() : "(index not present)"; + outs() << formatv(" String {0}\n", I); + outs() << formatv(" {0}: Hash - {1}, Value - {2}\n", + File1.getFilePath(), OutId1, S1); + outs() << formatv(" {0}: Hash - {1}, Value - {2}\n", + File2.getFilePath(), OutId2, S2); + HasDiff = true; + } + } else { + std::vector Strings1, Strings2; + Strings1.reserve(IdList1.size()); + Strings2.reserve(IdList2.size()); + for (auto ID : IdList1) { + auto S = ST1.getStringForID(ID); + if (!S) + return S.takeError(); + Strings1.push_back(*S); + } + for (auto ID : IdList2) { + auto S = ST2.getStringForID(ID); + if (!S) + return S.takeError(); + Strings2.push_back(*S); + } + + SmallVector OnlyP; + SmallVector OnlyQ; + auto End1 = std::remove(Strings1.begin(), Strings1.end(), ""); + auto End2 = std::remove(Strings2.begin(), Strings2.end(), ""); + uint32_t Empty1 = std::distance(End1, Strings1.end()); + uint32_t Empty2 = std::distance(End2, Strings2.end()); + Strings1.erase(End1, Strings1.end()); + Strings2.erase(End2, Strings2.end()); + set_differences(Strings1, Strings2, &OnlyP, &OnlyQ); + printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String"); + + if (Empty1 != Empty2) { + PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2; + PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2; + uint32_t Difference = AbsoluteDifference(Empty1, Empty2); + outs() << formatv(" {0} had {1} more empty strings than {2}\n", + MoreF.getFilePath(), Difference, LessF.getFilePath()); + } + } + if (!HasDiff) + outs() << "String Table: No differences detected!\n"; + return Error::success(); +} + +Error DiffStyle::diffFreePageMap() { return Error::success(); } + +Error DiffStyle::diffInfoStream() { + auto ExpectedInfo1 = File1.getPDBInfoStream(); + auto ExpectedInfo2 = File2.getPDBInfoStream(); + + outs() << "PDB Stream: Searching for differences...\n"; + bool Has1 = !!ExpectedInfo1; + bool Has2 = !!ExpectedInfo2; + if (!(Has1 && Has2)) { + if (Has1 != Has2) + outs() << formatv("{0} does not have a PDB Stream!\n", + Has1 ? File1.getFilePath() : File2.getFilePath()); + consumeError(ExpectedInfo2.takeError()); + consumeError(ExpectedInfo2.takeError()); + return Error::success(); + } + + bool HasDiff = false; + auto &IS1 = *ExpectedInfo1; + auto &IS2 = *ExpectedInfo2; + if (IS1.getStreamSize() != IS2.getStreamSize()) { + outs() << " Stream Size\n"; + outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(), + IS1.getStreamSize()); + outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(), + IS2.getStreamSize()); + outs() << formatv( + " Difference: {0} bytes\n", + AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize())); + HasDiff = true; + } + HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge()); + HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid()); + HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(), + IS2.getSignature()); + HasDiff |= + diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion()); + HasDiff |= diffAndPrint("Features", File1, File2, IS1.getFeatureSignatures(), + IS2.getFeatureSignatures()); + HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2, + IS1.getNamedStreamMapByteSize(), + IS2.getNamedStreamMapByteSize()); + SmallVector NS1; + SmallVector NS2; + for (const auto &X : IS1.getNamedStreams().entries()) + NS1.push_back(X.getKey()); + for (const auto &X : IS2.getNamedStreams().entries()) + NS2.push_back(X.getKey()); + SmallVector OnlyP; + SmallVector OnlyQ; + set_differences(NS1, NS2, &OnlyP, &OnlyQ); + printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams"); + if (!HasDiff) + outs() << "PDB Stream: No differences detected!\n"; + + return Error::success(); +} + +Error DiffStyle::diffDbiStream() { return Error::success(); } + +Error DiffStyle::diffSectionContribs() { return Error::success(); } + +Error DiffStyle::diffSectionMap() { return Error::success(); } + +Error DiffStyle::diffFpoStream() { return Error::success(); } + +Error DiffStyle::diffTpiStream(int Index) { return Error::success(); } + +Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); } + +Error DiffStyle::diffPublics() { return Error::success(); } + +Error DiffStyle::diffGlobals() { return Error::success(); } Index: llvm/trunk/tools/llvm-pdbutil/LLVMBuild.txt =================================================================== --- llvm/trunk/tools/llvm-pdbutil/LLVMBuild.txt +++ llvm/trunk/tools/llvm-pdbutil/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./tools/llvm-pdbutil/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-pdbutil +parent = Tools +required_libraries = DebugInfoMSF DebugInfoPDB + Index: llvm/trunk/tools/llvm-pdbutil/LLVMOutputStyle.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/LLVMOutputStyle.h +++ llvm/trunk/tools/llvm-pdbutil/LLVMOutputStyle.h @@ -0,0 +1,70 @@ +//===- LLVMOutputStyle.h -------------------------------------- *- 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_LLVMPDBDUMP_LLVMOUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_LLVMOUTPUTSTYLE_H + +#include "OutputStyle.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/TypeDatabase.h" +#include "llvm/Support/ScopedPrinter.h" + +#include + +namespace llvm { +class BitVector; + +namespace codeview { +class LazyRandomTypeCollection; +} + +namespace pdb { +class LLVMOutputStyle : public OutputStyle { +public: + LLVMOutputStyle(PDBFile &File); + + Error dump() override; + +private: + Expected + initializeTypeDatabase(uint32_t SN); + + Error dumpFileHeaders(); + Error dumpStreamSummary(); + Error dumpFreePageMap(); + Error dumpBlockRanges(); + Error dumpGlobalsStream(); + Error dumpStreamBytes(); + Error dumpStreamBlocks(); + Error dumpStringTable(); + Error dumpInfoStream(); + Error dumpTpiStream(uint32_t StreamIdx); + Error dumpDbiStream(); + Error dumpSectionContribs(); + Error dumpSectionMap(); + Error dumpPublicsStream(); + Error dumpSectionHeaders(); + Error dumpFpoStream(); + + void dumpBitVector(StringRef Name, const BitVector &V); + + void flush(); + + PDBFile &File; + ScopedPrinter P; + std::unique_ptr TpiTypes; + std::unique_ptr IpiTypes; + SmallVector StreamPurposes; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/LLVMOutputStyle.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/LLVMOutputStyle.cpp +++ llvm/trunk/tools/llvm-pdbutil/LLVMOutputStyle.cpp @@ -0,0 +1,1177 @@ +//===- LLVMOutputStyle.cpp ------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LLVMOutputStyle.h" + +#include "CompactTypeDumpVisitor.h" +#include "StreamUtil.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/EnumTables.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/FormatVariadic.h" + +#include + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace { +struct PageStats { + explicit PageStats(const BitVector &FreePages) + : Upm(FreePages), ActualUsedPages(FreePages.size()), + MultiUsePages(FreePages.size()), UseAfterFreePages(FreePages.size()) { + const_cast(Upm).flip(); + // To calculate orphaned pages, we start with the set of pages that the + // MSF thinks are used. Each time we find one that actually *is* used, + // we unset it. Whichever bits remain set at the end are orphaned. + OrphanedPages = Upm; + } + + // The inverse of the MSF File's copy of the Fpm. The basis for which we + // determine the allocation status of each page. + const BitVector Upm; + + // Pages which are marked as used in the FPM and are used at least once. + BitVector ActualUsedPages; + + // Pages which are marked as used in the FPM but are used more than once. + BitVector MultiUsePages; + + // Pages which are marked as used in the FPM but are not used at all. + BitVector OrphanedPages; + + // Pages which are marked free in the FPM but are used. + BitVector UseAfterFreePages; +}; + +class C13RawVisitor : public DebugSubsectionVisitor { +public: + C13RawVisitor(ScopedPrinter &P, LazyRandomTypeCollection &TPI, + LazyRandomTypeCollection &IPI) + : P(P), TPI(TPI), IPI(IPI) {} + + Error visitUnknown(DebugUnknownSubsectionRef &Unknown) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::Unknown)) + return Error::success(); + DictScope DD(P, "Unknown"); + P.printHex("Kind", static_cast(Unknown.kind())); + ArrayRef Data; + BinaryStreamReader Reader(Unknown.getData()); + consumeError(Reader.readBytes(Data, Reader.bytesRemaining())); + P.printBinaryBlock("Data", Data); + return Error::success(); + } + + Error visitLines(DebugLinesSubsectionRef &Lines, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::Lines)) + return Error::success(); + + DictScope DD(P, "Lines"); + + P.printNumber("RelocSegment", Lines.header()->RelocSegment); + P.printNumber("RelocOffset", Lines.header()->RelocOffset); + P.printNumber("CodeSize", Lines.header()->CodeSize); + P.printBoolean("HasColumns", Lines.hasColumnInfo()); + + for (const auto &L : Lines) { + DictScope DDDD(P, "FileEntry"); + + if (auto EC = printFileName("FileName", L.NameIndex, State)) + return EC; + + for (const auto &N : L.LineNumbers) { + DictScope DDD(P, "Line"); + LineInfo LI(N.Flags); + P.printNumber("Offset", N.Offset); + if (LI.isAlwaysStepInto()) + P.printString("StepInto", StringRef("Always")); + else if (LI.isNeverStepInto()) + P.printString("StepInto", StringRef("Never")); + else + P.printNumber("LineNumberStart", LI.getStartLine()); + P.printNumber("EndDelta", LI.getLineDelta()); + P.printBoolean("IsStatement", LI.isStatement()); + } + for (const auto &C : L.Columns) { + DictScope DDD(P, "Column"); + P.printNumber("Start", C.StartColumn); + P.printNumber("End", C.EndColumn); + } + } + + return Error::success(); + } + + Error visitFileChecksums(DebugChecksumsSubsectionRef &Checksums, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::FileChecksums)) + return Error::success(); + + DictScope DD(P, "FileChecksums"); + for (const auto &CS : Checksums) { + DictScope DDD(P, "Checksum"); + if (auto Result = getNameFromStringTable(CS.FileNameOffset, State)) + P.printString("FileName", *Result); + else + return Result.takeError(); + P.printEnum("Kind", uint8_t(CS.Kind), getFileChecksumNames()); + P.printBinaryBlock("Checksum", CS.Checksum); + } + return Error::success(); + } + + Error visitInlineeLines(DebugInlineeLinesSubsectionRef &Inlinees, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::InlineeLines)) + return Error::success(); + + DictScope D(P, "InlineeLines"); + P.printBoolean("HasExtraFiles", Inlinees.hasExtraFiles()); + ListScope LS(P, "Lines"); + for (const auto &L : Inlinees) { + DictScope DDD(P, "Inlinee"); + if (auto EC = printFileName("FileName", L.Header->FileID, State)) + return EC; + + if (auto EC = dumpTypeRecord("Function", L.Header->Inlinee)) + return EC; + P.printNumber("SourceLine", L.Header->SourceLineNum); + if (Inlinees.hasExtraFiles()) { + ListScope DDDD(P, "ExtraFiles"); + for (const auto &EF : L.ExtraFiles) { + if (auto EC = printFileName("File", EF, State)) + return EC; + } + } + } + return Error::success(); + } + + Error visitCrossModuleExports(DebugCrossModuleExportsSubsectionRef &CSE, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::CrossScopeExports)) + return Error::success(); + + ListScope D(P, "CrossModuleExports"); + for (const auto &M : CSE) { + DictScope D(P, "Export"); + P.printHex("Local", M.Local); + P.printHex("Global", M.Global); + } + return Error::success(); + } + + Error visitCrossModuleImports(DebugCrossModuleImportsSubsectionRef &CSI, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::CrossScopeImports)) + return Error::success(); + + ListScope L(P, "CrossModuleImports"); + for (const auto &M : CSI) { + DictScope D(P, "ModuleImport"); + auto Name = getNameFromStringTable(M.Header->ModuleNameOffset, State); + if (!Name) + return Name.takeError(); + P.printString("Module", *Name); + P.printHexList("Imports", M.Imports); + } + return Error::success(); + } + + Error visitFrameData(DebugFrameDataSubsectionRef &FD, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::FrameData)) + return Error::success(); + + ListScope L(P, "FrameData"); + for (const auto &Frame : FD) { + DictScope D(P, "Frame"); + auto Name = getNameFromStringTable(Frame.FrameFunc, State); + if (!Name) + return joinErrors(make_error(raw_error_code::invalid_format, + "Invalid Frame.FrameFunc index"), + Name.takeError()); + P.printNumber("Rva", Frame.RvaStart); + P.printNumber("CodeSize", Frame.CodeSize); + P.printNumber("LocalSize", Frame.LocalSize); + P.printNumber("ParamsSize", Frame.ParamsSize); + P.printNumber("MaxStackSize", Frame.MaxStackSize); + P.printString("FrameFunc", *Name); + P.printNumber("PrologSize", Frame.PrologSize); + P.printNumber("SavedRegsSize", Frame.SavedRegsSize); + P.printNumber("Flags", Frame.Flags); + } + return Error::success(); + } + + Error visitSymbols(DebugSymbolsSubsectionRef &Symbols, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::Symbols)) + return Error::success(); + ListScope L(P, "Symbols"); + + // This section should not actually appear in a PDB file, it really only + // appears in object files. But we support it here for testing. So we + // specify the Object File container type. + codeview::CVSymbolDumper SD(P, TPI, CodeViewContainer::ObjectFile, nullptr, + false); + for (auto S : Symbols) { + DictScope LL(P, ""); + if (auto EC = SD.dump(S)) { + return make_error( + raw_error_code::corrupt_file, + "DEBUG_S_SYMBOLS subsection contained corrupt symbol record"); + } + } + return Error::success(); + } + + Error visitStringTable(DebugStringTableSubsectionRef &Strings, + const DebugSubsectionState &State) override { + if (!opts::checkModuleSubsection(opts::ModuleSubsection::StringTable)) + return Error::success(); + + ListScope D(P, "String Table"); + BinaryStreamReader Reader(Strings.getBuffer()); + StringRef S; + consumeError(Reader.readCString(S)); + while (Reader.bytesRemaining() > 0) { + consumeError(Reader.readCString(S)); + if (S.empty() && Reader.bytesRemaining() < 4) + break; + P.printString(S); + } + return Error::success(); + } + +private: + Error dumpTypeRecord(StringRef Label, TypeIndex Index) { + CompactTypeDumpVisitor CTDV(IPI, Index, &P); + DictScope D(P, Label); + if (IPI.contains(Index)) { + CVType Type = IPI.getType(Index); + if (auto EC = codeview::visitTypeRecord(Type, CTDV)) + return EC; + } else { + P.printString( + llvm::formatv("Index: {0:x} (unknown function)", Index.getIndex()) + .str()); + } + return Error::success(); + } + Error printFileName(StringRef Label, uint32_t Offset, + const DebugSubsectionState &State) { + if (auto Result = getNameFromChecksumsBuffer(Offset, State)) { + P.printString(Label, *Result); + return Error::success(); + } else + return Result.takeError(); + } + + Expected + getNameFromStringTable(uint32_t Offset, const DebugSubsectionState &State) { + return State.strings().getString(Offset); + } + + Expected + getNameFromChecksumsBuffer(uint32_t Offset, + const DebugSubsectionState &State) { + auto Array = State.checksums().getArray(); + auto ChecksumIter = Array.at(Offset); + if (ChecksumIter == Array.end()) + return make_error(raw_error_code::invalid_format); + const auto &Entry = *ChecksumIter; + return getNameFromStringTable(Entry.FileNameOffset, State); + } + + ScopedPrinter &P; + LazyRandomTypeCollection &TPI; + LazyRandomTypeCollection &IPI; +}; +} + +static void recordKnownUsedPage(PageStats &Stats, uint32_t UsedIndex) { + if (Stats.Upm.test(UsedIndex)) { + if (Stats.ActualUsedPages.test(UsedIndex)) + Stats.MultiUsePages.set(UsedIndex); + Stats.ActualUsedPages.set(UsedIndex); + Stats.OrphanedPages.reset(UsedIndex); + } else { + // The MSF doesn't think this page is used, but it is. + Stats.UseAfterFreePages.set(UsedIndex); + } +} + +static void printSectionOffset(llvm::raw_ostream &OS, + const SectionOffset &Off) { + OS << Off.Off << ", " << Off.Isect; +} + +LLVMOutputStyle::LLVMOutputStyle(PDBFile &File) : File(File), P(outs()) {} + +Error LLVMOutputStyle::dump() { + if (auto EC = dumpFileHeaders()) + return EC; + + if (auto EC = dumpStreamSummary()) + return EC; + + if (auto EC = dumpFreePageMap()) + return EC; + + if (auto EC = dumpStreamBlocks()) + return EC; + + if (auto EC = dumpBlockRanges()) + return EC; + + if (auto EC = dumpStreamBytes()) + return EC; + + if (auto EC = dumpStringTable()) + return EC; + + if (auto EC = dumpInfoStream()) + return EC; + + if (auto EC = dumpTpiStream(StreamTPI)) + return EC; + + if (auto EC = dumpTpiStream(StreamIPI)) + return EC; + + if (auto EC = dumpDbiStream()) + return EC; + + if (auto EC = dumpSectionContribs()) + return EC; + + if (auto EC = dumpSectionMap()) + return EC; + + if (auto EC = dumpGlobalsStream()) + return EC; + + if (auto EC = dumpPublicsStream()) + return EC; + + if (auto EC = dumpSectionHeaders()) + return EC; + + if (auto EC = dumpFpoStream()) + return EC; + + flush(); + + return Error::success(); +} + +Error LLVMOutputStyle::dumpFileHeaders() { + if (!opts::raw::DumpHeaders) + return Error::success(); + + DictScope D(P, "FileHeaders"); + P.printNumber("BlockSize", File.getBlockSize()); + P.printNumber("FreeBlockMap", File.getFreeBlockMapBlock()); + P.printNumber("NumBlocks", File.getBlockCount()); + P.printNumber("NumDirectoryBytes", File.getNumDirectoryBytes()); + P.printNumber("Unknown1", File.getUnknown1()); + P.printNumber("BlockMapAddr", File.getBlockMapIndex()); + P.printNumber("NumDirectoryBlocks", File.getNumDirectoryBlocks()); + + // The directory is not contiguous. Instead, the block map contains a + // contiguous list of block numbers whose contents, when concatenated in + // order, make up the directory. + P.printList("DirectoryBlocks", File.getDirectoryBlockArray()); + P.printNumber("NumStreams", File.getNumStreams()); + return Error::success(); +} + +Error LLVMOutputStyle::dumpStreamSummary() { + if (!opts::raw::DumpStreamSummary) + return Error::success(); + + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); + + uint32_t StreamCount = File.getNumStreams(); + + ListScope L(P, "Streams"); + for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { + std::string Label("Stream "); + Label += to_string(StreamIdx); + + std::string Value = "[" + StreamPurposes[StreamIdx] + "] ("; + Value += to_string(File.getStreamByteSize(StreamIdx)); + Value += " bytes)"; + + P.printString(Label, Value); + } + + P.flush(); + return Error::success(); +} + +Error LLVMOutputStyle::dumpFreePageMap() { + if (!opts::raw::DumpPageStats) + return Error::success(); + + // Start with used pages instead of free pages because + // the number of free pages is far larger than used pages. + BitVector FPM = File.getMsfLayout().FreePageMap; + + PageStats PS(FPM); + + recordKnownUsedPage(PS, 0); // MSF Super Block + + uint32_t BlocksPerSection = msf::getFpmIntervalLength(File.getMsfLayout()); + uint32_t NumSections = msf::getNumFpmIntervals(File.getMsfLayout()); + for (uint32_t I = 0; I < NumSections; ++I) { + uint32_t Fpm0 = 1 + BlocksPerSection * I; + // 2 Fpm blocks spaced at `getBlockSize()` block intervals + recordKnownUsedPage(PS, Fpm0); + recordKnownUsedPage(PS, Fpm0 + 1); + } + + recordKnownUsedPage(PS, File.getBlockMapIndex()); // Stream Table + + for (auto DB : File.getDirectoryBlockArray()) + recordKnownUsedPage(PS, DB); + + // Record pages used by streams. Note that pages for stream 0 + // are considered being unused because that's what MSVC tools do. + // Stream 0 doesn't contain actual data, so it makes some sense, + // though it's a bit confusing to us. + for (auto &SE : File.getStreamMap().drop_front(1)) + for (auto &S : SE) + recordKnownUsedPage(PS, S); + + dumpBitVector("Msf Free Pages", FPM); + dumpBitVector("Orphaned Pages", PS.OrphanedPages); + dumpBitVector("Multiply Used Pages", PS.MultiUsePages); + dumpBitVector("Use After Free Pages", PS.UseAfterFreePages); + return Error::success(); +} + +void LLVMOutputStyle::dumpBitVector(StringRef Name, const BitVector &V) { + std::vector Vec; + for (uint32_t I = 0, E = V.size(); I != E; ++I) + if (V[I]) + Vec.push_back(I); + P.printList(Name, Vec); +} + +Error LLVMOutputStyle::dumpGlobalsStream() { + if (!opts::raw::DumpGlobals) + return Error::success(); + if (!File.hasPDBGlobalsStream()) { + P.printString("Globals Stream not present"); + return Error::success(); + } + + auto Globals = File.getPDBGlobalsStream(); + if (!Globals) + return Globals.takeError(); + DictScope D(P, "Globals Stream"); + + auto Dbi = File.getPDBDbiStream(); + if (!Dbi) + return Dbi.takeError(); + + P.printNumber("Stream number", Dbi->getGlobalSymbolStreamIndex()); + P.printNumber("Number of buckets", Globals->getNumBuckets()); + P.printList("Hash Buckets", Globals->getHashBuckets()); + + return Error::success(); +} + +Error LLVMOutputStyle::dumpStreamBlocks() { + if (!opts::raw::DumpStreamBlocks) + return Error::success(); + + ListScope L(P, "StreamBlocks"); + uint32_t StreamCount = File.getNumStreams(); + for (uint32_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { + std::string Name("Stream "); + Name += to_string(StreamIdx); + auto StreamBlocks = File.getStreamBlockList(StreamIdx); + P.printList(Name, StreamBlocks); + } + return Error::success(); +} + +Error LLVMOutputStyle::dumpBlockRanges() { + if (!opts::raw::DumpBlockRange.hasValue()) + return Error::success(); + auto &R = *opts::raw::DumpBlockRange; + uint32_t Max = R.Max.getValueOr(R.Min); + + if (Max < R.Min) + return make_error( + "Invalid block range specified. Max < Min", + std::make_error_code(std::errc::bad_address)); + if (Max >= File.getBlockCount()) + return make_error( + "Invalid block range specified. Requested block out of bounds", + std::make_error_code(std::errc::bad_address)); + + DictScope D(P, "Block Data"); + for (uint32_t I = R.Min; I <= Max; ++I) { + auto ExpectedData = File.getBlockData(I, File.getBlockSize()); + if (!ExpectedData) + return ExpectedData.takeError(); + std::string Label; + llvm::raw_string_ostream S(Label); + S << "Block " << I; + S.flush(); + P.printBinaryBlock(Label, *ExpectedData); + } + + return Error::success(); +} + +static Error parseStreamSpec(StringRef Str, uint32_t &SI, uint32_t &Offset, + uint32_t &Size) { + if (Str.consumeInteger(0, SI)) + return make_error(raw_error_code::invalid_format, + "Invalid Stream Specification"); + if (Str.consume_front(":")) { + if (Str.consumeInteger(0, Offset)) + return make_error(raw_error_code::invalid_format, + "Invalid Stream Specification"); + } + if (Str.consume_front("@")) { + if (Str.consumeInteger(0, Size)) + return make_error(raw_error_code::invalid_format, + "Invalid Stream Specification"); + } + if (!Str.empty()) + return make_error(raw_error_code::invalid_format, + "Invalid Stream Specification"); + return Error::success(); +} + +Error LLVMOutputStyle::dumpStreamBytes() { + if (opts::raw::DumpStreamData.empty()) + return Error::success(); + + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); + + DictScope D(P, "Stream Data"); + for (auto &Str : opts::raw::DumpStreamData) { + uint32_t SI = 0; + uint32_t Begin = 0; + uint32_t Size = 0; + uint32_t End = 0; + + if (auto EC = parseStreamSpec(Str, SI, Begin, Size)) + return EC; + + if (SI >= File.getNumStreams()) + return make_error(raw_error_code::no_stream); + + auto S = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), SI, File.getAllocator()); + if (!S) + continue; + DictScope DD(P, "Stream"); + if (Size == 0) + End = S->getLength(); + else { + End = Begin + Size; + if (End >= S->getLength()) + return make_error(raw_error_code::index_out_of_bounds, + "Stream is not long enough!"); + } + + P.printNumber("Index", SI); + P.printString("Type", StreamPurposes[SI]); + P.printNumber("Size", S->getLength()); + auto Blocks = File.getMsfLayout().StreamMap[SI]; + P.printList("Blocks", Blocks); + + BinaryStreamReader R(*S); + ArrayRef StreamData; + if (auto EC = R.readBytes(StreamData, S->getLength())) + return EC; + Size = End - Begin; + StreamData = StreamData.slice(Begin, Size); + P.printBinaryBlock("Data", StreamData, Begin); + } + return Error::success(); +} + +Error LLVMOutputStyle::dumpStringTable() { + if (!opts::raw::DumpStringTable) + return Error::success(); + + auto IS = File.getStringTable(); + if (!IS) + return IS.takeError(); + + DictScope D(P, "String Table"); + for (uint32_t I : IS->name_ids()) { + auto ES = IS->getStringForID(I); + if (!ES) + return ES.takeError(); + + if (ES->empty()) + continue; + llvm::SmallString<32> Str; + Str.append("'"); + Str.append(*ES); + Str.append("'"); + P.printString(Str); + } + return Error::success(); +} + +Error LLVMOutputStyle::dumpInfoStream() { + if (!opts::raw::DumpHeaders) + return Error::success(); + if (!File.hasPDBInfoStream()) { + P.printString("PDB Stream not present"); + return Error::success(); + } + auto IS = File.getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + DictScope D(P, "PDB Stream"); + P.printNumber("Version", IS->getVersion()); + P.printHex("Signature", IS->getSignature()); + P.printNumber("Age", IS->getAge()); + P.printObject("Guid", IS->getGuid()); + P.printHex("Features", IS->getFeatures()); + { + DictScope DD(P, "Named Streams"); + for (const auto &S : IS->getNamedStreams().entries()) + P.printObject(S.getKey(), S.getValue()); + } + return Error::success(); +} + +namespace { +class RecordBytesVisitor : public TypeVisitorCallbacks { +public: + explicit RecordBytesVisitor(ScopedPrinter &P) : P(P) {} + + Error visitTypeEnd(CVType &Record) override { + P.printBinaryBlock("Bytes", Record.content()); + return Error::success(); + } + +private: + ScopedPrinter &P; +}; +} + +Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) { + assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); + + bool DumpRecordBytes = false; + bool DumpRecords = false; + bool DumpTpiHash = false; + StringRef Label; + StringRef VerLabel; + if (StreamIdx == StreamTPI) { + if (!File.hasPDBTpiStream()) { + P.printString("Type Info Stream (TPI) not present"); + return Error::success(); + } + DumpRecordBytes = opts::raw::DumpTpiRecordBytes; + DumpRecords = opts::raw::DumpTpiRecords; + DumpTpiHash = opts::raw::DumpTpiHash; + Label = "Type Info Stream (TPI)"; + VerLabel = "TPI Version"; + } else if (StreamIdx == StreamIPI) { + if (!File.hasPDBIpiStream()) { + P.printString("Type Info Stream (IPI) not present"); + return Error::success(); + } + DumpRecordBytes = opts::raw::DumpIpiRecordBytes; + DumpRecords = opts::raw::DumpIpiRecords; + Label = "Type Info Stream (IPI)"; + VerLabel = "IPI Version"; + } + + auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream() + : File.getPDBIpiStream(); + if (!Tpi) + return Tpi.takeError(); + + auto ExpectedTypes = initializeTypeDatabase(StreamIdx); + if (!ExpectedTypes) + return ExpectedTypes.takeError(); + auto &Types = *ExpectedTypes; + + if (!DumpRecordBytes && !DumpRecords && !DumpTpiHash) + return Error::success(); + + std::unique_ptr StreamScope; + std::unique_ptr RecordScope; + + StreamScope = llvm::make_unique(P, Label); + P.printNumber(VerLabel, Tpi->getTpiVersion()); + P.printNumber("Record count", Tpi->getNumTypeRecords()); + + std::vector> Visitors; + + // If we're in dump mode, add a dumper with the appropriate detail level. + if (DumpRecords) { + std::unique_ptr Dumper; + if (opts::raw::CompactRecords) + Dumper = make_unique(Types, &P); + else { + assert(TpiTypes); + + auto X = make_unique(*TpiTypes, &P, false); + if (StreamIdx == StreamIPI) + X->setIpiTypes(*IpiTypes); + Dumper = std::move(X); + } + Visitors.push_back(std::move(Dumper)); + } + if (DumpRecordBytes) + Visitors.push_back(make_unique(P)); + + // We always need to deserialize and add it to the type database. This is + // true if even if we're not dumping anything, because we could need the + // type database for the purposes of dumping symbols. + TypeVisitorCallbackPipeline Pipeline; + for (const auto &V : Visitors) + Pipeline.addCallbackToPipeline(*V); + + if (DumpRecords || DumpRecordBytes) + RecordScope = llvm::make_unique(P, "Records"); + + Optional I = Types.getFirst(); + while (I) { + std::unique_ptr OneRecordScope; + + if ((DumpRecords || DumpRecordBytes) && !opts::raw::CompactRecords) + OneRecordScope = llvm::make_unique(P, ""); + + auto T = Types.getType(*I); + if (auto EC = codeview::visitTypeRecord(T, *I, Pipeline)) + return EC; + I = Types.getNext(*I); + } + + if (DumpTpiHash) { + DictScope DD(P, "Hash"); + P.printNumber("Number of Hash Buckets", Tpi->getNumHashBuckets()); + P.printNumber("Hash Key Size", Tpi->getHashKeySize()); + P.printList("Values", Tpi->getHashValues()); + + ListScope LHA(P, "Adjusters"); + auto ExpectedST = File.getStringTable(); + if (!ExpectedST) + return ExpectedST.takeError(); + const auto &ST = *ExpectedST; + for (const auto &E : Tpi->getHashAdjusters()) { + DictScope DHA(P); + auto Name = ST.getStringForID(E.first); + if (!Name) + return Name.takeError(); + + P.printString("Type", *Name); + P.printHex("TI", E.second); + } + } + + ListScope L(P, "TypeIndexOffsets"); + for (const auto &IO : Tpi->getTypeIndexOffsets()) { + P.printString(formatv("Index: {0:x}, Offset: {1:N}", IO.Type.getIndex(), + (uint32_t)IO.Offset) + .str()); + } + + P.flush(); + return Error::success(); +} + +Expected +LLVMOutputStyle::initializeTypeDatabase(uint32_t SN) { + auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes; + auto Tpi = + (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); + if (!Tpi) + return Tpi.takeError(); + + if (!TypeCollection) { + // Initialize the type collection, even if we're not going to dump it. This + // way if some other part of the dumper decides it wants to use some or all + // of the records for whatever purposes, it can still access them lazily. + auto &Types = Tpi->typeArray(); + uint32_t Count = Tpi->getNumTypeRecords(); + auto Offsets = Tpi->getTypeIndexOffsets(); + TypeCollection = + llvm::make_unique(Types, Count, Offsets); + } + + return *TypeCollection; +} + +Error LLVMOutputStyle::dumpDbiStream() { + bool DumpModules = opts::shared::DumpModules || + opts::shared::DumpModuleSyms || + opts::shared::DumpModuleFiles || + !opts::shared::DumpModuleSubsections.empty(); + if (!opts::raw::DumpHeaders && !DumpModules) + return Error::success(); + if (!File.hasPDBDbiStream()) { + P.printString("DBI Stream not present"); + return Error::success(); + } + + auto DS = File.getPDBDbiStream(); + if (!DS) + return DS.takeError(); + + DictScope D(P, "DBI Stream"); + P.printNumber("Dbi Version", DS->getDbiVersion()); + P.printNumber("Age", DS->getAge()); + P.printBoolean("Incremental Linking", DS->isIncrementallyLinked()); + P.printBoolean("Has CTypes", DS->hasCTypes()); + P.printBoolean("Is Stripped", DS->isStripped()); + P.printObject("Machine Type", DS->getMachineType()); + P.printNumber("Symbol Record Stream Index", DS->getSymRecordStreamIndex()); + P.printNumber("Public Symbol Stream Index", DS->getPublicSymbolStreamIndex()); + P.printNumber("Global Symbol Stream Index", DS->getGlobalSymbolStreamIndex()); + + uint16_t Major = DS->getBuildMajorVersion(); + uint16_t Minor = DS->getBuildMinorVersion(); + P.printVersion("Toolchain Version", Major, Minor); + + std::string DllName; + raw_string_ostream DllStream(DllName); + DllStream << "mspdb" << Major << Minor << ".dll version"; + DllStream.flush(); + P.printVersion(DllName, Major, Minor, DS->getPdbDllVersion()); + + if (DumpModules) { + ListScope L(P, "Modules"); + const DbiModuleList &Modules = DS->modules(); + for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { + const DbiModuleDescriptor &Modi = Modules.getModuleDescriptor(I); + DictScope DD(P); + P.printString("Name", Modi.getModuleName().str()); + P.printNumber("Debug Stream Index", Modi.getModuleStreamIndex()); + P.printString("Object File Name", Modi.getObjFileName().str()); + P.printNumber("Num Files", Modi.getNumberOfFiles()); + P.printNumber("Source File Name Idx", Modi.getSourceFileNameIndex()); + P.printNumber("Pdb File Name Idx", Modi.getPdbFilePathNameIndex()); + P.printNumber("Line Info Byte Size", Modi.getC11LineInfoByteSize()); + P.printNumber("C13 Line Info Byte Size", Modi.getC13LineInfoByteSize()); + P.printNumber("Symbol Byte Size", Modi.getSymbolDebugInfoByteSize()); + P.printNumber("Type Server Index", Modi.getTypeServerIndex()); + P.printBoolean("Has EC Info", Modi.hasECInfo()); + if (opts::shared::DumpModuleFiles) { + std::string FileListName = to_string(Modules.getSourceFileCount(I)) + + " Contributing Source Files"; + ListScope LL(P, FileListName); + for (auto File : Modules.source_files(I)) + P.printString(File); + } + bool HasModuleDI = (Modi.getModuleStreamIndex() < File.getNumStreams()); + bool ShouldDumpSymbols = + (opts::shared::DumpModuleSyms || opts::raw::DumpSymRecordBytes); + if (HasModuleDI && + (ShouldDumpSymbols || !opts::shared::DumpModuleSubsections.empty())) { + auto ModStreamData = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), + Modi.getModuleStreamIndex(), File.getAllocator()); + + ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); + if (auto EC = ModS.reload()) + return EC; + + auto ExpectedTpi = initializeTypeDatabase(StreamTPI); + if (!ExpectedTpi) + return ExpectedTpi.takeError(); + auto &Tpi = *ExpectedTpi; + if (ShouldDumpSymbols) { + + ListScope SS(P, "Symbols"); + codeview::CVSymbolDumper SD(P, Tpi, CodeViewContainer::Pdb, nullptr, + false); + bool HadError = false; + for (auto S : ModS.symbols(&HadError)) { + DictScope LL(P, ""); + if (opts::shared::DumpModuleSyms) { + if (auto EC = SD.dump(S)) { + llvm::consumeError(std::move(EC)); + HadError = true; + break; + } + } + if (opts::raw::DumpSymRecordBytes) + P.printBinaryBlock("Bytes", S.content()); + } + if (HadError) + return make_error( + raw_error_code::corrupt_file, + "DBI stream contained corrupt symbol record"); + } + if (!opts::shared::DumpModuleSubsections.empty()) { + ListScope SS(P, "Subsections"); + auto ExpectedIpi = initializeTypeDatabase(StreamIPI); + if (!ExpectedIpi) + return ExpectedIpi.takeError(); + auto &Ipi = *ExpectedIpi; + auto ExpectedStrings = File.getStringTable(); + if (!ExpectedStrings) + return joinErrors( + make_error(raw_error_code::no_stream, + "Could not get string table!"), + ExpectedStrings.takeError()); + + C13RawVisitor V(P, Tpi, Ipi); + if (auto EC = codeview::visitDebugSubsections( + ModS.subsections(), V, ExpectedStrings->getStringTable())) + return EC; + } + } + } + } + return Error::success(); +} + +Error LLVMOutputStyle::dumpSectionContribs() { + if (!opts::raw::DumpSectionContribs) + return Error::success(); + if (!File.hasPDBDbiStream()) { + P.printString("DBI Stream not present"); + return Error::success(); + } + + auto Dbi = File.getPDBDbiStream(); + if (!Dbi) + return Dbi.takeError(); + + ListScope L(P, "Section Contributions"); + class Visitor : public ISectionContribVisitor { + public: + Visitor(ScopedPrinter &P, DbiStream &DS) : P(P), DS(DS) {} + void visit(const SectionContrib &SC) override { + DictScope D(P, "Contribution"); + P.printNumber("ISect", SC.ISect); + P.printNumber("Off", SC.Off); + P.printNumber("Size", SC.Size); + P.printFlags("Characteristics", SC.Characteristics, + codeview::getImageSectionCharacteristicNames(), + COFF::SectionCharacteristics(0x00F00000)); + { + DictScope DD(P, "Module"); + P.printNumber("Index", SC.Imod); + const DbiModuleList &Modules = DS.modules(); + if (Modules.getModuleCount() > SC.Imod) { + P.printString("Name", + Modules.getModuleDescriptor(SC.Imod).getModuleName()); + } + } + P.printNumber("Data CRC", SC.DataCrc); + P.printNumber("Reloc CRC", SC.RelocCrc); + P.flush(); + } + void visit(const SectionContrib2 &SC) override { + visit(SC.Base); + P.printNumber("ISect Coff", SC.ISectCoff); + P.flush(); + } + + private: + ScopedPrinter &P; + DbiStream &DS; + }; + Visitor V(P, *Dbi); + Dbi->visitSectionContributions(V); + return Error::success(); +} + +Error LLVMOutputStyle::dumpSectionMap() { + if (!opts::raw::DumpSectionMap) + return Error::success(); + if (!File.hasPDBDbiStream()) { + P.printString("DBI Stream not present"); + return Error::success(); + } + + auto Dbi = File.getPDBDbiStream(); + if (!Dbi) + return Dbi.takeError(); + + ListScope L(P, "Section Map"); + for (auto &M : Dbi->getSectionMap()) { + DictScope D(P, "Entry"); + P.printFlags("Flags", M.Flags, getOMFSegMapDescFlagNames()); + P.printNumber("Ovl", M.Ovl); + P.printNumber("Group", M.Group); + P.printNumber("Frame", M.Frame); + P.printNumber("SecName", M.SecName); + P.printNumber("ClassName", M.ClassName); + P.printNumber("Offset", M.Offset); + P.printNumber("SecByteLength", M.SecByteLength); + P.flush(); + } + return Error::success(); +} + +Error LLVMOutputStyle::dumpPublicsStream() { + if (!opts::raw::DumpPublics) + return Error::success(); + if (!File.hasPDBPublicsStream()) { + P.printString("Publics Stream not present"); + return Error::success(); + } + + auto Publics = File.getPDBPublicsStream(); + if (!Publics) + return Publics.takeError(); + DictScope D(P, "Publics Stream"); + + auto Dbi = File.getPDBDbiStream(); + if (!Dbi) + return Dbi.takeError(); + + P.printNumber("Stream number", Dbi->getPublicSymbolStreamIndex()); + P.printNumber("SymHash", Publics->getSymHash()); + P.printNumber("AddrMap", Publics->getAddrMap()); + P.printNumber("Number of buckets", Publics->getNumBuckets()); + P.printList("Hash Buckets", Publics->getHashBuckets()); + P.printList("Address Map", Publics->getAddressMap()); + P.printList("Thunk Map", Publics->getThunkMap()); + P.printList("Section Offsets", Publics->getSectionOffsets(), + printSectionOffset); + ListScope L(P, "Symbols"); + auto ExpectedTypes = initializeTypeDatabase(StreamTPI); + if (!ExpectedTypes) + return ExpectedTypes.takeError(); + auto &Tpi = *ExpectedTypes; + + codeview::CVSymbolDumper SD(P, Tpi, CodeViewContainer::Pdb, nullptr, false); + bool HadError = false; + for (auto S : Publics->getSymbols(&HadError)) { + DictScope DD(P, ""); + + if (auto EC = SD.dump(S)) { + HadError = true; + break; + } + if (opts::raw::DumpSymRecordBytes) + P.printBinaryBlock("Bytes", S.content()); + } + if (HadError) + return make_error( + raw_error_code::corrupt_file, + "Public symbol stream contained corrupt record"); + + return Error::success(); +} + +Error LLVMOutputStyle::dumpSectionHeaders() { + if (!opts::raw::DumpSectionHeaders) + return Error::success(); + if (!File.hasPDBDbiStream()) { + P.printString("DBI Stream not present"); + return Error::success(); + } + + auto Dbi = File.getPDBDbiStream(); + if (!Dbi) + return Dbi.takeError(); + + ListScope D(P, "Section Headers"); + for (const object::coff_section &Section : Dbi->getSectionHeaders()) { + DictScope DD(P, ""); + + // If a name is 8 characters long, there is no NUL character at end. + StringRef Name(Section.Name, strnlen(Section.Name, sizeof(Section.Name))); + P.printString("Name", Name); + P.printNumber("Virtual Size", Section.VirtualSize); + P.printNumber("Virtual Address", Section.VirtualAddress); + P.printNumber("Size of Raw Data", Section.SizeOfRawData); + P.printNumber("File Pointer to Raw Data", Section.PointerToRawData); + P.printNumber("File Pointer to Relocations", Section.PointerToRelocations); + P.printNumber("File Pointer to Linenumbers", Section.PointerToLinenumbers); + P.printNumber("Number of Relocations", Section.NumberOfRelocations); + P.printNumber("Number of Linenumbers", Section.NumberOfLinenumbers); + P.printFlags("Characteristics", Section.Characteristics, + getImageSectionCharacteristicNames()); + } + return Error::success(); +} + +Error LLVMOutputStyle::dumpFpoStream() { + if (!opts::raw::DumpFpo) + return Error::success(); + if (!File.hasPDBDbiStream()) { + P.printString("DBI Stream not present"); + return Error::success(); + } + + auto Dbi = File.getPDBDbiStream(); + if (!Dbi) + return Dbi.takeError(); + + ListScope D(P, "New FPO"); + for (const object::FpoData &Fpo : Dbi->getFpoRecords()) { + DictScope DD(P, ""); + P.printNumber("Offset", Fpo.Offset); + P.printNumber("Size", Fpo.Size); + P.printNumber("Number of locals", Fpo.NumLocals); + P.printNumber("Number of params", Fpo.NumParams); + P.printNumber("Size of Prolog", Fpo.getPrologSize()); + P.printNumber("Number of Saved Registers", Fpo.getNumSavedRegs()); + P.printBoolean("Has SEH", Fpo.hasSEH()); + P.printBoolean("Use BP", Fpo.useBP()); + P.printNumber("Frame Pointer", Fpo.getFP()); + } + return Error::success(); +} + +void LLVMOutputStyle::flush() { P.flush(); } Index: llvm/trunk/tools/llvm-pdbutil/LinePrinter.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/LinePrinter.h +++ llvm/trunk/tools/llvm-pdbutil/LinePrinter.h @@ -0,0 +1,102 @@ +//===- LinePrinter.h ------------------------------------------ *- 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_LLVMPDBDUMP_LINEPRINTER_H +#define LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Regex.h" + +#include + +namespace llvm { +namespace pdb { + +class ClassLayout; + +class LinePrinter { + friend class WithColor; + +public: + LinePrinter(int Indent, bool UseColor, raw_ostream &Stream); + + void Indent(); + void Unindent(); + void NewLine(); + + bool hasColor() const { return UseColor; } + raw_ostream &getStream() { return OS; } + int getIndentLevel() const { return CurrentIndent; } + + bool IsClassExcluded(const ClassLayout &Class); + bool IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size); + bool IsSymbolExcluded(llvm::StringRef SymbolName); + bool IsCompilandExcluded(llvm::StringRef CompilandName); + +private: + template + void SetFilters(std::list &List, Iter Begin, Iter End) { + List.clear(); + for (; Begin != End; ++Begin) + List.emplace_back(StringRef(*Begin)); + } + + raw_ostream &OS; + int IndentSpaces; + int CurrentIndent; + bool UseColor; + + std::list ExcludeCompilandFilters; + std::list ExcludeTypeFilters; + std::list ExcludeSymbolFilters; + + std::list IncludeCompilandFilters; + std::list IncludeTypeFilters; + std::list IncludeSymbolFilters; +}; + +template +inline raw_ostream &operator<<(LinePrinter &Printer, const T &Item) { + Printer.getStream() << Item; + return Printer.getStream(); +} + +enum class PDB_ColorItem { + None, + Address, + Type, + Comment, + Padding, + Keyword, + Offset, + Identifier, + Path, + SectionHeader, + LiteralValue, + Register, +}; + +class WithColor { +public: + WithColor(LinePrinter &P, PDB_ColorItem C); + ~WithColor(); + + raw_ostream &get() { return OS; } + +private: + void applyColor(PDB_ColorItem C); + raw_ostream &OS; + bool UseColor; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/LinePrinter.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/LinePrinter.cpp +++ llvm/trunk/tools/llvm-pdbutil/LinePrinter.cpp @@ -0,0 +1,145 @@ +//===- LinePrinter.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LinePrinter.h" + +#include "llvm-pdbutil.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Regex.h" + +#include + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +bool IsItemExcluded(llvm::StringRef Item, + std::list &IncludeFilters, + std::list &ExcludeFilters) { + if (Item.empty()) + return false; + + auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); }; + + // Include takes priority over exclude. If the user specified include + // filters, and none of them include this item, them item is gone. + if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred)) + return true; + + if (any_of(ExcludeFilters, match_pred)) + return true; + + return false; +} +} + +using namespace llvm; + +LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream) + : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) { + SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(), + opts::pretty::ExcludeTypes.end()); + SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(), + opts::pretty::ExcludeSymbols.end()); + SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(), + opts::pretty::ExcludeCompilands.end()); + + SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(), + opts::pretty::IncludeTypes.end()); + SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(), + opts::pretty::IncludeSymbols.end()); + SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(), + opts::pretty::IncludeCompilands.end()); +} + +void LinePrinter::Indent() { CurrentIndent += IndentSpaces; } + +void LinePrinter::Unindent() { + CurrentIndent = std::max(0, CurrentIndent - IndentSpaces); +} + +void LinePrinter::NewLine() { + OS << "\n"; + OS.indent(CurrentIndent); +} + +bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { + if (IsTypeExcluded(Class.getName(), Class.getSize())) + return true; + if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold) + return true; + return false; +} + +bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) { + if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters)) + return true; + if (Size < opts::pretty::SizeThreshold) + return true; + return false; +} + +bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { + return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters); +} + +bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { + return IsItemExcluded(CompilandName, IncludeCompilandFilters, + ExcludeCompilandFilters); +} + +WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) + : OS(P.OS), UseColor(P.hasColor()) { + if (UseColor) + applyColor(C); +} + +WithColor::~WithColor() { + if (UseColor) + OS.resetColor(); +} + +void WithColor::applyColor(PDB_ColorItem C) { + switch (C) { + case PDB_ColorItem::None: + OS.resetColor(); + return; + case PDB_ColorItem::Comment: + OS.changeColor(raw_ostream::GREEN, false); + return; + case PDB_ColorItem::Address: + OS.changeColor(raw_ostream::YELLOW, /*bold=*/true); + return; + case PDB_ColorItem::Keyword: + OS.changeColor(raw_ostream::MAGENTA, true); + return; + case PDB_ColorItem::Register: + case PDB_ColorItem::Offset: + OS.changeColor(raw_ostream::YELLOW, false); + return; + case PDB_ColorItem::Type: + OS.changeColor(raw_ostream::CYAN, true); + return; + case PDB_ColorItem::Identifier: + OS.changeColor(raw_ostream::CYAN, false); + return; + case PDB_ColorItem::Path: + OS.changeColor(raw_ostream::CYAN, false); + return; + case PDB_ColorItem::Padding: + case PDB_ColorItem::SectionHeader: + OS.changeColor(raw_ostream::RED, true); + return; + case PDB_ColorItem::LiteralValue: + OS.changeColor(raw_ostream::GREEN, true); + return; + } +} Index: llvm/trunk/tools/llvm-pdbutil/OutputStyle.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/OutputStyle.h +++ llvm/trunk/tools/llvm-pdbutil/OutputStyle.h @@ -0,0 +1,28 @@ +//===- OutputStyle.h ------------------------------------------ *- 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_LLVMPDBDUMP_OUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_OUTPUTSTYLE_H + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class PDBFile; + +class OutputStyle { +public: + virtual ~OutputStyle() {} + + virtual Error dump() = 0; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/PdbYaml.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PdbYaml.h +++ llvm/trunk/tools/llvm-pdbutil/PdbYaml.h @@ -0,0 +1,125 @@ +//===- PdbYAML.h ---------------------------------------------- *- 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_LLVMPDBDUMP_PDBYAML_H +#define LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H + +#include "OutputStyle.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/ObjectYAML/CodeViewYAMLDebugSections.h" +#include "llvm/ObjectYAML/CodeViewYAMLSymbols.h" +#include "llvm/ObjectYAML/CodeViewYAMLTypes.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/YAMLTraits.h" + +#include + +namespace llvm { +namespace codeview { +class DebugStringTableSubsection; +} +namespace pdb { + +namespace yaml { +struct SerializationContext; + +struct MSFHeaders { + msf::SuperBlock SuperBlock; + uint32_t NumDirectoryBlocks = 0; + std::vector DirectoryBlocks; + uint32_t NumStreams = 0; + uint32_t FileSize = 0; +}; + +struct StreamBlockList { + std::vector Blocks; +}; + +struct NamedStreamMapping { + StringRef StreamName; + uint32_t StreamNumber; +}; + +struct PdbInfoStream { + PdbRaw_ImplVer Version = PdbImplVC70; + uint32_t Signature = 0; + uint32_t Age = 1; + PDB_UniqueId Guid; + std::vector Features; + std::vector NamedStreams; +}; + +struct PdbModiStream { + uint32_t Signature; + std::vector Symbols; +}; + +struct PdbDbiModuleInfo { + StringRef Obj; + StringRef Mod; + std::vector SourceFiles; + std::vector Subsections; + Optional Modi; +}; + +struct PdbDbiStream { + PdbRaw_DbiVer VerHeader = PdbDbiV70; + uint32_t Age = 1; + uint16_t BuildNumber = 0; + uint32_t PdbDllVersion = 0; + uint16_t PdbDllRbld = 0; + uint16_t Flags = 1; + PDB_Machine MachineType = PDB_Machine::x86; + + std::vector ModInfos; +}; + +struct PdbTpiStream { + PdbRaw_TpiVer Version = PdbTpiV80; + std::vector Records; +}; + +struct PdbObject { + explicit PdbObject(BumpPtrAllocator &Allocator) : Allocator(Allocator) {} + + Optional Headers; + Optional> StreamSizes; + Optional> StreamMap; + Optional PdbStream; + Optional DbiStream; + Optional TpiStream; + Optional IpiStream; + + Optional> StringTable; + + BumpPtrAllocator &Allocator; +}; +} +} +} + +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbObject) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::MSFHeaders) +LLVM_YAML_DECLARE_MAPPING_TRAITS(msf::SuperBlock) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::StreamBlockList) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbInfoStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbTpiStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::NamedStreamMapping) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbModiStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiModuleInfo) + +#endif // LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H Index: llvm/trunk/tools/llvm-pdbutil/PdbYaml.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PdbYaml.cpp +++ llvm/trunk/tools/llvm-pdbutil/PdbYaml.cpp @@ -0,0 +1,227 @@ +//===- PdbYAML.cpp -------------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PdbYaml.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/TypeSerializer.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawTypes.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/ObjectYAML/CodeViewYAMLDebugSections.h" +#include "llvm/ObjectYAML/CodeViewYAMLTypes.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::pdb::yaml; +using namespace llvm::yaml; + +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(uint32_t) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::StringRef) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::NamedStreamMapping) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbDbiModuleInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::pdb::PdbRaw_FeatureSig) + +namespace llvm { +namespace yaml { + +template <> struct ScalarTraits { + static void output(const llvm::pdb::PDB_UniqueId &S, void *, + llvm::raw_ostream &OS) { + OS << S; + } + + static StringRef input(StringRef Scalar, void *Ctx, + llvm::pdb::PDB_UniqueId &S) { + if (Scalar.size() != 38) + return "GUID strings are 38 characters long"; + if (Scalar[0] != '{' || Scalar[37] != '}') + return "GUID is not enclosed in {}"; + if (Scalar[9] != '-' || Scalar[14] != '-' || Scalar[19] != '-' || + Scalar[24] != '-') + return "GUID sections are not properly delineated with dashes"; + + uint8_t *OutBuffer = S.Guid; + for (auto Iter = Scalar.begin(); Iter != Scalar.end();) { + if (*Iter == '-' || *Iter == '{' || *Iter == '}') { + ++Iter; + continue; + } + uint8_t Value = (llvm::hexDigitValue(*Iter) << 4); + ++Iter; + Value |= llvm::hexDigitValue(*Iter); + ++Iter; + *OutBuffer++ = Value; + } + + return ""; + } + + static bool mustQuote(StringRef Scalar) { return needsQuotes(Scalar); } +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, llvm::pdb::PDB_Machine &Value) { + io.enumCase(Value, "Invalid", PDB_Machine::Invalid); + io.enumCase(Value, "Am33", PDB_Machine::Am33); + io.enumCase(Value, "Amd64", PDB_Machine::Amd64); + io.enumCase(Value, "Arm", PDB_Machine::Arm); + io.enumCase(Value, "ArmNT", PDB_Machine::ArmNT); + io.enumCase(Value, "Ebc", PDB_Machine::Ebc); + io.enumCase(Value, "x86", PDB_Machine::x86); + io.enumCase(Value, "Ia64", PDB_Machine::Ia64); + io.enumCase(Value, "M32R", PDB_Machine::M32R); + io.enumCase(Value, "Mips16", PDB_Machine::Mips16); + io.enumCase(Value, "MipsFpu", PDB_Machine::MipsFpu); + io.enumCase(Value, "MipsFpu16", PDB_Machine::MipsFpu16); + io.enumCase(Value, "PowerPCFP", PDB_Machine::PowerPCFP); + io.enumCase(Value, "R4000", PDB_Machine::R4000); + io.enumCase(Value, "SH3", PDB_Machine::SH3); + io.enumCase(Value, "SH3DSP", PDB_Machine::SH3DSP); + io.enumCase(Value, "Thumb", PDB_Machine::Thumb); + io.enumCase(Value, "WceMipsV2", PDB_Machine::WceMipsV2); + } +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, llvm::pdb::PdbRaw_DbiVer &Value) { + io.enumCase(Value, "V41", llvm::pdb::PdbRaw_DbiVer::PdbDbiVC41); + io.enumCase(Value, "V50", llvm::pdb::PdbRaw_DbiVer::PdbDbiV50); + io.enumCase(Value, "V60", llvm::pdb::PdbRaw_DbiVer::PdbDbiV60); + io.enumCase(Value, "V70", llvm::pdb::PdbRaw_DbiVer::PdbDbiV70); + io.enumCase(Value, "V110", llvm::pdb::PdbRaw_DbiVer::PdbDbiV110); + } +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, llvm::pdb::PdbRaw_ImplVer &Value) { + io.enumCase(Value, "VC2", llvm::pdb::PdbRaw_ImplVer::PdbImplVC2); + io.enumCase(Value, "VC4", llvm::pdb::PdbRaw_ImplVer::PdbImplVC4); + io.enumCase(Value, "VC41", llvm::pdb::PdbRaw_ImplVer::PdbImplVC41); + io.enumCase(Value, "VC50", llvm::pdb::PdbRaw_ImplVer::PdbImplVC50); + io.enumCase(Value, "VC98", llvm::pdb::PdbRaw_ImplVer::PdbImplVC98); + io.enumCase(Value, "VC70Dep", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70Dep); + io.enumCase(Value, "VC70", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70); + io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_ImplVer::PdbImplVC80); + io.enumCase(Value, "VC110", llvm::pdb::PdbRaw_ImplVer::PdbImplVC110); + io.enumCase(Value, "VC140", llvm::pdb::PdbRaw_ImplVer::PdbImplVC140); + } +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, llvm::pdb::PdbRaw_TpiVer &Value) { + io.enumCase(Value, "VC40", llvm::pdb::PdbRaw_TpiVer::PdbTpiV40); + io.enumCase(Value, "VC41", llvm::pdb::PdbRaw_TpiVer::PdbTpiV41); + io.enumCase(Value, "VC50", llvm::pdb::PdbRaw_TpiVer::PdbTpiV50); + io.enumCase(Value, "VC70", llvm::pdb::PdbRaw_TpiVer::PdbTpiV70); + io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_TpiVer::PdbTpiV80); + } +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, PdbRaw_FeatureSig &Features) { + io.enumCase(Features, "MinimalDebugInfo", + PdbRaw_FeatureSig::MinimalDebugInfo); + io.enumCase(Features, "NoTypeMerge", PdbRaw_FeatureSig::NoTypeMerge); + io.enumCase(Features, "VC110", PdbRaw_FeatureSig::VC110); + io.enumCase(Features, "VC140", PdbRaw_FeatureSig::VC140); + } +}; +} +} + +void MappingTraits::mapping(IO &IO, PdbObject &Obj) { + IO.mapOptional("MSF", Obj.Headers); + IO.mapOptional("StreamSizes", Obj.StreamSizes); + IO.mapOptional("StreamMap", Obj.StreamMap); + IO.mapOptional("StringTable", Obj.StringTable); + IO.mapOptional("PdbStream", Obj.PdbStream); + IO.mapOptional("DbiStream", Obj.DbiStream); + IO.mapOptional("TpiStream", Obj.TpiStream); + IO.mapOptional("IpiStream", Obj.IpiStream); +} + +void MappingTraits::mapping(IO &IO, MSFHeaders &Obj) { + IO.mapOptional("SuperBlock", Obj.SuperBlock); + IO.mapOptional("NumDirectoryBlocks", Obj.NumDirectoryBlocks); + IO.mapOptional("DirectoryBlocks", Obj.DirectoryBlocks); + IO.mapOptional("NumStreams", Obj.NumStreams); + IO.mapOptional("FileSize", Obj.FileSize); +} + +void MappingTraits::mapping(IO &IO, msf::SuperBlock &SB) { + if (!IO.outputting()) { + ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic)); + } + + using u32 = support::ulittle32_t; + IO.mapOptional("BlockSize", SB.BlockSize, u32(4096U)); + IO.mapOptional("FreeBlockMap", SB.FreeBlockMapBlock, u32(0U)); + IO.mapOptional("NumBlocks", SB.NumBlocks, u32(0U)); + IO.mapOptional("NumDirectoryBytes", SB.NumDirectoryBytes, u32(0U)); + IO.mapOptional("Unknown1", SB.Unknown1, u32(0U)); + IO.mapOptional("BlockMapAddr", SB.BlockMapAddr, u32(0U)); +} + +void MappingTraits::mapping(IO &IO, StreamBlockList &SB) { + IO.mapRequired("Stream", SB.Blocks); +} + +void MappingTraits::mapping(IO &IO, PdbInfoStream &Obj) { + IO.mapOptional("Age", Obj.Age, 1U); + IO.mapOptional("Guid", Obj.Guid); + IO.mapOptional("Signature", Obj.Signature, 0U); + IO.mapOptional("Features", Obj.Features); + IO.mapOptional("Version", Obj.Version, PdbImplVC70); +} + +void MappingTraits::mapping(IO &IO, PdbDbiStream &Obj) { + IO.mapOptional("VerHeader", Obj.VerHeader, PdbDbiV70); + IO.mapOptional("Age", Obj.Age, 1U); + IO.mapOptional("BuildNumber", Obj.BuildNumber, uint16_t(0U)); + IO.mapOptional("PdbDllVersion", Obj.PdbDllVersion, 0U); + IO.mapOptional("PdbDllRbld", Obj.PdbDllRbld, uint16_t(0U)); + IO.mapOptional("Flags", Obj.Flags, uint16_t(1U)); + IO.mapOptional("MachineType", Obj.MachineType, PDB_Machine::x86); + IO.mapOptional("Modules", Obj.ModInfos); +} + +void MappingTraits::mapping(IO &IO, + pdb::yaml::PdbTpiStream &Obj) { + IO.mapOptional("Version", Obj.Version, PdbTpiV80); + IO.mapRequired("Records", Obj.Records); +} + +void MappingTraits::mapping(IO &IO, + NamedStreamMapping &Obj) { + IO.mapRequired("Name", Obj.StreamName); + IO.mapRequired("StreamNum", Obj.StreamNumber); +} + +void MappingTraits::mapping(IO &IO, PdbModiStream &Obj) { + IO.mapOptional("Signature", Obj.Signature, 4U); + IO.mapRequired("Records", Obj.Symbols); +} + +void MappingTraits::mapping(IO &IO, PdbDbiModuleInfo &Obj) { + IO.mapRequired("Module", Obj.Mod); + IO.mapOptional("ObjFile", Obj.Obj, Obj.Mod); + IO.mapOptional("SourceFiles", Obj.SourceFiles); + IO.mapOptional("Subsections", Obj.Subsections); + IO.mapOptional("Modi", Obj.Modi); +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyBuiltinDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyBuiltinDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyBuiltinDumper.h @@ -0,0 +1,35 @@ +//===- PrettyBuiltinDumper.h ---------------------------------- *- 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_LLVMPDBDUMP_PRETTYBUILTINDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYBUILTINDUMPER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class BuiltinDumper : public PDBSymDumper { +public: + BuiltinDumper(LinePrinter &P); + + void start(const PDBSymbolTypeBuiltin &Symbol); + +private: + StringRef getTypeName(const PDBSymbolTypeBuiltin &Symbol); + + LinePrinter &Printer; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp @@ -0,0 +1,94 @@ +//===- PrettyBuiltinDumper.cpp ---------------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyBuiltinDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +using namespace llvm; +using namespace llvm::pdb; + +BuiltinDumper::BuiltinDumper(LinePrinter &P) + : PDBSymDumper(false), Printer(P) {} + +void BuiltinDumper::start(const PDBSymbolTypeBuiltin &Symbol) { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + WithColor(Printer, PDB_ColorItem::Type).get() << getTypeName(Symbol); +} + +StringRef BuiltinDumper::getTypeName(const PDBSymbolTypeBuiltin &Symbol) { + PDB_BuiltinType Type = Symbol.getBuiltinType(); + switch (Type) { + case PDB_BuiltinType::Float: + if (Symbol.getLength() == 4) + return "float"; + return "double"; + case PDB_BuiltinType::UInt: + switch (Symbol.getLength()) { + case 8: + return "unsigned __int64"; + case 4: + return "unsigned int"; + case 2: + return "unsigned short"; + case 1: + return "unsigned char"; + default: + return "unsigned"; + } + case PDB_BuiltinType::Int: + switch (Symbol.getLength()) { + case 8: + return "__int64"; + case 4: + return "int"; + case 2: + return "short"; + case 1: + return "char"; + default: + return "int"; + } + case PDB_BuiltinType::Char: + return "char"; + case PDB_BuiltinType::WCharT: + return "wchar_t"; + case PDB_BuiltinType::Void: + return "void"; + case PDB_BuiltinType::Long: + return "long"; + case PDB_BuiltinType::ULong: + return "unsigned long"; + case PDB_BuiltinType::Bool: + return "bool"; + case PDB_BuiltinType::Currency: + return "CURRENCY"; + case PDB_BuiltinType::Date: + return "DATE"; + case PDB_BuiltinType::Variant: + return "VARIANT"; + case PDB_BuiltinType::Complex: + return "complex"; + case PDB_BuiltinType::Bitfield: + return "bitfield"; + case PDB_BuiltinType::BSTR: + return "BSTR"; + case PDB_BuiltinType::HResult: + return "HRESULT"; + case PDB_BuiltinType::BCD: + return "HRESULT"; + default: + return "void"; + } +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h @@ -0,0 +1,47 @@ +//===- PrettyClassDefinitionDumper.h ----------------------------*- 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_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" + +#include +#include +#include + +namespace llvm { +class BitVector; + +namespace pdb { + +class ClassLayout; +class LinePrinter; + +class ClassDefinitionDumper : public PDBSymDumper { +public: + ClassDefinitionDumper(LinePrinter &P); + + void start(const PDBSymbolTypeUDT &Class); + void start(const ClassLayout &Class); + +private: + void prettyPrintClassIntro(const ClassLayout &Class); + void prettyPrintClassOutro(const ClassLayout &Class); + + LinePrinter &Printer; + bool DumpedAnything = false; +}; +} +} +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp @@ -0,0 +1,108 @@ +//===- PrettyClassDefinitionDumper.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyClassDefinitionDumper.h" + +#include "LinePrinter.h" +#include "PrettyClassLayoutGraphicalDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" + +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::pdb; + +ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) { + assert(opts::pretty::ClassFormat != + opts::pretty::ClassDefinitionFormat::None); + + ClassLayout Layout(Class); + start(Layout); +} + +void ClassDefinitionDumper::start(const ClassLayout &Layout) { + prettyPrintClassIntro(Layout); + + PrettyClassLayoutGraphicalDumper Dumper(Printer, 1, 0); + DumpedAnything |= Dumper.start(Layout); + + prettyPrintClassOutro(Layout); +} + +void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) { + DumpedAnything = false; + Printer.NewLine(); + + uint32_t Size = Layout.getSize(); + const PDBSymbolTypeUDT &Class = Layout.getClass(); + + WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " "; + WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); + WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size + << "]"; + uint32_t BaseCount = Layout.bases().size(); + if (BaseCount > 0) { + Printer.Indent(); + char NextSeparator = ':'; + for (auto BC : Layout.bases()) { + const auto &Base = BC->getBase(); + if (Base.isIndirectVirtualBaseClass()) + continue; + + Printer.NewLine(); + Printer << NextSeparator << " "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess(); + if (BC->isVirtualBase()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual"; + + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName(); + NextSeparator = ','; + } + + Printer.Unindent(); + } + + Printer << " {"; + Printer.Indent(); +} + +void ClassDefinitionDumper::prettyPrintClassOutro(const ClassLayout &Layout) { + Printer.Unindent(); + if (DumpedAnything) + Printer.NewLine(); + Printer << "}"; + Printer.NewLine(); + if (Layout.deepPaddingSize() > 0) { + APFloat Pct(100.0 * (double)Layout.deepPaddingSize() / + (double)Layout.getSize()); + SmallString<8> PctStr; + Pct.toString(PctStr, 4); + WithColor(Printer, PDB_ColorItem::Padding).get() + << "Total padding " << Layout.deepPaddingSize() << " bytes (" << PctStr + << "% of class size)"; + Printer.NewLine(); + APFloat Pct2(100.0 * (double)Layout.immediatePadding() / + (double)Layout.getSize()); + PctStr.clear(); + Pct2.toString(PctStr, 4); + WithColor(Printer, PDB_ColorItem::Padding).get() + << "Immediate padding " << Layout.immediatePadding() << " bytes (" + << PctStr << "% of class size)"; + Printer.NewLine(); + } +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h @@ -0,0 +1,58 @@ +//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- 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_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H + +#include "llvm/ADT/BitVector.h" + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +namespace pdb { + +class UDTLayoutBase; +class LayoutItemBase; +class LinePrinter; + +class PrettyClassLayoutGraphicalDumper : public PDBSymDumper { +public: + PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t RecurseLevel, + uint32_t InitialOffset); + + bool start(const UDTLayoutBase &Layout); + + // Layout based symbol types. + void dump(const PDBSymbolTypeBaseClass &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolTypeVTable &Symbol) override; + + // Non layout-based symbol types. + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolFunc &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + +private: + bool shouldRecurse() const; + void printPaddingRow(uint32_t Amount); + + LinePrinter &Printer; + + LayoutItemBase *CurrentItem = nullptr; + uint32_t RecursionLevel = 0; + uint32_t ClassOffsetZero = 0; + uint32_t CurrentAbsoluteOffset = 0; + bool DumpedAnything = false; +}; +} +} +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp @@ -0,0 +1,216 @@ +//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyClassLayoutGraphicalDumper.h" + +#include "LinePrinter.h" +#include "PrettyClassDefinitionDumper.h" +#include "PrettyEnumDumper.h" +#include "PrettyFunctionDumper.h" +#include "PrettyTypedefDumper.h" +#include "PrettyVariableDumper.h" +#include "PrettyVariableDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::pdb; + +PrettyClassLayoutGraphicalDumper::PrettyClassLayoutGraphicalDumper( + LinePrinter &P, uint32_t RecurseLevel, uint32_t InitialOffset) + : PDBSymDumper(true), Printer(P), RecursionLevel(RecurseLevel), + ClassOffsetZero(InitialOffset), CurrentAbsoluteOffset(InitialOffset) {} + +bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) { + + if (RecursionLevel == 1 && + opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::All) { + for (auto &Other : Layout.other_items()) + Other->dump(*this); + for (auto &Func : Layout.funcs()) + Func->dump(*this); + } + + const BitVector &UseMap = Layout.usedBytes(); + int NextPaddingByte = UseMap.find_first_unset(); + + for (auto &Item : Layout.layout_items()) { + // Calculate the absolute offset of the first byte of the next field. + uint32_t RelativeOffset = Item->getOffsetInParent(); + CurrentAbsoluteOffset = ClassOffsetZero + RelativeOffset; + + // Since there is storage there, it should be set! However, this might + // be an empty base, in which case it could extend outside the bounds of + // the parent class. + if (RelativeOffset < UseMap.size() && (Item->getSize() > 0)) { + assert(UseMap.test(RelativeOffset)); + + // If there is any remaining padding in this class, and the offset of the + // new item is after the padding, then we must have just jumped over some + // padding. Print a padding row and then look for where the next block + // of padding begins. + if ((NextPaddingByte >= 0) && + (RelativeOffset > uint32_t(NextPaddingByte))) { + printPaddingRow(RelativeOffset - NextPaddingByte); + NextPaddingByte = UseMap.find_next_unset(RelativeOffset); + } + } + + CurrentItem = Item; + if (Item->isVBPtr()) { + VTableLayoutItem &Layout = static_cast(*CurrentItem); + + VariableDumper VarDumper(Printer); + VarDumper.startVbptr(CurrentAbsoluteOffset, Layout.getSize()); + } else { + if (auto Sym = Item->getSymbol()) + Sym->dump(*this); + } + + if (Item->getLayoutSize() > 0) { + uint32_t Prev = RelativeOffset + Item->getLayoutSize() - 1; + if (Prev < UseMap.size()) + NextPaddingByte = UseMap.find_next_unset(Prev); + } + } + + auto TailPadding = Layout.tailPadding(); + if (TailPadding > 0) { + if (TailPadding != 1 || Layout.getSize() != 1) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() + << " (" << TailPadding << " bytes)"; + DumpedAnything = true; + } + } + + return DumpedAnything; +} + +void PrettyClassLayoutGraphicalDumper::printPaddingRow(uint32_t Amount) { + if (Amount == 0) + return; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << " (" << Amount + << " bytes)"; + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump( + const PDBSymbolTypeBaseClass &Symbol) { + assert(CurrentItem != nullptr); + + Printer.NewLine(); + BaseClassLayout &Layout = static_cast(*CurrentItem); + + std::string Label = "base"; + if (Layout.isVirtualBase()) { + Label.insert(Label.begin(), 'v'); + if (Layout.getBase().isIndirectVirtualBaseClass()) + Label.insert(Label.begin(), 'i'); + } + Printer << Label << " "; + + uint32_t Size = Layout.isEmptyBase() ? 1 : Layout.getLayoutSize(); + + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(CurrentAbsoluteOffset, 4) << " [sizeof=" << Size + << "] "; + + WithColor(Printer, PDB_ColorItem::Identifier).get() << Layout.getName(); + + if (shouldRecurse()) { + Printer.Indent(); + uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent(); + PrettyClassLayoutGraphicalDumper BaseDumper(Printer, RecursionLevel + 1, + ChildOffsetZero); + DumpedAnything |= BaseDumper.start(Layout); + Printer.Unindent(); + } + + DumpedAnything = true; +} + +bool PrettyClassLayoutGraphicalDumper::shouldRecurse() const { + uint32_t Limit = opts::pretty::ClassRecursionDepth; + if (Limit == 0) + return true; + return RecursionLevel < Limit; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) { + assert(CurrentItem != nullptr); + + DataMemberLayoutItem &Layout = + static_cast(*CurrentItem); + + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + if (Layout.hasUDTLayout() && shouldRecurse()) { + uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent(); + Printer.Indent(); + PrettyClassLayoutGraphicalDumper TypeDumper(Printer, RecursionLevel + 1, + ChildOffsetZero); + TypeDumper.start(Layout.getUDTLayout()); + Printer.Unindent(); + } + + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeVTable &Symbol) { + assert(CurrentItem != nullptr); + + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeEnum &Symbol) { + DumpedAnything = true; + Printer.NewLine(); + EnumDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void PrettyClassLayoutGraphicalDumper::dump( + const PDBSymbolTypeTypedef &Symbol) { + DumpedAnything = true; + Printer.NewLine(); + TypedefDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void PrettyClassLayoutGraphicalDumper::dump( + const PDBSymbolTypeBuiltin &Symbol) {} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeUDT &Symbol) {} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolFunc &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + if (Symbol.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated) + return; + if (Symbol.getLength() == 0 && !Symbol.isPureVirtual() && + !Symbol.isIntroVirtualFunction()) + return; + + DumpedAnything = true; + Printer.NewLine(); + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, FunctionDumper::PointerType::None); +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyCompilandDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyCompilandDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyCompilandDumper.h @@ -0,0 +1,44 @@ +//===- PrettyCompilandDumper.h - llvm-pdbutil compiland dumper -*- 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_LLVMPDBDUMP_PRETTYCOMPILANDDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCOMPILANDDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +typedef int CompilandDumpFlags; +class CompilandDumper : public PDBSymDumper { +public: + enum Flags { None = 0x0, Children = 0x1, Symbols = 0x2, Lines = 0x4 }; + + CompilandDumper(LinePrinter &P); + + void start(const PDBSymbolCompiland &Symbol, CompilandDumpFlags flags); + + void dump(const PDBSymbolCompilandDetails &Symbol) override; + void dump(const PDBSymbolCompilandEnv &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolFunc &Symbol) override; + void dump(const PDBSymbolLabel &Symbol) override; + void dump(const PDBSymbolThunk &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolUnknown &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyCompilandDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyCompilandDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyCompilandDumper.cpp @@ -0,0 +1,207 @@ +//===- PrettyCompilandDumper.cpp - llvm-pdbutil compiland dumper -*- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyCompilandDumper.h" + +#include "LinePrinter.h" +#include "PrettyFunctionDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h" +#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; +using namespace llvm::pdb; + +CompilandDumper::CompilandDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void CompilandDumper::dump(const PDBSymbolCompilandDetails &Symbol) {} + +void CompilandDumper::dump(const PDBSymbolCompilandEnv &Symbol) {} + +void CompilandDumper::start(const PDBSymbolCompiland &Symbol, + CompilandDumpFlags opts) { + std::string FullName = Symbol.getName(); + if (Printer.IsCompilandExcluded(FullName)) + return; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Path).get() << FullName; + + if (opts & Flags::Lines) { + const IPDBSession &Session = Symbol.getSession(); + auto Files = Session.getSourceFilesForCompiland(Symbol); + Printer.Indent(); + while (auto File = Files->getNext()) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Path).get() << File->getFileName(); + + auto Lines = Session.findLineNumbers(Symbol, *File); + Printer.Indent(); + while (auto Line = Lines->getNext()) { + Printer.NewLine(); + uint32_t LineStart = Line->getLineNumber(); + uint32_t LineEnd = Line->getLineNumberEnd(); + + Printer << "Line "; + PDB_ColorItem StatementColor = Line->isStatement() + ? PDB_ColorItem::Keyword + : PDB_ColorItem::LiteralValue; + WithColor(Printer, StatementColor).get() << LineStart; + if (LineStart != LineEnd) + WithColor(Printer, StatementColor).get() << " - " << LineEnd; + + uint32_t ColumnStart = Line->getColumnNumber(); + uint32_t ColumnEnd = Line->getColumnNumberEnd(); + if (ColumnStart != 0 || ColumnEnd != 0) { + Printer << ", Column: "; + WithColor(Printer, StatementColor).get() << ColumnStart; + if (ColumnEnd != ColumnStart) + WithColor(Printer, StatementColor).get() << " - " << ColumnEnd; + } + + Printer << ", Address: "; + if (Line->getLength() > 0) { + uint64_t AddrStart = Line->getVirtualAddress(); + uint64_t AddrEnd = AddrStart + Line->getLength() - 1; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(AddrStart, 10) << " - " + << format_hex(AddrEnd, 10) << "]"; + Printer << " (" << Line->getLength() << " bytes)"; + } else { + uint64_t AddrStart = Line->getVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(AddrStart, 10) << "] "; + Printer << "(0 bytes)"; + } + } + Printer.Unindent(); + } + Printer.Unindent(); + } + + if (opts & Flags::Children) { + auto ChildrenEnum = Symbol.findAllChildren(); + Printer.Indent(); + while (auto Child = ChildrenEnum->getNext()) + Child->dump(*this); + Printer.Unindent(); + } +} + +void CompilandDumper::dump(const PDBSymbolData &Symbol) { + if (!shouldDumpSymLevel(opts::pretty::SymLevel::Data)) + return; + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + + switch (auto LocType = Symbol.getLocationType()) { + case PDB_LocType::Static: + Printer << "data: "; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "]"; + + WithColor(Printer, PDB_ColorItem::Comment).get() + << " [sizeof = " << getTypeLength(Symbol) << "]"; + + break; + case PDB_LocType::Constant: + Printer << "constant: "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() + << "[" << Symbol.getValue() << "]"; + WithColor(Printer, PDB_ColorItem::Comment).get() + << " [sizeof = " << getTypeLength(Symbol) << "]"; + break; + default: + Printer << "data(unexpected type=" << LocType << ")"; + } + + Printer << " "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); +} + +void CompilandDumper::dump(const PDBSymbolFunc &Symbol) { + if (!shouldDumpSymLevel(opts::pretty::SymLevel::Functions)) + return; + if (Symbol.getLength() == 0) + return; + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, FunctionDumper::PointerType::None); +} + +void CompilandDumper::dump(const PDBSymbolLabel &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + Printer << "label "; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); +} + +void CompilandDumper::dump(const PDBSymbolThunk &Symbol) { + if (!shouldDumpSymLevel(opts::pretty::SymLevel::Thunks)) + return; + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + Printer << "thunk "; + codeview::ThunkOrdinal Ordinal = Symbol.getThunkOrdinal(); + uint64_t VA = Symbol.getVirtualAddress(); + if (Ordinal == codeview::ThunkOrdinal::TrampIncremental) { + uint64_t Target = Symbol.getTargetVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(VA, 10); + Printer << " -> "; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Target, 10); + } else { + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(VA, 10) << " - " + << format_hex(VA + Symbol.getLength(), 10) << "]"; + } + Printer << " ("; + WithColor(Printer, PDB_ColorItem::Register).get() << Ordinal; + Printer << ") "; + std::string Name = Symbol.getName(); + if (!Name.empty()) + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; +} + +void CompilandDumper::dump(const PDBSymbolTypeTypedef &Symbol) {} + +void CompilandDumper::dump(const PDBSymbolUnknown &Symbol) { + Printer.NewLine(); + Printer << "unknown (" << Symbol.getSymTag() << ")"; +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyEnumDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyEnumDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyEnumDumper.h @@ -0,0 +1,31 @@ +//===- PrettyEnumDumper.h ---------------------------------------*- 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_LLVMPDBDUMP_PRETTYENUMDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYENUMDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class EnumDumper : public PDBSymDumper { +public: + EnumDumper(LinePrinter &P); + + void start(const PDBSymbolTypeEnum &Symbol); + +private: + LinePrinter &Printer; +}; +} +} +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyEnumDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyEnumDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyEnumDumper.cpp @@ -0,0 +1,53 @@ +//===- PrettyEnumDumper.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyEnumDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +using namespace llvm; +using namespace llvm::pdb; + +EnumDumper::EnumDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void EnumDumper::start(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); + if (!opts::pretty::NoEnumDefs) { + auto BuiltinType = Symbol.getUnderlyingType(); + if (BuiltinType->getBuiltinType() != PDB_BuiltinType::Int || + BuiltinType->getLength() != 4) { + Printer << " : "; + BuiltinDumper Dumper(Printer); + Dumper.start(*BuiltinType); + } + Printer << " {"; + Printer.Indent(); + auto EnumValues = Symbol.findAllChildren(); + while (auto EnumValue = EnumValues->getNext()) { + if (EnumValue->getDataKind() != PDB_DataKind::Constant) + continue; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() + << EnumValue->getName(); + Printer << " = "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() + << EnumValue->getValue(); + } + Printer.Unindent(); + Printer.NewLine(); + Printer << "}"; + } +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h @@ -0,0 +1,34 @@ +//===- PrettyExternalSymbolDumper.h --------------------------- *- 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_LLVMPDBDUMP_PRETTYEXTERNALSYMBOLDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYEXTERNALSYMBOLDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class ExternalSymbolDumper : public PDBSymDumper { +public: + ExternalSymbolDumper(LinePrinter &P); + + void start(const PDBSymbolExe &Symbol); + + void dump(const PDBSymbolPublicSymbol &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp @@ -0,0 +1,41 @@ +//===- PrettyExternalSymbolDumper.cpp -------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyExternalSymbolDumper.h" +#include "LinePrinter.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::pdb; + +ExternalSymbolDumper::ExternalSymbolDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void ExternalSymbolDumper::start(const PDBSymbolExe &Symbol) { + auto Vars = Symbol.findAllChildren(); + while (auto Var = Vars->getNext()) + Var->dump(*this); +} + +void ExternalSymbolDumper::dump(const PDBSymbolPublicSymbol &Symbol) { + std::string LinkageName = Symbol.getName(); + if (Printer.IsSymbolExcluded(LinkageName)) + return; + + Printer.NewLine(); + uint64_t Addr = Symbol.getVirtualAddress(); + + Printer << "["; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Addr, 10); + Printer << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << LinkageName; +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyFunctionDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyFunctionDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyFunctionDumper.h @@ -0,0 +1,43 @@ +//===- PrettyFunctionDumper.h --------------------------------- *- 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_LLVMPDBDUMP_PRETTYFUNCTIONDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYFUNCTIONDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { +class LinePrinter; + +class FunctionDumper : public PDBSymDumper { +public: + FunctionDumper(LinePrinter &P); + + enum class PointerType { None, Pointer, Reference }; + + void start(const PDBSymbolTypeFunctionSig &Symbol, const char *Name, + PointerType Pointer); + void start(const PDBSymbolFunc &Symbol, PointerType Pointer); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionArg &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyFunctionDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyFunctionDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyFunctionDumper.cpp @@ -0,0 +1,259 @@ +//===- PrettyFunctionDumper.cpp --------------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyFunctionDumper.h" +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +template +void dumpClassParentWithScopeOperator(const T &Symbol, LinePrinter &Printer, + FunctionDumper &Dumper) { + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().template getConcreteSymbolById( + ClassParentId); + if (!ClassParent) + return; + + WithColor(Printer, PDB_ColorItem::Type).get() << ClassParent->getName(); + Printer << "::"; +} +} + +FunctionDumper::FunctionDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol, + const char *Name, PointerType Pointer) { + auto ReturnType = Symbol.getReturnType(); + ReturnType->dump(*this); + Printer << " "; + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().getConcreteSymbolById( + ClassParentId); + + PDB_CallingConv CC = Symbol.getCallingConvention(); + bool ShouldDumpCallingConvention = true; + if ((ClassParent && CC == CallingConvention::ThisCall) || + (!ClassParent && CC == CallingConvention::NearStdCall)) { + ShouldDumpCallingConvention = false; + } + + if (Pointer == PointerType::None) { + if (ShouldDumpCallingConvention) + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + if (ClassParent) { + Printer << "("; + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::)"; + } + } else { + Printer << "("; + if (ShouldDumpCallingConvention) + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + if (ClassParent) { + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::"; + } + if (Pointer == PointerType::Reference) + Printer << "&"; + else + Printer << "*"; + if (Name) + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; + Printer << ")"; + } + + Printer << "("; + if (auto ChildEnum = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = ChildEnum->getNext()) { + Arg->dump(*this); + if (++Index < ChildEnum->getChildCount()) + Printer << ", "; + } + } + Printer << ")"; + + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const"; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile"; +} + +void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) { + uint64_t FuncStart = Symbol.getVirtualAddress(); + uint64_t FuncEnd = FuncStart + Symbol.getLength(); + + Printer << "func ["; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncStart, 10); + if (auto DebugStart = Symbol.findOneChild()) { + uint64_t Prologue = DebugStart->getVirtualAddress() - FuncStart; + WithColor(Printer, PDB_ColorItem::Offset).get() + << formatv("+{0,2}", Prologue); + } + Printer << " - "; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncEnd, 10); + if (auto DebugEnd = Symbol.findOneChild()) { + uint64_t Epilogue = FuncEnd - DebugEnd->getVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Offset).get() + << formatv("-{0,2}", Epilogue); + } + + WithColor(Printer, PDB_ColorItem::Comment).get() + << formatv(" | sizeof={0,3}", Symbol.getLength()); + Printer << "] ("; + + if (Symbol.hasFramePointer()) { + WithColor(Printer, PDB_ColorItem::Register).get() + << Symbol.getLocalBasePointerRegisterId(); + } else { + WithColor(Printer, PDB_ColorItem::Register).get() << "FPO"; + } + Printer << ") "; + + if (Symbol.isVirtual() || Symbol.isPureVirtual()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "virtual "; + + auto Signature = Symbol.getSignature(); + if (!Signature) { + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + if (Pointer == PointerType::Pointer) + Printer << "*"; + else if (Pointer == FunctionDumper::PointerType::Reference) + Printer << "&"; + return; + } + + auto ReturnType = Signature->getReturnType(); + ReturnType->dump(*this); + Printer << " "; + + auto ClassParent = Symbol.getClassParent(); + CallingConvention CC = Signature->getCallingConvention(); + if (Pointer != FunctionDumper::PointerType::None) + Printer << "("; + + if ((ClassParent && CC != CallingConvention::ThisCall) || + (!ClassParent && CC != CallingConvention::NearStdCall)) { + WithColor(Printer, PDB_ColorItem::Keyword).get() + << Signature->getCallingConvention() << " "; + } + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + if (Pointer != FunctionDumper::PointerType::None) { + if (Pointer == PointerType::Pointer) + Printer << "*"; + else if (Pointer == FunctionDumper::PointerType::Reference) + Printer << "&"; + Printer << ")"; + } + + Printer << "("; + if (auto Arguments = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = Arguments->getNext()) { + auto ArgType = Arg->getType(); + ArgType->dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " + << Arg->getName(); + if (++Index < Arguments->getChildCount()) + Printer << ", "; + } + } + Printer << ")"; + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const"; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile"; + if (Symbol.isPureVirtual()) + Printer << " = 0"; +} + +void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) { + auto ElementType = Symbol.getElementType(); + + ElementType->dump(*this); + Printer << "["; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getLength(); + Printer << "]"; +} + +void FunctionDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void FunctionDumper::dump(const PDBSymbolTypeEnum &Symbol) { + dumpClassParentWithScopeOperator(Symbol, Printer, *this); + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) { + // PDBSymbolTypeFunctionArg is just a shim over the real argument. Just drill + // through to the real thing and dump it. + uint32_t TypeId = Symbol.getTypeId(); + auto Type = Symbol.getSession().getSymbolById(TypeId); + if (!Type) + return; + Type->dump(*this); +} + +void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + dumpClassParentWithScopeOperator(Symbol, Printer, *this); + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) { + auto PointeeType = Symbol.getPointeeType(); + if (!PointeeType) + return; + + if (auto FuncSig = unique_dyn_cast(PointeeType)) { + FunctionDumper NestedDumper(Printer); + PointerType Pointer = + Symbol.isReference() ? PointerType::Reference : PointerType::Pointer; + NestedDumper.start(*FuncSig, nullptr, Pointer); + } else { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + PointeeType->dump(*this); + Printer << (Symbol.isReference() ? "&" : "*"); + } +} + +void FunctionDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyTypeDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyTypeDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyTypeDumper.h @@ -0,0 +1,36 @@ +//===- PrettyTypeDumper.h - PDBSymDumper implementation for types *- 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_LLVMPDBDUMP_PRETTYTYPEDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { +class LinePrinter; +class ClassLayout; + +class TypeDumper : public PDBSymDumper { +public: + TypeDumper(LinePrinter &P); + + void start(const PDBSymbolExe &Exe); + + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + + void dumpClassLayout(const ClassLayout &Class); + +private: + LinePrinter &Printer; +}; +} +} +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyTypeDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyTypeDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyTypeDumper.cpp @@ -0,0 +1,255 @@ +//===- PrettyTypeDumper.cpp - PDBSymDumper type dumper *------------ C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyTypeDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "PrettyClassDefinitionDumper.h" +#include "PrettyEnumDumper.h" +#include "PrettyTypedefDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +using LayoutPtr = std::unique_ptr; + +typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2); + +static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->getName() < S2->getName(); +} + +static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->getSize() < S2->getSize(); +} + +static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->deepPaddingSize() < S2->deepPaddingSize(); +} + +static bool ComparePaddingPct(const LayoutPtr &S1, const LayoutPtr &S2) { + double Pct1 = (double)S1->deepPaddingSize() / (double)S1->getSize(); + double Pct2 = (double)S2->deepPaddingSize() / (double)S2->getSize(); + return Pct1 < Pct2; +} + +static bool ComparePaddingImmediate(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->immediatePadding() < S2->immediatePadding(); +} + +static bool ComparePaddingPctImmediate(const LayoutPtr &S1, + const LayoutPtr &S2) { + double Pct1 = (double)S1->immediatePadding() / (double)S1->getSize(); + double Pct2 = (double)S2->immediatePadding() / (double)S2->getSize(); + return Pct1 < Pct2; +} + +static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) { + switch (Mode) { + case opts::pretty::ClassSortMode::Name: + return CompareNames; + case opts::pretty::ClassSortMode::Size: + return CompareSizes; + case opts::pretty::ClassSortMode::Padding: + return ComparePadding; + case opts::pretty::ClassSortMode::PaddingPct: + return ComparePaddingPct; + case opts::pretty::ClassSortMode::PaddingImmediate: + return ComparePaddingImmediate; + case opts::pretty::ClassSortMode::PaddingPctImmediate: + return ComparePaddingPctImmediate; + default: + return nullptr; + } +} + +template +static std::vector> +filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E, + uint32_t UnfilteredCount) { + std::vector> Filtered; + + Filtered.reserve(UnfilteredCount); + CompareFunc Comp = getComparisonFunc(opts::pretty::ClassOrder); + + if (UnfilteredCount > 10000) { + errs() << formatv("Filtering and sorting {0} types", UnfilteredCount); + errs().flush(); + } + uint32_t Examined = 0; + uint32_t Discarded = 0; + while (auto Class = E.getNext()) { + ++Examined; + if (Examined % 10000 == 0) { + errs() << formatv("Examined {0}/{1} items. {2} items discarded\n", + Examined, UnfilteredCount, Discarded); + errs().flush(); + } + + if (Class->getUnmodifiedTypeId() != 0) { + ++Discarded; + continue; + } + + if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) { + ++Discarded; + continue; + } + + auto Layout = llvm::make_unique(std::move(Class)); + if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) { + ++Discarded; + continue; + } + if (Layout->immediatePadding() < opts::pretty::ImmediatePaddingThreshold) { + ++Discarded; + continue; + } + + Filtered.push_back(std::move(Layout)); + } + + if (Comp) + std::sort(Filtered.begin(), Filtered.end(), Comp); + return Filtered; +} + +TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void TypeDumper::start(const PDBSymbolExe &Exe) { + auto Children = Exe.findAllChildren(); + if (opts::pretty::Enums) { + if (auto Enums = Exe.findAllChildren()) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums"; + Printer << ": (" << Enums->getChildCount() << " items)"; + Printer.Indent(); + while (auto Enum = Enums->getNext()) + Enum->dump(*this); + Printer.Unindent(); + } + } + + if (opts::pretty::Typedefs) { + if (auto Typedefs = Exe.findAllChildren()) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs"; + Printer << ": (" << Typedefs->getChildCount() << " items)"; + Printer.Indent(); + while (auto Typedef = Typedefs->getNext()) + Typedef->dump(*this); + Printer.Unindent(); + } + } + + if (opts::pretty::Classes) { + if (auto Classes = Exe.findAllChildren()) { + uint32_t All = Classes->getChildCount(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes"; + + bool Precompute = false; + Precompute = + (opts::pretty::ClassOrder != opts::pretty::ClassSortMode::None); + + // If we're using no sort mode, then we can start getting immediate output + // from the tool by just filtering as we go, rather than processing + // everything up front so that we can sort it. This makes the tool more + // responsive. So only precompute the filtered/sorted set of classes if + // necessary due to the specified options. + std::vector Filtered; + uint32_t Shown = All; + if (Precompute) { + Filtered = filterAndSortClassDefs(Printer, *Classes, All); + + Shown = Filtered.size(); + } + + Printer << ": (Showing " << Shown << " items"; + if (Shown < All) + Printer << ", " << (All - Shown) << " filtered"; + Printer << ")"; + Printer.Indent(); + + // If we pre-computed, iterate the filtered/sorted list, otherwise iterate + // the DIA enumerator and filter on the fly. + if (Precompute) { + for (auto &Class : Filtered) + dumpClassLayout(*Class); + } else { + while (auto Class = Classes->getNext()) { + if (Class->getUnmodifiedTypeId() != 0) + continue; + + if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) + continue; + + auto Layout = llvm::make_unique(std::move(Class)); + if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) + continue; + + dumpClassLayout(*Layout); + } + } + + Printer.Unindent(); + } + } +} + +void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { + assert(opts::pretty::Enums); + + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) + return; + // Dump member enums when dumping their class definition. + if (nullptr != Symbol.getClassParent()) + return; + + Printer.NewLine(); + EnumDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + assert(opts::pretty::Typedefs); + + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) + return; + + Printer.NewLine(); + TypedefDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypeDumper::dumpClassLayout(const ClassLayout &Class) { + assert(opts::pretty::Classes); + + if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getName(); + } else { + ClassDefinitionDumper Dumper(Printer); + Dumper.start(Class); + } +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyTypedefDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyTypedefDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyTypedefDumper.h @@ -0,0 +1,39 @@ +//===- PrettyTypedefDumper.h - llvm-pdbutil typedef dumper ---*- 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_LLVMPDBDUMP_PRETTYTYPEDEFDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDEFDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class TypedefDumper : public PDBSymDumper { +public: + TypedefDumper(LinePrinter &P); + + void start(const PDBSymbolTypeTypedef &Symbol); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionSig &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyTypedefDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyTypedefDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyTypedefDumper.cpp @@ -0,0 +1,77 @@ +//===- PrettyTypedefDumper.cpp - PDBSymDumper impl for typedefs -- * C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyTypedefDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "PrettyFunctionDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +using namespace llvm; +using namespace llvm::pdb; + +TypedefDumper::TypedefDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void TypedefDumper::start(const PDBSymbolTypeTypedef &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef "; + uint32_t TargetId = Symbol.getTypeId(); + if (auto TypeSymbol = Symbol.getSession().getSymbolById(TargetId)) + TypeSymbol->dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " + << Symbol.getName(); +} + +void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) {} + +void TypedefDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypedefDumper::dump(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Symbol.getName(); +} + +void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + auto PointeeType = Symbol.getPointeeType(); + if (auto FuncSig = unique_dyn_cast(PointeeType)) { + FunctionDumper::PointerType Pointer = FunctionDumper::PointerType::Pointer; + if (Symbol.isReference()) + Pointer = FunctionDumper::PointerType::Reference; + FunctionDumper NestedDumper(Printer); + NestedDumper.start(*FuncSig, nullptr, Pointer); + } else { + PointeeType->dump(*this); + Printer << ((Symbol.isReference()) ? "&" : "*"); + } +} + +void TypedefDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, nullptr, FunctionDumper::PointerType::None); +} + +void TypedefDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} Index: llvm/trunk/tools/llvm-pdbutil/PrettyVariableDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyVariableDumper.h +++ llvm/trunk/tools/llvm-pdbutil/PrettyVariableDumper.h @@ -0,0 +1,50 @@ +//===- PrettyVariableDumper.h - PDBSymDumper variable dumper ----*- 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_LLVMPDBDUMP_PRETTYVARIABLEDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYVARIABLEDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class StringRef; + +namespace pdb { + +class LinePrinter; + +class VariableDumper : public PDBSymDumper { +public: + VariableDumper(LinePrinter &P); + + void start(const PDBSymbolData &Var, uint32_t Offset = 0); + void start(const PDBSymbolTypeVTable &Var, uint32_t Offset = 0); + void startVbptr(uint32_t Offset, uint32_t Size); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionSig &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + + void dumpRight(const PDBSymbolTypeArray &Symbol) override; + void dumpRight(const PDBSymbolTypeFunctionSig &Symbol) override; + void dumpRight(const PDBSymbolTypePointer &Symbol) override; + +private: + void dumpSymbolTypeAndName(const PDBSymbol &Type, StringRef Name); + + LinePrinter &Printer; +}; +} +} +#endif Index: llvm/trunk/tools/llvm-pdbutil/PrettyVariableDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/PrettyVariableDumper.cpp +++ llvm/trunk/tools/llvm-pdbutil/PrettyVariableDumper.cpp @@ -0,0 +1,220 @@ +//===- PrettyVariableDumper.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyVariableDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "PrettyFunctionDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +VariableDumper::VariableDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void VariableDumper::start(const PDBSymbolData &Var, uint32_t Offset) { + if (Var.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated) + return; + if (Printer.IsSymbolExcluded(Var.getName())) + return; + + auto VarType = Var.getType(); + + uint64_t Length = VarType->getRawSymbol().getLength(); + + switch (auto LocType = Var.getLocationType()) { + case PDB_LocType::Static: + Printer.NewLine(); + Printer << "data ["; + WithColor(Printer, PDB_ColorItem::Address).get() + << format_hex(Var.getVirtualAddress(), 10); + Printer << ", sizeof=" << Length << "] "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << "static "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + break; + case PDB_LocType::Constant: + if (isa(*VarType)) + break; + Printer.NewLine(); + Printer << "data [sizeof=" << Length << "] "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + Printer << " = "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getValue(); + break; + case PDB_LocType::ThisRel: + Printer.NewLine(); + Printer << "data "; + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << Length << "] "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + break; + case PDB_LocType::BitField: + Printer.NewLine(); + Printer << "data "; + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << Length << "] "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + Printer << " : "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getLength(); + break; + default: + Printer.NewLine(); + Printer << "data [sizeof=" << Length << "] "; + Printer << "unknown(" << LocType << ") "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Var.getName(); + break; + } +} + +void VariableDumper::startVbptr(uint32_t Offset, uint32_t Size) { + Printer.NewLine(); + Printer << "vbptr "; + + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Offset, 4) << " [sizeof=" << Size << "] "; +} + +void VariableDumper::start(const PDBSymbolTypeVTable &Var, uint32_t Offset) { + Printer.NewLine(); + Printer << "vfptr "; + auto VTableType = cast(Var.getType()); + uint32_t PointerSize = VTableType->getLength(); + + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Offset + Var.getOffset(), 4) + << " [sizeof=" << PointerSize << "] "; +} + +void VariableDumper::dump(const PDBSymbolTypeArray &Symbol) { + auto ElementType = Symbol.getElementType(); + assert(ElementType); + if (!ElementType) + return; + ElementType->dump(*this); +} + +void VariableDumper::dumpRight(const PDBSymbolTypeArray &Symbol) { + auto ElementType = Symbol.getElementType(); + assert(ElementType); + if (!ElementType) + return; + Printer << '[' << Symbol.getCount() << ']'; + ElementType->dumpRight(*this); +} + +void VariableDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void VariableDumper::dump(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { + auto ReturnType = Symbol.getReturnType(); + ReturnType->dump(*this); + Printer << " "; + + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().getConcreteSymbolById( + ClassParentId); + + if (ClassParent) { + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::"; + } +} + +void VariableDumper::dumpRight(const PDBSymbolTypeFunctionSig &Symbol) { + Printer << "("; + if (auto Arguments = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = Arguments->getNext()) { + Arg->dump(*this); + if (++Index < Arguments->getChildCount()) + Printer << ", "; + } + } + Printer << ")"; + + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const"; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile"; +} + +void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) { + auto PointeeType = Symbol.getPointeeType(); + if (!PointeeType) + return; + PointeeType->dump(*this); + if (auto FuncSig = unique_dyn_cast(PointeeType)) { + // A hack to get the calling convention in the right spot. + Printer << " ("; + PDB_CallingConv CC = FuncSig->getCallingConvention(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + } else if (isa(PointeeType)) { + Printer << " ("; + } + Printer << (Symbol.isReference() ? "&" : "*"); + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile "; +} + +void VariableDumper::dumpRight(const PDBSymbolTypePointer &Symbol) { + auto PointeeType = Symbol.getPointeeType(); + assert(PointeeType); + if (!PointeeType) + return; + if (isa(PointeeType) || + isa(PointeeType)) { + Printer << ")"; + } + PointeeType->dumpRight(*this); +} + +void VariableDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dumpSymbolTypeAndName(const PDBSymbol &Type, + StringRef Name) { + Type.dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name; + Type.dumpRight(*this); +} Index: llvm/trunk/tools/llvm-pdbutil/StreamUtil.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/StreamUtil.h +++ llvm/trunk/tools/llvm-pdbutil/StreamUtil.h @@ -0,0 +1,25 @@ +//===- Streamutil.h - PDB stream utilities ----------------------*- 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_LLVMPDBDUMP_STREAMUTIL_H +#define LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H + +#include "llvm/ADT/SmallVector.h" + +#include + +namespace llvm { +namespace pdb { +class PDBFile; +void discoverStreamPurposes(PDBFile &File, + SmallVectorImpl &Purposes); +} +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/StreamUtil.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/StreamUtil.cpp +++ llvm/trunk/tools/llvm-pdbutil/StreamUtil.cpp @@ -0,0 +1,139 @@ +//===- StreamUtil.cpp - PDB stream utilities --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StreamUtil.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +namespace llvm { +namespace pdb { +void discoverStreamPurposes(PDBFile &File, + SmallVectorImpl &Purposes) { + + // It's OK if we fail to load some of these streams, we still attempt to print + // what we can. + auto Dbi = File.getPDBDbiStream(); + auto Tpi = File.getPDBTpiStream(); + auto Ipi = File.getPDBIpiStream(); + auto Info = File.getPDBInfoStream(); + + uint32_t StreamCount = File.getNumStreams(); + DenseMap ModStreams; + DenseMap NamedStreams; + + if (Dbi) { + const DbiModuleList &Modules = Dbi->modules(); + for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { + DbiModuleDescriptor Descriptor = Modules.getModuleDescriptor(I); + uint16_t SN = Descriptor.getModuleStreamIndex(); + if (SN != kInvalidStreamIndex) + ModStreams[SN] = Descriptor; + } + } + if (Info) { + for (auto &NSE : Info->named_streams()) { + if (NSE.second != kInvalidStreamIndex) + NamedStreams[NSE.second] = NSE.first(); + } + } + + Purposes.resize(StreamCount); + for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { + std::string Value; + if (StreamIdx == OldMSFDirectory) + Value = "Old MSF Directory"; + else if (StreamIdx == StreamPDB) + Value = "PDB Stream"; + else if (StreamIdx == StreamDBI) + Value = "DBI Stream"; + else if (StreamIdx == StreamTPI) + Value = "TPI Stream"; + else if (StreamIdx == StreamIPI) + Value = "IPI Stream"; + else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) + Value = "Global Symbol Hash"; + else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) + Value = "Public Symbol Hash"; + else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) + Value = "Public Symbol Records"; + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) + Value = "TPI Hash"; + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) + Value = "TPI Aux Hash"; + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) + Value = "IPI Hash"; + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) + Value = "IPI Aux Hash"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) + Value = "Exception Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) + Value = "Fixup Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) + Value = "FPO Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) + Value = "New FPO Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) + Value = "Omap From Source Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) + Value = "Omap To Source Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) + Value = "Pdata"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) + Value = "Section Header Data"; + else if (Dbi && + StreamIdx == + Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) + Value = "Section Header Original Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) + Value = "Token Rid Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) + Value = "Xdata"; + else { + auto ModIter = ModStreams.find(StreamIdx); + auto NSIter = NamedStreams.find(StreamIdx); + if (ModIter != ModStreams.end()) { + Value = "Module \""; + Value += ModIter->second.getModuleName(); + Value += "\""; + } else if (NSIter != NamedStreams.end()) { + Value = "Named Stream \""; + Value += NSIter->second; + Value += "\""; + } else { + Value = "???"; + } + } + Purposes[StreamIdx] = Value; + } + + // Consume errors from missing streams. + if (!Dbi) + consumeError(Dbi.takeError()); + if (!Tpi) + consumeError(Tpi.takeError()); + if (!Ipi) + consumeError(Ipi.takeError()); + if (!Info) + consumeError(Info.takeError()); +} +} +} Index: llvm/trunk/tools/llvm-pdbutil/YAMLOutputStyle.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/YAMLOutputStyle.h +++ llvm/trunk/tools/llvm-pdbutil/YAMLOutputStyle.h @@ -0,0 +1,49 @@ +//===- YAMLOutputStyle.h -------------------------------------- *- 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_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H + +#include "OutputStyle.h" +#include "PdbYaml.h" + +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { +namespace pdb { +class ModuleDebugStreamRef; + +class YAMLOutputStyle : public OutputStyle { +public: + YAMLOutputStyle(PDBFile &File); + + Error dump() override; + +private: + Error dumpStringTable(); + Error dumpFileHeaders(); + Error dumpStreamMetadata(); + Error dumpStreamDirectory(); + Error dumpPDBStream(); + Error dumpDbiStream(); + Error dumpTpiStream(); + Error dumpIpiStream(); + + void flush(); + + PDBFile &File; + llvm::yaml::Output Out; + + yaml::PdbObject Obj; +}; +} // namespace pdb +} // namespace llvm + +#endif // LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H Index: llvm/trunk/tools/llvm-pdbutil/YAMLOutputStyle.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/YAMLOutputStyle.cpp +++ llvm/trunk/tools/llvm-pdbutil/YAMLOutputStyle.cpp @@ -0,0 +1,317 @@ +//===- YAMLOutputStyle.cpp ------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "YAMLOutputStyle.h" + +#include "PdbYaml.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) + : File(File), Out(outs()), Obj(File.getAllocator()) { + Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal); +} + +Error YAMLOutputStyle::dump() { + if (opts::pdb2yaml::StreamDirectory) + opts::pdb2yaml::StreamMetadata = true; + + if (auto EC = dumpFileHeaders()) + return EC; + + if (auto EC = dumpStreamMetadata()) + return EC; + + if (auto EC = dumpStreamDirectory()) + return EC; + + if (auto EC = dumpStringTable()) + return EC; + + if (auto EC = dumpPDBStream()) + return EC; + + if (auto EC = dumpDbiStream()) + return EC; + + if (auto EC = dumpTpiStream()) + return EC; + + if (auto EC = dumpIpiStream()) + return EC; + + flush(); + return Error::success(); +} + + +Error YAMLOutputStyle::dumpFileHeaders() { + if (opts::pdb2yaml::NoFileHeaders) + return Error::success(); + + yaml::MSFHeaders Headers; + Obj.Headers.emplace(); + Obj.Headers->SuperBlock.NumBlocks = File.getBlockCount(); + Obj.Headers->SuperBlock.BlockMapAddr = File.getBlockMapIndex(); + Obj.Headers->SuperBlock.BlockSize = File.getBlockSize(); + auto Blocks = File.getDirectoryBlockArray(); + Obj.Headers->DirectoryBlocks.assign(Blocks.begin(), Blocks.end()); + Obj.Headers->NumDirectoryBlocks = File.getNumDirectoryBlocks(); + Obj.Headers->SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes(); + Obj.Headers->NumStreams = + opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0; + Obj.Headers->SuperBlock.FreeBlockMapBlock = File.getFreeBlockMapBlock(); + Obj.Headers->SuperBlock.Unknown1 = File.getUnknown1(); + Obj.Headers->FileSize = File.getFileSize(); + + return Error::success(); +} + +Error YAMLOutputStyle::dumpStringTable() { + bool RequiresStringTable = opts::shared::DumpModuleFiles || + !opts::shared::DumpModuleSubsections.empty(); + bool RequestedStringTable = opts::pdb2yaml::StringTable; + if (!RequiresStringTable && !RequestedStringTable) + return Error::success(); + + auto ExpectedST = File.getStringTable(); + if (!ExpectedST) + return ExpectedST.takeError(); + + Obj.StringTable.emplace(); + const auto &ST = ExpectedST.get(); + for (auto ID : ST.name_ids()) { + auto S = ST.getStringForID(ID); + if (!S) + return S.takeError(); + if (S->empty()) + continue; + Obj.StringTable->push_back(*S); + } + return Error::success(); +} + +Error YAMLOutputStyle::dumpStreamMetadata() { + if (!opts::pdb2yaml::StreamMetadata) + return Error::success(); + + Obj.StreamSizes.emplace(); + Obj.StreamSizes->assign(File.getStreamSizes().begin(), + File.getStreamSizes().end()); + return Error::success(); +} + +Error YAMLOutputStyle::dumpStreamDirectory() { + if (!opts::pdb2yaml::StreamDirectory) + return Error::success(); + + auto StreamMap = File.getStreamMap(); + Obj.StreamMap.emplace(); + for (auto &Stream : StreamMap) { + pdb::yaml::StreamBlockList BlockList; + BlockList.Blocks.assign(Stream.begin(), Stream.end()); + Obj.StreamMap->push_back(BlockList); + } + + return Error::success(); +} + +Error YAMLOutputStyle::dumpPDBStream() { + if (!opts::pdb2yaml::PdbStream) + return Error::success(); + + auto IS = File.getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + auto &InfoS = IS.get(); + Obj.PdbStream.emplace(); + Obj.PdbStream->Age = InfoS.getAge(); + Obj.PdbStream->Guid = InfoS.getGuid(); + Obj.PdbStream->Signature = InfoS.getSignature(); + Obj.PdbStream->Version = InfoS.getVersion(); + Obj.PdbStream->Features = InfoS.getFeatureSignatures(); + + return Error::success(); +} + +static opts::ModuleSubsection convertSubsectionKind(DebugSubsectionKind K) { + switch (K) { + case DebugSubsectionKind::CrossScopeExports: + return opts::ModuleSubsection::CrossScopeExports; + case DebugSubsectionKind::CrossScopeImports: + return opts::ModuleSubsection::CrossScopeImports; + case DebugSubsectionKind::FileChecksums: + return opts::ModuleSubsection::FileChecksums; + case DebugSubsectionKind::InlineeLines: + return opts::ModuleSubsection::InlineeLines; + case DebugSubsectionKind::Lines: + return opts::ModuleSubsection::Lines; + case DebugSubsectionKind::Symbols: + return opts::ModuleSubsection::Symbols; + case DebugSubsectionKind::StringTable: + return opts::ModuleSubsection::StringTable; + case DebugSubsectionKind::FrameData: + return opts::ModuleSubsection::FrameData; + default: + return opts::ModuleSubsection::Unknown; + } + llvm_unreachable("Unreachable!"); +} + +Error YAMLOutputStyle::dumpDbiStream() { + if (!opts::pdb2yaml::DbiStream) + return Error::success(); + + auto DbiS = File.getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto &DS = DbiS.get(); + Obj.DbiStream.emplace(); + Obj.DbiStream->Age = DS.getAge(); + Obj.DbiStream->BuildNumber = DS.getBuildNumber(); + Obj.DbiStream->Flags = DS.getFlags(); + Obj.DbiStream->MachineType = DS.getMachineType(); + Obj.DbiStream->PdbDllRbld = DS.getPdbDllRbld(); + Obj.DbiStream->PdbDllVersion = DS.getPdbDllVersion(); + Obj.DbiStream->VerHeader = DS.getDbiVersion(); + if (opts::shared::DumpModules) { + const auto &Modules = DS.modules(); + for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { + DbiModuleDescriptor MI = Modules.getModuleDescriptor(I); + + Obj.DbiStream->ModInfos.emplace_back(); + yaml::PdbDbiModuleInfo &DMI = Obj.DbiStream->ModInfos.back(); + + DMI.Mod = MI.getModuleName(); + DMI.Obj = MI.getObjFileName(); + if (opts::shared::DumpModuleFiles) { + auto Files = Modules.source_files(I); + DMI.SourceFiles.assign(Files.begin(), Files.end()); + } + + uint16_t ModiStream = MI.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) + continue; + + auto ModStreamData = msf::MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), ModiStream, + File.getAllocator()); + + pdb::ModuleDebugStreamRef ModS(MI, std::move(ModStreamData)); + if (auto EC = ModS.reload()) + return EC; + + auto ExpectedST = File.getStringTable(); + if (!ExpectedST) + return ExpectedST.takeError(); + if (!opts::shared::DumpModuleSubsections.empty() && + ModS.hasDebugSubsections()) { + auto ExpectedChecksums = ModS.findChecksumsSubsection(); + if (!ExpectedChecksums) + return ExpectedChecksums.takeError(); + + for (const auto &SS : ModS.subsections()) { + opts::ModuleSubsection OptionKind = convertSubsectionKind(SS.kind()); + if (!opts::checkModuleSubsection(OptionKind)) + continue; + + auto Converted = + CodeViewYAML::YAMLDebugSubsection::fromCodeViewSubection( + ExpectedST->getStringTable(), *ExpectedChecksums, SS); + if (!Converted) + return Converted.takeError(); + DMI.Subsections.push_back(*Converted); + } + } + + if (opts::shared::DumpModuleSyms) { + DMI.Modi.emplace(); + + DMI.Modi->Signature = ModS.signature(); + bool HadError = false; + for (auto &Sym : ModS.symbols(&HadError)) { + auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(Sym); + if (!ES) + return ES.takeError(); + + DMI.Modi->Symbols.push_back(*ES); + } + } + } + } + return Error::success(); +} + +Error YAMLOutputStyle::dumpTpiStream() { + if (!opts::pdb2yaml::TpiStream) + return Error::success(); + + auto TpiS = File.getPDBTpiStream(); + if (!TpiS) + return TpiS.takeError(); + + auto &TS = TpiS.get(); + Obj.TpiStream.emplace(); + Obj.TpiStream->Version = TS.getTpiVersion(); + for (auto &Record : TS.types(nullptr)) { + auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record); + if (!ExpectedRecord) + return ExpectedRecord.takeError(); + Obj.TpiStream->Records.push_back(*ExpectedRecord); + } + + return Error::success(); +} + +Error YAMLOutputStyle::dumpIpiStream() { + if (!opts::pdb2yaml::IpiStream) + return Error::success(); + + auto IpiS = File.getPDBIpiStream(); + if (!IpiS) + return IpiS.takeError(); + + auto &IS = IpiS.get(); + Obj.IpiStream.emplace(); + Obj.IpiStream->Version = IS.getTpiVersion(); + for (auto &Record : IS.types(nullptr)) { + auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record); + if (!ExpectedRecord) + return ExpectedRecord.takeError(); + + Obj.IpiStream->Records.push_back(*ExpectedRecord); + } + + return Error::success(); +} + +void YAMLOutputStyle::flush() { + Out << Obj; + outs().flush(); +} Index: llvm/trunk/tools/llvm-pdbutil/fuzzer/CMakeLists.txt =================================================================== --- llvm/trunk/tools/llvm-pdbutil/fuzzer/CMakeLists.txt +++ llvm/trunk/tools/llvm-pdbutil/fuzzer/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS + DebugInfoCodeView + DebugInfoPDB + Object + Support + ) + +add_llvm_executable(llvm-pdbutil-fuzzer + EXCLUDE_FROM_ALL + llvm-pdbutil-fuzzer.cpp + ) + +target_link_libraries(llvm-pdbutil-fuzzer + LLVMFuzzer + ) Index: llvm/trunk/tools/llvm-pdbutil/fuzzer/llvm-pdbutil-fuzzer.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/fuzzer/llvm-pdbutil-fuzzer.cpp +++ llvm/trunk/tools/llvm-pdbutil/fuzzer/llvm-pdbutil-fuzzer.cpp @@ -0,0 +1,105 @@ +//===-- llvm-pdbutil-fuzzer.cpp - Fuzz the llvm-pdbutil tool --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements a function that runs llvm-pdbutil +/// on a single input. This function is then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/BinaryByteStream.h" +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/DebugInfo/CodeView/TypeDumper.h" +#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" +#include "llvm/DebugInfo/PDB/Raw/IPDBStreamData.h" +#include "llvm/DebugInfo/PDB/Raw/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Raw/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/RawSession.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; + +namespace { +// We need a class which behaves like an immutable BinaryByteStream, but whose +// data +// is backed by an llvm::MemoryBuffer. It also needs to own the underlying +// MemoryBuffer, so this simple adapter is a good way to achieve that. +class InputByteStream : public codeview::BinaryByteStream { +public: + explicit InputByteStream(std::unique_ptr Buffer) + : BinaryByteStream(ArrayRef(Buffer->getBuffer().bytes_begin(), + Buffer->getBuffer().bytes_end())), + MemBuffer(std::move(Buffer)) {} + + std::unique_ptr MemBuffer; +}; +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + std::unique_ptr Buff = MemoryBuffer::getMemBuffer( + StringRef((const char *)data, size), "", false); + + ScopedPrinter P(nulls()); + codeview::CVTypeDumper TD(&P, false); + + auto InputStream = llvm::make_unique(std::move(Buff)); + std::unique_ptr File(new pdb::PDBFile(std::move(InputStream))); + if (auto E = File->parseFileHeaders()) { + consumeError(std::move(E)); + return 0; + } + if (auto E = File->parseStreamData()) { + consumeError(std::move(E)); + return 0; + } + + auto DbiS = File->getPDBDbiStream(); + if (auto E = DbiS.takeError()) { + consumeError(std::move(E)); + return 0; + } + auto TpiS = File->getPDBTpiStream(); + if (auto E = TpiS.takeError()) { + consumeError(std::move(E)); + return 0; + } + auto IpiS = File->getPDBIpiStream(); + if (auto E = IpiS.takeError()) { + consumeError(std::move(E)); + return 0; + } + auto InfoS = File->getPDBInfoStream(); + if (auto E = InfoS.takeError()) { + consumeError(std::move(E)); + return 0; + } + pdb::DbiStream &DS = DbiS.get(); + + for (auto &Modi : DS.modules()) { + auto ModStreamData = pdb::MappedBlockStream::createIndexedStream( + Modi.Info.getModuleStreamIndex(), *File, File->getAllocator()); + if (!ModStreamData) { + consumeError(ModStreamData.takeError()); + return 0; + } + pdb::ModuleDebugStreamRef ModS(Modi.Info, std::move(*ModStreamData)); + if (auto E = ModS.reload()) { + consumeError(std::move(E)); + return 0; + } + codeview::CVSymbolDumper SD(P, TD, nullptr, false); + bool HadError = false; + for (auto &S : ModS.symbols(&HadError)) { + SD.dump(S); + } + } + return 0; +} Index: llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h =================================================================== --- llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h +++ llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.h @@ -0,0 +1,156 @@ +//===- llvm-pdbutil.h ----------------------------------------- *- 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_LLVMPDBDUMP_LLVMPDBDUMP_H +#define LLVM_TOOLS_LLVMPDBDUMP_LLVMPDBDUMP_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +namespace llvm { +namespace pdb { +class PDBSymbolData; +class PDBSymbolFunc; +uint32_t getTypeLength(const PDBSymbolData &Symbol); +} +} + +namespace opts { + +enum class ModuleSubsection { + Unknown, + Lines, + FileChecksums, + InlineeLines, + CrossScopeImports, + CrossScopeExports, + StringTable, + Symbols, + FrameData, + All +}; + +bool checkModuleSubsection(ModuleSubsection Kind); + +template +bool checkModuleSubsection(ModuleSubsection K1, ModuleSubsection K2, + Ts &&... Rest) { + return checkModuleSubsection(K1) || + checkModuleSubsection(K2, std::forward(Rest)...); +} + +namespace pretty { + +enum class ClassDefinitionFormat { None, Layout, All }; +enum class ClassSortMode { + None, + Name, + Size, + Padding, + PaddingPct, + PaddingImmediate, + PaddingPctImmediate +}; + +enum class SymbolSortMode { None, Name, Size }; + +enum class SymLevel { Functions, Data, Thunks, All }; + +bool shouldDumpSymLevel(SymLevel Level); +bool compareFunctionSymbols( + const std::unique_ptr &F1, + const std::unique_ptr &F2); +bool compareDataSymbols(const std::unique_ptr &F1, + const std::unique_ptr &F2); + +extern llvm::cl::opt Compilands; +extern llvm::cl::opt Symbols; +extern llvm::cl::opt Globals; +extern llvm::cl::opt Classes; +extern llvm::cl::opt Enums; +extern llvm::cl::opt Typedefs; +extern llvm::cl::opt All; +extern llvm::cl::opt ExcludeCompilerGenerated; + +extern llvm::cl::opt NoEnumDefs; +extern llvm::cl::list ExcludeTypes; +extern llvm::cl::list ExcludeSymbols; +extern llvm::cl::list ExcludeCompilands; +extern llvm::cl::list IncludeTypes; +extern llvm::cl::list IncludeSymbols; +extern llvm::cl::list IncludeCompilands; +extern llvm::cl::opt SymbolOrder; +extern llvm::cl::opt ClassOrder; +extern llvm::cl::opt SizeThreshold; +extern llvm::cl::opt PaddingThreshold; +extern llvm::cl::opt ImmediatePaddingThreshold; +extern llvm::cl::opt ClassFormat; +extern llvm::cl::opt ClassRecursionDepth; +} + +namespace raw { +struct BlockRange { + uint32_t Min; + llvm::Optional Max; +}; + +extern llvm::Optional DumpBlockRange; +extern llvm::cl::list DumpStreamData; + +extern llvm::cl::opt CompactRecords; +extern llvm::cl::opt DumpGlobals; +extern llvm::cl::opt DumpHeaders; +extern llvm::cl::opt DumpStreamBlocks; +extern llvm::cl::opt DumpStreamSummary; +extern llvm::cl::opt DumpPageStats; +extern llvm::cl::opt DumpTpiHash; +extern llvm::cl::opt DumpTpiRecordBytes; +extern llvm::cl::opt DumpTpiRecords; +extern llvm::cl::opt DumpIpiRecords; +extern llvm::cl::opt DumpIpiRecordBytes; +extern llvm::cl::opt DumpPublics; +extern llvm::cl::opt DumpSectionContribs; +extern llvm::cl::opt DumpSectionMap; +extern llvm::cl::opt DumpSymRecordBytes; +extern llvm::cl::opt DumpSectionHeaders; +extern llvm::cl::opt DumpFpo; +extern llvm::cl::opt DumpStringTable; +} + +namespace diff { +extern llvm::cl::opt Pedantic; +} + +namespace pdb2yaml { +extern llvm::cl::opt All; +extern llvm::cl::opt NoFileHeaders; +extern llvm::cl::opt Minimal; +extern llvm::cl::opt StreamMetadata; +extern llvm::cl::opt StreamDirectory; +extern llvm::cl::opt StringTable; +extern llvm::cl::opt PdbStream; +extern llvm::cl::opt DbiStream; +extern llvm::cl::opt TpiStream; +extern llvm::cl::opt IpiStream; +extern llvm::cl::list InputFilename; +} + +namespace shared { +extern llvm::cl::opt DumpModules; +extern llvm::cl::opt DumpModuleFiles; +extern llvm::cl::list DumpModuleSubsections; +extern llvm::cl::opt DumpModuleSyms; +} // namespace shared +} + +#endif Index: llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ llvm/trunk/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -0,0 +1,1023 @@ +//===- llvm-pdbutil.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. +// +//===----------------------------------------------------------------------===// + +#include "llvm-pdbutil.h" + +#include "Analyze.h" +#include "Diff.h" +#include "LLVMOutputStyle.h" +#include "LinePrinter.h" +#include "OutputStyle.h" +#include "PrettyCompilandDumper.h" +#include "PrettyExternalSymbolDumper.h" +#include "PrettyFunctionDumper.h" +#include "PrettyTypeDumper.h" +#include "PrettyVariableDumper.h" +#include "YAMLOutputStyle.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace opts { + +cl::SubCommand RawSubcommand("raw", "Dump raw structure of the PDB file"); +cl::SubCommand + PrettySubcommand("pretty", + "Dump semantic information about types and symbols"); + +cl::SubCommand DiffSubcommand("diff", "Diff the contents of 2 PDB files"); + +cl::SubCommand + YamlToPdbSubcommand("yaml2pdb", + "Generate a PDB file from a YAML description"); +cl::SubCommand + PdbToYamlSubcommand("pdb2yaml", + "Generate a detailed YAML description of a PDB File"); + +cl::SubCommand + AnalyzeSubcommand("analyze", + "Analyze various aspects of a PDB's structure"); + +cl::SubCommand MergeSubcommand("merge", + "Merge multiple PDBs into a single PDB"); + +cl::OptionCategory TypeCategory("Symbol Type Options"); +cl::OptionCategory FilterCategory("Filtering and Sorting Options"); +cl::OptionCategory OtherOptions("Other Options"); + +namespace pretty { +cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::OneOrMore, cl::sub(PrettySubcommand)); + +cl::opt Compilands("compilands", cl::desc("Display compilands"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt Symbols("module-syms", + cl::desc("Display symbols for each compiland"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt Globals("globals", cl::desc("Dump global symbols"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt Externals("externals", cl::desc("Dump external symbols"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::list SymTypes( + "sym-types", cl::desc("Type of symbols to dump (default all)"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand), cl::ZeroOrMore, + cl::values( + clEnumValN(SymLevel::Thunks, "thunks", "Display thunk symbols"), + clEnumValN(SymLevel::Data, "data", "Display data symbols"), + clEnumValN(SymLevel::Functions, "funcs", "Display function symbols"), + clEnumValN(SymLevel::All, "all", "Display all symbols (default)"))); + +cl::opt + Types("types", + cl::desc("Display all types (implies -classes, -enums, -typedefs)"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt Classes("classes", cl::desc("Display class types"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt Enums("enums", cl::desc("Display enum types"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt Typedefs("typedefs", cl::desc("Display typedef types"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt SymbolOrder( + "symbol-order", cl::desc("symbol sort order"), + cl::init(SymbolSortMode::None), + cl::values(clEnumValN(SymbolSortMode::None, "none", + "Undefined / no particular sort order"), + clEnumValN(SymbolSortMode::Name, "name", "Sort symbols by name"), + clEnumValN(SymbolSortMode::Size, "size", + "Sort symbols by size")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +cl::opt ClassOrder( + "class-order", cl::desc("Class sort order"), cl::init(ClassSortMode::None), + cl::values( + clEnumValN(ClassSortMode::None, "none", + "Undefined / no particular sort order"), + clEnumValN(ClassSortMode::Name, "name", "Sort classes by name"), + clEnumValN(ClassSortMode::Size, "size", "Sort classes by size"), + clEnumValN(ClassSortMode::Padding, "padding", + "Sort classes by amount of padding"), + clEnumValN(ClassSortMode::PaddingPct, "padding-pct", + "Sort classes by percentage of space consumed by padding"), + clEnumValN(ClassSortMode::PaddingImmediate, "padding-imm", + "Sort classes by amount of immediate padding"), + clEnumValN(ClassSortMode::PaddingPctImmediate, "padding-pct-imm", + "Sort classes by percentage of space consumed by immediate " + "padding")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +cl::opt ClassFormat( + "class-definitions", cl::desc("Class definition format"), + cl::init(ClassDefinitionFormat::All), + cl::values( + clEnumValN(ClassDefinitionFormat::All, "all", + "Display all class members including data, constants, " + "typedefs, functions, etc"), + clEnumValN(ClassDefinitionFormat::Layout, "layout", + "Only display members that contribute to class size."), + clEnumValN(ClassDefinitionFormat::None, "none", + "Don't display class definitions")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt ClassRecursionDepth( + "class-recurse-depth", cl::desc("Class recursion depth (0=no limit)"), + cl::init(0), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +cl::opt Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory), + cl::sub(PrettySubcommand)); +cl::opt + All("all", cl::desc("Implies all other options in 'Symbol Types' category"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +cl::opt LoadAddress( + "load-address", + cl::desc("Assume the module is loaded at the specified address"), + cl::cat(OtherOptions), cl::sub(PrettySubcommand)); +cl::opt Native("native", cl::desc("Use native PDB reader instead of DIA"), + cl::cat(OtherOptions), cl::sub(PrettySubcommand)); +cl::opt + ColorOutput("color-output", + cl::desc("Override use of color (default = isatty)"), + cl::cat(OtherOptions), cl::sub(PrettySubcommand)); +cl::list ExcludeTypes( + "exclude-types", cl::desc("Exclude types by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::list ExcludeSymbols( + "exclude-symbols", cl::desc("Exclude symbols by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::list ExcludeCompilands( + "exclude-compilands", cl::desc("Exclude compilands by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); + +cl::list IncludeTypes( + "include-types", + cl::desc("Include only types which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::list IncludeSymbols( + "include-symbols", + cl::desc("Include only symbols which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::list IncludeCompilands( + "include-compilands", + cl::desc("Include only compilands those which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt SizeThreshold( + "min-type-size", cl::desc("Displays only those types which are greater " + "than or equal to the specified size."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt PaddingThreshold( + "min-class-padding", cl::desc("Displays only those classes which have at " + "least the specified amount of padding."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt ImmediatePaddingThreshold( + "min-class-padding-imm", + cl::desc("Displays only those classes which have at least the specified " + "amount of immediate padding, ignoring padding internal to bases " + "and aggregates."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); + +cl::opt ExcludeCompilerGenerated( + "no-compiler-generated", + cl::desc("Don't show compiler generated types and symbols"), + cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt + ExcludeSystemLibraries("no-system-libs", + cl::desc("Don't show symbols from system libraries"), + cl::cat(FilterCategory), cl::sub(PrettySubcommand)); + +cl::opt NoEnumDefs("no-enum-definitions", + cl::desc("Don't display full enum definitions"), + cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +} + +namespace diff { +cl::opt Pedantic("pedantic", + cl::desc("Finds all differences (even structural ones " + "that produce otherwise identical PDBs)"), + cl::sub(DiffSubcommand)); + +cl::list InputFilenames(cl::Positional, + cl::desc(" "), + cl::OneOrMore, cl::sub(DiffSubcommand)); +} + +namespace raw { + +cl::OptionCategory MsfOptions("MSF Container Options"); +cl::OptionCategory TypeOptions("Type Record Options"); +cl::OptionCategory SymbolOptions("Symbol Options"); +cl::OptionCategory MiscOptions("Miscellaneous Options"); + +// MSF OPTIONS +cl::opt DumpHeaders("headers", cl::desc("dump PDB headers"), + cl::cat(MsfOptions), cl::sub(RawSubcommand)); +cl::opt DumpStreamBlocks("stream-blocks", + cl::desc("dump PDB stream blocks"), + cl::cat(MsfOptions), cl::sub(RawSubcommand)); +cl::opt DumpStreamSummary("stream-summary", + cl::desc("dump summary of the PDB streams"), + cl::cat(MsfOptions), cl::sub(RawSubcommand)); +cl::opt DumpPageStats( + "page-stats", + cl::desc("dump allocation stats of the pages in the MSF file"), + cl::cat(MsfOptions), cl::sub(RawSubcommand)); +cl::opt + DumpBlockRangeOpt("block-data", cl::value_desc("start[-end]"), + cl::desc("Dump binary data from specified range."), + cl::cat(MsfOptions), cl::sub(RawSubcommand)); +llvm::Optional DumpBlockRange; + +cl::list + DumpStreamData("stream-data", cl::CommaSeparated, cl::ZeroOrMore, + cl::desc("Dump binary data from specified streams. Format " + "is SN[:Start][@Size]"), + cl::cat(MsfOptions), cl::sub(RawSubcommand)); + +// TYPE OPTIONS +cl::opt + CompactRecords("compact-records", + cl::desc("Dump type and symbol records with less detail"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); + +cl::opt + DumpTpiRecords("tpi-records", + cl::desc("dump CodeView type records from TPI stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); +cl::opt DumpTpiRecordBytes( + "tpi-record-bytes", + cl::desc("dump CodeView type record raw bytes from TPI stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); +cl::opt DumpTpiHash("tpi-hash", cl::desc("dump CodeView TPI hash stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); +cl::opt + DumpIpiRecords("ipi-records", + cl::desc("dump CodeView type records from IPI stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); +cl::opt DumpIpiRecordBytes( + "ipi-record-bytes", + cl::desc("dump CodeView type record raw bytes from IPI stream"), + cl::cat(TypeOptions), cl::sub(RawSubcommand)); + +// SYMBOL OPTIONS +cl::opt DumpGlobals("globals", cl::desc("dump globals stream data"), + cl::cat(SymbolOptions), cl::sub(RawSubcommand)); +cl::opt DumpPublics("publics", cl::desc("dump Publics stream data"), + cl::cat(SymbolOptions), cl::sub(RawSubcommand)); +cl::opt + DumpSymRecordBytes("sym-record-bytes", + cl::desc("dump CodeView symbol record raw bytes"), + cl::cat(SymbolOptions), cl::sub(RawSubcommand)); + +// MISCELLANEOUS OPTIONS +cl::opt DumpStringTable("string-table", cl::desc("dump PDB String Table"), + cl::cat(MiscOptions), cl::sub(RawSubcommand)); + +cl::opt DumpSectionContribs("section-contribs", + cl::desc("dump section contributions"), + cl::cat(MiscOptions), cl::sub(RawSubcommand)); +cl::opt DumpSectionMap("section-map", cl::desc("dump section map"), + cl::cat(MiscOptions), cl::sub(RawSubcommand)); +cl::opt DumpSectionHeaders("section-headers", + cl::desc("dump section headers"), + cl::cat(MiscOptions), cl::sub(RawSubcommand)); +cl::opt DumpFpo("fpo", cl::desc("dump FPO records"), cl::cat(MiscOptions), + cl::sub(RawSubcommand)); + +cl::opt RawAll("all", cl::desc("Implies most other options."), + cl::cat(MiscOptions), cl::sub(RawSubcommand)); + +cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::OneOrMore, cl::sub(RawSubcommand)); +} + +namespace yaml2pdb { +cl::opt + YamlPdbOutputFile("pdb", cl::desc("the name of the PDB file to write"), + cl::sub(YamlToPdbSubcommand)); + +cl::opt InputFilename(cl::Positional, + cl::desc(""), cl::Required, + cl::sub(YamlToPdbSubcommand)); +} + +namespace pdb2yaml { +cl::opt All("all", + cl::desc("Dump everything we know how to dump."), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt NoFileHeaders("no-file-headers", + cl::desc("Do not dump MSF file headers"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt Minimal("minimal", + cl::desc("Don't write fields with default values"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt StreamMetadata( + "stream-metadata", + cl::desc("Dump the number of streams and each stream's size"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt StreamDirectory( + "stream-directory", + cl::desc("Dump each stream's block map (implies -stream-metadata)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt PdbStream("pdb-stream", + cl::desc("Dump the PDB Stream (Stream 1)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt StringTable("string-table", cl::desc("Dump the PDB String Table"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt DbiStream("dbi-stream", + cl::desc("Dump the DBI Stream Headers (Stream 2)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt TpiStream("tpi-stream", + cl::desc("Dump the TPI Stream (Stream 3)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt IpiStream("ipi-stream", + cl::desc("Dump the IPI Stream (Stream 5)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::list InputFilename(cl::Positional, + cl::desc(""), cl::Required, + cl::sub(PdbToYamlSubcommand)); +} + +namespace shared { +cl::OptionCategory FileOptions("Module & File Options"); + +// MODULE & FILE OPTIONS +cl::opt DumpModules("modules", cl::desc("dump compiland information"), + cl::cat(FileOptions), cl::sub(RawSubcommand), + cl::sub(PdbToYamlSubcommand)); +cl::opt DumpModuleFiles("module-files", cl::desc("dump file information"), + cl::cat(FileOptions), cl::sub(RawSubcommand), + cl::sub(PdbToYamlSubcommand)); +cl::list DumpModuleSubsections( + "subsections", cl::ZeroOrMore, cl::CommaSeparated, + cl::desc("dump subsections from each module's debug stream"), + cl::values( + clEnumValN( + ModuleSubsection::CrossScopeExports, "cme", + "Cross module exports (DEBUG_S_CROSSSCOPEEXPORTS subsection)"), + clEnumValN( + ModuleSubsection::CrossScopeImports, "cmi", + "Cross module imports (DEBUG_S_CROSSSCOPEIMPORTS subsection)"), + clEnumValN(ModuleSubsection::FileChecksums, "fc", + "File checksums (DEBUG_S_CHECKSUMS subsection)"), + clEnumValN(ModuleSubsection::InlineeLines, "ilines", + "Inlinee lines (DEBUG_S_INLINEELINES subsection)"), + clEnumValN(ModuleSubsection::Lines, "lines", + "Lines (DEBUG_S_LINES subsection)"), + clEnumValN(ModuleSubsection::StringTable, "strings", + "String Table (DEBUG_S_STRINGTABLE subsection) (not " + "typically present in PDB file)"), + clEnumValN(ModuleSubsection::FrameData, "frames", + "Frame Data (DEBUG_S_FRAMEDATA subsection)"), + clEnumValN(ModuleSubsection::Symbols, "symbols", + "Symbols (DEBUG_S_SYMBOLS subsection) (not typically " + "present in PDB file)"), + clEnumValN(ModuleSubsection::Unknown, "unknown", + "Any subsection not covered by another option"), + clEnumValN(ModuleSubsection::All, "all", "All known subsections")), + cl::cat(FileOptions), cl::sub(RawSubcommand), cl::sub(PdbToYamlSubcommand)); +cl::opt DumpModuleSyms("module-syms", cl::desc("dump module symbols"), + cl::cat(FileOptions), cl::sub(RawSubcommand), + cl::sub(PdbToYamlSubcommand)); +} // namespace shared + +namespace analyze { +cl::opt StringTable("hash-collisions", cl::desc("Find hash collisions"), + cl::sub(AnalyzeSubcommand), cl::init(false)); +cl::list InputFilename(cl::Positional, + cl::desc(""), cl::Required, + cl::sub(AnalyzeSubcommand)); +} + +namespace merge { +cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::OneOrMore, cl::sub(MergeSubcommand)); +cl::opt + PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"), + cl::sub(MergeSubcommand)); +} +} + +static ExitOnError ExitOnErr; + +bool opts::checkModuleSubsection(opts::ModuleSubsection MS) { + return any_of(opts::shared::DumpModuleSubsections, + [=](opts::ModuleSubsection M) { + return M == MS || M == opts::ModuleSubsection::All; + }); +} + +static void yamlToPdb(StringRef Path) { + BumpPtrAllocator Allocator; + ErrorOr> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + + if (ErrorOrBuffer.getError()) { + ExitOnErr(make_error(generic_error_code::invalid_path, Path)); + } + + std::unique_ptr &Buffer = ErrorOrBuffer.get(); + + llvm::yaml::Input In(Buffer->getBuffer()); + pdb::yaml::PdbObject YamlObj(Allocator); + In >> YamlObj; + + PDBFileBuilder Builder(Allocator); + + uint32_t BlockSize = 4096; + if (YamlObj.Headers.hasValue()) + BlockSize = YamlObj.Headers->SuperBlock.BlockSize; + ExitOnErr(Builder.initialize(BlockSize)); + // Add each of the reserved streams. We ignore stream metadata in the + // yaml, because we will reconstruct our own view of the streams. For + // example, the YAML may say that there were 20 streams in the original + // PDB, but maybe we only dump a subset of those 20 streams, so we will + // have fewer, and the ones we do have may end up with different indices + // than the ones in the original PDB. So we just start with a clean slate. + for (uint32_t I = 0; I < kSpecialStreamCount; ++I) + ExitOnErr(Builder.getMsfBuilder().addStream(0)); + + if (YamlObj.StringTable.hasValue()) { + auto &Strings = Builder.getStringTableBuilder(); + for (auto S : *YamlObj.StringTable) + Strings.insert(S); + } + + pdb::yaml::PdbInfoStream DefaultInfoStream; + pdb::yaml::PdbDbiStream DefaultDbiStream; + pdb::yaml::PdbTpiStream DefaultTpiStream; + pdb::yaml::PdbTpiStream DefaultIpiStream; + + const auto &Info = YamlObj.PdbStream.getValueOr(DefaultInfoStream); + + auto &InfoBuilder = Builder.getInfoBuilder(); + InfoBuilder.setAge(Info.Age); + InfoBuilder.setGuid(Info.Guid); + InfoBuilder.setSignature(Info.Signature); + InfoBuilder.setVersion(Info.Version); + for (auto F : Info.Features) + InfoBuilder.addFeature(F); + + auto &Strings = Builder.getStringTableBuilder().getStrings(); + + const auto &Dbi = YamlObj.DbiStream.getValueOr(DefaultDbiStream); + auto &DbiBuilder = Builder.getDbiBuilder(); + DbiBuilder.setAge(Dbi.Age); + DbiBuilder.setBuildNumber(Dbi.BuildNumber); + DbiBuilder.setFlags(Dbi.Flags); + DbiBuilder.setMachineType(Dbi.MachineType); + DbiBuilder.setPdbDllRbld(Dbi.PdbDllRbld); + DbiBuilder.setPdbDllVersion(Dbi.PdbDllVersion); + DbiBuilder.setVersionHeader(Dbi.VerHeader); + for (const auto &MI : Dbi.ModInfos) { + auto &ModiBuilder = ExitOnErr(DbiBuilder.addModuleInfo(MI.Mod)); + ModiBuilder.setObjFileName(MI.Obj); + + for (auto S : MI.SourceFiles) + ExitOnErr(DbiBuilder.addModuleSourceFile(MI.Mod, S)); + if (MI.Modi.hasValue()) { + const auto &ModiStream = *MI.Modi; + for (auto Symbol : ModiStream.Symbols) { + ModiBuilder.addSymbol( + Symbol.toCodeViewSymbol(Allocator, CodeViewContainer::Pdb)); + } + } + + auto CodeViewSubsections = ExitOnErr(CodeViewYAML::toCodeViewSubsectionList( + Allocator, MI.Subsections, Strings)); + for (auto &SS : CodeViewSubsections) { + ModiBuilder.addDebugSubsection(std::move(SS)); + } + } + + auto &TpiBuilder = Builder.getTpiBuilder(); + const auto &Tpi = YamlObj.TpiStream.getValueOr(DefaultTpiStream); + TpiBuilder.setVersionHeader(Tpi.Version); + for (const auto &R : Tpi.Records) { + CVType Type = R.toCodeViewRecord(Allocator); + TpiBuilder.addTypeRecord(Type.RecordData, None); + } + + const auto &Ipi = YamlObj.IpiStream.getValueOr(DefaultIpiStream); + auto &IpiBuilder = Builder.getIpiBuilder(); + IpiBuilder.setVersionHeader(Ipi.Version); + for (const auto &R : Ipi.Records) { + CVType Type = R.toCodeViewRecord(Allocator); + IpiBuilder.addTypeRecord(Type.RecordData, None); + } + + ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile)); +} + +static PDBFile &loadPDB(StringRef Path, std::unique_ptr &Session) { + ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session)); + + NativeSession *NS = static_cast(Session.get()); + return NS->getPDBFile(); +} + +static void pdb2Yaml(StringRef Path) { + std::unique_ptr Session; + auto &File = loadPDB(Path, Session); + + auto O = llvm::make_unique(File); + O = llvm::make_unique(File); + + ExitOnErr(O->dump()); +} + +static void dumpRaw(StringRef Path) { + std::unique_ptr Session; + auto &File = loadPDB(Path, Session); + + auto O = llvm::make_unique(File); + + ExitOnErr(O->dump()); +} + +static void dumpAnalysis(StringRef Path) { + std::unique_ptr Session; + auto &File = loadPDB(Path, Session); + auto O = llvm::make_unique(File); + + ExitOnErr(O->dump()); +} + +static void diff(StringRef Path1, StringRef Path2) { + std::unique_ptr Session1; + std::unique_ptr Session2; + + auto &File1 = loadPDB(Path1, Session1); + auto &File2 = loadPDB(Path2, Session2); + + auto O = llvm::make_unique(File1, File2); + + ExitOnErr(O->dump()); +} + +bool opts::pretty::shouldDumpSymLevel(SymLevel Search) { + if (SymTypes.empty()) + return true; + if (llvm::find(SymTypes, Search) != SymTypes.end()) + return true; + if (llvm::find(SymTypes, SymLevel::All) != SymTypes.end()) + return true; + return false; +} + +uint32_t llvm::pdb::getTypeLength(const PDBSymbolData &Symbol) { + auto SymbolType = Symbol.getType(); + const IPDBRawSymbol &RawType = SymbolType->getRawSymbol(); + + return RawType.getLength(); +} + +bool opts::pretty::compareFunctionSymbols( + const std::unique_ptr &F1, + const std::unique_ptr &F2) { + assert(opts::pretty::SymbolOrder != opts::pretty::SymbolSortMode::None); + + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::Name) + return F1->getName() < F2->getName(); + + // Note that we intentionally sort in descending order on length, since + // long functions are more interesting than short functions. + return F1->getLength() > F2->getLength(); +} + +bool opts::pretty::compareDataSymbols( + const std::unique_ptr &F1, + const std::unique_ptr &F2) { + assert(opts::pretty::SymbolOrder != opts::pretty::SymbolSortMode::None); + + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::Name) + return F1->getName() < F2->getName(); + + // Note that we intentionally sort in descending order on length, since + // large types are more interesting than short ones. + return getTypeLength(*F1) > getTypeLength(*F2); +} + +static void dumpPretty(StringRef Path) { + std::unique_ptr Session; + + const auto ReaderType = + opts::pretty::Native ? PDB_ReaderType::Native : PDB_ReaderType::DIA; + ExitOnErr(loadDataForPDB(ReaderType, Path, Session)); + + if (opts::pretty::LoadAddress) + Session->setLoadAddress(opts::pretty::LoadAddress); + + auto &Stream = outs(); + const bool UseColor = opts::pretty::ColorOutput == cl::BOU_UNSET + ? Stream.has_colors() + : opts::pretty::ColorOutput == cl::BOU_TRUE; + LinePrinter Printer(2, UseColor, Stream); + + auto GlobalScope(Session->getGlobalScope()); + std::string FileName(GlobalScope->getSymbolsFileName()); + + WithColor(Printer, PDB_ColorItem::None).get() << "Summary for "; + WithColor(Printer, PDB_ColorItem::Path).get() << FileName; + Printer.Indent(); + uint64_t FileSize = 0; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Size"; + if (!sys::fs::file_size(FileName, FileSize)) { + Printer << ": " << FileSize << " bytes"; + } else { + Printer << ": (Unable to obtain file size)"; + } + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Guid"; + Printer << ": " << GlobalScope->getGuid(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Age"; + Printer << ": " << GlobalScope->getAge(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Attributes"; + Printer << ": "; + if (GlobalScope->hasCTypes()) + outs() << "HasCTypes "; + if (GlobalScope->hasPrivateSymbols()) + outs() << "HasPrivateSymbols "; + Printer.Unindent(); + + if (opts::pretty::Compilands) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() + << "---COMPILANDS---"; + Printer.Indent(); + auto Compilands = GlobalScope->findAllChildren(); + CompilandDumper Dumper(Printer); + CompilandDumpFlags options = CompilandDumper::Flags::None; + if (opts::pretty::Lines) + options = options | CompilandDumper::Flags::Lines; + while (auto Compiland = Compilands->getNext()) + Dumper.start(*Compiland, options); + Printer.Unindent(); + } + + if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---"; + Printer.Indent(); + TypeDumper Dumper(Printer); + Dumper.start(*GlobalScope); + Printer.Unindent(); + } + + if (opts::pretty::Symbols) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---SYMBOLS---"; + Printer.Indent(); + auto Compilands = GlobalScope->findAllChildren(); + CompilandDumper Dumper(Printer); + while (auto Compiland = Compilands->getNext()) + Dumper.start(*Compiland, true); + Printer.Unindent(); + } + + if (opts::pretty::Globals) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---GLOBALS---"; + Printer.Indent(); + if (shouldDumpSymLevel(opts::pretty::SymLevel::Functions)) { + FunctionDumper Dumper(Printer); + auto Functions = GlobalScope->findAllChildren(); + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) { + while (auto Function = Functions->getNext()) { + Printer.NewLine(); + Dumper.start(*Function, FunctionDumper::PointerType::None); + } + } else { + std::vector> Funcs; + while (auto Func = Functions->getNext()) + Funcs.push_back(std::move(Func)); + std::sort(Funcs.begin(), Funcs.end(), + opts::pretty::compareFunctionSymbols); + for (const auto &Func : Funcs) { + Printer.NewLine(); + Dumper.start(*Func, FunctionDumper::PointerType::None); + } + } + } + if (shouldDumpSymLevel(opts::pretty::SymLevel::Data)) { + auto Vars = GlobalScope->findAllChildren(); + VariableDumper Dumper(Printer); + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) { + while (auto Var = Vars->getNext()) + Dumper.start(*Var); + } else { + std::vector> Datas; + while (auto Var = Vars->getNext()) + Datas.push_back(std::move(Var)); + std::sort(Datas.begin(), Datas.end(), opts::pretty::compareDataSymbols); + for (const auto &Var : Datas) + Dumper.start(*Var); + } + } + if (shouldDumpSymLevel(opts::pretty::SymLevel::Thunks)) { + auto Thunks = GlobalScope->findAllChildren(); + CompilandDumper Dumper(Printer); + while (auto Thunk = Thunks->getNext()) + Dumper.dump(*Thunk); + } + Printer.Unindent(); + } + if (opts::pretty::Externals) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---EXTERNALS---"; + Printer.Indent(); + ExternalSymbolDumper Dumper(Printer); + Dumper.start(*GlobalScope); + } + if (opts::pretty::Lines) { + Printer.NewLine(); + } + outs().flush(); +} + +static void mergePdbs() { + BumpPtrAllocator Allocator; + TypeTableBuilder MergedTpi(Allocator); + TypeTableBuilder MergedIpi(Allocator); + + // Create a Tpi and Ipi type table with all types from all input files. + for (const auto &Path : opts::merge::InputFilenames) { + std::unique_ptr Session; + auto &File = loadPDB(Path, Session); + SmallVector TypeMap; + SmallVector IdMap; + if (File.hasPDBTpiStream()) { + auto &Tpi = ExitOnErr(File.getPDBTpiStream()); + ExitOnErr(codeview::mergeTypeRecords(MergedTpi, TypeMap, nullptr, + Tpi.typeArray())); + } + if (File.hasPDBIpiStream()) { + auto &Ipi = ExitOnErr(File.getPDBIpiStream()); + ExitOnErr(codeview::mergeIdRecords(MergedIpi, TypeMap, IdMap, + Ipi.typeArray())); + } + } + + // Then write the PDB. + PDBFileBuilder Builder(Allocator); + ExitOnErr(Builder.initialize(4096)); + // Add each of the reserved streams. We might not put any data in them, + // but at least they have to be present. + for (uint32_t I = 0; I < kSpecialStreamCount; ++I) + ExitOnErr(Builder.getMsfBuilder().addStream(0)); + + auto &DestTpi = Builder.getTpiBuilder(); + auto &DestIpi = Builder.getIpiBuilder(); + MergedTpi.ForEachRecord([&DestTpi](TypeIndex TI, ArrayRef Data) { + DestTpi.addTypeRecord(Data, None); + }); + MergedIpi.ForEachRecord([&DestIpi](TypeIndex TI, ArrayRef Data) { + DestIpi.addTypeRecord(Data, None); + }); + + SmallString<64> OutFile(opts::merge::PdbOutputFile); + if (OutFile.empty()) { + OutFile = opts::merge::InputFilenames[0]; + llvm::sys::path::replace_extension(OutFile, "merged.pdb"); + } + ExitOnErr(Builder.commit(OutFile)); +} + +int main(int argc_, const char *argv_[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(argv_[0]); + PrettyStackTraceProgram X(argc_, argv_); + + ExitOnErr.setBanner("llvm-pdbutil: "); + + SmallVector argv; + SpecificBumpPtrAllocator ArgAllocator; + ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector( + argv, makeArrayRef(argv_, argc_), ArgAllocator))); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n"); + if (!opts::raw::DumpBlockRangeOpt.empty()) { + llvm::Regex R("^([0-9]+)(-([0-9]+))?$"); + llvm::SmallVector Matches; + if (!R.match(opts::raw::DumpBlockRangeOpt, &Matches)) { + errs() << "Argument '" << opts::raw::DumpBlockRangeOpt + << "' invalid format.\n"; + errs().flush(); + exit(1); + } + opts::raw::DumpBlockRange.emplace(); + Matches[1].getAsInteger(10, opts::raw::DumpBlockRange->Min); + if (!Matches[3].empty()) { + opts::raw::DumpBlockRange->Max.emplace(); + Matches[3].getAsInteger(10, *opts::raw::DumpBlockRange->Max); + } + } + + if ((opts::RawSubcommand && opts::raw::RawAll) || + (opts::PdbToYamlSubcommand && opts::pdb2yaml::All)) { + opts::shared::DumpModules = true; + opts::shared::DumpModuleFiles = true; + opts::shared::DumpModuleSyms = true; + opts::shared::DumpModuleSubsections.push_back(opts::ModuleSubsection::All); + if (llvm::is_contained(opts::shared::DumpModuleSubsections, + opts::ModuleSubsection::All)) { + opts::shared::DumpModuleSubsections.reset(); + opts::shared::DumpModuleSubsections.push_back( + opts::ModuleSubsection::All); + } + } + + if (opts::shared::DumpModuleSyms || opts::shared::DumpModuleFiles) + opts::shared::DumpModules = true; + + if (opts::shared::DumpModules) + opts::pdb2yaml::DbiStream = true; + + if (opts::RawSubcommand) { + if (opts::raw::RawAll) { + opts::raw::DumpHeaders = true; + opts::raw::DumpGlobals = true; + opts::raw::DumpPublics = true; + opts::raw::DumpSectionHeaders = true; + opts::raw::DumpStreamSummary = true; + opts::raw::DumpPageStats = true; + opts::raw::DumpStreamBlocks = true; + opts::raw::DumpTpiRecords = true; + opts::raw::DumpTpiHash = true; + opts::raw::DumpIpiRecords = true; + opts::raw::DumpSectionMap = true; + opts::raw::DumpSectionContribs = true; + opts::raw::DumpFpo = true; + opts::raw::DumpStringTable = true; + } + + if (opts::raw::CompactRecords && + (opts::raw::DumpTpiRecordBytes || opts::raw::DumpIpiRecordBytes)) { + errs() << "-compact-records is incompatible with -tpi-record-bytes and " + "-ipi-record-bytes.\n"; + exit(1); + } + } + if (opts::PdbToYamlSubcommand) { + if (opts::pdb2yaml::All) { + opts::pdb2yaml::StreamMetadata = true; + opts::pdb2yaml::StreamDirectory = true; + opts::pdb2yaml::PdbStream = true; + opts::pdb2yaml::StringTable = true; + opts::pdb2yaml::DbiStream = true; + opts::pdb2yaml::TpiStream = true; + opts::pdb2yaml::IpiStream = true; + } + } + + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + + if (opts::PdbToYamlSubcommand) { + pdb2Yaml(opts::pdb2yaml::InputFilename.front()); + } else if (opts::YamlToPdbSubcommand) { + if (opts::yaml2pdb::YamlPdbOutputFile.empty()) { + SmallString<16> OutputFilename(opts::yaml2pdb::InputFilename.getValue()); + sys::path::replace_extension(OutputFilename, ".pdb"); + opts::yaml2pdb::YamlPdbOutputFile = OutputFilename.str(); + } + yamlToPdb(opts::yaml2pdb::InputFilename); + } else if (opts::AnalyzeSubcommand) { + dumpAnalysis(opts::analyze::InputFilename.front()); + } else if (opts::PrettySubcommand) { + if (opts::pretty::Lines) + opts::pretty::Compilands = true; + + if (opts::pretty::All) { + opts::pretty::Compilands = true; + opts::pretty::Symbols = true; + opts::pretty::Globals = true; + opts::pretty::Types = true; + opts::pretty::Externals = true; + opts::pretty::Lines = true; + } + + if (opts::pretty::Types) { + opts::pretty::Classes = true; + opts::pretty::Typedefs = true; + opts::pretty::Enums = true; + } + + // When adding filters for excluded compilands and types, we need to + // remember that these are regexes. So special characters such as * and \ + // need to be escaped in the regex. In the case of a literal \, this means + // it needs to be escaped again in the C++. So matching a single \ in the + // input requires 4 \es in the C++. + if (opts::pretty::ExcludeCompilerGenerated) { + opts::pretty::ExcludeTypes.push_back("__vc_attributes"); + opts::pretty::ExcludeCompilands.push_back("\\* Linker \\*"); + } + if (opts::pretty::ExcludeSystemLibraries) { + opts::pretty::ExcludeCompilands.push_back( + "f:\\\\binaries\\\\Intermediate\\\\vctools\\\\crt_bld"); + opts::pretty::ExcludeCompilands.push_back("f:\\\\dd\\\\vctools\\\\crt"); + opts::pretty::ExcludeCompilands.push_back( + "d:\\\\th.obj.x86fre\\\\minkernel"); + } + std::for_each(opts::pretty::InputFilenames.begin(), + opts::pretty::InputFilenames.end(), dumpPretty); + } else if (opts::RawSubcommand) { + std::for_each(opts::raw::InputFilenames.begin(), + opts::raw::InputFilenames.end(), dumpRaw); + } else if (opts::DiffSubcommand) { + if (opts::diff::InputFilenames.size() != 2) { + errs() << "diff subcommand expects exactly 2 arguments.\n"; + exit(1); + } + diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]); + } else if (opts::MergeSubcommand) { + if (opts::merge::InputFilenames.size() < 2) { + errs() << "merge subcommand requires at least 2 input files.\n"; + exit(1); + } + mergePdbs(); + } + + outs().flush(); + return 0; +}