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