1*3cab2bb3Spatrick //===-- sanitizer_flag_parser.cpp -----------------------------------------===//
2*3cab2bb3Spatrick //
3*3cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*3cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*3cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*3cab2bb3Spatrick //
7*3cab2bb3Spatrick //===----------------------------------------------------------------------===//
8*3cab2bb3Spatrick //
9*3cab2bb3Spatrick // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
10*3cab2bb3Spatrick //
11*3cab2bb3Spatrick //===----------------------------------------------------------------------===//
12*3cab2bb3Spatrick
13*3cab2bb3Spatrick #include "sanitizer_flag_parser.h"
14*3cab2bb3Spatrick
15*3cab2bb3Spatrick #include "sanitizer_common.h"
16*3cab2bb3Spatrick #include "sanitizer_libc.h"
17*3cab2bb3Spatrick #include "sanitizer_flags.h"
18*3cab2bb3Spatrick #include "sanitizer_flag_parser.h"
19*3cab2bb3Spatrick
20*3cab2bb3Spatrick namespace __sanitizer {
21*3cab2bb3Spatrick
22*3cab2bb3Spatrick LowLevelAllocator FlagParser::Alloc;
23*3cab2bb3Spatrick
24*3cab2bb3Spatrick class UnknownFlags {
25*3cab2bb3Spatrick static const int kMaxUnknownFlags = 20;
26*3cab2bb3Spatrick const char *unknown_flags_[kMaxUnknownFlags];
27*3cab2bb3Spatrick int n_unknown_flags_;
28*3cab2bb3Spatrick
29*3cab2bb3Spatrick public:
Add(const char * name)30*3cab2bb3Spatrick void Add(const char *name) {
31*3cab2bb3Spatrick CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
32*3cab2bb3Spatrick unknown_flags_[n_unknown_flags_++] = name;
33*3cab2bb3Spatrick }
34*3cab2bb3Spatrick
Report()35*3cab2bb3Spatrick void Report() {
36*3cab2bb3Spatrick if (!n_unknown_flags_) return;
37*3cab2bb3Spatrick Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
38*3cab2bb3Spatrick for (int i = 0; i < n_unknown_flags_; ++i)
39*3cab2bb3Spatrick Printf(" %s\n", unknown_flags_[i]);
40*3cab2bb3Spatrick n_unknown_flags_ = 0;
41*3cab2bb3Spatrick }
42*3cab2bb3Spatrick };
43*3cab2bb3Spatrick
44*3cab2bb3Spatrick UnknownFlags unknown_flags;
45*3cab2bb3Spatrick
ReportUnrecognizedFlags()46*3cab2bb3Spatrick void ReportUnrecognizedFlags() {
47*3cab2bb3Spatrick unknown_flags.Report();
48*3cab2bb3Spatrick }
49*3cab2bb3Spatrick
ll_strndup(const char * s,uptr n)50*3cab2bb3Spatrick char *FlagParser::ll_strndup(const char *s, uptr n) {
51*3cab2bb3Spatrick uptr len = internal_strnlen(s, n);
52*3cab2bb3Spatrick char *s2 = (char*)Alloc.Allocate(len + 1);
53*3cab2bb3Spatrick internal_memcpy(s2, s, len);
54*3cab2bb3Spatrick s2[len] = 0;
55*3cab2bb3Spatrick return s2;
56*3cab2bb3Spatrick }
57*3cab2bb3Spatrick
PrintFlagDescriptions()58*3cab2bb3Spatrick void FlagParser::PrintFlagDescriptions() {
59*3cab2bb3Spatrick char buffer[128];
60*3cab2bb3Spatrick buffer[sizeof(buffer) - 1] = '\0';
61*3cab2bb3Spatrick Printf("Available flags for %s:\n", SanitizerToolName);
62*3cab2bb3Spatrick for (int i = 0; i < n_flags_; ++i) {
63*3cab2bb3Spatrick bool truncated = !(flags_[i].handler->Format(buffer, sizeof(buffer)));
64*3cab2bb3Spatrick CHECK_EQ(buffer[sizeof(buffer) - 1], '\0');
65*3cab2bb3Spatrick const char *truncation_str = truncated ? " Truncated" : "";
66*3cab2bb3Spatrick Printf("\t%s\n\t\t- %s (Current Value%s: %s)\n", flags_[i].name,
67*3cab2bb3Spatrick flags_[i].desc, truncation_str, buffer);
68*3cab2bb3Spatrick }
69*3cab2bb3Spatrick }
70*3cab2bb3Spatrick
fatal_error(const char * err)71*3cab2bb3Spatrick void FlagParser::fatal_error(const char *err) {
72*3cab2bb3Spatrick Printf("%s: ERROR: %s\n", SanitizerToolName, err);
73*3cab2bb3Spatrick Die();
74*3cab2bb3Spatrick }
75*3cab2bb3Spatrick
is_space(char c)76*3cab2bb3Spatrick bool FlagParser::is_space(char c) {
77*3cab2bb3Spatrick return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
78*3cab2bb3Spatrick c == '\r';
79*3cab2bb3Spatrick }
80*3cab2bb3Spatrick
skip_whitespace()81*3cab2bb3Spatrick void FlagParser::skip_whitespace() {
82*3cab2bb3Spatrick while (is_space(buf_[pos_])) ++pos_;
83*3cab2bb3Spatrick }
84*3cab2bb3Spatrick
parse_flag(const char * env_option_name)85*3cab2bb3Spatrick void FlagParser::parse_flag(const char *env_option_name) {
86*3cab2bb3Spatrick uptr name_start = pos_;
87*3cab2bb3Spatrick while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
88*3cab2bb3Spatrick if (buf_[pos_] != '=') {
89*3cab2bb3Spatrick if (env_option_name) {
90*3cab2bb3Spatrick Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName,
91*3cab2bb3Spatrick env_option_name);
92*3cab2bb3Spatrick Die();
93*3cab2bb3Spatrick } else {
94*3cab2bb3Spatrick fatal_error("expected '='");
95*3cab2bb3Spatrick }
96*3cab2bb3Spatrick }
97*3cab2bb3Spatrick char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
98*3cab2bb3Spatrick
99*3cab2bb3Spatrick uptr value_start = ++pos_;
100*3cab2bb3Spatrick char *value;
101*3cab2bb3Spatrick if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
102*3cab2bb3Spatrick char quote = buf_[pos_++];
103*3cab2bb3Spatrick while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
104*3cab2bb3Spatrick if (buf_[pos_] == 0) fatal_error("unterminated string");
105*3cab2bb3Spatrick value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
106*3cab2bb3Spatrick ++pos_; // consume the closing quote
107*3cab2bb3Spatrick } else {
108*3cab2bb3Spatrick while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
109*3cab2bb3Spatrick if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
110*3cab2bb3Spatrick fatal_error("expected separator or eol");
111*3cab2bb3Spatrick value = ll_strndup(buf_ + value_start, pos_ - value_start);
112*3cab2bb3Spatrick }
113*3cab2bb3Spatrick
114*3cab2bb3Spatrick bool res = run_handler(name, value);
115*3cab2bb3Spatrick if (!res) fatal_error("Flag parsing failed.");
116*3cab2bb3Spatrick }
117*3cab2bb3Spatrick
parse_flags(const char * env_option_name)118*3cab2bb3Spatrick void FlagParser::parse_flags(const char *env_option_name) {
119*3cab2bb3Spatrick while (true) {
120*3cab2bb3Spatrick skip_whitespace();
121*3cab2bb3Spatrick if (buf_[pos_] == 0) break;
122*3cab2bb3Spatrick parse_flag(env_option_name);
123*3cab2bb3Spatrick }
124*3cab2bb3Spatrick
125*3cab2bb3Spatrick // Do a sanity check for certain flags.
126*3cab2bb3Spatrick if (common_flags_dont_use.malloc_context_size < 1)
127*3cab2bb3Spatrick common_flags_dont_use.malloc_context_size = 1;
128*3cab2bb3Spatrick }
129*3cab2bb3Spatrick
ParseStringFromEnv(const char * env_name)130*3cab2bb3Spatrick void FlagParser::ParseStringFromEnv(const char *env_name) {
131*3cab2bb3Spatrick const char *env = GetEnv(env_name);
132*3cab2bb3Spatrick VPrintf(1, "%s: %s\n", env_name, env ? env : "<empty>");
133*3cab2bb3Spatrick ParseString(env, env_name);
134*3cab2bb3Spatrick }
135*3cab2bb3Spatrick
ParseString(const char * s,const char * env_option_name)136*3cab2bb3Spatrick void FlagParser::ParseString(const char *s, const char *env_option_name) {
137*3cab2bb3Spatrick if (!s) return;
138*3cab2bb3Spatrick // Backup current parser state to allow nested ParseString() calls.
139*3cab2bb3Spatrick const char *old_buf_ = buf_;
140*3cab2bb3Spatrick uptr old_pos_ = pos_;
141*3cab2bb3Spatrick buf_ = s;
142*3cab2bb3Spatrick pos_ = 0;
143*3cab2bb3Spatrick
144*3cab2bb3Spatrick parse_flags(env_option_name);
145*3cab2bb3Spatrick
146*3cab2bb3Spatrick buf_ = old_buf_;
147*3cab2bb3Spatrick pos_ = old_pos_;
148*3cab2bb3Spatrick }
149*3cab2bb3Spatrick
ParseFile(const char * path,bool ignore_missing)150*3cab2bb3Spatrick bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
151*3cab2bb3Spatrick static const uptr kMaxIncludeSize = 1 << 15;
152*3cab2bb3Spatrick char *data;
153*3cab2bb3Spatrick uptr data_mapped_size;
154*3cab2bb3Spatrick error_t err;
155*3cab2bb3Spatrick uptr len;
156*3cab2bb3Spatrick if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
157*3cab2bb3Spatrick Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
158*3cab2bb3Spatrick if (ignore_missing)
159*3cab2bb3Spatrick return true;
160*3cab2bb3Spatrick Printf("Failed to read options from '%s': error %d\n", path, err);
161*3cab2bb3Spatrick return false;
162*3cab2bb3Spatrick }
163*3cab2bb3Spatrick ParseString(data, path);
164*3cab2bb3Spatrick UnmapOrDie(data, data_mapped_size);
165*3cab2bb3Spatrick return true;
166*3cab2bb3Spatrick }
167*3cab2bb3Spatrick
run_handler(const char * name,const char * value)168*3cab2bb3Spatrick bool FlagParser::run_handler(const char *name, const char *value) {
169*3cab2bb3Spatrick for (int i = 0; i < n_flags_; ++i) {
170*3cab2bb3Spatrick if (internal_strcmp(name, flags_[i].name) == 0)
171*3cab2bb3Spatrick return flags_[i].handler->Parse(value);
172*3cab2bb3Spatrick }
173*3cab2bb3Spatrick // Unrecognized flag. This is not a fatal error, we may print a warning later.
174*3cab2bb3Spatrick unknown_flags.Add(name);
175*3cab2bb3Spatrick return true;
176*3cab2bb3Spatrick }
177*3cab2bb3Spatrick
RegisterHandler(const char * name,FlagHandlerBase * handler,const char * desc)178*3cab2bb3Spatrick void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
179*3cab2bb3Spatrick const char *desc) {
180*3cab2bb3Spatrick CHECK_LT(n_flags_, kMaxFlags);
181*3cab2bb3Spatrick flags_[n_flags_].name = name;
182*3cab2bb3Spatrick flags_[n_flags_].desc = desc;
183*3cab2bb3Spatrick flags_[n_flags_].handler = handler;
184*3cab2bb3Spatrick ++n_flags_;
185*3cab2bb3Spatrick }
186*3cab2bb3Spatrick
FlagParser()187*3cab2bb3Spatrick FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
188*3cab2bb3Spatrick flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
189*3cab2bb3Spatrick }
190*3cab2bb3Spatrick
191*3cab2bb3Spatrick } // namespace __sanitizer
192