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_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() { 59 char buffer[128]; 60 buffer[sizeof(buffer) - 1] = '\0'; 61 Printf("Available flags for %s:\n", SanitizerToolName); 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 } 69 } 70 71 void FlagParser::fatal_error(const char *err) { 72 Printf("%s: ERROR: %s\n", SanitizerToolName, err); 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 85 void FlagParser::parse_flag(const char *env_option_name) { 86 uptr name_start = pos_; 87 while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_; 88 if (buf_[pos_] != '=') { 89 if (env_option_name) { 90 Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName, 91 env_option_name); 92 Die(); 93 } else { 94 fatal_error("expected '='"); 95 } 96 } 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 118 void FlagParser::parse_flags(const char *env_option_name) { 119 while (true) { 120 skip_whitespace(); 121 if (buf_[pos_] == 0) break; 122 parse_flag(env_option_name); 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 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) { 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 144 parse_flags(env_option_name); 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 } 163 ParseString(data, path); 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 192