1 //===-- options_parser.cpp --------------------------------------*- C++ -*-===// 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 #include "gwp_asan/optional/options_parser.h" 10 #include "gwp_asan/optional/printf.h" 11 #include "gwp_asan/utilities.h" 12 13 #include <assert.h> 14 #include <stdarg.h> 15 #include <stdint.h> 16 #include <stdlib.h> 17 #include <string.h> 18 19 namespace { 20 enum class OptionType : uint8_t { 21 OT_bool, 22 OT_int, 23 }; 24 25 #define InvokeIfNonNull(Printf, ...) \ 26 do { \ 27 if (Printf) \ 28 Printf(__VA_ARGS__); \ 29 } while (0); 30 31 class OptionParser { 32 public: 33 explicit OptionParser(gwp_asan::Printf_t PrintfForWarnings) 34 : Printf(PrintfForWarnings) {} 35 void registerOption(const char *Name, const char *Desc, OptionType Type, 36 void *Var); 37 void parseString(const char *S); 38 void printOptionDescriptions(); 39 40 private: 41 // Calculate at compile-time how many options are available. 42 #define GWP_ASAN_OPTION(...) +1 43 static constexpr size_t MaxOptions = 0 44 #include "gwp_asan/options.inc" 45 ; 46 #undef GWP_ASAN_OPTION 47 48 struct Option { 49 const char *Name; 50 const char *Desc; 51 OptionType Type; 52 void *Var; 53 } Options[MaxOptions]; 54 55 size_t NumberOfOptions = 0; 56 const char *Buffer = nullptr; 57 uintptr_t Pos = 0; 58 gwp_asan::Printf_t Printf = nullptr; 59 60 void skipWhitespace(); 61 void parseOptions(); 62 bool parseOption(); 63 bool setOptionToValue(const char *Name, const char *Value); 64 }; 65 66 void OptionParser::printOptionDescriptions() { 67 InvokeIfNonNull(Printf, "GWP-ASan: Available options:\n"); 68 for (size_t I = 0; I < NumberOfOptions; ++I) 69 InvokeIfNonNull(Printf, "\t%s\n\t\t- %s\n", Options[I].Name, 70 Options[I].Desc); 71 } 72 73 bool isSeparator(char C) { 74 return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' || 75 C == '\r'; 76 } 77 78 bool isSeparatorOrNull(char C) { return !C || isSeparator(C); } 79 80 void OptionParser::skipWhitespace() { 81 while (isSeparator(Buffer[Pos])) 82 ++Pos; 83 } 84 85 bool OptionParser::parseOption() { 86 const uintptr_t NameStart = Pos; 87 while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos])) 88 ++Pos; 89 90 const char *Name = Buffer + NameStart; 91 if (Buffer[Pos] != '=') { 92 InvokeIfNonNull(Printf, "GWP-ASan: Expected '=' when parsing option '%s'.", 93 Name); 94 return false; 95 } 96 const uintptr_t ValueStart = ++Pos; 97 const char *Value; 98 if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') { 99 const char Quote = Buffer[Pos++]; 100 while (Buffer[Pos] != 0 && Buffer[Pos] != Quote) 101 ++Pos; 102 if (Buffer[Pos] == 0) { 103 InvokeIfNonNull(Printf, "GWP-ASan: Unterminated string in option '%s'.", 104 Name); 105 return false; 106 } 107 Value = Buffer + ValueStart + 1; 108 ++Pos; // consume the closing quote 109 } else { 110 while (!isSeparatorOrNull(Buffer[Pos])) 111 ++Pos; 112 Value = Buffer + ValueStart; 113 } 114 115 return setOptionToValue(Name, Value); 116 } 117 118 void OptionParser::parseOptions() { 119 while (true) { 120 skipWhitespace(); 121 if (Buffer[Pos] == 0) 122 break; 123 if (!parseOption()) { 124 InvokeIfNonNull(Printf, "GWP-ASan: Options parsing failed.\n"); 125 return; 126 } 127 } 128 } 129 130 void OptionParser::parseString(const char *S) { 131 if (!S) 132 return; 133 Buffer = S; 134 Pos = 0; 135 parseOptions(); 136 } 137 138 bool parseBool(const char *Value, bool *b) { 139 if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 || 140 strncmp(Value, "false", 5) == 0) { 141 *b = false; 142 return true; 143 } 144 if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 || 145 strncmp(Value, "true", 4) == 0) { 146 *b = true; 147 return true; 148 } 149 return false; 150 } 151 152 bool OptionParser::setOptionToValue(const char *Name, const char *Value) { 153 for (size_t I = 0; I < NumberOfOptions; ++I) { 154 const uintptr_t Len = strlen(Options[I].Name); 155 if (strncmp(Name, Options[I].Name, Len) != 0 || Name[Len] != '=') 156 continue; 157 bool Ok = false; 158 switch (Options[I].Type) { 159 case OptionType::OT_bool: 160 Ok = parseBool(Value, reinterpret_cast<bool *>(Options[I].Var)); 161 if (!Ok) 162 InvokeIfNonNull( 163 Printf, "GWP-ASan: Invalid boolean value '%s' for option '%s'.\n", 164 Value, Options[I].Name); 165 break; 166 case OptionType::OT_int: 167 char *ValueEnd; 168 *reinterpret_cast<int *>(Options[I].Var) = 169 static_cast<int>(strtol(Value, &ValueEnd, 10)); 170 Ok = 171 *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd); 172 if (!Ok) 173 InvokeIfNonNull( 174 Printf, "GWP-ASan: Invalid integer value '%s' for option '%s'.\n", 175 Value, Options[I].Name); 176 break; 177 } 178 return Ok; 179 } 180 181 InvokeIfNonNull(Printf, "GWP-ASan: Unknown option '%s'.", Name); 182 return true; 183 } 184 185 void OptionParser::registerOption(const char *Name, const char *Desc, 186 OptionType Type, void *Var) { 187 assert(NumberOfOptions < MaxOptions && 188 "GWP-ASan Error: Ran out of space for options.\n"); 189 Options[NumberOfOptions].Name = Name; 190 Options[NumberOfOptions].Desc = Desc; 191 Options[NumberOfOptions].Type = Type; 192 Options[NumberOfOptions].Var = Var; 193 ++NumberOfOptions; 194 } 195 196 void registerGwpAsanOptions(OptionParser *parser, 197 gwp_asan::options::Options *o) { 198 #define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \ 199 parser->registerOption(#Name, Description, OptionType::OT_##Type, &o->Name); 200 #include "gwp_asan/options.inc" 201 #undef GWP_ASAN_OPTION 202 } 203 204 const char *getGwpAsanDefaultOptions() { 205 return (__gwp_asan_default_options) ? __gwp_asan_default_options() : ""; 206 } 207 208 gwp_asan::options::Options *getOptionsInternal() { 209 static gwp_asan::options::Options GwpAsanOptions; 210 return &GwpAsanOptions; 211 } 212 } // anonymous namespace 213 214 namespace gwp_asan { 215 namespace options { 216 217 void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings) { 218 Options *o = getOptionsInternal(); 219 o->setDefaults(); 220 221 OptionParser Parser(PrintfForWarnings); 222 registerGwpAsanOptions(&Parser, o); 223 224 // Override from the weak function definition in this executable. 225 Parser.parseString(getGwpAsanDefaultOptions()); 226 227 // Override from the provided options string. 228 Parser.parseString(OptionsStr); 229 230 if (o->help) 231 Parser.printOptionDescriptions(); 232 233 if (!o->Enabled) 234 return; 235 236 if (o->MaxSimultaneousAllocations <= 0) { 237 InvokeIfNonNull( 238 PrintfForWarnings, 239 "GWP-ASan ERROR: MaxSimultaneousAllocations must be > 0 when GWP-ASan " 240 "is enabled.\n"); 241 o->Enabled = false; 242 } 243 if (o->SampleRate <= 0) { 244 InvokeIfNonNull( 245 PrintfForWarnings, 246 "GWP-ASan ERROR: SampleRate must be > 0 when GWP-ASan is enabled.\n"); 247 o->Enabled = false; 248 } 249 } 250 251 void initOptions(Printf_t PrintfForWarnings) { 252 initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings); 253 } 254 255 Options &getOptions() { return *getOptionsInternal(); } 256 257 } // namespace options 258 } // namespace gwp_asan 259