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
21namespace __sanitizer {
22
23LowLevelAllocator FlagParser::Alloc;
24
25class 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
45UnknownFlags unknown_flags;
46
47void ReportUnrecognizedFlags() {
48  unknown_flags.Report();
49}
50
51char *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
59void 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
65void FlagParser::fatal_error(const char *err) {
66  Printf("ERROR: %s\n", err);
67  Die();
68}
69
70bool FlagParser::is_space(char c) {
71  return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
72         c == '\r';
73}
74
75void FlagParser::skip_whitespace() {
76  while (is_space(buf_[pos_])) ++pos_;
77}
78
79void 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
104void 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
116void 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
130bool 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
148bool 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
158void 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
167FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
168  flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
169}
170
171}  // namespace __sanitizer
172