1 | //===- sanstats.cpp - Sanitizer statistics dumper -------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This tool dumps statistics information from files in the format produced |
10 | // by clang's -fsanitize-stats feature. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" |
15 | #include "llvm/DebugInfo/Symbolize/Symbolize.h" |
16 | #include "llvm/Support/CommandLine.h" |
17 | #include "llvm/Support/ErrorOr.h" |
18 | #include "llvm/Support/FileSystem.h" |
19 | #include "llvm/Support/MemoryBuffer.h" |
20 | #include "llvm/Support/Path.h" |
21 | #include "llvm/Transforms/Utils/SanitizerStats.h" |
22 | #include <stdint.h> |
23 | |
24 | using namespace llvm; |
25 | |
26 | static cl::OptionCategory Cat("sanstats Options" ); |
27 | |
28 | static cl::opt<std::string> ClInputFile(cl::Positional, cl::Required, |
29 | cl::desc("<filename>" )); |
30 | |
31 | static cl::opt<bool> ClDemangle("demangle" , cl::init(Val: false), |
32 | cl::desc("Print demangled function name" ), |
33 | cl::cat(Cat)); |
34 | |
35 | inline uint64_t KindFromData(uint64_t Data, char SizeofPtr) { |
36 | return Data >> (SizeofPtr * 8 - kSanitizerStatKindBits); |
37 | } |
38 | |
39 | inline uint64_t CountFromData(uint64_t Data, char SizeofPtr) { |
40 | return Data & ((1ull << (SizeofPtr * 8 - kSanitizerStatKindBits)) - 1); |
41 | } |
42 | |
43 | static uint64_t ReadLE(char Size, const char *Begin, const char *End) { |
44 | uint64_t Result = 0; |
45 | char Pos = 0; |
46 | while (Begin < End && Pos != Size) { |
47 | Result |= uint64_t(uint8_t(*Begin)) << (Pos * 8); |
48 | ++Begin; |
49 | ++Pos; |
50 | } |
51 | return Result; |
52 | } |
53 | |
54 | static const char *ReadModule(char SizeofPtr, const char *Begin, |
55 | const char *End) { |
56 | const char *FilenameBegin = Begin; |
57 | while (Begin != End && *Begin) |
58 | ++Begin; |
59 | if (Begin == End) |
60 | return nullptr; |
61 | std::string Filename(FilenameBegin, Begin - FilenameBegin); |
62 | |
63 | if (!llvm::sys::fs::exists(Path: Filename)) |
64 | Filename = std::string(llvm::sys::path::parent_path(path: ClInputFile)) + |
65 | std::string(llvm::sys::path::filename(path: Filename)); |
66 | |
67 | ++Begin; |
68 | if (Begin == End) |
69 | return nullptr; |
70 | |
71 | symbolize::LLVMSymbolizer::Options SymbolizerOptions; |
72 | SymbolizerOptions.Demangle = ClDemangle; |
73 | SymbolizerOptions.UseSymbolTable = true; |
74 | symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions); |
75 | |
76 | while (true) { |
77 | uint64_t Addr = ReadLE(Size: SizeofPtr, Begin, End); |
78 | Begin += SizeofPtr; |
79 | uint64_t Data = ReadLE(Size: SizeofPtr, Begin, End); |
80 | Begin += SizeofPtr; |
81 | |
82 | if (Begin > End) |
83 | return nullptr; |
84 | if (Addr == 0 && Data == 0) |
85 | return Begin; |
86 | if (Begin == End) |
87 | return nullptr; |
88 | |
89 | // As the instrumentation tracks the return address and not |
90 | // the address of the call to `__sanitizer_stat_report` we |
91 | // remove one from the address to get the correct DI. |
92 | // TODO: it would be neccessary to set proper section index here. |
93 | // object::SectionedAddress::UndefSection works for only absolute addresses. |
94 | if (Expected<DILineInfo> LineInfo = Symbolizer.symbolizeCode( |
95 | ModuleName: Filename, ModuleOffset: {.Address: Addr - 1, .SectionIndex: object::SectionedAddress::UndefSection})) { |
96 | llvm::outs() << format_hex(N: Addr - 1, Width: 18) << ' ' << LineInfo->FileName |
97 | << ':' << LineInfo->Line << ' ' << LineInfo->FunctionName |
98 | << ' '; |
99 | } else { |
100 | logAllUnhandledErrors(E: LineInfo.takeError(), OS&: llvm::outs(), ErrorBanner: "<error> " ); |
101 | } |
102 | |
103 | switch (KindFromData(Data, SizeofPtr)) { |
104 | case SanStat_CFI_VCall: |
105 | llvm::outs() << "cfi-vcall" ; |
106 | break; |
107 | case SanStat_CFI_NVCall: |
108 | llvm::outs() << "cfi-nvcall" ; |
109 | break; |
110 | case SanStat_CFI_DerivedCast: |
111 | llvm::outs() << "cfi-derived-cast" ; |
112 | break; |
113 | case SanStat_CFI_UnrelatedCast: |
114 | llvm::outs() << "cfi-unrelated-cast" ; |
115 | break; |
116 | case SanStat_CFI_ICall: |
117 | llvm::outs() << "cfi-icall" ; |
118 | break; |
119 | default: |
120 | llvm::outs() << "<unknown>" ; |
121 | break; |
122 | } |
123 | |
124 | llvm::outs() << " " << CountFromData(Data, SizeofPtr) << '\n'; |
125 | } |
126 | } |
127 | |
128 | int main(int argc, char **argv) { |
129 | cl::HideUnrelatedOptions(Category&: Cat); |
130 | cl::ParseCommandLineOptions(argc, argv, |
131 | Overview: "Sanitizer Statistics Processing Tool" ); |
132 | |
133 | ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile( |
134 | Filename: ClInputFile, /*IsText=*/false, /*RequiresNullTerminator=*/false); |
135 | if (!MBOrErr) { |
136 | errs() << argv[0] << ": " << ClInputFile << ": " |
137 | << MBOrErr.getError().message() << '\n'; |
138 | return 1; |
139 | } |
140 | std::unique_ptr<MemoryBuffer> MB = std::move(MBOrErr.get()); |
141 | const char *Begin = MB->getBufferStart(), *End = MB->getBufferEnd(); |
142 | if (Begin == End) { |
143 | errs() << argv[0] << ": " << ClInputFile << ": short read\n" ; |
144 | return 1; |
145 | } |
146 | char SizeofPtr = *Begin++; |
147 | while (Begin != End) { |
148 | Begin = ReadModule(SizeofPtr, Begin, End); |
149 | if (Begin == nullptr) { |
150 | errs() << argv[0] << ": " << ClInputFile << ": short read\n" ; |
151 | return 1; |
152 | } |
153 | assert(Begin <= End); |
154 | } |
155 | } |
156 | |