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 20 namespace __sanitizer { 21 22 class 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("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_); 36 for (int i = 0; i < n_unknown_flags_; ++i) 37 Printf(" %s\n", unknown_flags_[i]); 38 n_unknown_flags_ = 0; 39 } 40 }; 41 42 UnknownFlags unknown_flags; 43 44 void ReportUnrecognizedFlags() { 45 unknown_flags.Report(); 46 } 47 48 char *FlagParser::ll_strndup(const char *s, uptr n) { 49 uptr len = internal_strnlen(s, n); 50 char *s2 = (char *)GetGlobalLowLevelAllocator().Allocate(len + 1); 51 internal_memcpy(s2, s, len); 52 s2[len] = 0; 53 return s2; 54 } 55 56 void FlagParser::PrintFlagDescriptions() { 57 char buffer[128]; 58 buffer[sizeof(buffer) - 1] = '\0'; 59 Printf("Available flags for %s:\n", SanitizerToolName); 60 for (int i = 0; i < n_flags_; ++i) { 61 bool truncated = !(flags_[i].handler->Format(buffer, sizeof(buffer))); 62 CHECK_EQ(buffer[sizeof(buffer) - 1], '\0'); 63 const char *truncation_str = truncated ? " Truncated" : ""; 64 Printf("\t%s\n\t\t- %s (Current Value%s: %s)\n", flags_[i].name, 65 flags_[i].desc, truncation_str, buffer); 66 } 67 } 68 69 void FlagParser::fatal_error(const char *err) { 70 Printf("%s: ERROR: %s\n", SanitizerToolName, err); 71 Die(); 72 } 73 74 bool FlagParser::is_space(char c) { 75 return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' || 76 c == '\r'; 77 } 78 79 void FlagParser::skip_whitespace() { 80 while (is_space(buf_[pos_])) ++pos_; 81 } 82 83 void FlagParser::parse_flag(const char *env_option_name) { 84 uptr name_start = pos_; 85 while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_; 86 if (buf_[pos_] != '=') { 87 if (env_option_name) { 88 Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName, 89 env_option_name); 90 Die(); 91 } else { 92 fatal_error("expected '='"); 93 } 94 } 95 char *name = ll_strndup(buf_ + name_start, 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("unterminated string"); 103 value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1); 104 ++pos_; // consume the closing quote 105 } else { 106 while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_; 107 if (buf_[pos_] != 0 && !is_space(buf_[pos_])) 108 fatal_error("expected separator or eol"); 109 value = ll_strndup(buf_ + value_start, pos_ - value_start); 110 } 111 112 bool res = run_handler(name, value); 113 if (!res) fatal_error("Flag parsing failed."); 114 } 115 116 void 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 128 void FlagParser::ParseStringFromEnv(const char *env_name) { 129 const char *env = GetEnv(env_name); 130 VPrintf(1, "%s: %s\n", env_name, env ? env : "<empty>"); 131 ParseString(env, env_name); 132 } 133 134 void 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 148 bool 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(path, &data, &data_mapped_size, &len, 155 Max(kMaxIncludeSize, GetPageSizeCached()), &err)) { 156 if (ignore_missing) 157 return true; 158 Printf("Failed to read options from '%s': error %d\n", path, err); 159 return false; 160 } 161 ParseString(data, path); 162 UnmapOrDie(data, data_mapped_size); 163 return true; 164 } 165 166 bool FlagParser::run_handler(const char *name, const char *value) { 167 for (int i = 0; i < n_flags_; ++i) { 168 if (internal_strcmp(name, 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 176 void 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 185 FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) { 186 flags_ = 187 (Flag *)GetGlobalLowLevelAllocator().Allocate(sizeof(Flag) * kMaxFlags); 188 } 189 190 } // namespace __sanitizer 191