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