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