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