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