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