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