Index: lib/asan/asan_activation.cc =================================================================== --- lib/asan/asan_activation.cc +++ lib/asan/asan_activation.cc @@ -38,7 +38,7 @@ #undef ASAN_ACTIVATION_FLAG #undef COMMON_ACTIVATION_FLAG - RegisterIncludeFlag(parser, cf); + RegisterIncludeFlags(parser, cf); } void OverrideFromActivationFlags() { Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -363,12 +363,12 @@ CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; + CacheBinaryName(); + // Initialize flags. This must be done early, because most of the // initialization steps look at flags(). InitializeFlags(); - CacheBinaryName(); - AsanCheckIncompatibleRT(); AsanCheckDynamicRTPrereqs(); Index: lib/lsan/lsan.cc =================================================================== --- lib/lsan/lsan.cc +++ lib/lsan/lsan.cc @@ -69,6 +69,7 @@ return; lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; + CacheBinaryName(); InitializeFlags(); InitCommonLsan(); InitializeAllocator(); Index: lib/msan/msan.cc =================================================================== --- lib/msan/msan.cc +++ lib/msan/msan.cc @@ -372,8 +372,8 @@ SetDieCallback(MsanDie); InitTlsSize(); - InitializeFlags(); CacheBinaryName(); + InitializeFlags(); __sanitizer_set_report_path(common_flags()->log_path); InitializeInterceptors(); Index: lib/sanitizer_common/sanitizer_flag_parser.h =================================================================== --- lib/sanitizer_common/sanitizer_flag_parser.h +++ lib/sanitizer_common/sanitizer_flag_parser.h @@ -93,6 +93,7 @@ void RegisterHandler(const char *name, FlagHandlerBase *handler, const char *desc); void ParseString(const char *s); + bool ParseFile(const char *path, bool ignore_missing); void PrintFlagDescriptions(); static LowLevelAllocator Alloc; Index: lib/sanitizer_common/sanitizer_flag_parser.cc =================================================================== --- lib/sanitizer_common/sanitizer_flag_parser.cc +++ lib/sanitizer_common/sanitizer_flag_parser.cc @@ -127,6 +127,24 @@ pos_ = old_pos_; } +bool FlagParser::ParseFile(const char *path, bool ignore_missing) { + static const uptr kMaxIncludeSize = 1 << 15; + char *data; + uptr data_mapped_size; + error_t err; + uptr len; + if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len, + Max(kMaxIncludeSize, GetPageSizeCached()), &err)) { + if (ignore_missing) + return true; + Printf("Failed to read options from '%s': error %d\n", path, err); + return false; + } + ParseString(data); + UnmapOrDie(data, data_mapped_size); + return true; +} + 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) Index: lib/sanitizer_common/sanitizer_flags.h =================================================================== --- lib/sanitizer_common/sanitizer_flags.h +++ lib/sanitizer_common/sanitizer_flags.h @@ -49,7 +49,7 @@ class FlagParser; void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf = &common_flags_dont_use); -void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf); +void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf); } // namespace __sanitizer #endif // SANITIZER_FLAGS_H Index: lib/sanitizer_common/sanitizer_flags.cc =================================================================== --- lib/sanitizer_common/sanitizer_flags.cc +++ lib/sanitizer_common/sanitizer_flags.cc @@ -45,33 +45,49 @@ internal_memcpy(this, &other, sizeof(*this)); } +// Copy the string from "s" to "out", replacing "%b" with the binary basename. +static void SubstituteBinaryName(const char *s, char *out, uptr out_size) { + char *out_end = out + out_size; + while (*s && out < out_end - 1) { + if (s[0] != '%' || s[1] != 'b') { *out++ = *s++; continue; } + const char *base = GetBinaryBasename(); + CHECK(base); + while (*base && out < out_end - 1) + *out++ = *base++; + s += 2; // skip "%b" + } + *out = '\0'; +} + class FlagHandlerInclude : public FlagHandlerBase { - static const uptr kMaxIncludeSize = 1 << 15; FlagParser *parser_; + bool ignore_missing_; public: - explicit FlagHandlerInclude(FlagParser *parser) : parser_(parser) {} + explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing) + : parser_(parser), ignore_missing_(ignore_missing) {} bool Parse(const char *value) final { - char *data; - uptr data_mapped_size; - uptr len; - error_t err; - if (!ReadFileToBuffer(value, &data, &data_mapped_size, &len, - Max(kMaxIncludeSize, GetPageSizeCached()), &err)) { - Printf("Failed to read options from '%s': error %d\n", value, err); - return false; + if (internal_strchr(value, '%')) { + char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude"); + SubstituteBinaryName(value, buf, kMaxPathLength); + bool res = parser_->ParseFile(buf, ignore_missing_); + UnmapOrDie(buf, kMaxPathLength); + return res; } - parser_->ParseString(data); - UnmapOrDie(data, data_mapped_size); - return true; + return parser_->ParseFile(value, ignore_missing_); } }; -void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf) { - FlagHandlerInclude *fh_include = - new (FlagParser::Alloc) FlagHandlerInclude(parser); // NOLINT +void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) { + FlagHandlerInclude *fh_include = new (FlagParser::Alloc) // NOLINT + FlagHandlerInclude(parser, /*ignore_missing*/ false); parser->RegisterHandler("include", fh_include, "read more options from the given file"); + FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc) // NOLINT + FlagHandlerInclude(parser, /*ignore_missing*/ true); + parser->RegisterHandler( + "include_if_exists", fh_include_if_exists, + "read more options from the given file (if it exists)"); } void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) { @@ -80,7 +96,7 @@ #include "sanitizer_flags.inc" #undef COMMON_FLAG - RegisterIncludeFlag(parser, cf); + RegisterIncludeFlags(parser, cf); } } // namespace __sanitizer Index: lib/tsan/rtl/tsan_rtl.cc =================================================================== --- lib/tsan/rtl/tsan_rtl.cc +++ lib/tsan/rtl/tsan_rtl.cc @@ -318,8 +318,8 @@ ctx = new(ctx_placeholder) Context; const char *options = GetEnv(kTsanOptionsEnv); - InitializeFlags(&ctx->flags, options); CacheBinaryName(); + InitializeFlags(&ctx->flags, options); #ifndef SANITIZER_GO InitializeAllocator(); #endif Index: test/sanitizer_common/TestCases/options-include.cc =================================================================== --- test/sanitizer_common/TestCases/options-include.cc +++ test/sanitizer_common/TestCases/options-include.cc @@ -1,21 +1,46 @@ // RUN: %clangxx -O0 %s -o %t + +// Recursive include: options1 includes options2 // RUN: echo -e "symbolize=1\ninclude='%t.options2.txt'" >%t.options1.txt // RUN: echo -e "help=1\n" >%t.options2.txt +// RUN: echo -e "help=1\n" >%t.options.options-include.cc.tmp // RUN: cat %t.options1.txt // RUN: cat %t.options2.txt + // RUN: %tool_options="help=0:include='%t.options1.txt'" %run %t 2>&1 | tee %t.out -// RUN: FileCheck %s --check-prefix=CHECK-VERBOSITY1 <%t.out +// RUN: FileCheck %s --check-prefix=CHECK-WITH-HELP --check-prefix=CHECK-FOUND <%t.out + // RUN: %tool_options="include='%t.options1.txt',help=0" %run %t 2>&1 | tee %t.out -// RUN: FileCheck %s --check-prefix=CHECK-VERBOSITY0 <%t.out +// RUN: FileCheck %s --check-prefix=CHECK-WITHOUT-HELP --check-prefix=CHECK-FOUND <%t.out + // RUN: %tool_options="include='%t.options-not-found.txt',help=1" not %run %t 2>&1 | tee %t.out // RUN: FileCheck %s --check-prefix=CHECK-NOT-FOUND < %t.out +// include_if_exists does not fail when the file is missing +// RUN: %tool_options="include_if_exists='%t.options-not-found.txt',help=1" %run %t 2>&1 | tee %t.out +// RUN: FileCheck %s --check-prefix=CHECK-WITH-HELP --check-prefix=CHECK-FOUND < %t.out + +// %b (binary basename substitution) +// RUN: %tool_options="include='%t.options.%b'" %run %t 2>&1 | tee %t.out +// RUN: FileCheck %s --check-prefix=CHECK-WITH-HELP --check-prefix=CHECK-FOUND < %t.out + +// RUN: %tool_options="include='%t.options-not-found.%b'" not %run %t 2>&1 | tee %t.out +// RUN: FileCheck %s --check-prefix=CHECK-WITHOUT-HELP --check-prefix=CHECK-NOT-FOUND < %t.out + +// RUN: %tool_options="include_if_exists='%t.options.%b'" %run %t 2>&1 | tee %t.out +// RUN: FileCheck %s --check-prefix=CHECK-WITH-HELP --check-prefix=CHECK-FOUND < %t.out + +// RUN: %tool_options="include_if_exists='%t.options-not-found.%b'" %run %t 2>&1 | tee %t.out +// RUN: FileCheck %s --check-prefix=CHECK-WITHOUT-HELP --check-prefix=CHECK-FOUND < %t.out + + #include int main() { fprintf(stderr, "done\n"); } -// CHECK-VERBOSITY1: Available flags for -// CHECK-VERBOSITY0-NOT: Available flags for +// CHECK-WITH-HELP: Available flags for +// CHECK-WITHOUT-HELP-NOT: Available flags for +// CHECK-FOUND-NOT: Failed to read options from // CHECK-NOT-FOUND: Failed to read options from