259 lines · cpp
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-exception6//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 19namespace {20enum 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 31class OptionParser {32public: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 40private:41 // Calculate at compile-time how many options are available.42#define GWP_ASAN_OPTION(...) +143 static constexpr size_t MaxOptions = 044#include "gwp_asan/options.inc"45 ;46#undef GWP_ASAN_OPTION47 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 66void 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 73bool isSeparator(char C) {74 return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||75 C == '\r';76}77 78bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }79 80void OptionParser::skipWhitespace() {81 while (isSeparator(Buffer[Pos]))82 ++Pos;83}84 85bool 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 quote109 } else {110 while (!isSeparatorOrNull(Buffer[Pos]))111 ++Pos;112 Value = Buffer + ValueStart;113 }114 115 return setOptionToValue(Name, Value);116}117 118void 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 130void OptionParser::parseString(const char *S) {131 if (!S)132 return;133 Buffer = S;134 Pos = 0;135 parseOptions();136}137 138bool 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 152bool 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 185void 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 196void 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_OPTION202}203 204const char *getGwpAsanDefaultOptions() {205 return (__gwp_asan_default_options) ? __gwp_asan_default_options() : "";206}207 208gwp_asan::options::Options *getOptionsInternal() {209 static gwp_asan::options::Options GwpAsanOptions;210 return &GwpAsanOptions;211}212} // anonymous namespace213 214namespace gwp_asan {215namespace options {216 217void 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 251void initOptions(Printf_t PrintfForWarnings) {252 initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings);253}254 255Options &getOptions() { return *getOptionsInternal(); }256 257} // namespace options258} // namespace gwp_asan259