1/*
2 * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Options.h"
28
29#include "AssemblerCommon.h"
30#include "CPU.h"
31#include "LLIntCommon.h"
32#include "MinimumReservedZoneSize.h"
33#include "SigillCrashAnalyzer.h"
34#include <algorithm>
35#include <limits>
36#include <math.h>
37#include <mutex>
38#include <stdlib.h>
39#include <string.h>
40#include <wtf/ASCIICType.h>
41#include <wtf/Compiler.h>
42#include <wtf/DataLog.h>
43#include <wtf/NumberOfCores.h>
44#include <wtf/Optional.h>
45#include <wtf/PointerPreparations.h>
46#include <wtf/StdLibExtras.h>
47#include <wtf/text/StringBuilder.h>
48#include <wtf/threads/Signals.h>
49
50#if PLATFORM(COCOA)
51#include <crt_externs.h>
52#endif
53
54#if ENABLE(JIT)
55#include "MacroAssembler.h"
56#endif
57
58namespace JSC {
59
60template<typename T>
61Optional<T> parse(const char* string);
62
63template<>
64Optional<OptionsStorage::Bool> parse(const char* string)
65{
66 if (equalLettersIgnoringASCIICase(string, "true") || equalLettersIgnoringASCIICase(string, "yes") || !strcmp(string, "1"))
67 return true;
68 if (equalLettersIgnoringASCIICase(string, "false") || equalLettersIgnoringASCIICase(string, "no") || !strcmp(string, "0"))
69 return false;
70 return WTF::nullopt;
71}
72
73template<>
74Optional<OptionsStorage::Int32> parse(const char* string)
75{
76 int32_t value;
77 if (sscanf(string, "%d", &value) == 1)
78 return value;
79 return WTF::nullopt;
80}
81
82template<>
83Optional<OptionsStorage::Unsigned> parse(const char* string)
84{
85 unsigned value;
86 if (sscanf(string, "%u", &value) == 1)
87 return value;
88 return WTF::nullopt;
89}
90
91#if CPU(ADDRESS64) || OS(DARWIN)
92template<>
93Optional<OptionsStorage::Size> parse(const char* string)
94{
95 size_t value;
96 if (sscanf(string, "%zu", &value) == 1)
97 return value;
98 return WTF::nullopt;
99}
100#endif // CPU(ADDRESS64) || OS(DARWIN)
101
102template<>
103Optional<OptionsStorage::Double> parse(const char* string)
104{
105 double value;
106 if (sscanf(string, "%lf", &value) == 1)
107 return value;
108 return WTF::nullopt;
109}
110
111template<>
112Optional<OptionsStorage::OptionRange> parse(const char* string)
113{
114 OptionRange range;
115 if (range.init(string))
116 return range;
117 return WTF::nullopt;
118}
119
120template<>
121Optional<OptionsStorage::OptionString> parse(const char* string)
122{
123 const char* value = nullptr;
124 if (!strlen(string))
125 return value;
126
127 // FIXME <https://webkit.org/b/169057>: This could leak if this option is set more than once.
128 // Given that Options are typically used for testing, this isn't considered to be a problem.
129 value = WTF::fastStrDup(string);
130 return value;
131}
132
133template<>
134Optional<OptionsStorage::GCLogLevel> parse(const char* string)
135{
136 if (equalLettersIgnoringASCIICase(string, "none") || equalLettersIgnoringASCIICase(string, "no") || equalLettersIgnoringASCIICase(string, "false") || !strcmp(string, "0"))
137 return GCLogging::None;
138
139 if (equalLettersIgnoringASCIICase(string, "basic") || equalLettersIgnoringASCIICase(string, "yes") || equalLettersIgnoringASCIICase(string, "true") || !strcmp(string, "1"))
140 return GCLogging::Basic;
141
142 if (equalLettersIgnoringASCIICase(string, "verbose") || !strcmp(string, "2"))
143 return GCLogging::Verbose;
144
145 return WTF::nullopt;
146}
147
148bool Options::isAvailable(Options::ID id, Options::Availability availability)
149{
150 if (availability == Availability::Restricted)
151 return g_jscConfig.restrictedOptionsEnabled;
152 ASSERT(availability == Availability::Configurable);
153
154 UNUSED_PARAM(id);
155#if !defined(NDEBUG)
156 if (id == maxSingleAllocationSizeID)
157 return true;
158#endif
159#if OS(DARWIN)
160 if (id == useSigillCrashAnalyzerID)
161 return true;
162#endif
163#if ENABLE(ASSEMBLER) && OS(LINUX)
164 if (id == logJITCodeForPerfID)
165 return true;
166#endif
167 if (id == traceLLIntExecutionID)
168 return !!LLINT_TRACING;
169 if (id == traceLLIntSlowPathID)
170 return !!LLINT_TRACING;
171 return false;
172}
173
174template<typename T>
175bool overrideOptionWithHeuristic(T& variable, Options::ID id, const char* name, Options::Availability availability)
176{
177 bool available = (availability == Options::Availability::Normal)
178 || Options::isAvailable(id, availability);
179
180 const char* stringValue = getenv(name);
181 if (!stringValue)
182 return false;
183
184 if (available) {
185 Optional<T> value = parse<T>(stringValue);
186 if (value) {
187 variable = value.value();
188 return true;
189 }
190 }
191
192 fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
193 return false;
194}
195
196bool Options::overrideAliasedOptionWithHeuristic(const char* name)
197{
198 const char* stringValue = getenv(name);
199 if (!stringValue)
200 return false;
201
202 String aliasedOption;
203 aliasedOption = String(&name[4]) + "=" + stringValue;
204 if (Options::setOption(aliasedOption.utf8().data()))
205 return true;
206
207 fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
208 return false;
209}
210
211static unsigned computeNumberOfWorkerThreads(int maxNumberOfWorkerThreads, int minimum = 1)
212{
213 int cpusToUse = std::min(kernTCSMAwareNumberOfProcessorCores(), maxNumberOfWorkerThreads);
214
215 // Be paranoid, it is the OS we're dealing with, after all.
216 ASSERT(cpusToUse >= 1);
217 return std::max(cpusToUse, minimum);
218}
219
220static int32_t computePriorityDeltaOfWorkerThreads(int32_t twoCorePriorityDelta, int32_t multiCorePriorityDelta)
221{
222 if (kernTCSMAwareNumberOfProcessorCores() <= 2)
223 return twoCorePriorityDelta;
224
225 return multiCorePriorityDelta;
226}
227
228static bool jitEnabledByDefault()
229{
230 return is32Bit() || isAddress64Bit();
231}
232
233static unsigned computeNumberOfGCMarkers(unsigned maxNumberOfGCMarkers)
234{
235 return computeNumberOfWorkerThreads(maxNumberOfGCMarkers);
236}
237
238const char* const OptionRange::s_nullRangeStr = "<null>";
239
240bool OptionRange::init(const char* rangeString)
241{
242 // rangeString should be in the form of [!]<low>[:<high>]
243 // where low and high are unsigned
244
245 bool invert = false;
246
247 if (!rangeString) {
248 m_state = InitError;
249 return false;
250 }
251
252 if (!strcmp(rangeString, s_nullRangeStr)) {
253 m_state = Uninitialized;
254 return true;
255 }
256
257 const char* p = rangeString;
258
259 if (*p == '!') {
260 invert = true;
261 p++;
262 }
263
264 int scanResult = sscanf(p, " %u:%u", &m_lowLimit, &m_highLimit);
265
266 if (!scanResult || scanResult == EOF) {
267 m_state = InitError;
268 return false;
269 }
270
271 if (scanResult == 1)
272 m_highLimit = m_lowLimit;
273
274 if (m_lowLimit > m_highLimit) {
275 m_state = InitError;
276 return false;
277 }
278
279 // FIXME <https://webkit.org/b/169057>: This could leak if this particular option is set more than once.
280 // Given that these options are used for testing, this isn't considered to be problem.
281 m_rangeString = WTF::fastStrDup(rangeString);
282 m_state = invert ? Inverted : Normal;
283
284 return true;
285}
286
287bool OptionRange::isInRange(unsigned count)
288{
289 if (m_state < Normal)
290 return true;
291
292 if ((m_lowLimit <= count) && (count <= m_highLimit))
293 return m_state == Normal ? true : false;
294
295 return m_state == Normal ? false : true;
296}
297
298void OptionRange::dump(PrintStream& out) const
299{
300 out.print(m_rangeString);
301}
302
303// Realize the names for each of the options:
304const Options::ConstMetaData Options::s_constMetaData[NumberOfOptions] = {
305#define FILL_OPTION_INFO(type_, name_, defaultValue_, availability_, description_) \
306 { #name_, description_, Options::Type::type_, Availability::availability_, offsetof(OptionsStorage, name_), offsetof(OptionsStorage, name_##Default) },
307 FOR_EACH_JSC_OPTION(FILL_OPTION_INFO)
308#undef FILL_OPTION_INFO
309};
310
311static void scaleJITPolicy()
312{
313 auto& scaleFactor = Options::jitPolicyScale();
314 if (scaleFactor > 1.0)
315 scaleFactor = 1.0;
316 else if (scaleFactor < 0.0)
317 scaleFactor = 0.0;
318
319 auto scaleOption = [&] (int32_t& optionValue, int32_t minValue) {
320 optionValue *= scaleFactor;
321 optionValue = std::max(optionValue, minValue);
322 };
323
324 scaleOption(Options::thresholdForJITAfterWarmUp(), 0);
325 scaleOption(Options::thresholdForJITSoon(), 0);
326 scaleOption(Options::thresholdForOptimizeAfterWarmUp(), 1);
327 scaleOption(Options::thresholdForOptimizeAfterLongWarmUp(), 1);
328 scaleOption(Options::thresholdForOptimizeSoon(), 1);
329 scaleOption(Options::thresholdForFTLOptimizeSoon(), 2);
330 scaleOption(Options::thresholdForFTLOptimizeAfterWarmUp(), 2);
331}
332
333static void overrideDefaults()
334{
335#if !PLATFORM(IOS_FAMILY)
336 if (WTF::numberOfProcessorCores() < 4)
337#endif
338 {
339 Options::maximumMutatorUtilization() = 0.6;
340 Options::concurrentGCMaxHeadroom() = 1.4;
341 Options::minimumGCPauseMS() = 1;
342 Options::useStochasticMutatorScheduler() = false;
343 if (WTF::numberOfProcessorCores() <= 1)
344 Options::gcIncrementScale() = 1;
345 else
346 Options::gcIncrementScale() = 0;
347 }
348
349#if PLATFORM(IOS_FAMILY)
350 // On iOS, we control heap growth using process memory footprint. Therefore these values can be agressive.
351 Options::smallHeapRAMFraction() = 0.8;
352 Options::mediumHeapRAMFraction() = 0.9;
353
354#if !PLATFORM(WATCHOS) && defined(__LP64__)
355 Options::useSigillCrashAnalyzer() = true;
356#endif
357#endif
358
359#if !ENABLE(SIGNAL_BASED_VM_TRAPS)
360 Options::usePollingTraps() = true;
361#endif
362
363#if !ENABLE(WEBASSEMBLY_FAST_MEMORY)
364 Options::useWebAssemblyFastMemory() = false;
365#endif
366
367#if !HAVE(MACH_EXCEPTIONS)
368 Options::useMachForExceptions() = false;
369#endif
370
371 if (Options::useWasmLLInt() && !Options::wasmLLIntTiersUpToBBQ()) {
372 Options::thresholdForOMGOptimizeAfterWarmUp() = 1500;
373 Options::thresholdForOMGOptimizeSoon() = 100;
374 }
375}
376
377static void correctOptions()
378{
379 unsigned thresholdForGlobalLexicalBindingEpoch = Options::thresholdForGlobalLexicalBindingEpoch();
380 if (thresholdForGlobalLexicalBindingEpoch == 0 || thresholdForGlobalLexicalBindingEpoch == 1)
381 Options::thresholdForGlobalLexicalBindingEpoch() = UINT_MAX;
382}
383
384static void recomputeDependentOptions()
385{
386#if !defined(NDEBUG)
387 Options::validateDFGExceptionHandling() = true;
388#endif
389#if !ENABLE(JIT)
390 Options::useLLInt() = true;
391 Options::useJIT() = false;
392 Options::useBaselineJIT() = false;
393 Options::useDFGJIT() = false;
394 Options::useFTLJIT() = false;
395 Options::useDOMJIT() = false;
396 Options::useRegExpJIT() = false;
397#endif
398#if !ENABLE(CONCURRENT_JS)
399 Options::useConcurrentJIT() = false;
400#endif
401#if !ENABLE(YARR_JIT)
402 Options::useRegExpJIT() = false;
403#endif
404#if !ENABLE(DFG_JIT)
405 Options::useDFGJIT() = false;
406 Options::useFTLJIT() = false;
407#endif
408#if !ENABLE(FTL_JIT)
409 Options::useFTLJIT() = false;
410#endif
411
412#if !CPU(X86_64) && !CPU(ARM64)
413 Options::useConcurrentGC() = false;
414#endif
415
416 if (!Options::useJIT()) {
417 Options::useSigillCrashAnalyzer() = false;
418 Options::useWebAssembly() = false;
419 }
420
421 if (!jitEnabledByDefault() && !Options::useJIT())
422 Options::useLLInt() = true;
423
424 if (!Options::useWebAssembly())
425 Options::useFastTLSForWasmContext() = false;
426
427 if (Options::dumpDisassembly()
428 || Options::dumpDFGDisassembly()
429 || Options::dumpFTLDisassembly()
430 || Options::dumpBytecodeAtDFGTime()
431 || Options::dumpGraphAtEachPhase()
432 || Options::dumpDFGGraphAtEachPhase()
433 || Options::dumpDFGFTLGraphAtEachPhase()
434 || Options::dumpB3GraphAtEachPhase()
435 || Options::dumpAirGraphAtEachPhase()
436 || Options::verboseCompilation()
437 || Options::verboseFTLCompilation()
438 || Options::logCompilationChanges()
439 || Options::validateGraph()
440 || Options::validateGraphAtEachPhase()
441 || Options::verboseOSR()
442 || Options::verboseCompilationQueue()
443 || Options::reportCompileTimes()
444 || Options::reportBaselineCompileTimes()
445 || Options::reportDFGCompileTimes()
446 || Options::reportFTLCompileTimes()
447 || Options::logPhaseTimes()
448 || Options::verboseCFA()
449 || Options::verboseDFGFailure()
450 || Options::verboseFTLFailure()
451 || Options::dumpRandomizingFuzzerAgentPredictions())
452 Options::alwaysComputeHash() = true;
453
454 if (!Options::useConcurrentGC())
455 Options::collectContinuously() = false;
456
457 if (Options::jitPolicyScale() != Options::jitPolicyScaleDefault())
458 scaleJITPolicy();
459
460 if (Options::forceEagerCompilation()) {
461 Options::thresholdForJITAfterWarmUp() = 10;
462 Options::thresholdForJITSoon() = 10;
463 Options::thresholdForOptimizeAfterWarmUp() = 20;
464 Options::thresholdForOptimizeAfterLongWarmUp() = 20;
465 Options::thresholdForOptimizeSoon() = 20;
466 Options::thresholdForFTLOptimizeAfterWarmUp() = 20;
467 Options::thresholdForFTLOptimizeSoon() = 20;
468 Options::maximumEvalCacheableSourceLength() = 150000;
469 Options::useConcurrentJIT() = false;
470 }
471#if ENABLE(SEPARATED_WX_HEAP)
472 // Override globally for now. Longer term we'll just make the default
473 // be to have this option enabled, and have platforms that don't support
474 // it just silently use a single mapping.
475 Options::useSeparatedWXHeap() = true;
476#else
477 Options::useSeparatedWXHeap() = false;
478#endif
479
480 if (Options::alwaysUseShadowChicken())
481 Options::maximumInliningDepth() = 1;
482
483 // Compute the maximum value of the reoptimization retry counter. This is simply
484 // the largest value at which we don't overflow the execute counter, when using it
485 // to left-shift the execution counter by this amount. Currently the value ends
486 // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
487 // total on a 32-bit processor.
488 Options::reoptimizationRetryCounterMax() = 0;
489 while ((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << (Options::reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
490 Options::reoptimizationRetryCounterMax()++;
491
492 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) > 0);
493 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
494
495#if !defined(NDEBUG)
496 if (Options::maxSingleAllocationSize())
497 fastSetMaxSingleAllocationSize(Options::maxSingleAllocationSize());
498 else
499 fastSetMaxSingleAllocationSize(std::numeric_limits<size_t>::max());
500#endif
501
502 if (Options::useZombieMode()) {
503 Options::sweepSynchronously() = true;
504 Options::scribbleFreeCells() = true;
505 }
506
507 if (Options::reservedZoneSize() < minimumReservedZoneSize)
508 Options::reservedZoneSize() = minimumReservedZoneSize;
509 if (Options::softReservedZoneSize() < Options::reservedZoneSize() + minimumReservedZoneSize)
510 Options::softReservedZoneSize() = Options::reservedZoneSize() + minimumReservedZoneSize;
511
512#if USE(JSVALUE32_64)
513 // FIXME: Make probe OSR exit work on 32-bit:
514 // https://bugs.webkit.org/show_bug.cgi?id=177956
515 Options::useProbeOSRExit() = false;
516#endif
517
518 if (!Options::useCodeCache())
519 Options::diskCachePath() = nullptr;
520
521 if (Options::randomIntegrityAuditRate() < 0)
522 Options::randomIntegrityAuditRate() = 0;
523 else if (Options::randomIntegrityAuditRate() > 1.0)
524 Options::randomIntegrityAuditRate() = 1.0;
525}
526
527inline void* Options::addressOfOption(Options::ID id)
528{
529 auto offset = Options::s_constMetaData[id].offsetOfOption;
530 return reinterpret_cast<uint8_t*>(&g_jscConfig.options) + offset;
531}
532
533inline void* Options::addressOfOptionDefault(Options::ID id)
534{
535 auto offset = Options::s_constMetaData[id].offsetOfOptionDefault;
536 return reinterpret_cast<uint8_t*>(&g_jscConfig.options) + offset;
537}
538
539void Options::initialize()
540{
541 static std::once_flag initializeOptionsOnceFlag;
542
543 std::call_once(
544 initializeOptionsOnceFlag,
545 [] {
546 // Sanity check that options address computation is working.
547 RELEASE_ASSERT(Options::addressOfOption(useKernTCSMID) == &Options::useKernTCSM());
548 RELEASE_ASSERT(Options::addressOfOptionDefault(useKernTCSMID) == &Options::useKernTCSMDefault());
549 RELEASE_ASSERT(Options::addressOfOption(gcMaxHeapSizeID) == &Options::gcMaxHeapSize());
550 RELEASE_ASSERT(Options::addressOfOptionDefault(gcMaxHeapSizeID) == &Options::gcMaxHeapSizeDefault());
551 RELEASE_ASSERT(Options::addressOfOption(forceOSRExitToLLIntID) == &Options::forceOSRExitToLLInt());
552 RELEASE_ASSERT(Options::addressOfOptionDefault(forceOSRExitToLLIntID) == &Options::forceOSRExitToLLIntDefault());
553
554#ifndef NDEBUG
555 Config::enableRestrictedOptions();
556#endif
557 // Initialize each of the options with their default values:
558#define INIT_OPTION(type_, name_, defaultValue_, availability_, description_) { \
559 auto value = defaultValue_; \
560 name_() = value; \
561 name_##Default() = value; \
562 }
563 FOR_EACH_JSC_OPTION(INIT_OPTION)
564#undef INIT_OPTION
565
566 overrideDefaults();
567
568 // Allow environment vars to override options if applicable.
569 // The evn var should be the name of the option prefixed with
570 // "JSC_".
571#if PLATFORM(COCOA)
572 bool hasBadOptions = false;
573 for (char** envp = *_NSGetEnviron(); *envp; envp++) {
574 const char* env = *envp;
575 if (!strncmp("JSC_", env, 4)) {
576 if (!Options::setOption(&env[4])) {
577 dataLog("ERROR: invalid option: ", *envp, "\n");
578 hasBadOptions = true;
579 }
580 }
581 }
582 if (hasBadOptions && Options::validateOptions())
583 CRASH();
584#else // PLATFORM(COCOA)
585#define OVERRIDE_OPTION_WITH_HEURISTICS(type_, name_, defaultValue_, availability_, description_) \
586 overrideOptionWithHeuristic(name_(), name_##ID, "JSC_" #name_, Availability::availability_);
587 FOR_EACH_JSC_OPTION(OVERRIDE_OPTION_WITH_HEURISTICS)
588#undef OVERRIDE_OPTION_WITH_HEURISTICS
589#endif // PLATFORM(COCOA)
590
591#define OVERRIDE_ALIASED_OPTION_WITH_HEURISTICS(aliasedName_, unaliasedName_, equivalence_) \
592 overrideAliasedOptionWithHeuristic("JSC_" #aliasedName_);
593 FOR_EACH_JSC_ALIASED_OPTION(OVERRIDE_ALIASED_OPTION_WITH_HEURISTICS)
594#undef OVERRIDE_ALIASED_OPTION_WITH_HEURISTICS
595
596#if 0
597 ; // Deconfuse editors that do auto indentation
598#endif
599
600 correctOptions();
601
602 recomputeDependentOptions();
603
604 // Do range checks where needed and make corrections to the options:
605 ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
606 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
607 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);
608 ASSERT(Options::criticalGCMemoryThreshold() > 0.0 && Options::criticalGCMemoryThreshold() < 1.0);
609
610 dumpOptionsIfNeeded();
611 ensureOptionsAreCoherent();
612
613#if HAVE(MACH_EXCEPTIONS)
614 if (Options::useMachForExceptions())
615 handleSignalsWithMach();
616#endif
617
618#if ASAN_ENABLED && OS(LINUX) && ENABLE(WEBASSEMBLY_FAST_MEMORY)
619 if (Options::useWebAssemblyFastMemory()) {
620 const char* asanOptions = getenv("ASAN_OPTIONS");
621 bool okToUseWebAssemblyFastMemory = asanOptions
622 && (strstr(asanOptions, "allow_user_segv_handler=1") || strstr(asanOptions, "handle_segv=0"));
623 if (!okToUseWebAssemblyFastMemory) {
624 dataLogLn("WARNING: ASAN interferes with JSC signal handlers; useWebAssemblyFastMemory will be disabled.");
625 Options::useWebAssemblyFastMemory() = false;
626 }
627 }
628#endif
629
630#if CPU(X86_64) && OS(DARWIN)
631 Options::dumpZappedCellCrashData() =
632 (hwPhysicalCPUMax() >= 4) && (hwL3CacheSize() >= static_cast<int64_t>(6 * MB));
633#endif
634 });
635}
636
637void Options::dumpOptionsIfNeeded()
638{
639 if (Options::dumpOptions()) {
640 DumpLevel level = static_cast<DumpLevel>(Options::dumpOptions());
641 if (level > DumpLevel::Verbose)
642 level = DumpLevel::Verbose;
643
644 const char* title = nullptr;
645 switch (level) {
646 case DumpLevel::None:
647 break;
648 case DumpLevel::Overridden:
649 title = "Overridden JSC options:";
650 break;
651 case DumpLevel::All:
652 title = "All JSC options:";
653 break;
654 case DumpLevel::Verbose:
655 title = "All JSC options with descriptions:";
656 break;
657 }
658
659 StringBuilder builder;
660 dumpAllOptions(builder, level, title, nullptr, " ", "\n", DumpDefaults);
661 dataLog(builder.toString());
662 }
663}
664
665static bool isSeparator(char c)
666{
667 return isASCIISpace(c) || (c == ',');
668}
669
670bool Options::setOptions(const char* optionsStr)
671{
672 RELEASE_ASSERT(!g_jscConfig.isPermanentlyFrozen);
673 Vector<char*> options;
674
675 size_t length = strlen(optionsStr);
676 char* optionsStrCopy = WTF::fastStrDup(optionsStr);
677 char* end = optionsStrCopy + length;
678 char* p = optionsStrCopy;
679
680 while (p < end) {
681 // Skip separators (white space or commas).
682 while (p < end && isSeparator(*p))
683 p++;
684 if (p == end)
685 break;
686
687 char* optionStart = p;
688 p = strstr(p, "=");
689 if (!p) {
690 dataLogF("'=' not found in option string: %p\n", optionStart);
691 WTF::fastFree(optionsStrCopy);
692 return false;
693 }
694 p++;
695
696 char* valueBegin = p;
697 bool hasStringValue = false;
698 const int minStringLength = 2; // The min is an empty string i.e. 2 double quotes.
699 if ((p + minStringLength < end) && (*p == '"')) {
700 p = strstr(p + 1, "\"");
701 if (!p) {
702 dataLogF("Missing trailing '\"' in option string: %p\n", optionStart);
703 WTF::fastFree(optionsStrCopy);
704 return false; // End of string not found.
705 }
706 hasStringValue = true;
707 }
708
709 // Find next separator (white space or commas).
710 while (p < end && !isSeparator(*p))
711 p++;
712 if (!p)
713 p = end; // No more " " separator. Hence, this is the last arg.
714
715 // If we have a well-formed string value, strip the quotes.
716 if (hasStringValue) {
717 char* valueEnd = p;
718 ASSERT((*valueBegin == '"') && ((valueEnd - valueBegin) >= minStringLength) && (valueEnd[-1] == '"'));
719 memmove(valueBegin, valueBegin + 1, valueEnd - valueBegin - minStringLength);
720 valueEnd[-minStringLength] = '\0';
721 }
722
723 // Strip leading -- if present.
724 if ((p - optionStart > 2) && optionStart[0] == '-' && optionStart[1] == '-')
725 optionStart += 2;
726
727 *p++ = '\0';
728 options.append(optionStart);
729 }
730
731 bool success = true;
732 for (auto& option : options) {
733 bool optionSuccess = setOption(option);
734 if (!optionSuccess) {
735 dataLogF("Failed to set option : %s\n", option);
736 success = false;
737 }
738 }
739
740 correctOptions();
741
742 recomputeDependentOptions();
743
744 dumpOptionsIfNeeded();
745
746 ensureOptionsAreCoherent();
747
748 WTF::fastFree(optionsStrCopy);
749
750 return success;
751}
752
753// Parses a single command line option in the format "<optionName>=<value>"
754// (no spaces allowed) and set the specified option if appropriate.
755bool Options::setOptionWithoutAlias(const char* arg)
756{
757 // arg should look like this:
758 // <jscOptionName>=<appropriate value>
759 const char* equalStr = strchr(arg, '=');
760 if (!equalStr)
761 return false;
762
763 const char* valueStr = equalStr + 1;
764
765 // For each option, check if the specified arg is a match. If so, set the arg
766 // if the value makes sense. Otherwise, move on to checking the next option.
767#define SET_OPTION_IF_MATCH(type_, name_, defaultValue_, availability_, description_) \
768 if (strlen(#name_) == static_cast<size_t>(equalStr - arg) \
769 && !strncmp(arg, #name_, equalStr - arg)) { \
770 if (Availability::availability_ != Availability::Normal \
771 && !isAvailable(name_##ID, Availability::availability_)) \
772 return false; \
773 Optional<OptionsStorage::type_> value; \
774 value = parse<OptionsStorage::type_>(valueStr); \
775 if (value) { \
776 name_() = value.value(); \
777 correctOptions(); \
778 recomputeDependentOptions(); \
779 return true; \
780 } \
781 return false; \
782 }
783
784 FOR_EACH_JSC_OPTION(SET_OPTION_IF_MATCH)
785#undef SET_OPTION_IF_MATCH
786
787 return false; // No option matched.
788}
789
790static const char* invertBoolOptionValue(const char* valueStr)
791{
792 Optional<OptionsStorage::Bool> value = parse<OptionsStorage::Bool>(valueStr);
793 if (!value)
794 return nullptr;
795 return value.value() ? "false" : "true";
796}
797
798
799bool Options::setAliasedOption(const char* arg)
800{
801 // arg should look like this:
802 // <jscOptionName>=<appropriate value>
803 const char* equalStr = strchr(arg, '=');
804 if (!equalStr)
805 return false;
806
807 IGNORE_WARNINGS_BEGIN("tautological-compare")
808
809 // For each option, check if the specify arg is a match. If so, set the arg
810 // if the value makes sense. Otherwise, move on to checking the next option.
811#define FOR_EACH_OPTION(aliasedName_, unaliasedName_, equivalence) \
812 if (strlen(#aliasedName_) == static_cast<size_t>(equalStr - arg) \
813 && !strncmp(arg, #aliasedName_, equalStr - arg)) { \
814 String unaliasedOption(#unaliasedName_); \
815 if (equivalence == SameOption) \
816 unaliasedOption = unaliasedOption + equalStr; \
817 else { \
818 ASSERT(equivalence == InvertedOption); \
819 auto* invertedValueStr = invertBoolOptionValue(equalStr + 1); \
820 if (!invertedValueStr) \
821 return false; \
822 unaliasedOption = unaliasedOption + "=" + invertedValueStr; \
823 } \
824 return setOptionWithoutAlias(unaliasedOption.utf8().data()); \
825 }
826
827 FOR_EACH_JSC_ALIASED_OPTION(FOR_EACH_OPTION)
828#undef FOR_EACH_OPTION
829
830 IGNORE_WARNINGS_END
831
832 return false; // No option matched.
833}
834
835bool Options::setOption(const char* arg)
836{
837 bool success = setOptionWithoutAlias(arg);
838 if (success)
839 return true;
840 return setAliasedOption(arg);
841}
842
843
844void Options::dumpAllOptions(StringBuilder& builder, DumpLevel level, const char* title,
845 const char* separator, const char* optionHeader, const char* optionFooter, DumpDefaultsOption dumpDefaultsOption)
846{
847 if (title) {
848 builder.append(title);
849 builder.append('\n');
850 }
851
852 for (size_t id = 0; id < NumberOfOptions; id++) {
853 if (separator && id)
854 builder.append(separator);
855 dumpOption(builder, level, static_cast<ID>(id), optionHeader, optionFooter, dumpDefaultsOption);
856 }
857}
858
859void Options::dumpAllOptionsInALine(StringBuilder& builder)
860{
861 dumpAllOptions(builder, DumpLevel::All, nullptr, " ", nullptr, nullptr, DontDumpDefaults);
862}
863
864void Options::dumpAllOptions(FILE* stream, DumpLevel level, const char* title)
865{
866 StringBuilder builder;
867 dumpAllOptions(builder, level, title, nullptr, " ", "\n", DumpDefaults);
868 fprintf(stream, "%s", builder.toString().utf8().data());
869}
870
871struct OptionReader {
872 class Option {
873 public:
874 void dump(StringBuilder&) const;
875
876 bool operator==(const Option& other) const;
877 bool operator!=(const Option& other) const { return !(*this == other); }
878
879 const char* name() const { return Options::s_constMetaData[m_id].name; }
880 const char* description() const { return Options::s_constMetaData[m_id].description; }
881 Options::Type type() const { return Options::s_constMetaData[m_id].type; }
882 Options::Availability availability() const { return Options::s_constMetaData[m_id].availability; }
883 bool isOverridden() const { return *this != OptionReader::defaultFor(m_id); }
884
885 private:
886 Option(Options::ID id, void* addressOfValue)
887 : m_id(id)
888 {
889 initValue(addressOfValue);
890 }
891
892 void initValue(void* addressOfValue);
893
894 Options::ID m_id;
895 union {
896 bool m_bool;
897 unsigned m_unsigned;
898 double m_double;
899 int32_t m_int32;
900 size_t m_size;
901 OptionRange m_optionRange;
902 const char* m_optionString;
903 GCLogging::Level m_gcLogLevel;
904 };
905
906 friend struct OptionReader;
907 };
908
909 static const Option optionFor(Options::ID);
910 static const Option defaultFor(Options::ID);
911};
912
913void Options::dumpOption(StringBuilder& builder, DumpLevel level, Options::ID id,
914 const char* header, const char* footer, DumpDefaultsOption dumpDefaultsOption)
915{
916 RELEASE_ASSERT(static_cast<size_t>(id) < NumberOfOptions);
917
918 auto option = OptionReader::optionFor(id);
919 Availability availability = option.availability();
920 if (availability != Availability::Normal && !isAvailable(id, availability))
921 return;
922
923 bool wasOverridden = option.isOverridden();
924 bool needsDescription = (level == DumpLevel::Verbose && option.description());
925
926 if (level == DumpLevel::Overridden && !wasOverridden)
927 return;
928
929 if (header)
930 builder.append(header);
931 builder.append(option.name(), '=');
932 option.dump(builder);
933
934 if (wasOverridden && (dumpDefaultsOption == DumpDefaults)) {
935 auto defaultOption = OptionReader::defaultFor(id);
936 builder.appendLiteral(" (default: ");
937 defaultOption.dump(builder);
938 builder.appendLiteral(")");
939 }
940
941 if (needsDescription)
942 builder.append(" ... ", option.description());
943
944 builder.append(footer);
945}
946
947void Options::ensureOptionsAreCoherent()
948{
949 bool coherent = true;
950 if (!(useLLInt() || useJIT())) {
951 coherent = false;
952 dataLog("INCOHERENT OPTIONS: at least one of useLLInt or useJIT must be true\n");
953 }
954 if (!coherent)
955 CRASH();
956}
957
958const OptionReader::Option OptionReader::optionFor(Options::ID id)
959{
960 return Option(id, Options::addressOfOption(id));
961}
962
963const OptionReader::Option OptionReader::defaultFor(Options::ID id)
964{
965 return Option(id, Options::addressOfOptionDefault(id));
966}
967
968void OptionReader::Option::initValue(void* addressOfValue)
969{
970 Options::Type type = Options::s_constMetaData[m_id].type;
971 switch (type) {
972 case Options::Type::Bool:
973 memcpy(&m_bool, addressOfValue, sizeof(OptionsStorage::Bool));
974 break;
975 case Options::Type::Unsigned:
976 memcpy(&m_unsigned, addressOfValue, sizeof(OptionsStorage::Unsigned));
977 break;
978 case Options::Type::Double:
979 memcpy(&m_double, addressOfValue, sizeof(OptionsStorage::Double));
980 break;
981 case Options::Type::Int32:
982 memcpy(&m_int32, addressOfValue, sizeof(OptionsStorage::Int32));
983 break;
984 case Options::Type::Size:
985 memcpy(&m_size, addressOfValue, sizeof(OptionsStorage::Size));
986 break;
987 case Options::Type::OptionRange:
988 memcpy(&m_optionRange, addressOfValue, sizeof(OptionsStorage::OptionRange));
989 break;
990 case Options::Type::OptionString:
991 memcpy(&m_optionString, addressOfValue, sizeof(OptionsStorage::OptionString));
992 break;
993 case Options::Type::GCLogLevel:
994 memcpy(&m_gcLogLevel, addressOfValue, sizeof(OptionsStorage::GCLogLevel));
995 break;
996 }
997}
998
999void OptionReader::Option::dump(StringBuilder& builder) const
1000{
1001 switch (type()) {
1002 case Options::Type::Bool:
1003 builder.append(m_bool ? "true" : "false");
1004 break;
1005 case Options::Type::Unsigned:
1006 builder.appendNumber(m_unsigned);
1007 break;
1008 case Options::Type::Size:
1009 builder.appendNumber(m_size);
1010 break;
1011 case Options::Type::Double:
1012 builder.appendFixedPrecisionNumber(m_double);
1013 break;
1014 case Options::Type::Int32:
1015 builder.appendNumber(m_int32);
1016 break;
1017 case Options::Type::OptionRange:
1018 builder.append(m_optionRange.rangeString());
1019 break;
1020 case Options::Type::OptionString: {
1021 const char* option = m_optionString;
1022 if (!option)
1023 option = "";
1024 builder.append('"');
1025 builder.append(option);
1026 builder.append('"');
1027 break;
1028 }
1029 case Options::Type::GCLogLevel: {
1030 builder.append(GCLogging::levelAsString(m_gcLogLevel));
1031 break;
1032 }
1033 }
1034}
1035
1036bool OptionReader::Option::operator==(const Option& other) const
1037{
1038 ASSERT(type() == other.type());
1039 switch (type()) {
1040 case Options::Type::Bool:
1041 return m_bool == other.m_bool;
1042 case Options::Type::Unsigned:
1043 return m_unsigned == other.m_unsigned;
1044 case Options::Type::Size:
1045 return m_size == other.m_size;
1046 case Options::Type::Double:
1047 return (m_double == other.m_double) || (std::isnan(m_double) && std::isnan(other.m_double));
1048 case Options::Type::Int32:
1049 return m_int32 == other.m_int32;
1050 case Options::Type::OptionRange:
1051 return m_optionRange.rangeString() == other.m_optionRange.rangeString();
1052 case Options::Type::OptionString:
1053 return (m_optionString == other.m_optionString)
1054 || (m_optionString && other.m_optionString && !strcmp(m_optionString, other.m_optionString));
1055 case Options::Type::GCLogLevel:
1056 return m_gcLogLevel == other.m_gcLogLevel;
1057 }
1058 return false;
1059}
1060
1061} // namespace JSC
1062