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_flag_parser.h"
17 #include "sanitizer_flags.h"
18 #include "sanitizer_libc.h"
20 namespace __sanitizer
{
23 static const int kMaxUnknownFlags
= 20;
24 const char *unknown_flags_
[kMaxUnknownFlags
];
28 void Add(const char *name
) {
29 CHECK_LT(n_unknown_flags_
, kMaxUnknownFlags
);
30 unknown_flags_
[n_unknown_flags_
++] = name
;
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
]);
42 UnknownFlags unknown_flags
;
44 void ReportUnrecognizedFlags() {
45 unknown_flags
.Report();
48 char *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
);
56 void FlagParser::PrintFlagDescriptions() {
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
);
69 void FlagParser::fatal_error(const char *err
) {
70 Printf("%s: ERROR: %s\n", SanitizerToolName
, err
);
74 bool FlagParser::is_space(char c
) {
75 return c
== ' ' || c
== ',' || c
== ':' || c
== '\n' || c
== '\t' ||
79 void FlagParser::skip_whitespace() {
80 while (is_space(buf_
[pos_
])) ++pos_
;
83 void 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
,
92 fatal_error("expected '='");
95 char *name
= ll_strndup(buf_
+ name_start
, pos_
- name_start
);
97 uptr value_start
= ++pos_
;
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
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
);
112 bool res
= run_handler(name
, value
);
113 if (!res
) fatal_error("Flag parsing failed.");
116 void FlagParser::parse_flags(const char *env_option_name
) {
119 if (buf_
[pos_
] == 0) break;
120 parse_flag(env_option_name
);
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;
128 void 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
);
134 void FlagParser::ParseString(const char *s
, const char *env_option_name
) {
136 // Backup current parser state to allow nested ParseString() calls.
137 const char *old_buf_
= buf_
;
138 uptr old_pos_
= pos_
;
142 parse_flags(env_option_name
);
148 bool FlagParser::ParseFile(const char *path
, bool ignore_missing
) {
149 static const uptr kMaxIncludeSize
= 1 << 15;
151 uptr data_mapped_size
;
154 if (!ReadFileToBuffer(path
, &data
, &data_mapped_size
, &len
,
155 Max(kMaxIncludeSize
, GetPageSizeCached()), &err
)) {
158 Printf("Failed to read options from '%s': error %d\n", path
, err
);
161 ParseString(data
, path
);
162 UnmapOrDie(data
, data_mapped_size
);
166 bool 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
);
171 // Unrecognized flag. This is not a fatal error, we may print a warning later.
172 unknown_flags
.Add(name
);
176 void FlagParser::RegisterHandler(const char *name
, FlagHandlerBase
*handler
,
178 CHECK_LT(n_flags_
, kMaxFlags
);
179 flags_
[n_flags_
].name
= name
;
180 flags_
[n_flags_
].desc
= desc
;
181 flags_
[n_flags_
].handler
= handler
;
185 FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
187 (Flag
*)GetGlobalLowLevelAllocator().Allocate(sizeof(Flag
) * kMaxFlags
);
190 } // namespace __sanitizer