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