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