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