brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · 6023412 Raw
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