1 //===-- options_parser.cpp --------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "gwp_asan/optional/options_parser.h"
10 #include "gwp_asan/optional/printf.h"
11 #include "gwp_asan/utilities.h"
20 enum class OptionType
: uint8_t {
25 #define InvokeIfNonNull(Printf, ...) \
28 Printf(__VA_ARGS__); \
33 explicit OptionParser(gwp_asan::Printf_t PrintfForWarnings
)
34 : Printf(PrintfForWarnings
) {}
35 void registerOption(const char *Name
, const char *Desc
, OptionType Type
,
37 void parseString(const char *S
);
38 void printOptionDescriptions();
41 // Calculate at compile-time how many options are available.
42 #define GWP_ASAN_OPTION(...) +1
43 static constexpr size_t MaxOptions
= 0
44 #include "gwp_asan/options.inc"
46 #undef GWP_ASAN_OPTION
53 } Options
[MaxOptions
];
55 size_t NumberOfOptions
= 0;
56 const char *Buffer
= nullptr;
58 gwp_asan::Printf_t Printf
= nullptr;
60 void skipWhitespace();
63 bool setOptionToValue(const char *Name
, const char *Value
);
66 void 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
,
73 bool isSeparator(char C
) {
74 return C
== ' ' || C
== ',' || C
== ':' || C
== '\n' || C
== '\t' ||
78 bool isSeparatorOrNull(char C
) { return !C
|| isSeparator(C
); }
80 void OptionParser::skipWhitespace() {
81 while (isSeparator(Buffer
[Pos
]))
85 bool OptionParser::parseOption() {
86 const uintptr_t NameStart
= Pos
;
87 while (Buffer
[Pos
] != '=' && !isSeparatorOrNull(Buffer
[Pos
]))
90 const char *Name
= Buffer
+ NameStart
;
91 if (Buffer
[Pos
] != '=') {
92 InvokeIfNonNull(Printf
, "GWP-ASan: Expected '=' when parsing option '%s'.",
96 const uintptr_t ValueStart
= ++Pos
;
98 if (Buffer
[Pos
] == '\'' || Buffer
[Pos
] == '"') {
99 const char Quote
= Buffer
[Pos
++];
100 while (Buffer
[Pos
] != 0 && Buffer
[Pos
] != Quote
)
102 if (Buffer
[Pos
] == 0) {
103 InvokeIfNonNull(Printf
, "GWP-ASan: Unterminated string in option '%s'.",
107 Value
= Buffer
+ ValueStart
+ 1;
108 ++Pos
; // consume the closing quote
110 while (!isSeparatorOrNull(Buffer
[Pos
]))
112 Value
= Buffer
+ ValueStart
;
115 return setOptionToValue(Name
, Value
);
118 void OptionParser::parseOptions() {
121 if (Buffer
[Pos
] == 0)
123 if (!parseOption()) {
124 InvokeIfNonNull(Printf
, "GWP-ASan: Options parsing failed.\n");
130 void OptionParser::parseString(const char *S
) {
138 bool parseBool(const char *Value
, bool *b
) {
139 if (strncmp(Value
, "0", 1) == 0 || strncmp(Value
, "no", 2) == 0 ||
140 strncmp(Value
, "false", 5) == 0) {
144 if (strncmp(Value
, "1", 1) == 0 || strncmp(Value
, "yes", 3) == 0 ||
145 strncmp(Value
, "true", 4) == 0) {
152 bool 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
] != '=')
158 switch (Options
[I
].Type
) {
159 case OptionType::OT_bool
:
160 Ok
= parseBool(Value
, reinterpret_cast<bool *>(Options
[I
].Var
));
163 Printf
, "GWP-ASan: Invalid boolean value '%s' for option '%s'.\n",
164 Value
, Options
[I
].Name
);
166 case OptionType::OT_int
:
168 *reinterpret_cast<int *>(Options
[I
].Var
) =
169 static_cast<int>(strtol(Value
, &ValueEnd
, 10));
171 *ValueEnd
== '"' || *ValueEnd
== '\'' || isSeparatorOrNull(*ValueEnd
);
174 Printf
, "GWP-ASan: Invalid integer value '%s' for option '%s'.\n",
175 Value
, Options
[I
].Name
);
181 InvokeIfNonNull(Printf
, "GWP-ASan: Unknown option '%s'.", Name
);
185 void 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
;
196 void 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_OPTION
204 const char *getGwpAsanDefaultOptions() {
205 return (__gwp_asan_default_options
) ? __gwp_asan_default_options() : "";
208 gwp_asan::options::Options
*getOptionsInternal() {
209 static gwp_asan::options::Options GwpAsanOptions
;
210 return &GwpAsanOptions
;
212 } // anonymous namespace
217 void initOptions(const char *OptionsStr
, Printf_t PrintfForWarnings
) {
218 Options
*o
= getOptionsInternal();
221 OptionParser
Parser(PrintfForWarnings
);
222 registerGwpAsanOptions(&Parser
, o
);
224 // Override from the weak function definition in this executable.
225 Parser
.parseString(getGwpAsanDefaultOptions());
227 // Override from the provided options string.
228 Parser
.parseString(OptionsStr
);
231 Parser
.printOptionDescriptions();
236 if (o
->MaxSimultaneousAllocations
<= 0) {
239 "GWP-ASan ERROR: MaxSimultaneousAllocations must be > 0 when GWP-ASan "
243 if (o
->SampleRate
<= 0) {
246 "GWP-ASan ERROR: SampleRate must be > 0 when GWP-ASan is enabled.\n");
251 void initOptions(Printf_t PrintfForWarnings
) {
252 initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings
);
255 Options
&getOptions() { return *getOptionsInternal(); }
257 } // namespace options
258 } // namespace gwp_asan