diff options
author | Evgeniy Stepanov <eugeni.stepanov@gmail.com> | 2015-01-15 15:13:43 +0000 |
---|---|---|
committer | Evgeniy Stepanov <eugeni.stepanov@gmail.com> | 2015-01-15 15:13:43 +0000 |
commit | 096af34b7cf9920da660a3084b5485ba97059f35 (patch) | |
tree | 8f2a32c53687aba186ca91e2b782fa9bb57e4d44 /lib | |
parent | 05fe0575f84ea38fa2eca8a5bdea5c77f3217cda (diff) | |
download | compiler-rt-096af34b7cf9920da660a3084b5485ba97059f35.tar.gz |
[sanitizer] Flag parser rewrite.
The new parser is a lot stricter about syntax, reports unrecognized
flags, and will make it easier to implemented some of the planned features.
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@226169 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
25 files changed, 442 insertions, 265 deletions
diff --git a/lib/asan/asan_activation.cc b/lib/asan/asan_activation.cc index a90484aa8..1ee99cd24 100644 --- a/lib/asan/asan_activation.cc +++ b/lib/asan/asan_activation.cc @@ -32,6 +32,9 @@ static struct AsanDeactivatedFlags { void OverrideFromActivationFlags() { Flags f; CommonFlags cf; + FlagParser parser; + RegisterAsanFlags(&parser, &f); + RegisterCommonFlags(&parser, &cf); // Copy the current activation flags. allocator_options.CopyTo(&f, &cf); @@ -44,15 +47,13 @@ static struct AsanDeactivatedFlags { // FIXME: Add diagnostic to check that activation flags string doesn't // contain any other flags. if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) { - cf.ParseFromString(env); - f.ParseFromString(env); + parser.ParseString(env); } // Override from getprop asan.options. char buf[100]; GetExtraActivationFlags(buf, sizeof(buf)); - cf.ParseFromString(buf); - f.ParseFromString(buf); + parser.ParseString(buf); allocator_options.SetFrom(&f, &cf); malloc_context_size = cf.malloc_context_size; diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc index 2c2d5c8f1..d73e43370 100644 --- a/lib/asan/asan_flags.cc +++ b/lib/asan/asan_flags.cc @@ -19,6 +19,7 @@ #include "lsan/lsan_common.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" namespace __asan { @@ -45,14 +46,18 @@ void Flags::SetDefaults() { #undef ASAN_FLAG } -void Flags::ParseFromString(const char *str) { -#define ASAN_FLAG(Type, Name, DefaultValue, Description) \ - ParseFlag(str, &Name, #Name, Description); +void RegisterAsanFlags(FlagParser *parser, Flags *f) { +#define ASAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); #include "asan_flags.inc" #undef ASAN_FLAG } void InitializeFlags(Flags *f) { + FlagParser parser; + RegisterAsanFlags(&parser, f); + RegisterCommonFlags(&parser); + SetCommonFlagsDefaults(); { CommonFlags cf; @@ -69,20 +74,17 @@ void InitializeFlags(Flags *f) { // Override from compile definition. const char *compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); - ParseCommonFlagsFromString(compile_def); - f->ParseFromString(compile_def); + parser.ParseString(compile_def); // Override from user-specified string. const char *default_options = MaybeCallAsanDefaultOptions(); - ParseCommonFlagsFromString(default_options); - f->ParseFromString(default_options); + parser.ParseString(default_options); VReport(1, "Using the defaults from __asan_default_options: %s\n", MaybeCallAsanDefaultOptions()); // Override from command line. if (const char *env = GetEnv("ASAN_OPTIONS")) { - ParseCommonFlagsFromString(env); - f->ParseFromString(env); + parser.ParseString(env); VReport(1, "Parsed ASAN_OPTIONS: %s\n", env); } @@ -91,14 +93,13 @@ void InitializeFlags(Flags *f) { if (!flags()->start_deactivated) { char buf[100]; GetExtraActivationFlags(buf, sizeof(buf)); - ParseCommonFlagsFromString(buf); - f->ParseFromString(buf); + parser.ParseString(buf); if (buf[0] != '\0') VReport(1, "Parsed activation flags: %s\n", buf); } if (common_flags()->help) { - PrintFlagDescriptions(); + parser.PrintFlagDescriptions(); } // Flag validation: diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index 32937e837..7653543f6 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -16,6 +16,7 @@ #define ASAN_FLAGS_H #include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_flag_parser.h" // ASan flag values can be defined in four ways: // 1) initialized with default values at startup. @@ -34,13 +35,13 @@ struct Flags { #undef ASAN_FLAG void SetDefaults(); - void ParseFromString(const char *str); }; extern Flags asan_flags_dont_use_directly; inline Flags *flags() { return &asan_flags_dont_use_directly; } +void RegisterAsanFlags(FlagParser *parser, Flags *f); void InitializeFlags(Flags *f); } // namespace __asan diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index b1e34c7f8..cdebf9aa0 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -30,7 +30,8 @@ set(ASAN_UNITTEST_COMMON_CFLAGS -fno-rtti -O2 -Wno-format - -Werror=sign-compare) + -Werror=sign-compare + -Wno-non-virtual-dtor) append_list_if(COMPILER_RT_HAS_WVARIADIC_MACROS_FLAG -Wno-variadic-macros ASAN_UNITTEST_COMMON_CFLAGS) # -gline-tables-only must be enough for ASan, so use it if possible. diff --git a/lib/dfsan/CMakeLists.txt b/lib/dfsan/CMakeLists.txt index 257a15a93..971146aa3 100644 --- a/lib/dfsan/CMakeLists.txt +++ b/lib/dfsan/CMakeLists.txt @@ -5,7 +5,7 @@ set(DFSAN_RTL_SOURCES dfsan.cc dfsan_custom.cc dfsan_interceptors.cc) -set(DFSAN_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +set(DFSAN_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS} -fno-rtti) # Prevent clang from generating libc calls. append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding DFSAN_COMMON_CFLAGS) diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc index b8eb1f22d..dd0ea6114 100644 --- a/lib/dfsan/dfsan.cc +++ b/lib/dfsan/dfsan.cc @@ -22,6 +22,7 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "dfsan/dfsan.h" @@ -316,16 +317,18 @@ void Flags::SetDefaults() { #undef DFSAN_FLAG } -void Flags::ParseFromString(const char *str) { -#define DFSAN_FLAG(Type, Name, DefaultValue, Description) \ - ParseFlag(str, &Name, #Name, Description); +void RegisterDfsanFlags(FlagParser *parser, Flags *f) { +#define DFSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); #include "dfsan_flags.inc" #undef DFSAN_FLAG } static void InitializeFlags(Flags &f, const char *env) { + FlagParser parser; + RegisterDfsanFlags(&parser, &f); f.SetDefaults(); - f.ParseFromString(env); + parser.ParseString(env); } static void dfsan_fini() { diff --git a/lib/dfsan/dfsan.h b/lib/dfsan/dfsan.h index 8f242197f..ceba3533a 100644 --- a/lib/dfsan/dfsan.h +++ b/lib/dfsan/dfsan.h @@ -61,7 +61,6 @@ struct Flags { #undef DFSAN_FLAG void SetDefaults(); - void ParseFromString(const char *str); }; extern Flags flags_data; diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc index 7c52526a5..80cf1322a 100644 --- a/lib/lsan/lsan_common.cc +++ b/lib/lsan/lsan_common.cc @@ -16,6 +16,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -42,19 +43,20 @@ void Flags::SetDefaults() { #undef LSAN_FLAG } -void Flags::ParseFromString(const char *str) { -#define LSAN_FLAG(Type, Name, DefaultValue, Description) \ - ParseFlag(str, &Name, #Name, Description); +static void RegisterLsanFlags(FlagParser *parser, Flags *f) { +#define LSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); #include "lsan_flags.inc" #undef LSAN_FLAG } static void InitializeFlags(bool standalone) { Flags *f = flags(); - f->SetDefaults(); + FlagParser parser; + RegisterLsanFlags(&parser, f); + RegisterCommonFlags(&parser); - const char *options = GetEnv("LSAN_OPTIONS"); - f->ParseFromString(options); + f->SetDefaults(); // Set defaults for common flags (only in standalone mode) and parse // them from LSAN_OPTIONS. @@ -67,7 +69,9 @@ static void InitializeFlags(bool standalone) { cf.detect_leaks = true; OverrideCommonFlags(cf); } - ParseCommonFlagsFromString(options); + + const char *options = GetEnv("LSAN_OPTIONS"); + parser.ParseString(options); } #define LOG_POINTERS(...) \ diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index b82b9a727..64cbef38c 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -43,7 +43,6 @@ struct Flags { #undef LSAN_FLAG void SetDefaults(); - void ParseFromString(const char *str); uptr pointer_alignment() const { return use_unaligned ? 1 : sizeof(uptr); } diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc index 6eccb5bf6..c8bd6039c 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -19,13 +19,13 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "sanitizer_common/sanitizer_stackdepot.h" - // ACHTUNG! No system header includes in this file. using namespace __sanitizer; @@ -102,20 +102,40 @@ void Flags::SetDefaults() { #undef MSAN_FLAG } -void Flags::ParseFromString(const char *str) { - // keep_going is an old name for halt_on_error, - // and it has inverse meaning. - halt_on_error = !halt_on_error; - ParseFlag(str, &halt_on_error, "keep_going", ""); - halt_on_error = !halt_on_error; +// keep_going is an old name for halt_on_error, +// and it has inverse meaning. +class FlagHandlerKeepGoing : public FlagHandlerBase { + bool *halt_on_error_; + + public: + explicit FlagHandlerKeepGoing(bool *halt_on_error) + : halt_on_error_(halt_on_error) {} + bool Parse(const char *value) { + bool tmp; + FlagHandler<bool> h(&tmp); + if (!h.Parse(value)) return false; + *halt_on_error_ = !tmp; + return true; + } +}; -#define MSAN_FLAG(Type, Name, DefaultValue, Description) \ - ParseFlag(str, &Name, #Name, Description); +void RegisterMsanFlags(FlagParser *parser, Flags *f) { +#define MSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); #include "msan_flags.inc" #undef MSAN_FLAG + + FlagHandlerKeepGoing *fh_keep_going = + new (INTERNAL_ALLOC) FlagHandlerKeepGoing(&f->halt_on_error); // NOLINT + parser->RegisterHandler("keep_going", fh_keep_going, + "deprecated, use halt_on_error"); } static void InitializeFlags(Flags *f, const char *options) { + FlagParser parser; + RegisterMsanFlags(&parser, f); + RegisterCommonFlags(&parser); + SetCommonFlagsDefaults(); { CommonFlags cf; @@ -132,13 +152,12 @@ static void InitializeFlags(Flags *f, const char *options) { f->SetDefaults(); // Override from user-specified string. - if (__msan_default_options) { - f->ParseFromString(__msan_default_options()); - ParseCommonFlagsFromString(__msan_default_options()); - } + if (__msan_default_options) + parser.ParseString(__msan_default_options()); + + parser.ParseString(options); - f->ParseFromString(options); - ParseCommonFlagsFromString(options); + if (common_flags()->help) parser.PrintFlagDescriptions(); // Check flag values: if (f->exit_code < 0 || f->exit_code > 127) { @@ -328,7 +347,6 @@ void __msan_init() { const char *msan_options = GetEnv("MSAN_OPTIONS"); InitializeFlags(&msan_flags, msan_options); - if (common_flags()->help) PrintFlagDescriptions(); __sanitizer_set_report_path(common_flags()->log_path); InitializeInterceptors(); diff --git a/lib/msan/msan_flags.h b/lib/msan/msan_flags.h index 5ec684dfc..4fc6d172a 100644 --- a/lib/msan/msan_flags.h +++ b/lib/msan/msan_flags.h @@ -21,7 +21,6 @@ struct Flags { #undef MSAN_FLAG void SetDefaults(); - void ParseFromString(const char *str); }; Flags *flags(); diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt index f4ee72a22..86697e7f7 100644 --- a/lib/sanitizer_common/CMakeLists.txt +++ b/lib/sanitizer_common/CMakeLists.txt @@ -7,6 +7,7 @@ set(SANITIZER_SOURCES sanitizer_deadlock_detector1.cc sanitizer_deadlock_detector2.cc sanitizer_flags.cc + sanitizer_flag_parser.cc sanitizer_libc.cc sanitizer_libignore.cc sanitizer_linux.cc @@ -63,6 +64,7 @@ set(SANITIZER_HEADERS sanitizer_common_syscalls.inc sanitizer_deadlock_detector.h sanitizer_deadlock_detector_interface.h + sanitizer_flag_parser.h sanitizer_flags.h sanitizer_flags.inc sanitizer_internal_defs.h diff --git a/lib/sanitizer_common/sanitizer_allocator_internal.h b/lib/sanitizer_common/sanitizer_allocator_internal.h index 4409fd65b..9b9cfd0b5 100644 --- a/lib/sanitizer_common/sanitizer_allocator_internal.h +++ b/lib/sanitizer_common/sanitizer_allocator_internal.h @@ -49,6 +49,15 @@ void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); void InternalFree(void *p, InternalAllocatorCache *cache = 0); InternalAllocator *internal_allocator(); +enum InternalAllocEnum { + INTERNAL_ALLOC +}; + } // namespace __sanitizer +inline void *operator new(__sanitizer::operator_new_size_type size, + InternalAllocEnum) { + return InternalAlloc(size); +} + #endif // SANITIZER_ALLOCATOR_INTERNAL_H diff --git a/lib/sanitizer_common/sanitizer_flag_parser.cc b/lib/sanitizer_common/sanitizer_flag_parser.cc new file mode 100644 index 000000000..96ee728e8 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_flag_parser.cc @@ -0,0 +1,117 @@ +//===-- sanitizer_flag_parser.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_flag_parser.h" + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_flags.h" +#include "sanitizer_flag_parser.h" +#include "sanitizer_allocator_internal.h" + +namespace __sanitizer { + +void FlagParser::PrintFlagDescriptions() { + Printf("Available flags for %s:\n", SanitizerToolName); + for (int i = 0; i < n_flags_; ++i) + Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc); +} + +void FlagParser::fatal_error(const char *err) { + Printf("ERROR: %s\n", err); + Die(); +} + +bool FlagParser::is_space(char c) { + return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' || + c == '\r'; +} + +void FlagParser::skip_whitespace() { + while (is_space(buf_[pos_])) ++pos_; +} + +void FlagParser::parse_flag() { + uptr name_start = pos_; + while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_; + if (buf_[pos_] != '=') fatal_error("expected '='"); + const char *name = internal_strndup(buf_ + name_start, pos_ - name_start); + + uptr value_start = ++pos_; + const char *value; + if (buf_[pos_] == '\'' || buf_[pos_] == '"') { + char quote = buf_[pos_++]; + while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_; + if (buf_[pos_] == 0) fatal_error("unterminated string"); + value = internal_strndup(buf_ + value_start + 1, pos_ - value_start - 1); + ++pos_; // consume the closing quote + } else { + while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_; + if (buf_[pos_] != 0 && !is_space(buf_[pos_])) + fatal_error("expected separator or eol"); + value = internal_strndup(buf_ + value_start, pos_ - value_start); + } + + bool res = run_handler(name, value); + if (!res) { + Printf("Flag parsing failed."); + Die(); + } + InternalFree((void *)name); + InternalFree((void *)value); +} + +void FlagParser::ParseString(const char *s) { + if (!s) return; + buf_ = s; + pos_ = 0; + while (true) { + skip_whitespace(); + if (buf_[pos_] == 0) break; + parse_flag(); + } + + // Do a sanity check for certain flags. + if (common_flags_dont_use.malloc_context_size < 1) + common_flags_dont_use.malloc_context_size = 1; +} + +bool FlagParser::run_handler(const char *name, const char *value) { + for (int i = 0; i < n_flags_; ++i) { + if (internal_strcmp(name, flags_[i].name) == 0) + return flags_[i].handler->Parse(value); + } + Printf("ERROR: Unknown flag: '%s'\n", name); + return false; +} + +void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler, + const char *desc) { + CHECK(n_flags_ < kMaxFlags); + flags_[n_flags_].name = name; + flags_[n_flags_].desc = desc; + flags_[n_flags_].handler = handler; + ++n_flags_; +} + +FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) { + flags_ = (Flag *)InternalAlloc(sizeof(Flag) * kMaxFlags); +} + +FlagParser::~FlagParser() { + for (int i = 0; i < n_flags_; ++i) + InternalFree(flags_[i].handler); + InternalFree(flags_); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_flag_parser.h b/lib/sanitizer_common/sanitizer_flag_parser.h new file mode 100644 index 000000000..fe55f7a6f --- /dev/null +++ b/lib/sanitizer_common/sanitizer_flag_parser.h @@ -0,0 +1,117 @@ +//===-- sanitizer_flag_parser.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_FLAG_REGISTRY_H +#define SANITIZER_FLAG_REGISTRY_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_common.h" +#include "sanitizer_allocator_internal.h" + +namespace __sanitizer { + +class FlagHandlerBase { + public: + virtual bool Parse(const char *value) { return false; } +}; + +template <typename T> +class FlagHandler : public FlagHandlerBase { + T *t_; + + public: + explicit FlagHandler(T *t) : t_(t) {} + bool Parse(const char *value); +}; + +template <> +inline bool FlagHandler<bool>::Parse(const char *value) { + if (internal_strcmp(value, "0") == 0 || + internal_strcmp(value, "no") == 0 || + internal_strcmp(value, "false") == 0) { + *t_ = false; + return true; + } + if (internal_strcmp(value, "1") == 0 || + internal_strcmp(value, "yes") == 0 || + internal_strcmp(value, "true") == 0) { + *t_ = true; + return true; + } + Printf("ERROR: Invalid value for bool option: '%s'\n", value); + return false; +} + +template <> +inline bool FlagHandler<const char *>::Parse(const char *value) { + *t_ = internal_strdup(value); + return true; +} + +template <> +inline bool FlagHandler<int>::Parse(const char *value) { + char *value_end; + *t_ = internal_simple_strtoll(value, &value_end, 10); + bool ok = *value_end == 0; + if (!ok) Printf("ERROR: Invalid value for int option: '%s'\n", value); + return ok; +} + +template <> +inline bool FlagHandler<uptr>::Parse(const char *value) { + char *value_end; + *t_ = internal_simple_strtoll(value, &value_end, 10); + bool ok = *value_end == 0; + if (!ok) Printf("ERROR: Invalid value for uptr option: '%s'\n", value); + return ok; +} + +class FlagParser { + static const int kMaxFlags = 200; + struct Flag { + const char *name; + const char *desc; + FlagHandlerBase *handler; + } *flags_; + int n_flags_; + + const char *buf_; + uptr pos_; + + public: + FlagParser(); + ~FlagParser(); + void RegisterHandler(const char *name, FlagHandlerBase *handler, + const char *desc); + void ParseString(const char *s); + void PrintFlagDescriptions(); + + private: + void fatal_error(const char *err); + bool is_space(char c); + void skip_whitespace(); + void parse_flag(); + bool run_handler(const char *name, const char *value); +}; + +template <typename T> +static void RegisterFlag(FlagParser *parser, const char *name, const char *desc, + T *var) { + FlagHandler<T> *fh = new (INTERNAL_ALLOC) FlagHandler<T>(var); // NOLINT + parser->RegisterHandler(name, fh, desc); +} + +} // namespace __sanitizer + +#endif // SANITIZER_FLAG_REGISTRY_H diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc index b4ab1d940..b0ae4afb3 100644 --- a/lib/sanitizer_common/sanitizer_flags.cc +++ b/lib/sanitizer_common/sanitizer_flags.cc @@ -16,6 +16,7 @@ #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" +#include "sanitizer_flag_parser.h" namespace __sanitizer { @@ -40,150 +41,15 @@ void CommonFlags::SetDefaults() { #undef COMMON_FLAG } -void CommonFlags::ParseFromString(const char *str) { -#define COMMON_FLAG(Type, Name, DefaultValue, Description) \ - ParseFlag(str, &Name, #Name, Description); -#include "sanitizer_flags.inc" -#undef COMMON_FLAG - // Do a sanity check for certain flags. - if (malloc_context_size < 1) - malloc_context_size = 1; -} - void CommonFlags::CopyFrom(const CommonFlags &other) { internal_memcpy(this, &other, sizeof(*this)); } -static bool GetFlagValue(const char *env, const char *name, - const char **value, int *value_length) { - if (env == 0) - return false; - const char *pos = 0; - for (;;) { - pos = internal_strstr(env, name); - if (pos == 0) - return false; - const char *name_end = pos + internal_strlen(name); - if ((pos != env && - ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) || - *name_end != '=') { - // Seems to be middle of another flag name or value. - env = pos + 1; - continue; - } - pos = name_end; - break; - } - const char *end; - if (pos[0] != '=') { - end = pos; - } else { - pos += 1; - if (pos[0] == '"') { - pos += 1; - end = internal_strchr(pos, '"'); - } else if (pos[0] == '\'') { - pos += 1; - end = internal_strchr(pos, '\''); - } else { - // Read until the next space or colon. - end = pos + internal_strcspn(pos, " :\r\n\t"); - } - if (end == 0) - end = pos + internal_strlen(pos); - } - *value = pos; - *value_length = end - pos; - return true; -} - -static bool StartsWith(const char *flag, int flag_length, const char *value) { - if (!flag || !value) - return false; - int value_length = internal_strlen(value); - return (flag_length >= value_length) && - (0 == internal_strncmp(flag, value, value_length)); -} - -static LowLevelAllocator allocator_for_flags; - -// The linear scan is suboptimal, but the number of flags is relatively small. -bool FlagInDescriptionList(const char *name) { - IntrusiveList<FlagDescription>::Iterator it(&flag_descriptions); - while (it.hasNext()) { - if (!internal_strcmp(it.next()->name, name)) return true; - } - return false; -} - -void AddFlagDescription(const char *name, const char *description) { - if (FlagInDescriptionList(name)) return; - FlagDescription *new_description = new(allocator_for_flags) FlagDescription; - new_description->name = name; - new_description->description = description; - flag_descriptions.push_back(new_description); -} - -// TODO(glider): put the descriptions inside CommonFlags. -void PrintFlagDescriptions() { - IntrusiveList<FlagDescription>::Iterator it(&flag_descriptions); - Printf("Available flags for %s:\n", SanitizerToolName); - while (it.hasNext()) { - FlagDescription *descr = it.next(); - Printf("\t%s\n\t\t- %s\n", descr->name, descr->description); - } -} - -void ParseFlag(const char *env, bool *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - if (StartsWith(value, value_length, "0") || - StartsWith(value, value_length, "no") || - StartsWith(value, value_length, "false")) - *flag = false; - if (StartsWith(value, value_length, "1") || - StartsWith(value, value_length, "yes") || - StartsWith(value, value_length, "true")) - *flag = true; -} - -void ParseFlag(const char *env, int *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - *flag = static_cast<int>(internal_atoll(value)); -} - -void ParseFlag(const char *env, uptr *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - *flag = static_cast<uptr>(internal_atoll(value)); -} - -void ParseFlag(const char *env, const char **flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - // Copy the flag value. Don't use locks here, as flags are parsed at - // tool startup. - char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1)); - internal_memcpy(value_copy, value, value_length); - value_copy[value_length] = '\0'; - *flag = value_copy; +void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) { +#define COMMON_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &cf->Name); +#include "sanitizer_flags.inc" +#undef COMMON_FLAG } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_flags.h b/lib/sanitizer_common/sanitizer_flags.h index 9f0352a57..90f3bbfbf 100644 --- a/lib/sanitizer_common/sanitizer_flags.h +++ b/lib/sanitizer_common/sanitizer_flags.h @@ -18,22 +18,12 @@ namespace __sanitizer { -void ParseFlag(const char *env, bool *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, int *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, uptr *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, const char **flag, - const char *name, const char *descr); - struct CommonFlags { #define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name; #include "sanitizer_flags.inc" #undef COMMON_FLAG void SetDefaults(); - void ParseFromString(const char *str); void CopyFrom(const CommonFlags &other); }; @@ -47,10 +37,6 @@ inline void SetCommonFlagsDefaults() { common_flags_dont_use.SetDefaults(); } -inline void ParseCommonFlagsFromString(const char *str) { - common_flags_dont_use.ParseFromString(str); -} - // This function can only be used to setup tool-specific overrides for // CommonFlags defaults. Generally, it should only be used right after // SetCommonFlagsDefaults(), but before ParseCommonFlagsFromString(), and @@ -60,8 +46,9 @@ inline void OverrideCommonFlags(const CommonFlags &cf) { common_flags_dont_use.CopyFrom(cf); } -void PrintFlagDescriptions(); - +class FlagParser; +void RegisterCommonFlags(FlagParser *parser, + CommonFlags *cf = &common_flags_dont_use); } // namespace __sanitizer #endif // SANITIZER_FLAGS_H diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc index d8bd1cf7a..862cf68e7 100644 --- a/lib/sanitizer_common/sanitizer_libc.cc +++ b/lib/sanitizer_common/sanitizer_libc.cc @@ -101,6 +101,14 @@ char* internal_strdup(const char *s) { return s2; } +char* internal_strndup(const char *s, uptr n) { + uptr len = internal_strnlen(s, n); + char *s2 = (char*)InternalAlloc(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + int internal_strcmp(const char *s1, const char *s2) { while (true) { unsigned c1 = *s1; diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h index 9321e8736..0f264f106 100644 --- a/lib/sanitizer_common/sanitizer_libc.h +++ b/lib/sanitizer_common/sanitizer_libc.h @@ -38,6 +38,7 @@ char *internal_strchrnul(const char *s, int c); int internal_strcmp(const char *s1, const char *s2); uptr internal_strcspn(const char *s, const char *reject); char *internal_strdup(const char *s); +char *internal_strndup(const char *s, uptr n); uptr internal_strlen(const char *s); char *internal_strncat(char *dst, const char *src, uptr n); int internal_strncmp(const char *s1, const char *s2, uptr n); diff --git a/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/lib/sanitizer_common/tests/sanitizer_flags_test.cc index a8962917e..65adee07d 100644 --- a/lib/sanitizer_common/tests/sanitizer_flags_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_flags_test.cc @@ -12,7 +12,9 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "gtest/gtest.h" #include <string.h> @@ -20,58 +22,74 @@ namespace __sanitizer { static const char kFlagName[] = "flag_name"; +static const char kFlagDesc[] = "flag description"; template <typename T> static void TestFlag(T start_value, const char *env, T final_value) { T flag = start_value; - ParseFlag(env, &flag, kFlagName, "flag description"); + + FlagParser parser; + RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); + + parser.ParseString(env); + EXPECT_EQ(final_value, flag); } -static void TestStrFlag(const char *start_value, const char *env, - const char *final_value) { +template <> +void TestFlag(const char *start_value, const char *env, + const char *final_value) { const char *flag = start_value; - ParseFlag(env, &flag, kFlagName, "flag description"); + + FlagParser parser; + RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); + + parser.ParseString(env); + EXPECT_EQ(0, internal_strcmp(final_value, flag)); } TEST(SanitizerCommon, BooleanFlags) { - TestFlag(true, "--flag_name", true); - TestFlag(false, "flag_name", false); - TestFlag(false, "--flag_name=1", true); - TestFlag(true, "asdas flag_name=0 asdas", false); - TestFlag(true, " --flag_name=0 ", false); + TestFlag(false, "flag_name=1", true); TestFlag(false, "flag_name=yes", true); TestFlag(false, "flag_name=true", true); + TestFlag(true, "flag_name=0", false); TestFlag(true, "flag_name=no", false); TestFlag(true, "flag_name=false", false); } TEST(SanitizerCommon, IntFlags) { TestFlag(-11, 0, -11); - TestFlag(-11, "flag_name", -11); - TestFlag(-11, "--flag_name=", 0); - TestFlag(-11, "--flag_name=42", 42); - TestFlag(-11, "--flag_name=-42", -42); + TestFlag(-11, "flag_name=0", 0); + TestFlag(-11, "flag_name=42", 42); + TestFlag(-11, "flag_name=-42", -42); + EXPECT_DEATH(TestFlag(-11, "flag_name", 0), "expected '='"); + EXPECT_DEATH(TestFlag(-11, "--flag_name=42", 0), + "Unknown flag: '--flag_name'"); } TEST(SanitizerCommon, StrFlags) { - TestStrFlag("zzz", 0, "zzz"); - TestStrFlag("zzz", "flag_name", "zzz"); - TestStrFlag("zzz", "--flag_name=", ""); - TestStrFlag("", "--flag_name=abc", "abc"); - TestStrFlag("", "--flag_name='abc zxc'", "abc zxc"); - TestStrFlag("", "--flag_name='abc zxcc'", "abc zxcc"); - TestStrFlag("", "--flag_name=\"abc qwe\" asd", "abc qwe"); - TestStrFlag("", "other_flag_name=zzz", ""); + TestFlag("zzz", 0, "zzz"); + TestFlag("zzz", "flag_name=", ""); + TestFlag("zzz", "flag_name=abc", "abc"); + TestFlag("", "flag_name=abc", "abc"); + TestFlag("", "flag_name='abc zxc'", "abc zxc"); + // TestStrFlag("", "flag_name=\"abc qwe\" asd", "abc qwe"); } static void TestTwoFlags(const char *env, bool expected_flag1, - const char *expected_flag2) { + const char *expected_flag2, + const char *name1 = "flag1", + const char *name2 = "flag2") { bool flag1 = !expected_flag1; const char *flag2 = ""; - ParseFlag(env, &flag1, "flag1", "flag1 description"); - ParseFlag(env, &flag2, "flag2", "flag2 description"); + + FlagParser parser; + RegisterFlag(&parser, name1, kFlagDesc, &flag1); + RegisterFlag(&parser, name2, kFlagDesc, &flag2); + + parser.ParseString(env); + EXPECT_EQ(expected_flag1, flag1); EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2)); } @@ -86,8 +104,20 @@ TEST(SanitizerCommon, MultipleFlags) { TestTwoFlags("flag2=qxx\tflag1=yes", true, "qxx"); } +TEST(SanitizerCommon, CommonSuffixFlags) { + TestTwoFlags("flag=1 other_flag='zzz'", true, "zzz", "flag", "other_flag"); + TestTwoFlags("other_flag='zzz' flag=1", true, "zzz", "flag", "other_flag"); + TestTwoFlags("other_flag=' flag=0 ' flag=1", true, " flag=0 ", "flag", + "other_flag"); + TestTwoFlags("flag=1 other_flag=' flag=0 '", true, " flag=0 ", "flag", + "other_flag"); +} + TEST(SanitizerCommon, CommonFlags) { CommonFlags cf; + FlagParser parser; + RegisterCommonFlags(&parser, &cf); + cf.SetDefaults(); EXPECT_TRUE(cf.symbolize); EXPECT_STREQ(".", cf.coverage_dir); @@ -97,7 +127,7 @@ TEST(SanitizerCommon, CommonFlags) { cf.coverage_direct = true; cf.log_path = "path/one"; - cf.ParseFromString("symbolize=1:coverage_direct=false log_path='path/two'"); + parser.ParseString("symbolize=1:coverage_direct=false log_path='path/two'"); EXPECT_TRUE(cf.symbolize); EXPECT_TRUE(cf.coverage); EXPECT_FALSE(cf.coverage_direct); diff --git a/lib/tsan/dd/dd_rtl.cc b/lib/tsan/dd/dd_rtl.cc index 8728740f4..fe7077c20 100644 --- a/lib/tsan/dd/dd_rtl.cc +++ b/lib/tsan/dd/dd_rtl.cc @@ -11,6 +11,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -80,8 +81,10 @@ void InitializeFlags(Flags *f, const char *env) { } // Override from command line. - ParseFlag(env, &f->second_deadlock_stack, "second_deadlock_stack", ""); - ParseCommonFlagsFromString(env); + FlagParser parser; + RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack); + RegisterCommonFlags(&parser); + parser.ParseString(env); } void Initialize() { diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index 77677ebba..b99d92fc8 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "tsan_flags.h" #include "tsan_rtl.h" @@ -41,16 +42,18 @@ void Flags::SetDefaults() { second_deadlock_stack = false; } -void Flags::ParseFromString(const char *str) { -#define TSAN_FLAG(Type, Name, DefaultValue, Description) \ - ParseFlag(str, &Name, #Name, Description); +void RegisterTsanFlags(FlagParser *parser, Flags *f) { +#define TSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); #include "tsan_flags.inc" #undef TSAN_FLAG - // DDFlags - ParseFlag(str, &second_deadlock_stack, "second_deadlock_stack", ""); } void InitializeFlags(Flags *f, const char *env) { + FlagParser parser; + RegisterTsanFlags(&parser, f); + RegisterCommonFlags(&parser); + f->SetDefaults(); SetCommonFlagsDefaults(); @@ -66,11 +69,9 @@ void InitializeFlags(Flags *f, const char *env) { } // Let a frontend override. - f->ParseFromString(__tsan_default_options()); - ParseCommonFlagsFromString(__tsan_default_options()); + parser.ParseString(__tsan_default_options()); // Override from command line. - f->ParseFromString(env); - ParseCommonFlagsFromString(env); + parser.ParseString(env); // Sanity check. if (!f->report_bugs) { @@ -80,7 +81,7 @@ void InitializeFlags(Flags *f, const char *env) { } if (common_flags()->help) - PrintFlagDescriptions(); + parser.PrintFlagDescriptions(); if (f->history_size < 0 || f->history_size > 7) { Printf("ThreadSanitizer: incorrect value for history_size" diff --git a/lib/ubsan/ubsan_flags.cc b/lib/ubsan/ubsan_flags.cc index d25481e73..7bd23a8b1 100644 --- a/lib/ubsan/ubsan_flags.cc +++ b/lib/ubsan/ubsan_flags.cc @@ -14,6 +14,7 @@ #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" namespace __ubsan { @@ -21,18 +22,6 @@ static const char *MaybeCallUbsanDefaultOptions() { return (&__ubsan_default_options) ? __ubsan_default_options() : ""; } -void InitializeCommonFlags() { - SetCommonFlagsDefaults(); - CommonFlags cf; - cf.CopyFrom(*common_flags()); - cf.print_summary = false; - OverrideCommonFlags(cf); - // Override from user-specified string. - ParseCommonFlagsFromString(MaybeCallUbsanDefaultOptions()); - // Override from environment variable. - ParseCommonFlagsFromString(GetEnv("UBSAN_OPTIONS")); -} - Flags ubsan_flags; void Flags::SetDefaults() { @@ -41,20 +30,42 @@ void Flags::SetDefaults() { #undef UBSAN_FLAG } -void Flags::ParseFromString(const char *str) { -#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \ - ParseFlag(str, &Name, #Name, Description); +void RegisterUbsanFlags(FlagParser *parser, Flags *f) { +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); #include "ubsan_flags.inc" #undef UBSAN_FLAG } -void InitializeFlags() { +void InitializeFlags(bool standalone) { Flags *f = flags(); + FlagParser parser; + RegisterUbsanFlags(&parser, f); + + if (standalone) { + RegisterCommonFlags(&parser); + + SetCommonFlagsDefaults(); + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.print_summary = false; + OverrideCommonFlags(cf); + } else { + // Ignore common flags if not standalone. + // This is inconsistent with LSan, which allows common flags in LSAN_FLAGS. + // This is caused by undefined initialization order between ASan and UBsan, + // which makes it impossible to make sure that common flags from ASAN_OPTIONS + // have not been used (in __asan_init) before they are overwritten with flags + // from UBSAN_OPTIONS. + CommonFlags cf_ignored; + RegisterCommonFlags(&parser, &cf_ignored); + } + f->SetDefaults(); // Override from user-specified string. - f->ParseFromString(MaybeCallUbsanDefaultOptions()); + parser.ParseString(MaybeCallUbsanDefaultOptions()); // Override from environment variable. - f->ParseFromString(GetEnv("UBSAN_OPTIONS")); + parser.ParseString(GetEnv("UBSAN_OPTIONS")); } } // namespace __ubsan diff --git a/lib/ubsan/ubsan_flags.h b/lib/ubsan/ubsan_flags.h index 44c287e3d..b47f14e1e 100644 --- a/lib/ubsan/ubsan_flags.h +++ b/lib/ubsan/ubsan_flags.h @@ -23,14 +23,12 @@ struct Flags { #undef UBSAN_FLAG void SetDefaults(); - void ParseFromString(const char *str); }; extern Flags ubsan_flags; inline Flags *flags() { return &ubsan_flags; } -void InitializeCommonFlags(); -void InitializeFlags(); +void InitializeFlags(bool standalone); } // namespace __ubsan diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc index dbafbac81..48fa49248 100644 --- a/lib/ubsan/ubsan_init.cc +++ b/lib/ubsan/ubsan_init.cc @@ -31,6 +31,7 @@ void __ubsan::InitIfNecessary() { #endif if (LIKELY(ubsan_inited)) return; + bool standalone = false; if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) { // WARNING: If this condition holds, then either UBSan runs in a standalone // mode, or initializer for another sanitizer hasn't run yet. In a latter @@ -38,10 +39,10 @@ void __ubsan::InitIfNecessary() { // common flags. It means, that we are not allowed to *use* common flags // in this function. SanitizerToolName = "UndefinedBehaviorSanitizer"; - InitializeCommonFlags(); + standalone = true; } // Initialize UBSan-specific flags. - InitializeFlags(); + InitializeFlags(standalone); SuppressionContext::InitIfNecessary(); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); ubsan_inited = true; |