1 //===-- sanitizer_flag_parser.cpp -----------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_flag_parser.h"
15 #include "sanitizer_common.h"
16 #include "sanitizer_libc.h"
17 #include "sanitizer_flags.h"
18 #include "sanitizer_flag_parser.h"
20 namespace __sanitizer
{
22 LowLevelAllocator
FlagParser::Alloc
;
25 static const int kMaxUnknownFlags
= 20;
26 const char *unknown_flags_
[kMaxUnknownFlags
];
30 void Add(const char *name
) {
31 CHECK_LT(n_unknown_flags_
, kMaxUnknownFlags
);
32 unknown_flags_
[n_unknown_flags_
++] = name
;
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
]);
44 UnknownFlags unknown_flags
;
46 void ReportUnrecognizedFlags() {
47 unknown_flags
.Report();
50 char *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
);
58 void FlagParser::PrintFlagDescriptions() {
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
);
71 void FlagParser::fatal_error(const char *err
) {
72 Printf("%s: ERROR: %s\n", SanitizerToolName
, err
);
76 bool FlagParser::is_space(char c
) {
77 return c
== ' ' || c
== ',' || c
== ':' || c
== '\n' || c
== '\t' ||
81 void FlagParser::skip_whitespace() {
82 while (is_space(buf_
[pos_
])) ++pos_
;
85 void 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
,
94 fatal_error("expected '='");
97 char *name
= ll_strndup(buf_
+ name_start
, pos_
- name_start
);
99 uptr value_start
= ++pos_
;
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
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
);
114 bool res
= run_handler(name
, value
);
115 if (!res
) fatal_error("Flag parsing failed.");
118 void FlagParser::parse_flags(const char *env_option_name
) {
121 if (buf_
[pos_
] == 0) break;
122 parse_flag(env_option_name
);
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;
130 void 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
);
136 void FlagParser::ParseString(const char *s
, const char *env_option_name
) {
138 // Backup current parser state to allow nested ParseString() calls.
139 const char *old_buf_
= buf_
;
140 uptr old_pos_
= pos_
;
144 parse_flags(env_option_name
);
150 bool FlagParser::ParseFile(const char *path
, bool ignore_missing
) {
151 static const uptr kMaxIncludeSize
= 1 << 15;
153 uptr data_mapped_size
;
156 if (!ReadFileToBuffer(path
, &data
, &data_mapped_size
, &len
,
157 Max(kMaxIncludeSize
, GetPageSizeCached()), &err
)) {
160 Printf("Failed to read options from '%s': error %d\n", path
, err
);
163 ParseString(data
, path
);
164 UnmapOrDie(data
, data_mapped_size
);
168 bool 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
);
173 // Unrecognized flag. This is not a fatal error, we may print a warning later.
174 unknown_flags
.Add(name
);
178 void FlagParser::RegisterHandler(const char *name
, FlagHandlerBase
*handler
,
180 CHECK_LT(n_flags_
, kMaxFlags
);
181 flags_
[n_flags_
].name
= name
;
182 flags_
[n_flags_
].desc
= desc
;
183 flags_
[n_flags_
].handler
= handler
;
187 FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
188 flags_
= (Flag
*)Alloc
.Allocate(sizeof(Flag
) * kMaxFlags
);
191 } // namespace __sanitizer