xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
168d75effSDimitry Andric //===-- sanitizer_flag_parser.cpp -----------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric //===----------------------------------------------------------------------===//
1268d75effSDimitry Andric 
1368d75effSDimitry Andric #include "sanitizer_flag_parser.h"
1468d75effSDimitry Andric 
1568d75effSDimitry Andric #include "sanitizer_common.h"
1668d75effSDimitry Andric #include "sanitizer_flag_parser.h"
1706c3fb27SDimitry Andric #include "sanitizer_flags.h"
1806c3fb27SDimitry Andric #include "sanitizer_libc.h"
1968d75effSDimitry Andric 
2068d75effSDimitry Andric namespace __sanitizer {
2168d75effSDimitry Andric 
2268d75effSDimitry Andric class UnknownFlags {
2368d75effSDimitry Andric   static const int kMaxUnknownFlags = 20;
2468d75effSDimitry Andric   const char *unknown_flags_[kMaxUnknownFlags];
2568d75effSDimitry Andric   int n_unknown_flags_;
2668d75effSDimitry Andric 
2768d75effSDimitry Andric  public:
Add(const char * name)2868d75effSDimitry Andric   void Add(const char *name) {
2968d75effSDimitry Andric     CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
3068d75effSDimitry Andric     unknown_flags_[n_unknown_flags_++] = name;
3168d75effSDimitry Andric   }
3268d75effSDimitry Andric 
Report()3368d75effSDimitry Andric   void Report() {
3468d75effSDimitry Andric     if (!n_unknown_flags_) return;
3568d75effSDimitry Andric     Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
3668d75effSDimitry Andric     for (int i = 0; i < n_unknown_flags_; ++i)
3768d75effSDimitry Andric       Printf("    %s\n", unknown_flags_[i]);
3868d75effSDimitry Andric     n_unknown_flags_ = 0;
3968d75effSDimitry Andric   }
4068d75effSDimitry Andric };
4168d75effSDimitry Andric 
4268d75effSDimitry Andric UnknownFlags unknown_flags;
4368d75effSDimitry Andric 
ReportUnrecognizedFlags()4468d75effSDimitry Andric void ReportUnrecognizedFlags() {
4568d75effSDimitry Andric   unknown_flags.Report();
4668d75effSDimitry Andric }
4768d75effSDimitry Andric 
ll_strndup(const char * s,uptr n)4868d75effSDimitry Andric char *FlagParser::ll_strndup(const char *s, uptr n) {
4968d75effSDimitry Andric   uptr len = internal_strnlen(s, n);
50*5f757f3fSDimitry Andric   char *s2 = (char *)GetGlobalLowLevelAllocator().Allocate(len + 1);
5168d75effSDimitry Andric   internal_memcpy(s2, s, len);
5268d75effSDimitry Andric   s2[len] = 0;
5368d75effSDimitry Andric   return s2;
5468d75effSDimitry Andric }
5568d75effSDimitry Andric 
PrintFlagDescriptions()5668d75effSDimitry Andric void FlagParser::PrintFlagDescriptions() {
57480093f4SDimitry Andric   char buffer[128];
58480093f4SDimitry Andric   buffer[sizeof(buffer) - 1] = '\0';
5968d75effSDimitry Andric   Printf("Available flags for %s:\n", SanitizerToolName);
60480093f4SDimitry Andric   for (int i = 0; i < n_flags_; ++i) {
61480093f4SDimitry Andric     bool truncated = !(flags_[i].handler->Format(buffer, sizeof(buffer)));
62480093f4SDimitry Andric     CHECK_EQ(buffer[sizeof(buffer) - 1], '\0');
63480093f4SDimitry Andric     const char *truncation_str = truncated ? " Truncated" : "";
64480093f4SDimitry Andric     Printf("\t%s\n\t\t- %s (Current Value%s: %s)\n", flags_[i].name,
65480093f4SDimitry Andric            flags_[i].desc, truncation_str, buffer);
66480093f4SDimitry Andric   }
6768d75effSDimitry Andric }
6868d75effSDimitry Andric 
fatal_error(const char * err)6968d75effSDimitry Andric void FlagParser::fatal_error(const char *err) {
7068d75effSDimitry Andric   Printf("%s: ERROR: %s\n", SanitizerToolName, err);
7168d75effSDimitry Andric   Die();
7268d75effSDimitry Andric }
7368d75effSDimitry Andric 
is_space(char c)7468d75effSDimitry Andric bool FlagParser::is_space(char c) {
7568d75effSDimitry Andric   return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
7668d75effSDimitry Andric          c == '\r';
7768d75effSDimitry Andric }
7868d75effSDimitry Andric 
skip_whitespace()7968d75effSDimitry Andric void FlagParser::skip_whitespace() {
8068d75effSDimitry Andric   while (is_space(buf_[pos_])) ++pos_;
8168d75effSDimitry Andric }
8268d75effSDimitry Andric 
parse_flag(const char * env_option_name)8368d75effSDimitry Andric void FlagParser::parse_flag(const char *env_option_name) {
8468d75effSDimitry Andric   uptr name_start = pos_;
8568d75effSDimitry Andric   while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
8668d75effSDimitry Andric   if (buf_[pos_] != '=') {
8768d75effSDimitry Andric     if (env_option_name) {
8868d75effSDimitry Andric       Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName,
8968d75effSDimitry Andric              env_option_name);
9068d75effSDimitry Andric       Die();
9168d75effSDimitry Andric     } else {
9268d75effSDimitry Andric       fatal_error("expected '='");
9368d75effSDimitry Andric     }
9468d75effSDimitry Andric   }
9568d75effSDimitry Andric   char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
9668d75effSDimitry Andric 
9768d75effSDimitry Andric   uptr value_start = ++pos_;
9868d75effSDimitry Andric   char *value;
9968d75effSDimitry Andric   if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
10068d75effSDimitry Andric     char quote = buf_[pos_++];
10168d75effSDimitry Andric     while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
10268d75effSDimitry Andric     if (buf_[pos_] == 0) fatal_error("unterminated string");
10368d75effSDimitry Andric     value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
10468d75effSDimitry Andric     ++pos_; // consume the closing quote
10568d75effSDimitry Andric   } else {
10668d75effSDimitry Andric     while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
10768d75effSDimitry Andric     if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
10868d75effSDimitry Andric       fatal_error("expected separator or eol");
10968d75effSDimitry Andric     value = ll_strndup(buf_ + value_start, pos_ - value_start);
11068d75effSDimitry Andric   }
11168d75effSDimitry Andric 
11268d75effSDimitry Andric   bool res = run_handler(name, value);
11368d75effSDimitry Andric   if (!res) fatal_error("Flag parsing failed.");
11468d75effSDimitry Andric }
11568d75effSDimitry Andric 
parse_flags(const char * env_option_name)11668d75effSDimitry Andric void FlagParser::parse_flags(const char *env_option_name) {
11768d75effSDimitry Andric   while (true) {
11868d75effSDimitry Andric     skip_whitespace();
11968d75effSDimitry Andric     if (buf_[pos_] == 0) break;
12068d75effSDimitry Andric     parse_flag(env_option_name);
12168d75effSDimitry Andric   }
12268d75effSDimitry Andric 
12368d75effSDimitry Andric   // Do a sanity check for certain flags.
12468d75effSDimitry Andric   if (common_flags_dont_use.malloc_context_size < 1)
12568d75effSDimitry Andric     common_flags_dont_use.malloc_context_size = 1;
12668d75effSDimitry Andric }
12768d75effSDimitry Andric 
ParseStringFromEnv(const char * env_name)12868d75effSDimitry Andric void FlagParser::ParseStringFromEnv(const char *env_name) {
12968d75effSDimitry Andric   const char *env = GetEnv(env_name);
13068d75effSDimitry Andric   VPrintf(1, "%s: %s\n", env_name, env ? env : "<empty>");
13168d75effSDimitry Andric   ParseString(env, env_name);
13268d75effSDimitry Andric }
13368d75effSDimitry Andric 
ParseString(const char * s,const char * env_option_name)13468d75effSDimitry Andric void FlagParser::ParseString(const char *s, const char *env_option_name) {
13568d75effSDimitry Andric   if (!s) return;
13668d75effSDimitry Andric   // Backup current parser state to allow nested ParseString() calls.
13768d75effSDimitry Andric   const char *old_buf_ = buf_;
13868d75effSDimitry Andric   uptr old_pos_ = pos_;
13968d75effSDimitry Andric   buf_ = s;
14068d75effSDimitry Andric   pos_ = 0;
14168d75effSDimitry Andric 
14268d75effSDimitry Andric   parse_flags(env_option_name);
14368d75effSDimitry Andric 
14468d75effSDimitry Andric   buf_ = old_buf_;
14568d75effSDimitry Andric   pos_ = old_pos_;
14668d75effSDimitry Andric }
14768d75effSDimitry Andric 
ParseFile(const char * path,bool ignore_missing)14868d75effSDimitry Andric bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
14968d75effSDimitry Andric   static const uptr kMaxIncludeSize = 1 << 15;
15068d75effSDimitry Andric   char *data;
15168d75effSDimitry Andric   uptr data_mapped_size;
15268d75effSDimitry Andric   error_t err;
15368d75effSDimitry Andric   uptr len;
15468d75effSDimitry Andric   if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
15568d75effSDimitry Andric                         Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
15668d75effSDimitry Andric     if (ignore_missing)
15768d75effSDimitry Andric       return true;
15868d75effSDimitry Andric     Printf("Failed to read options from '%s': error %d\n", path, err);
15968d75effSDimitry Andric     return false;
16068d75effSDimitry Andric   }
16168d75effSDimitry Andric   ParseString(data, path);
16268d75effSDimitry Andric   UnmapOrDie(data, data_mapped_size);
16368d75effSDimitry Andric   return true;
16468d75effSDimitry Andric }
16568d75effSDimitry Andric 
run_handler(const char * name,const char * value)16668d75effSDimitry Andric bool FlagParser::run_handler(const char *name, const char *value) {
16768d75effSDimitry Andric   for (int i = 0; i < n_flags_; ++i) {
16868d75effSDimitry Andric     if (internal_strcmp(name, flags_[i].name) == 0)
16968d75effSDimitry Andric       return flags_[i].handler->Parse(value);
17068d75effSDimitry Andric   }
17168d75effSDimitry Andric   // Unrecognized flag. This is not a fatal error, we may print a warning later.
17268d75effSDimitry Andric   unknown_flags.Add(name);
17368d75effSDimitry Andric   return true;
17468d75effSDimitry Andric }
17568d75effSDimitry Andric 
RegisterHandler(const char * name,FlagHandlerBase * handler,const char * desc)17668d75effSDimitry Andric void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
17768d75effSDimitry Andric                                  const char *desc) {
17868d75effSDimitry Andric   CHECK_LT(n_flags_, kMaxFlags);
17968d75effSDimitry Andric   flags_[n_flags_].name = name;
18068d75effSDimitry Andric   flags_[n_flags_].desc = desc;
18168d75effSDimitry Andric   flags_[n_flags_].handler = handler;
18268d75effSDimitry Andric   ++n_flags_;
18368d75effSDimitry Andric }
18468d75effSDimitry Andric 
FlagParser()18568d75effSDimitry Andric FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
186*5f757f3fSDimitry Andric   flags_ =
187*5f757f3fSDimitry Andric       (Flag *)GetGlobalLowLevelAllocator().Allocate(sizeof(Flag) * kMaxFlags);
18868d75effSDimitry Andric }
18968d75effSDimitry Andric 
19068d75effSDimitry Andric }  // namespace __sanitizer
191