259 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- options_parser.cpp --------------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "gwp_asan/optional/options_parser.h"
 | 
						|
#include "gwp_asan/optional/printf.h"
 | 
						|
#include "gwp_asan/utilities.h"
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
namespace {
 | 
						|
enum class OptionType : uint8_t {
 | 
						|
  OT_bool,
 | 
						|
  OT_int,
 | 
						|
};
 | 
						|
 | 
						|
#define InvokeIfNonNull(Printf, ...)                                           \
 | 
						|
  do {                                                                         \
 | 
						|
    if (Printf)                                                                \
 | 
						|
      Printf(__VA_ARGS__);                                                     \
 | 
						|
  } while (0);
 | 
						|
 | 
						|
class OptionParser {
 | 
						|
public:
 | 
						|
  explicit OptionParser(gwp_asan::Printf_t PrintfForWarnings)
 | 
						|
      : Printf(PrintfForWarnings) {}
 | 
						|
  void registerOption(const char *Name, const char *Desc, OptionType Type,
 | 
						|
                      void *Var);
 | 
						|
  void parseString(const char *S);
 | 
						|
  void printOptionDescriptions();
 | 
						|
 | 
						|
private:
 | 
						|
  // Calculate at compile-time how many options are available.
 | 
						|
#define GWP_ASAN_OPTION(...) +1
 | 
						|
  static constexpr size_t MaxOptions = 0
 | 
						|
#include "gwp_asan/options.inc"
 | 
						|
      ;
 | 
						|
#undef GWP_ASAN_OPTION
 | 
						|
 | 
						|
  struct Option {
 | 
						|
    const char *Name;
 | 
						|
    const char *Desc;
 | 
						|
    OptionType Type;
 | 
						|
    void *Var;
 | 
						|
  } Options[MaxOptions];
 | 
						|
 | 
						|
  size_t NumberOfOptions = 0;
 | 
						|
  const char *Buffer = nullptr;
 | 
						|
  uintptr_t Pos = 0;
 | 
						|
  gwp_asan::Printf_t Printf = nullptr;
 | 
						|
 | 
						|
  void skipWhitespace();
 | 
						|
  void parseOptions();
 | 
						|
  bool parseOption();
 | 
						|
  bool setOptionToValue(const char *Name, const char *Value);
 | 
						|
};
 | 
						|
 | 
						|
void OptionParser::printOptionDescriptions() {
 | 
						|
  InvokeIfNonNull(Printf, "GWP-ASan: Available options:\n");
 | 
						|
  for (size_t I = 0; I < NumberOfOptions; ++I)
 | 
						|
    InvokeIfNonNull(Printf, "\t%s\n\t\t- %s\n", Options[I].Name,
 | 
						|
                    Options[I].Desc);
 | 
						|
}
 | 
						|
 | 
						|
bool isSeparator(char C) {
 | 
						|
  return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
 | 
						|
         C == '\r';
 | 
						|
}
 | 
						|
 | 
						|
bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
 | 
						|
 | 
						|
void OptionParser::skipWhitespace() {
 | 
						|
  while (isSeparator(Buffer[Pos]))
 | 
						|
    ++Pos;
 | 
						|
}
 | 
						|
 | 
						|
bool OptionParser::parseOption() {
 | 
						|
  const uintptr_t NameStart = Pos;
 | 
						|
  while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
 | 
						|
    ++Pos;
 | 
						|
 | 
						|
  const char *Name = Buffer + NameStart;
 | 
						|
  if (Buffer[Pos] != '=') {
 | 
						|
    InvokeIfNonNull(Printf, "GWP-ASan: Expected '=' when parsing option '%s'.",
 | 
						|
                    Name);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  const uintptr_t ValueStart = ++Pos;
 | 
						|
  const char *Value;
 | 
						|
  if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
 | 
						|
    const char Quote = Buffer[Pos++];
 | 
						|
    while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
 | 
						|
      ++Pos;
 | 
						|
    if (Buffer[Pos] == 0) {
 | 
						|
      InvokeIfNonNull(Printf, "GWP-ASan: Unterminated string in option '%s'.",
 | 
						|
                      Name);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    Value = Buffer + ValueStart + 1;
 | 
						|
    ++Pos; // consume the closing quote
 | 
						|
  } else {
 | 
						|
    while (!isSeparatorOrNull(Buffer[Pos]))
 | 
						|
      ++Pos;
 | 
						|
    Value = Buffer + ValueStart;
 | 
						|
  }
 | 
						|
 | 
						|
  return setOptionToValue(Name, Value);
 | 
						|
}
 | 
						|
 | 
						|
void OptionParser::parseOptions() {
 | 
						|
  while (true) {
 | 
						|
    skipWhitespace();
 | 
						|
    if (Buffer[Pos] == 0)
 | 
						|
      break;
 | 
						|
    if (!parseOption()) {
 | 
						|
      InvokeIfNonNull(Printf, "GWP-ASan: Options parsing failed.\n");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void OptionParser::parseString(const char *S) {
 | 
						|
  if (!S)
 | 
						|
    return;
 | 
						|
  Buffer = S;
 | 
						|
  Pos = 0;
 | 
						|
  parseOptions();
 | 
						|
}
 | 
						|
 | 
						|
bool parseBool(const char *Value, bool *b) {
 | 
						|
  if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
 | 
						|
      strncmp(Value, "false", 5) == 0) {
 | 
						|
    *b = false;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
 | 
						|
      strncmp(Value, "true", 4) == 0) {
 | 
						|
    *b = true;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool OptionParser::setOptionToValue(const char *Name, const char *Value) {
 | 
						|
  for (size_t I = 0; I < NumberOfOptions; ++I) {
 | 
						|
    const uintptr_t Len = strlen(Options[I].Name);
 | 
						|
    if (strncmp(Name, Options[I].Name, Len) != 0 || Name[Len] != '=')
 | 
						|
      continue;
 | 
						|
    bool Ok = false;
 | 
						|
    switch (Options[I].Type) {
 | 
						|
    case OptionType::OT_bool:
 | 
						|
      Ok = parseBool(Value, reinterpret_cast<bool *>(Options[I].Var));
 | 
						|
      if (!Ok)
 | 
						|
        InvokeIfNonNull(
 | 
						|
            Printf, "GWP-ASan: Invalid boolean value '%s' for option '%s'.\n",
 | 
						|
            Value, Options[I].Name);
 | 
						|
      break;
 | 
						|
    case OptionType::OT_int:
 | 
						|
      char *ValueEnd;
 | 
						|
      *reinterpret_cast<int *>(Options[I].Var) =
 | 
						|
          static_cast<int>(strtol(Value, &ValueEnd, 10));
 | 
						|
      Ok =
 | 
						|
          *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd);
 | 
						|
      if (!Ok)
 | 
						|
        InvokeIfNonNull(
 | 
						|
            Printf, "GWP-ASan: Invalid integer value '%s' for option '%s'.\n",
 | 
						|
            Value, Options[I].Name);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    return Ok;
 | 
						|
  }
 | 
						|
 | 
						|
  InvokeIfNonNull(Printf, "GWP-ASan: Unknown option '%s'.", Name);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void OptionParser::registerOption(const char *Name, const char *Desc,
 | 
						|
                                  OptionType Type, void *Var) {
 | 
						|
  assert(NumberOfOptions < MaxOptions &&
 | 
						|
         "GWP-ASan Error: Ran out of space for options.\n");
 | 
						|
  Options[NumberOfOptions].Name = Name;
 | 
						|
  Options[NumberOfOptions].Desc = Desc;
 | 
						|
  Options[NumberOfOptions].Type = Type;
 | 
						|
  Options[NumberOfOptions].Var = Var;
 | 
						|
  ++NumberOfOptions;
 | 
						|
}
 | 
						|
 | 
						|
void registerGwpAsanOptions(OptionParser *parser,
 | 
						|
                            gwp_asan::options::Options *o) {
 | 
						|
#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
 | 
						|
  parser->registerOption(#Name, Description, OptionType::OT_##Type, &o->Name);
 | 
						|
#include "gwp_asan/options.inc"
 | 
						|
#undef GWP_ASAN_OPTION
 | 
						|
}
 | 
						|
 | 
						|
const char *getGwpAsanDefaultOptions() {
 | 
						|
  return (__gwp_asan_default_options) ? __gwp_asan_default_options() : "";
 | 
						|
}
 | 
						|
 | 
						|
gwp_asan::options::Options *getOptionsInternal() {
 | 
						|
  static gwp_asan::options::Options GwpAsanOptions;
 | 
						|
  return &GwpAsanOptions;
 | 
						|
}
 | 
						|
} // anonymous namespace
 | 
						|
 | 
						|
namespace gwp_asan {
 | 
						|
namespace options {
 | 
						|
 | 
						|
void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings) {
 | 
						|
  Options *o = getOptionsInternal();
 | 
						|
  o->setDefaults();
 | 
						|
 | 
						|
  OptionParser Parser(PrintfForWarnings);
 | 
						|
  registerGwpAsanOptions(&Parser, o);
 | 
						|
 | 
						|
  // Override from the weak function definition in this executable.
 | 
						|
  Parser.parseString(getGwpAsanDefaultOptions());
 | 
						|
 | 
						|
  // Override from the provided options string.
 | 
						|
  Parser.parseString(OptionsStr);
 | 
						|
 | 
						|
  if (o->help)
 | 
						|
    Parser.printOptionDescriptions();
 | 
						|
 | 
						|
  if (!o->Enabled)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (o->MaxSimultaneousAllocations <= 0) {
 | 
						|
    InvokeIfNonNull(
 | 
						|
        PrintfForWarnings,
 | 
						|
        "GWP-ASan ERROR: MaxSimultaneousAllocations must be > 0 when GWP-ASan "
 | 
						|
        "is enabled.\n");
 | 
						|
    o->Enabled = false;
 | 
						|
  }
 | 
						|
  if (o->SampleRate <= 0) {
 | 
						|
    InvokeIfNonNull(
 | 
						|
        PrintfForWarnings,
 | 
						|
        "GWP-ASan ERROR: SampleRate must be > 0 when GWP-ASan is enabled.\n");
 | 
						|
    o->Enabled = false;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void initOptions(Printf_t PrintfForWarnings) {
 | 
						|
  initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings);
 | 
						|
}
 | 
						|
 | 
						|
Options &getOptions() { return *getOptionsInternal(); }
 | 
						|
 | 
						|
} // namespace options
 | 
						|
} // namespace gwp_asan
 |