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