options_parser.cpp 7.2 KB

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