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 
19 namespace __sanitizer {
20 
21 LowLevelAllocator FlagParser::Alloc;
22 
23 class UnknownFlags {
24   static const int kMaxUnknownFlags = 20;
25   const char *unknown_flags_[kMaxUnknownFlags];
26   int n_unknown_flags_;
27 
28  public:
Add(const char * name)29   void Add(const char *name) {
30     CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
31     unknown_flags_[n_unknown_flags_++] = name;
32   }
33 
Report()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 
43 UnknownFlags unknown_flags;
44 
ReportUnrecognizedFlags()45 void ReportUnrecognizedFlags() {
46   unknown_flags.Report();
47 }
48 
ll_strndup(const char * s,uptr n)49 char *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 
PrintFlagDescriptions()57 void 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 
fatal_error(const char * err)63 void FlagParser::fatal_error(const char *err) {
64   Printf("ERROR: %s\n", err);
65   Die();
66 }
67 
is_space(char c)68 bool FlagParser::is_space(char c) {
69   return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
70          c == '\r';
71 }
72 
skip_whitespace()73 void FlagParser::skip_whitespace() {
74   while (is_space(buf_[pos_])) ++pos_;
75 }
76 
parse_flag()77 void 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 
parse_flags()102 void 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 
ParseString(const char * s)114 void 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 
ParseFile(const char * path,bool ignore_missing)128 bool 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 
run_handler(const char * name,const char * value)146 bool 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 
RegisterHandler(const char * name,FlagHandlerBase * handler,const char * desc)156 void 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 
FlagParser()165 FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
166   flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
167 }
168 
169 }  // namespace __sanitizer
170