168d75effSDimitry Andric //===-- sanitizer_flag_parser.cpp -----------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of ThreadSanitizer/AddressSanitizer runtime. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric //===----------------------------------------------------------------------===// 1268d75effSDimitry Andric 1368d75effSDimitry Andric #include "sanitizer_flag_parser.h" 1468d75effSDimitry Andric 1568d75effSDimitry Andric #include "sanitizer_common.h" 1668d75effSDimitry Andric #include "sanitizer_flag_parser.h" 1706c3fb27SDimitry Andric #include "sanitizer_flags.h" 1806c3fb27SDimitry Andric #include "sanitizer_libc.h" 1968d75effSDimitry Andric 2068d75effSDimitry Andric namespace __sanitizer { 2168d75effSDimitry Andric 2268d75effSDimitry Andric class UnknownFlags { 2368d75effSDimitry Andric static const int kMaxUnknownFlags = 20; 2468d75effSDimitry Andric const char *unknown_flags_[kMaxUnknownFlags]; 2568d75effSDimitry Andric int n_unknown_flags_; 2668d75effSDimitry Andric 2768d75effSDimitry Andric public: 2868d75effSDimitry Andric void Add(const char *name) { 2968d75effSDimitry Andric CHECK_LT(n_unknown_flags_, kMaxUnknownFlags); 3068d75effSDimitry Andric unknown_flags_[n_unknown_flags_++] = name; 3168d75effSDimitry Andric } 3268d75effSDimitry Andric 3368d75effSDimitry Andric void Report() { 3468d75effSDimitry Andric if (!n_unknown_flags_) return; 3568d75effSDimitry Andric Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_); 3668d75effSDimitry Andric for (int i = 0; i < n_unknown_flags_; ++i) 3768d75effSDimitry Andric Printf(" %s\n", unknown_flags_[i]); 3868d75effSDimitry Andric n_unknown_flags_ = 0; 3968d75effSDimitry Andric } 4068d75effSDimitry Andric }; 4168d75effSDimitry Andric 4268d75effSDimitry Andric UnknownFlags unknown_flags; 4368d75effSDimitry Andric 4468d75effSDimitry Andric void ReportUnrecognizedFlags() { 4568d75effSDimitry Andric unknown_flags.Report(); 4668d75effSDimitry Andric } 4768d75effSDimitry Andric 4868d75effSDimitry Andric char *FlagParser::ll_strndup(const char *s, uptr n) { 4968d75effSDimitry Andric uptr len = internal_strnlen(s, n); 50*5f757f3fSDimitry Andric char *s2 = (char *)GetGlobalLowLevelAllocator().Allocate(len + 1); 5168d75effSDimitry Andric internal_memcpy(s2, s, len); 5268d75effSDimitry Andric s2[len] = 0; 5368d75effSDimitry Andric return s2; 5468d75effSDimitry Andric } 5568d75effSDimitry Andric 5668d75effSDimitry Andric void FlagParser::PrintFlagDescriptions() { 57480093f4SDimitry Andric char buffer[128]; 58480093f4SDimitry Andric buffer[sizeof(buffer) - 1] = '\0'; 5968d75effSDimitry Andric Printf("Available flags for %s:\n", SanitizerToolName); 60480093f4SDimitry Andric for (int i = 0; i < n_flags_; ++i) { 61480093f4SDimitry Andric bool truncated = !(flags_[i].handler->Format(buffer, sizeof(buffer))); 62480093f4SDimitry Andric CHECK_EQ(buffer[sizeof(buffer) - 1], '\0'); 63480093f4SDimitry Andric const char *truncation_str = truncated ? " Truncated" : ""; 64480093f4SDimitry Andric Printf("\t%s\n\t\t- %s (Current Value%s: %s)\n", flags_[i].name, 65480093f4SDimitry Andric flags_[i].desc, truncation_str, buffer); 66480093f4SDimitry Andric } 6768d75effSDimitry Andric } 6868d75effSDimitry Andric 6968d75effSDimitry Andric void FlagParser::fatal_error(const char *err) { 7068d75effSDimitry Andric Printf("%s: ERROR: %s\n", SanitizerToolName, err); 7168d75effSDimitry Andric Die(); 7268d75effSDimitry Andric } 7368d75effSDimitry Andric 7468d75effSDimitry Andric bool FlagParser::is_space(char c) { 7568d75effSDimitry Andric return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' || 7668d75effSDimitry Andric c == '\r'; 7768d75effSDimitry Andric } 7868d75effSDimitry Andric 7968d75effSDimitry Andric void FlagParser::skip_whitespace() { 8068d75effSDimitry Andric while (is_space(buf_[pos_])) ++pos_; 8168d75effSDimitry Andric } 8268d75effSDimitry Andric 8368d75effSDimitry Andric void FlagParser::parse_flag(const char *env_option_name) { 8468d75effSDimitry Andric uptr name_start = pos_; 8568d75effSDimitry Andric while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_; 8668d75effSDimitry Andric if (buf_[pos_] != '=') { 8768d75effSDimitry Andric if (env_option_name) { 8868d75effSDimitry Andric Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName, 8968d75effSDimitry Andric env_option_name); 9068d75effSDimitry Andric Die(); 9168d75effSDimitry Andric } else { 9268d75effSDimitry Andric fatal_error("expected '='"); 9368d75effSDimitry Andric } 9468d75effSDimitry Andric } 9568d75effSDimitry Andric char *name = ll_strndup(buf_ + name_start, pos_ - name_start); 9668d75effSDimitry Andric 9768d75effSDimitry Andric uptr value_start = ++pos_; 9868d75effSDimitry Andric char *value; 9968d75effSDimitry Andric if (buf_[pos_] == '\'' || buf_[pos_] == '"') { 10068d75effSDimitry Andric char quote = buf_[pos_++]; 10168d75effSDimitry Andric while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_; 10268d75effSDimitry Andric if (buf_[pos_] == 0) fatal_error("unterminated string"); 10368d75effSDimitry Andric value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1); 10468d75effSDimitry Andric ++pos_; // consume the closing quote 10568d75effSDimitry Andric } else { 10668d75effSDimitry Andric while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_; 10768d75effSDimitry Andric if (buf_[pos_] != 0 && !is_space(buf_[pos_])) 10868d75effSDimitry Andric fatal_error("expected separator or eol"); 10968d75effSDimitry Andric value = ll_strndup(buf_ + value_start, pos_ - value_start); 11068d75effSDimitry Andric } 11168d75effSDimitry Andric 11268d75effSDimitry Andric bool res = run_handler(name, value); 11368d75effSDimitry Andric if (!res) fatal_error("Flag parsing failed."); 11468d75effSDimitry Andric } 11568d75effSDimitry Andric 11668d75effSDimitry Andric void FlagParser::parse_flags(const char *env_option_name) { 11768d75effSDimitry Andric while (true) { 11868d75effSDimitry Andric skip_whitespace(); 11968d75effSDimitry Andric if (buf_[pos_] == 0) break; 12068d75effSDimitry Andric parse_flag(env_option_name); 12168d75effSDimitry Andric } 12268d75effSDimitry Andric 12368d75effSDimitry Andric // Do a sanity check for certain flags. 12468d75effSDimitry Andric if (common_flags_dont_use.malloc_context_size < 1) 12568d75effSDimitry Andric common_flags_dont_use.malloc_context_size = 1; 12668d75effSDimitry Andric } 12768d75effSDimitry Andric 12868d75effSDimitry Andric void FlagParser::ParseStringFromEnv(const char *env_name) { 12968d75effSDimitry Andric const char *env = GetEnv(env_name); 13068d75effSDimitry Andric VPrintf(1, "%s: %s\n", env_name, env ? env : "<empty>"); 13168d75effSDimitry Andric ParseString(env, env_name); 13268d75effSDimitry Andric } 13368d75effSDimitry Andric 13468d75effSDimitry Andric void FlagParser::ParseString(const char *s, const char *env_option_name) { 13568d75effSDimitry Andric if (!s) return; 13668d75effSDimitry Andric // Backup current parser state to allow nested ParseString() calls. 13768d75effSDimitry Andric const char *old_buf_ = buf_; 13868d75effSDimitry Andric uptr old_pos_ = pos_; 13968d75effSDimitry Andric buf_ = s; 14068d75effSDimitry Andric pos_ = 0; 14168d75effSDimitry Andric 14268d75effSDimitry Andric parse_flags(env_option_name); 14368d75effSDimitry Andric 14468d75effSDimitry Andric buf_ = old_buf_; 14568d75effSDimitry Andric pos_ = old_pos_; 14668d75effSDimitry Andric } 14768d75effSDimitry Andric 14868d75effSDimitry Andric bool FlagParser::ParseFile(const char *path, bool ignore_missing) { 14968d75effSDimitry Andric static const uptr kMaxIncludeSize = 1 << 15; 15068d75effSDimitry Andric char *data; 15168d75effSDimitry Andric uptr data_mapped_size; 15268d75effSDimitry Andric error_t err; 15368d75effSDimitry Andric uptr len; 15468d75effSDimitry Andric if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len, 15568d75effSDimitry Andric Max(kMaxIncludeSize, GetPageSizeCached()), &err)) { 15668d75effSDimitry Andric if (ignore_missing) 15768d75effSDimitry Andric return true; 15868d75effSDimitry Andric Printf("Failed to read options from '%s': error %d\n", path, err); 15968d75effSDimitry Andric return false; 16068d75effSDimitry Andric } 16168d75effSDimitry Andric ParseString(data, path); 16268d75effSDimitry Andric UnmapOrDie(data, data_mapped_size); 16368d75effSDimitry Andric return true; 16468d75effSDimitry Andric } 16568d75effSDimitry Andric 16668d75effSDimitry Andric bool FlagParser::run_handler(const char *name, const char *value) { 16768d75effSDimitry Andric for (int i = 0; i < n_flags_; ++i) { 16868d75effSDimitry Andric if (internal_strcmp(name, flags_[i].name) == 0) 16968d75effSDimitry Andric return flags_[i].handler->Parse(value); 17068d75effSDimitry Andric } 17168d75effSDimitry Andric // Unrecognized flag. This is not a fatal error, we may print a warning later. 17268d75effSDimitry Andric unknown_flags.Add(name); 17368d75effSDimitry Andric return true; 17468d75effSDimitry Andric } 17568d75effSDimitry Andric 17668d75effSDimitry Andric void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler, 17768d75effSDimitry Andric const char *desc) { 17868d75effSDimitry Andric CHECK_LT(n_flags_, kMaxFlags); 17968d75effSDimitry Andric flags_[n_flags_].name = name; 18068d75effSDimitry Andric flags_[n_flags_].desc = desc; 18168d75effSDimitry Andric flags_[n_flags_].handler = handler; 18268d75effSDimitry Andric ++n_flags_; 18368d75effSDimitry Andric } 18468d75effSDimitry Andric 18568d75effSDimitry Andric FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) { 186*5f757f3fSDimitry Andric flags_ = 187*5f757f3fSDimitry Andric (Flag *)GetGlobalLowLevelAllocator().Allocate(sizeof(Flag) * kMaxFlags); 18868d75effSDimitry Andric } 18968d75effSDimitry Andric 19068d75effSDimitry Andric } // namespace __sanitizer 191