1 //===- CheriSetBounds.cpp - Functions to log information on CSetBounds ----===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This family of functions perform various local transformations to the
11 // program.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "llvm/Support/CheriSetBounds.h"
16 #include "llvm/Support/CommandLine.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/YAMLParser.h"
19
20 #include <sys/file.h>
21 #include <unistd.h>
22
23 static llvm::cl::opt<llvm::cheri::StatsFormat, true> CollectSetBoundsStats(
24 "collect-csetbounds-stats", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
25 llvm::cl::desc("Collect statistics on CSetBounds uses"),
26 llvm::cl::location(llvm::cheri::ShouldCollectCSetBoundsStats),
27 llvm::cl::values(
28 clEnumValN(llvm::cheri::StatsOff, "off", "Do not collect statistics"),
29 clEnumValN(llvm::cheri::StatsCSV, "csv", "Print stats in CSV format"),
30 clEnumValN(llvm::cheri::StatsJSON, "json",
31 "Print stats in JSON format")));
32
33 llvm::cl::opt<std::string>
34 SetBoundsOutput("collect-csetbounds-output", llvm::cl::ZeroOrMore,
35 llvm::cl::Hidden,
36 llvm::cl::desc("output file for CSetBounds statistics"));
37
38 namespace llvm {
39 namespace cheri {
40
41 StatsFormat ShouldCollectCSetBoundsStats = StatsOff;
42 ManagedStatic<CSetBoundsStatistics> CSetBoundsStats;
43
add(Align KnownAlignment,Optional<uint64_t> KnownSize,StringRef Pass,SetBoundsPointerSource Kind,const Twine & Details,std::string SourceLoc,Optional<uint64_t> SizeMultipleOf)44 void CSetBoundsStatistics::add(Align KnownAlignment,
45 Optional<uint64_t> KnownSize, StringRef Pass,
46 SetBoundsPointerSource Kind,
47 const Twine &Details, std::string SourceLoc,
48 Optional<uint64_t> SizeMultipleOf) {
49 Entries.push_back({KnownSize, SizeMultipleOf, KnownAlignment, Kind,
50 std::move(SourceLoc), Pass.str(), Details.str()});
51 }
52
outputFile()53 StringRef CSetBoundsStatistics::outputFile() { return SetBoundsOutput; }
54
print(StatsOutputFile & S,StringRef MainFile)55 void CSetBoundsStatistics::print(StatsOutputFile &S, StringRef MainFile) {
56 print(S.stream(), MainFile, S.size() == 0);
57 }
58
print(llvm::raw_ostream & OS,StringRef MainFile,bool PrintHeader)59 void CSetBoundsStatistics::print(llvm::raw_ostream &OS, StringRef MainFile,
60 bool PrintHeader) {
61 if (ShouldCollectCSetBoundsStats == StatsJSON) {
62 OS << "{ \"csetbounds_stats\": {";
63 OS << "\n\t\"count\": " << Entries.size() << ',';
64 OS << "\n\t\"details\": [";
65 bool Comma = false;
66 for (const Entry &E : Entries) {
67 if (!Comma) {
68 Comma = true;
69 OS << "\n\t{";
70 } else {
71 OS << ",\n\t{";
72 }
73 OS << "\n\t\t\"alignment\": " << E.KnownAlignment.value();
74 if (E.RequestedSize)
75 OS << ",\n\t\t\"size\": " << *E.RequestedSize;
76 else
77 OS << ",\n\t\t\"size\": null,";
78 OS << ",\n\t\t\"location\": \"" << llvm::yaml::escape(E.SourceLocation)
79 << '"';
80 if (!E.Pass.empty())
81 OS << ",\n\t\t\"pass\": \"" << llvm::yaml::escape(E.Pass) << '"';
82 if (!E.Details.empty())
83 OS << ",\n\t\t\"details\": \"" << llvm::yaml::escape(E.Details) << '"';
84 OS << "\n\t}";
85 }
86 OS << "\n\t]\n} }\n";
87 } else if (ShouldCollectCSetBoundsStats == StatsCSV) {
88 if (PrintHeader) {
89 OS << "alignment_bits,size,kind,source_loc,compiler_pass,details\n";
90 }
91 for (const Entry &E : Entries) {
92 // The analysis scripts expect alignment as a power of two instead of the
93 // value
94 unsigned AlignmentBits = Log2(E.KnownAlignment);
95 OS << AlignmentBits << ',';
96 if (E.RequestedSize) {
97 OS << *E.RequestedSize;
98 } else {
99 // IF the size is unknown
100 if (E.RequestedSizeMultipleOf)
101 OS << "<unknown multiple of " << *E.RequestedSizeMultipleOf << ">";
102 else
103 OS << "<unknown>";
104 }
105
106 if (E.PointerKind == SetBoundsPointerSource::Stack) {
107 OS << ",s";
108 } else if (E.PointerKind == SetBoundsPointerSource::Heap) {
109 OS << ",h";
110 } else if (E.PointerKind == SetBoundsPointerSource::SubObject) {
111 OS << ",o";
112 } else if (E.PointerKind == SetBoundsPointerSource::GlobalVar) {
113 OS << ",g";
114 } else if (E.PointerKind == SetBoundsPointerSource::CodePointer) {
115 OS << ",c";
116 } else {
117 OS << ",?";
118 }
119 OS << ",\"" << llvm::yaml::escape(E.SourceLocation) << '"';
120 OS << ",\"" << llvm::yaml::escape(E.Pass) << '"';
121 OS << ",\"" << llvm::yaml::escape(E.Details) << '"';
122 OS << "\n";
123 }
124 }
125 }
126
CSetBoundsStatistics()127 CSetBoundsStatistics::CSetBoundsStatistics() {
128 assert(ShouldCollectCSetBoundsStats && "Created in invalid state");
129 }
130
~CSetBoundsStatistics()131 CSetBoundsStatistics::~CSetBoundsStatistics() {
132 // TODO: dump to stderr if not dumped already?
133 }
134
size()135 uint64_t StatsOutputFile::size() {
136 sys::fs::file_status s;
137 if (sys::fs::status(FD, s)) {
138 return 0;
139 }
140 return s.getSize();
141 }
142
~StatsOutputFile()143 StatsOutputFile::~StatsOutputFile() {
144 if (flock(FD, LOCK_UN) != 0 && errno != ENOTSUP) {
145 errs() << "WARNING: unlocking statistics FD failed. Should be "
146 "released automatically on exit anyway.\n";
147 }
148 }
149
150 std::unique_ptr<StatsOutputFile>
open(StringRef File,ErrorCallback OnOpenError,ErrorCallback OnLockError)151 StatsOutputFile::open(StringRef File, ErrorCallback OnOpenError,
152 ErrorCallback OnLockError) {
153 // raw_fd_ostream has no way of getting the FD and I don't want to change
154 // the interface just for this function to enable file locking since
155 // that would require a recompile of all of LLVM....
156 int StatsFD = STDERR_FILENO;
157 bool CloseOnStreamDelete = true;
158 if (!File.empty()) {
159 std::error_code EC = sys::fs::openFileForWrite(
160 File, StatsFD, sys::fs::CD_CreateAlways, sys::fs::OF_Append);
161 if (EC) {
162 OnOpenError(File, EC);
163 return nullptr;
164 }
165 } else {
166 File = "/dev/stderr";
167 CloseOnStreamDelete = false;
168 }
169 assert(StatsFD != -1);
170 if (flock(StatsFD, LOCK_EX) != 0 && errno != ENOTSUP) {
171 // Lock error is not fatal we just get mixed output in the stats file
172 OnLockError(File, std::error_code(errno, std::generic_category()));
173 }
174 // can't use make_unique here since the ctor is private
175 return std::unique_ptr<StatsOutputFile>(new StatsOutputFile(
176 StatsFD,
177 std::make_unique<llvm::raw_fd_ostream>(StatsFD, CloseOnStreamDelete)));
178 }
179
180 } // namespace cheri
181 } // namespace llvm
182