diff --git a/include/llvm/Transforms/Utils/SanitizerStats.h b/include/llvm/Transforms/Utils/SanitizerStats.h new file mode 100644 index 00000000000..d36e34258a3 --- /dev/null +++ b/include/llvm/Transforms/Utils/SanitizerStats.h @@ -0,0 +1,56 @@ +//===- SanitizerStats.h - Sanitizer statistics gathering -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Declares functions and data structures for sanitizer statistics gathering. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_UTILS_SANITIZERSTATS_H +#define LLVM_TRANSFORMS_UTILS_SANITIZERSTATS_H + +#include "llvm/IR/IRBuilder.h" + +namespace llvm { + +// Number of bits in data that are used for the sanitizer kind. Needs to match +// __sanitizer::kKindBits in compiler-rt/lib/stats/stats.h +enum { kSanitizerStatKindBits = 3 }; + +enum SanitizerStatKind { + SanStat_CFI_VCall, + SanStat_CFI_NVCall, + SanStat_CFI_DerivedCast, + SanStat_CFI_UnrelatedCast, + SanStat_CFI_ICall, +}; + +struct SanitizerStatReport { + SanitizerStatReport(Module *M); + + /// Generates code into B that increments a location-specific counter tagged + /// with the given sanitizer kind SK. + void create(IRBuilder<> &B, SanitizerStatKind SK); + + /// Finalize module stats array and add global constructor to register it. + void finish(); + +private: + Module *M; + GlobalVariable *ModuleStatsGV; + ArrayType *StatTy; + StructType *EmptyModuleStatsTy; + + std::vector Inits; + ArrayType *makeModuleStatsArrayTy(); + StructType *makeModuleStatsTy(); +}; + +} + +#endif diff --git a/lib/Transforms/Utils/CMakeLists.txt b/lib/Transforms/Utils/CMakeLists.txt index 8308a9b6914..ba2d6e91d5b 100644 --- a/lib/Transforms/Utils/CMakeLists.txt +++ b/lib/Transforms/Utils/CMakeLists.txt @@ -30,6 +30,7 @@ add_llvm_library(LLVMTransformUtils ModuleUtils.cpp PromoteMemoryToRegister.cpp SSAUpdater.cpp + SanitizerStats.cpp SimplifyCFG.cpp SimplifyIndVar.cpp SimplifyInstructions.cpp diff --git a/lib/Transforms/Utils/SanitizerStats.cpp b/lib/Transforms/Utils/SanitizerStats.cpp new file mode 100644 index 00000000000..af3df2fd948 --- /dev/null +++ b/lib/Transforms/Utils/SanitizerStats.cpp @@ -0,0 +1,108 @@ +//===- SanitizerStats.cpp - Sanitizer statistics gathering ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements code generation for sanitizer statistics gathering. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/SanitizerStats.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +SanitizerStatReport::SanitizerStatReport(Module *M) : M(M) { + StatTy = ArrayType::get(Type::getInt8PtrTy(M->getContext()), 2); + EmptyModuleStatsTy = makeModuleStatsTy(); + + ModuleStatsGV = new GlobalVariable(*M, EmptyModuleStatsTy, false, + GlobalValue::InternalLinkage, 0); +} + +ArrayType *SanitizerStatReport::makeModuleStatsArrayTy() { + return ArrayType::get(StatTy, Inits.size()); +} + +StructType *SanitizerStatReport::makeModuleStatsTy() { + return StructType::get(M->getContext(), {Type::getInt8PtrTy(M->getContext()), + Type::getInt32Ty(M->getContext()), + makeModuleStatsArrayTy()}); +} + +void SanitizerStatReport::create(IRBuilder<> &B, SanitizerStatKind SK) { + Function *F = B.GetInsertBlock()->getParent(); + Module *M = F->getParent(); + PointerType *Int8PtrTy = B.getInt8PtrTy(); + IntegerType *IntPtrTy = B.getIntPtrTy(M->getDataLayout()); + ArrayType *StatTy = ArrayType::get(Int8PtrTy, 2); + + Inits.push_back(ConstantArray::get( + StatTy, + {Constant::getNullValue(Int8PtrTy), + ConstantExpr::getIntToPtr( + ConstantInt::get(IntPtrTy, uint64_t(SK) << (IntPtrTy->getBitWidth() - + kSanitizerStatKindBits)), + Int8PtrTy)})); + + FunctionType *StatReportTy = + FunctionType::get(B.getVoidTy(), Int8PtrTy, false); + Constant *StatReport = M->getOrInsertFunction( + "__sanitizer_stat_report", StatReportTy); + + auto InitAddr = ConstantExpr::getGetElementPtr( + EmptyModuleStatsTy, ModuleStatsGV, + ArrayRef{ + ConstantInt::get(IntPtrTy, 0), ConstantInt::get(B.getInt32Ty(), 2), + ConstantInt::get(IntPtrTy, Inits.size() - 1), + }); + B.CreateCall(StatReport, ConstantExpr::getBitCast(InitAddr, Int8PtrTy)); +} + +void SanitizerStatReport::finish() { + if (Inits.empty()) { + ModuleStatsGV->eraseFromParent(); + return; + } + + PointerType *Int8PtrTy = Type::getInt8PtrTy(M->getContext()); + IntegerType *Int32Ty = Type::getInt32Ty(M->getContext()); + Type *VoidTy = Type::getVoidTy(M->getContext()); + + // Create a new ModuleStatsGV to replace the old one. We can't just set the + // old one's initializer because its type is different. + auto NewModuleStatsGV = new GlobalVariable( + *M, makeModuleStatsTy(), false, GlobalValue::InternalLinkage, + ConstantStruct::getAnon( + {Constant::getNullValue(Int8PtrTy), + ConstantInt::get(Int32Ty, Inits.size()), + ConstantArray::get(makeModuleStatsArrayTy(), Inits)})); + ModuleStatsGV->replaceAllUsesWith( + ConstantExpr::getBitCast(NewModuleStatsGV, ModuleStatsGV->getType())); + ModuleStatsGV->eraseFromParent(); + + // Create a global constructor to register NewModuleStatsGV. + auto F = Function::Create(FunctionType::get(VoidTy, false), + GlobalValue::InternalLinkage, "", M); + auto BB = BasicBlock::Create(M->getContext(), "", F); + IRBuilder<> B(BB); + + FunctionType *StatInitTy = FunctionType::get(VoidTy, Int8PtrTy, false); + Constant *StatInit = M->getOrInsertFunction( + "__sanitizer_stat_init", StatInitTy); + + B.CreateCall(StatInit, ConstantExpr::getBitCast(NewModuleStatsGV, Int8PtrTy)); + B.CreateRetVoid(); + + appendToGlobalCtors(*M, F, 0); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 138450ba8e0..e7c26f6e9b5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -61,6 +61,7 @@ set(LLVM_TEST_DEPENDS obj2yaml opt sancov + sanstats verify-uselistorder yaml-bench yaml2obj diff --git a/test/lit.cfg b/test/lit.cfg index e06c10f6421..6dc021c9af9 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -282,6 +282,7 @@ for pattern in [r"\bbugpoint\b(?!-)", r"\bFileCheck\b", r"\bobj2yaml\b", NOJUNK + r"\bsancov\b", + NOJUNK + r"\bsanstats\b", r"\byaml2obj\b", r"\byaml-bench\b", r"\bverify-uselistorder\b", diff --git a/test/tools/sanstats/elf.test b/test/tools/sanstats/elf.test new file mode 100644 index 00000000000..0b6292f82f8 --- /dev/null +++ b/test/tools/sanstats/elf.test @@ -0,0 +1,210 @@ +# RUN: yaml2obj -format=elf %s > %t1.o +# RUN: yaml2obj -format=elf %s > %t2.o + +# RUN: echo -ne "\x04" > %t.stats + +# RUN: echo -n "%t1.o" >> %t.stats +# RUN: echo -ne "\x00" >> %t.stats +# RUN: echo -ne "\x01\x00\x00\x00\x01\x00\x00\x00" >> %t.stats +# RUN: echo -ne "\x11\x00\x00\x00\x02\x00\x00\x20" >> %t.stats +# RUN: echo -ne "\x21\x00\x00\x00\x03\x00\x00\x40" >> %t.stats +# RUN: echo -ne "\x01\x00\x00\x00\x04\x00\x00\x60" >> %t.stats +# RUN: echo -ne "\x11\x00\x00\x00\x05\x00\x00\x80" >> %t.stats +# RUN: echo -ne "\x21\x00\x00\x00\x06\x00\x00\xa0" >> %t.stats +# RUN: echo -ne "\x00\x00\x00\x00\x00\x00\x00\x00" >> %t.stats + +# RUN: echo -n "%t2.o" >> %t.stats +# RUN: echo -ne "\x00" >> %t.stats +# RUN: echo -ne "\x21\x00\x00\x00\x07\x00\x00\x00" >> %t.stats +# RUN: echo -ne "\x11\x00\x00\x00\x08\x00\x00\x20" >> %t.stats +# RUN: echo -ne "\x01\x00\x00\x00\x09\x00\x00\x40" >> %t.stats +# RUN: echo -ne "\x21\x00\x00\x00\x0b\x00\x00\x60" >> %t.stats +# RUN: echo -ne "\x11\x00\x00\x00\x0c\x00\x00\x80" >> %t.stats +# RUN: echo -ne "\x01\x00\x00\x00\x0e\x00\x00\xa0" >> %t.stats +# RUN: echo -ne "\x00\x00\x00\x00\x00\x00\x00\x00" >> %t.stats + +# RUN: sanstats %t.stats | FileCheck %s + +# CHECK: /tmp{{[/\\]}}f.c:1 f1 cfi-vcall 1 +# CHECK: /tmp{{[/\\]}}f.c:2 f2 cfi-nvcall 2 +# CHECK: /tmp{{[/\\]}}f.c:3 f3 cfi-derived-cast 3 +# CHECK: /tmp{{[/\\]}}f.c:1 f1 cfi-unrelated-cast 4 +# CHECK: /tmp{{[/\\]}}f.c:2 f2 cfi-icall 5 +# CHECK: /tmp{{[/\\]}}f.c:3 f3 6 + +# CHECK: /tmp{{[/\\]}}f.c:3 f3 cfi-vcall 7 +# CHECK: /tmp{{[/\\]}}f.c:2 f2 cfi-nvcall 8 +# CHECK: /tmp{{[/\\]}}f.c:1 f1 cfi-derived-cast 9 +# CHECK: /tmp{{[/\\]}}f.c:3 f3 cfi-unrelated-cast 11 +# CHECK: /tmp{{[/\\]}}f.c:2 f2 cfi-icall 12 +# CHECK: /tmp{{[/\\]}}f.c:1 f1 14 + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E55DC3 + - Name: .debug_str + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 636C616E672076657273696F6E20332E382E3020287472756E6B203235353339332920286C6C766D2F7472756E6B203235353734352900662E63002F746D7000663100663200663300 + - Name: .debug_loc + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .debug_abbrev + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 011101250E1305030E10171B0E110112060000022E00110112064018030E3A0B3B0B3F19000000 + - Name: .debug_info + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 660000000400000000000801000000000C0000000000000000000000000000000000000000002600000002000000000000000006000000015600000000010102000000000000000006000000015600000000010202000000000000000006000000015600000000010300 + - Name: .rela.debug_info + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .debug_info + Relocations: + - Offset: 0x0000000000000006 + Symbol: '' + Type: R_X86_64_32 + - Offset: 0x000000000000000C + Symbol: '' + Type: R_X86_64_32 + - Offset: 0x0000000000000012 + Symbol: '' + Type: R_X86_64_32 + Addend: 55 + - Offset: 0x0000000000000016 + Symbol: '' + Type: R_X86_64_32 + - Offset: 0x000000000000001A + Symbol: '' + Type: R_X86_64_32 + Addend: 59 + - Offset: 0x000000000000001E + Symbol: '' + Type: R_X86_64_64 + - Offset: 0x000000000000002B + Symbol: '' + Type: R_X86_64_64 + - Offset: 0x0000000000000039 + Symbol: '' + Type: R_X86_64_32 + Addend: 64 + - Offset: 0x0000000000000040 + Symbol: '' + Type: R_X86_64_64 + Addend: 16 + - Offset: 0x000000000000004E + Symbol: '' + Type: R_X86_64_32 + Addend: 67 + - Offset: 0x0000000000000055 + Symbol: '' + Type: R_X86_64_64 + Addend: 32 + - Offset: 0x0000000000000063 + Symbol: '' + Type: R_X86_64_32 + Addend: 70 + - Name: .debug_ranges + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .debug_pubnames + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 230000000200000000006A0000002A0000006631003F0000006632005400000066330000000000 + - Name: .rela.debug_pubnames + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .debug_pubnames + Relocations: + - Offset: 0x0000000000000006 + Symbol: '' + Type: R_X86_64_32 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E382E3020287472756E6B203235353339332920286C6C766D2F7472756E6B203235353734352900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_X86_64_UNWIND + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000017A5200017810011B0C070890010000180000001C000000000000000600000000410E108602430D060000001800000038000000000000000600000000410E108602430D060000001C00000054000000000000000600000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: '' + Type: R_X86_64_PC32 + - Offset: 0x000000000000003C + Symbol: '' + Type: R_X86_64_PC32 + Addend: 16 + - Offset: 0x0000000000000058 + Symbol: '' + Type: R_X86_64_PC32 + Addend: 32 + - Name: .debug_line + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: 4300000002001A0000000101FB0E0D00010101010000000100000100662E630000000000000902000000000000000001050C0A4A0500BB050C0A4A0500BB050C0A4A0202000101 + - Name: .rela.debug_line + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .debug_line + Relocations: + - Offset: 0x0000000000000027 + Symbol: '' + Type: R_X86_64_64 +Symbols: + Local: + - Name: f.c + Type: STT_FILE + - Type: STT_SECTION + Section: .text + - Type: STT_SECTION + Section: .debug_str + - Type: STT_SECTION + Section: .debug_abbrev + - Type: STT_SECTION + Section: .debug_info + - Type: STT_SECTION + Section: .debug_line + Global: + - Name: f1 + Type: STT_FUNC + Section: .text + Size: 0x0000000000000006 + - Name: f2 + Type: STT_FUNC + Section: .text + Value: 0x0000000000000010 + Size: 0x0000000000000006 + - Name: f3 + Type: STT_FUNC + Section: .text + Value: 0x0000000000000020 + Size: 0x0000000000000006 +... diff --git a/tools/Makefile b/tools/Makefile index 92d49545187..da9193bc5c3 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -33,7 +33,7 @@ PARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \ llvm-dwarfdump llvm-cov llvm-size llvm-stress llvm-mcmarkup \ llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test \ llvm-cxxdump verify-uselistorder dsymutil llvm-pdbdump \ - llvm-split sancov llvm-dwp + llvm-split sancov sanstats llvm-dwp # If Intel JIT Events support is configured, build an extra tool to test it. ifeq ($(USE_INTEL_JITEVENTS), 1) diff --git a/tools/sanstats/CMakeLists.txt b/tools/sanstats/CMakeLists.txt new file mode 100644 index 00000000000..abfbdab3a04 --- /dev/null +++ b/tools/sanstats/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Support + Symbolize + ) + +add_llvm_tool(sanstats + sanstats.cpp + ) diff --git a/tools/sanstats/Makefile b/tools/sanstats/Makefile new file mode 100644 index 00000000000..73f71deb634 --- /dev/null +++ b/tools/sanstats/Makefile @@ -0,0 +1,17 @@ +##===- tools/sanstats/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../.. +TOOLNAME := sanstats +LINK_COMPONENTS := Support Symbolize + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/sanstats/sanstats.cpp b/tools/sanstats/sanstats.cpp new file mode 100644 index 00000000000..f089fd4dec7 --- /dev/null +++ b/tools/sanstats/sanstats.cpp @@ -0,0 +1,138 @@ +//===- sanstats.cpp - Sanitizer statistics dumper -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tool dumps statistics information from files in the format produced +// by clang's -fsanitize-stats feature. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Transforms/Utils/SanitizerStats.h" +#include + +using namespace llvm; + +static cl::opt ClInputFile(cl::Positional, cl::Required, + cl::desc("")); + +static cl::opt ClDemangle("demangle", cl::init(false), + cl::desc("Print demangled function name.")); + +inline uint64_t KindFromData(uint64_t Data, char SizeofPtr) { + return Data >> (SizeofPtr * 8 - kSanitizerStatKindBits); +} + +inline uint64_t CountFromData(uint64_t Data, char SizeofPtr) { + return Data & ((1ull << (SizeofPtr * 8 - kSanitizerStatKindBits)) - 1); +} + +uint64_t ReadLE(char Size, const char *Begin, const char *End) { + uint64_t Result = 0; + char Pos = 0; + while (Begin < End && Pos != Size) { + Result |= uint64_t(uint8_t(*Begin)) << (Pos * 8); + ++Begin; + ++Pos; + } + return Result; +} + +const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) { + const char *FilenameBegin = Begin; + while (Begin != End && *Begin) + ++Begin; + if (Begin == End) + return 0; + StringRef Filename(FilenameBegin, Begin - FilenameBegin); + + ++Begin; + if (Begin == End) + return 0; + + symbolize::LLVMSymbolizer::Options SymbolizerOptions; + SymbolizerOptions.Demangle = ClDemangle; + SymbolizerOptions.UseSymbolTable = true; + symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions); + + while (1) { + uint64_t Addr = ReadLE(SizeofPtr, Begin, End); + Begin += SizeofPtr; + uint64_t Data = ReadLE(SizeofPtr, Begin, End); + Begin += SizeofPtr; + + if (Begin > End) + return 0; + if (Addr == 0 && Data == 0) + return Begin; + if (Begin == End) + return 0; + + ErrorOr LineInfo = Symbolizer.symbolizeCode(Filename, Addr); + if (LineInfo) { + llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' ' + << LineInfo->FunctionName << ' '; + } else { + llvm::outs() << " "; + } + + switch (KindFromData(Data, SizeofPtr)) { + case SanStat_CFI_VCall: + llvm::outs() << "cfi-vcall"; + break; + case SanStat_CFI_NVCall: + llvm::outs() << "cfi-nvcall"; + break; + case SanStat_CFI_DerivedCast: + llvm::outs() << "cfi-derived-cast"; + break; + case SanStat_CFI_UnrelatedCast: + llvm::outs() << "cfi-unrelated-cast"; + break; + case SanStat_CFI_ICall: + llvm::outs() << "cfi-icall"; + break; + default: + llvm::outs() << ""; + break; + } + + llvm::outs() << " " << CountFromData(Data, SizeofPtr) << '\n'; + } +} + +int main(int argc, char **argv) { + cl::ParseCommandLineOptions(argc, argv, + "Sanitizer Statistics Processing Tool"); + + ErrorOr> MBOrErr = + MemoryBuffer::getFile(ClInputFile, -1, false); + if (!MBOrErr) { + errs() << argv[0] << ": " << ClInputFile << ": " + << MBOrErr.getError().message() << '\n'; + return 1; + } + std::unique_ptr MB = std::move(MBOrErr.get()); + const char *Begin = MB->getBufferStart(), *End = MB->getBufferEnd(); + if (Begin == End) { + errs() << argv[0] << ": " << ClInputFile << ": short read\n"; + return 1; + } + char SizeofPtr = *Begin++; + while (Begin != End) { + Begin = ReadModule(SizeofPtr, Begin, End); + if (Begin == 0) { + errs() << argv[0] << ": " << ClInputFile << ": short read\n"; + return 1; + } + assert(Begin <= End); + } +}