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"
20 
21 namespace __sanitizer {
22 
23 LowLevelAllocator FlagParser::Alloc;
24 
25 class UnknownFlags {
26   static const int kMaxUnknownFlags = 20;
27   const char *unknown_flags_[kMaxUnknownFlags];
28   int n_unknown_flags_;
29 
30  public:
Add(const char * name)31   void Add(const char *name) {
32     CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
33     unknown_flags_[n_unknown_flags_++] = name;
34   }
35 
Report()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 
ReportUnrecognizedFlags()47 void ReportUnrecognizedFlags() {
48   unknown_flags.Report();
49 }
50 
ll_strndup(const char * s,uptr n)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 
PrintFlagDescriptions()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 
fatal_error(const char * err)65 void FlagParser::fatal_error(const char *err) {
66   Printf("ERROR: %s\n", err);
67   Die();
68 }
69 
is_space(char c)70 bool FlagParser::is_space(char c) {
71   return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
72          c == '\r';
73 }
74 
skip_whitespace()75 void FlagParser::skip_whitespace() {
76   while (is_space(buf_[pos_])) ++pos_;
77 }
78 
parse_flag()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 '='");
83   char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
84 
85   uptr value_start = ++pos_;
86   char *value;
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");
91     value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
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");
97     value = ll_strndup(buf_ + value_start, pos_ - value_start);
98   }
99 
100   bool res = run_handler(name, value);
101   if (!res) fatal_error("Flag parsing failed.");
102 }
103 
parse_flags()104 void FlagParser::parse_flags() {
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 
ParseString(const char * s)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 
ParseFile(const char * path,bool ignore_missing)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 
run_handler(const char * name,const char * value)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   }
153   // Unrecognized flag. This is not a fatal error, we may print a warning later.
154   unknown_flags.Add(name);
155   return true;
156 }
157 
RegisterHandler(const char * name,FlagHandlerBase * handler,const char * desc)158 void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
159                                  const char *desc) {
160   CHECK_LT(n_flags_, kMaxFlags);
161   flags_[n_flags_].name = name;
162   flags_[n_flags_].desc = desc;
163   flags_[n_flags_].handler = handler;
164   ++n_flags_;
165 }
166 
FlagParser()167 FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
168   flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
169 }
170 
171 }  // namespace __sanitizer
172