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

source code of compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cpp