xref: /openbsd-src/gnu/llvm/compiler-rt/lib/gwp_asan/optional/options_parser.cpp (revision d89ec533011f513df1010f142a111086a0785f09)
13cab2bb3Spatrick //===-- options_parser.cpp --------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick 
93cab2bb3Spatrick #include "gwp_asan/optional/options_parser.h"
10*d89ec533Spatrick #include "gwp_asan/optional/printf.h"
11*d89ec533Spatrick #include "gwp_asan/utilities.h"
123cab2bb3Spatrick 
13*d89ec533Spatrick #include <assert.h>
143cab2bb3Spatrick #include <stdarg.h>
153cab2bb3Spatrick #include <stdint.h>
163cab2bb3Spatrick #include <stdlib.h>
173cab2bb3Spatrick #include <string.h>
183cab2bb3Spatrick 
193cab2bb3Spatrick namespace {
20*d89ec533Spatrick enum class OptionType : uint8_t {
21*d89ec533Spatrick   OT_bool,
22*d89ec533Spatrick   OT_int,
23*d89ec533Spatrick };
24*d89ec533Spatrick 
25*d89ec533Spatrick #define InvokeIfNonNull(Printf, ...)                                           \
26*d89ec533Spatrick   do {                                                                         \
27*d89ec533Spatrick     if (Printf)                                                                \
28*d89ec533Spatrick       Printf(__VA_ARGS__);                                                     \
29*d89ec533Spatrick   } while (0);
30*d89ec533Spatrick 
31*d89ec533Spatrick class OptionParser {
32*d89ec533Spatrick public:
OptionParser(gwp_asan::Printf_t PrintfForWarnings)33*d89ec533Spatrick   explicit OptionParser(gwp_asan::Printf_t PrintfForWarnings)
34*d89ec533Spatrick       : Printf(PrintfForWarnings) {}
35*d89ec533Spatrick   void registerOption(const char *Name, const char *Desc, OptionType Type,
36*d89ec533Spatrick                       void *Var);
37*d89ec533Spatrick   void parseString(const char *S);
38*d89ec533Spatrick   void printOptionDescriptions();
39*d89ec533Spatrick 
40*d89ec533Spatrick private:
41*d89ec533Spatrick   // Calculate at compile-time how many options are available.
42*d89ec533Spatrick #define GWP_ASAN_OPTION(...) +1
43*d89ec533Spatrick   static constexpr size_t MaxOptions = 0
443cab2bb3Spatrick #include "gwp_asan/options.inc"
45*d89ec533Spatrick       ;
463cab2bb3Spatrick #undef GWP_ASAN_OPTION
47*d89ec533Spatrick 
48*d89ec533Spatrick   struct Option {
49*d89ec533Spatrick     const char *Name;
50*d89ec533Spatrick     const char *Desc;
51*d89ec533Spatrick     OptionType Type;
52*d89ec533Spatrick     void *Var;
53*d89ec533Spatrick   } Options[MaxOptions];
54*d89ec533Spatrick 
55*d89ec533Spatrick   size_t NumberOfOptions = 0;
56*d89ec533Spatrick   const char *Buffer = nullptr;
57*d89ec533Spatrick   uintptr_t Pos = 0;
58*d89ec533Spatrick   gwp_asan::Printf_t Printf = nullptr;
59*d89ec533Spatrick 
60*d89ec533Spatrick   void skipWhitespace();
61*d89ec533Spatrick   void parseOptions();
62*d89ec533Spatrick   bool parseOption();
63*d89ec533Spatrick   bool setOptionToValue(const char *Name, const char *Value);
64*d89ec533Spatrick };
65*d89ec533Spatrick 
printOptionDescriptions()66*d89ec533Spatrick void OptionParser::printOptionDescriptions() {
67*d89ec533Spatrick   InvokeIfNonNull(Printf, "GWP-ASan: Available options:\n");
68*d89ec533Spatrick   for (size_t I = 0; I < NumberOfOptions; ++I)
69*d89ec533Spatrick     InvokeIfNonNull(Printf, "\t%s\n\t\t- %s\n", Options[I].Name,
70*d89ec533Spatrick                     Options[I].Desc);
713cab2bb3Spatrick }
723cab2bb3Spatrick 
isSeparator(char C)73*d89ec533Spatrick bool isSeparator(char C) {
74*d89ec533Spatrick   return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
75*d89ec533Spatrick          C == '\r';
76*d89ec533Spatrick }
77*d89ec533Spatrick 
isSeparatorOrNull(char C)78*d89ec533Spatrick bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
79*d89ec533Spatrick 
skipWhitespace()80*d89ec533Spatrick void OptionParser::skipWhitespace() {
81*d89ec533Spatrick   while (isSeparator(Buffer[Pos]))
82*d89ec533Spatrick     ++Pos;
83*d89ec533Spatrick }
84*d89ec533Spatrick 
parseOption()85*d89ec533Spatrick bool OptionParser::parseOption() {
86*d89ec533Spatrick   const uintptr_t NameStart = Pos;
87*d89ec533Spatrick   while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
88*d89ec533Spatrick     ++Pos;
89*d89ec533Spatrick 
90*d89ec533Spatrick   const char *Name = Buffer + NameStart;
91*d89ec533Spatrick   if (Buffer[Pos] != '=') {
92*d89ec533Spatrick     InvokeIfNonNull(Printf, "GWP-ASan: Expected '=' when parsing option '%s'.",
93*d89ec533Spatrick                     Name);
94*d89ec533Spatrick     return false;
95*d89ec533Spatrick   }
96*d89ec533Spatrick   const uintptr_t ValueStart = ++Pos;
97*d89ec533Spatrick   const char *Value;
98*d89ec533Spatrick   if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
99*d89ec533Spatrick     const char Quote = Buffer[Pos++];
100*d89ec533Spatrick     while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
101*d89ec533Spatrick       ++Pos;
102*d89ec533Spatrick     if (Buffer[Pos] == 0) {
103*d89ec533Spatrick       InvokeIfNonNull(Printf, "GWP-ASan: Unterminated string in option '%s'.",
104*d89ec533Spatrick                       Name);
105*d89ec533Spatrick       return false;
106*d89ec533Spatrick     }
107*d89ec533Spatrick     Value = Buffer + ValueStart + 1;
108*d89ec533Spatrick     ++Pos; // consume the closing quote
109*d89ec533Spatrick   } else {
110*d89ec533Spatrick     while (!isSeparatorOrNull(Buffer[Pos]))
111*d89ec533Spatrick       ++Pos;
112*d89ec533Spatrick     Value = Buffer + ValueStart;
113*d89ec533Spatrick   }
114*d89ec533Spatrick 
115*d89ec533Spatrick   return setOptionToValue(Name, Value);
116*d89ec533Spatrick }
117*d89ec533Spatrick 
parseOptions()118*d89ec533Spatrick void OptionParser::parseOptions() {
119*d89ec533Spatrick   while (true) {
120*d89ec533Spatrick     skipWhitespace();
121*d89ec533Spatrick     if (Buffer[Pos] == 0)
122*d89ec533Spatrick       break;
123*d89ec533Spatrick     if (!parseOption()) {
124*d89ec533Spatrick       InvokeIfNonNull(Printf, "GWP-ASan: Options parsing failed.\n");
125*d89ec533Spatrick       return;
126*d89ec533Spatrick     }
127*d89ec533Spatrick   }
128*d89ec533Spatrick }
129*d89ec533Spatrick 
parseString(const char * S)130*d89ec533Spatrick void OptionParser::parseString(const char *S) {
131*d89ec533Spatrick   if (!S)
132*d89ec533Spatrick     return;
133*d89ec533Spatrick   Buffer = S;
134*d89ec533Spatrick   Pos = 0;
135*d89ec533Spatrick   parseOptions();
136*d89ec533Spatrick }
137*d89ec533Spatrick 
parseBool(const char * Value,bool * b)138*d89ec533Spatrick bool parseBool(const char *Value, bool *b) {
139*d89ec533Spatrick   if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
140*d89ec533Spatrick       strncmp(Value, "false", 5) == 0) {
141*d89ec533Spatrick     *b = false;
142*d89ec533Spatrick     return true;
143*d89ec533Spatrick   }
144*d89ec533Spatrick   if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
145*d89ec533Spatrick       strncmp(Value, "true", 4) == 0) {
146*d89ec533Spatrick     *b = true;
147*d89ec533Spatrick     return true;
148*d89ec533Spatrick   }
149*d89ec533Spatrick   return false;
150*d89ec533Spatrick }
151*d89ec533Spatrick 
setOptionToValue(const char * Name,const char * Value)152*d89ec533Spatrick bool OptionParser::setOptionToValue(const char *Name, const char *Value) {
153*d89ec533Spatrick   for (size_t I = 0; I < NumberOfOptions; ++I) {
154*d89ec533Spatrick     const uintptr_t Len = strlen(Options[I].Name);
155*d89ec533Spatrick     if (strncmp(Name, Options[I].Name, Len) != 0 || Name[Len] != '=')
156*d89ec533Spatrick       continue;
157*d89ec533Spatrick     bool Ok = false;
158*d89ec533Spatrick     switch (Options[I].Type) {
159*d89ec533Spatrick     case OptionType::OT_bool:
160*d89ec533Spatrick       Ok = parseBool(Value, reinterpret_cast<bool *>(Options[I].Var));
161*d89ec533Spatrick       if (!Ok)
162*d89ec533Spatrick         InvokeIfNonNull(
163*d89ec533Spatrick             Printf, "GWP-ASan: Invalid boolean value '%s' for option '%s'.\n",
164*d89ec533Spatrick             Value, Options[I].Name);
165*d89ec533Spatrick       break;
166*d89ec533Spatrick     case OptionType::OT_int:
167*d89ec533Spatrick       char *ValueEnd;
168*d89ec533Spatrick       *reinterpret_cast<int *>(Options[I].Var) =
169*d89ec533Spatrick           static_cast<int>(strtol(Value, &ValueEnd, 10));
170*d89ec533Spatrick       Ok =
171*d89ec533Spatrick           *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd);
172*d89ec533Spatrick       if (!Ok)
173*d89ec533Spatrick         InvokeIfNonNull(
174*d89ec533Spatrick             Printf, "GWP-ASan: Invalid integer value '%s' for option '%s'.\n",
175*d89ec533Spatrick             Value, Options[I].Name);
176*d89ec533Spatrick       break;
177*d89ec533Spatrick     }
178*d89ec533Spatrick     return Ok;
179*d89ec533Spatrick   }
180*d89ec533Spatrick 
181*d89ec533Spatrick   InvokeIfNonNull(Printf, "GWP-ASan: Unknown option '%s'.", Name);
182*d89ec533Spatrick   return true;
183*d89ec533Spatrick }
184*d89ec533Spatrick 
registerOption(const char * Name,const char * Desc,OptionType Type,void * Var)185*d89ec533Spatrick void OptionParser::registerOption(const char *Name, const char *Desc,
186*d89ec533Spatrick                                   OptionType Type, void *Var) {
187*d89ec533Spatrick   assert(NumberOfOptions < MaxOptions &&
188*d89ec533Spatrick          "GWP-ASan Error: Ran out of space for options.\n");
189*d89ec533Spatrick   Options[NumberOfOptions].Name = Name;
190*d89ec533Spatrick   Options[NumberOfOptions].Desc = Desc;
191*d89ec533Spatrick   Options[NumberOfOptions].Type = Type;
192*d89ec533Spatrick   Options[NumberOfOptions].Var = Var;
193*d89ec533Spatrick   ++NumberOfOptions;
194*d89ec533Spatrick }
195*d89ec533Spatrick 
registerGwpAsanOptions(OptionParser * parser,gwp_asan::options::Options * o)196*d89ec533Spatrick void registerGwpAsanOptions(OptionParser *parser,
197*d89ec533Spatrick                             gwp_asan::options::Options *o) {
198*d89ec533Spatrick #define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
199*d89ec533Spatrick   parser->registerOption(#Name, Description, OptionType::OT_##Type, &o->Name);
200*d89ec533Spatrick #include "gwp_asan/options.inc"
201*d89ec533Spatrick #undef GWP_ASAN_OPTION
2023cab2bb3Spatrick }
2033cab2bb3Spatrick 
getGwpAsanDefaultOptions()2043cab2bb3Spatrick const char *getGwpAsanDefaultOptions() {
2053cab2bb3Spatrick   return (__gwp_asan_default_options) ? __gwp_asan_default_options() : "";
2063cab2bb3Spatrick }
2073cab2bb3Spatrick 
getOptionsInternal()208*d89ec533Spatrick gwp_asan::options::Options *getOptionsInternal() {
209*d89ec533Spatrick   static gwp_asan::options::Options GwpAsanOptions;
210*d89ec533Spatrick   return &GwpAsanOptions;
2113cab2bb3Spatrick }
2123cab2bb3Spatrick } // anonymous namespace
2133cab2bb3Spatrick 
214*d89ec533Spatrick namespace gwp_asan {
215*d89ec533Spatrick namespace options {
2163cab2bb3Spatrick 
initOptions(const char * OptionsStr,Printf_t PrintfForWarnings)217*d89ec533Spatrick void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings) {
2183cab2bb3Spatrick   Options *o = getOptionsInternal();
2193cab2bb3Spatrick   o->setDefaults();
2203cab2bb3Spatrick 
221*d89ec533Spatrick   OptionParser Parser(PrintfForWarnings);
222*d89ec533Spatrick   registerGwpAsanOptions(&Parser, o);
2233cab2bb3Spatrick 
224*d89ec533Spatrick   // Override from the weak function definition in this executable.
225*d89ec533Spatrick   Parser.parseString(getGwpAsanDefaultOptions());
2263cab2bb3Spatrick 
227*d89ec533Spatrick   // Override from the provided options string.
228*d89ec533Spatrick   Parser.parseString(OptionsStr);
2293cab2bb3Spatrick 
230*d89ec533Spatrick   if (o->help)
231*d89ec533Spatrick     Parser.printOptionDescriptions();
2323cab2bb3Spatrick 
2333cab2bb3Spatrick   if (!o->Enabled)
2343cab2bb3Spatrick     return;
2353cab2bb3Spatrick 
2363cab2bb3Spatrick   if (o->MaxSimultaneousAllocations <= 0) {
237*d89ec533Spatrick     InvokeIfNonNull(
238*d89ec533Spatrick         PrintfForWarnings,
239*d89ec533Spatrick         "GWP-ASan ERROR: MaxSimultaneousAllocations must be > 0 when GWP-ASan "
240*d89ec533Spatrick         "is enabled.\n");
241*d89ec533Spatrick     o->Enabled = false;
242*d89ec533Spatrick   }
243*d89ec533Spatrick   if (o->SampleRate <= 0) {
244*d89ec533Spatrick     InvokeIfNonNull(
245*d89ec533Spatrick         PrintfForWarnings,
246*d89ec533Spatrick         "GWP-ASan ERROR: SampleRate must be > 0 when GWP-ASan is enabled.\n");
247*d89ec533Spatrick     o->Enabled = false;
248*d89ec533Spatrick   }
2493cab2bb3Spatrick }
2503cab2bb3Spatrick 
initOptions(Printf_t PrintfForWarnings)251*d89ec533Spatrick void initOptions(Printf_t PrintfForWarnings) {
252*d89ec533Spatrick   initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings);
2533cab2bb3Spatrick }
2543cab2bb3Spatrick 
getOptions()2553cab2bb3Spatrick Options &getOptions() { return *getOptionsInternal(); }
2563cab2bb3Spatrick 
2573cab2bb3Spatrick } // namespace options
2583cab2bb3Spatrick } // namespace gwp_asan
259