Skip to content

Commit aa74fc3

Browse files
Jeffrey Tanfacebook-github-bot
authored andcommitted
Allow Debugger and TimeLimit share the same flag
Summary: Remove build mode for TimeLimit and allow debugger and timelimit to co-exist by sharing the same flag. It also uses uncatchable JS error instead of C++ exception for time limit. Reviewed By: davedets, ridiculousfish Differential Revision: D16657227 fbshipit-source-id: 5c923128940062f01f78f25031cd596df8fcbd66
1 parent a0e6e97 commit aa74fc3

12 files changed

Lines changed: 112 additions & 106 deletions

File tree

CMakeLists.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ set(HERMESVM_PROFILER_JSFUNCTION OFF CACHE BOOL
5757
set(HERMESVM_PROFILER_NATIVECALL OFF CACHE BOOL
5858
"Enable native call profiling in hermes VM")
5959

60-
set(HERMESVM_TIMELIMIT OFF CACHE BOOL
61-
"Enable time limit in hermes VM")
62-
6360
set(HERMESVM_INDIRECT_THREADING ${DEFAULT_INTERPRETER_THREADING} CACHE BOOL
6461
"Enable the indirect threaded interpreter")
6562

@@ -504,7 +501,6 @@ set(HERMES_LIT_TEST_PARAMS
504501
hbc_diff=${HERMES_BINARY_DIR}/bin/hbc-diff
505502
build_mode=${HERMES_ASSUMED_BUILD_MODE_IN_LIT_TEST}
506503
exception_on_oom_enabled=${HERMESVM_EXCEPTION_ON_OOM}
507-
timelimit_enabled=${HERMESVM_TIMELIMIT}
508504
)
509505

510506
set(LLVM_LIT_ARGS "-sv")

include/hermes/VM/Interpreter.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,6 @@ class Interpreter {
183183
}
184184
#endif
185185

186-
#ifdef HERMESVM_TIMELIMIT
187-
LLVM_ATTRIBUTE_ALWAYS_INLINE
188-
static inline void notifyTimeout(Runtime *runtime, const Inst *ip) {
189-
runtime->notifyTimeout(ip);
190-
llvm_unreachable("Timeout should terminate the execution.");
191-
}
192-
#endif
193-
194186
//===========================================================================
195187
// Out-of-line implementations of entire instructions.
196188

include/hermes/VM/Runtime.h

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,6 @@ enum class PropCacheID {
108108
_COUNT
109109
};
110110

111-
#ifdef HERMESVM_TIMELIMIT
112-
/// A std::runtime_error wrapper class for execution timeout.
113-
class JSTimeoutError : public std::runtime_error {
114-
public:
115-
JSTimeoutError(const std::string &what_arg) : std::runtime_error(what_arg) {}
116-
JSTimeoutError(const char *what_arg) : std::runtime_error(what_arg) {}
117-
};
118-
#endif
119-
120111
/// The Runtime encapsulates the entire context of a VM. Multiple instances can
121112
/// exist and are completely independent from each other.
122113
class Runtime : public HandleRootOwner,
@@ -468,14 +459,22 @@ class Runtime : public HandleRootOwner,
468459
return commonStorage_.get();
469460
}
470461

471-
#if defined(HERMES_ENABLE_DEBUGGER) || defined(HERMESVM_TIMELIMIT)
462+
#if defined(HERMES_ENABLE_DEBUGGER)
472463
/// Request the interpreter loop to take an asynchronous break at a convenient
473-
/// point. This may be called from any thread, or a signal handler.
474-
void triggerAsyncBreak() {
475-
asyncBreakRequestFlag_.store(1, std::memory_order_relaxed);
464+
/// point due to debugger UI request. This may be called from any thread, or a
465+
/// signal handler.
466+
void triggerDebuggerAsyncBreak() {
467+
triggerAsyncBreak(AsyncBreakReasonBits::Debugger);
476468
}
477469
#endif
478470

471+
/// Request the interpreter loop to take an asynchronous break at a convenient
472+
/// point due to previous registered timeout. This may be called from any
473+
/// thread, or a signal handler.
474+
void triggerTimeoutAsyncBreak() {
475+
triggerAsyncBreak(AsyncBreakReasonBits::Timeout);
476+
}
477+
479478
/// Register \p callback which will be called
480479
/// during runtime destruction.
481480
void registerDestructionCallback(DestructionCallback callback) {
@@ -560,6 +559,12 @@ class Runtime : public HandleRootOwner,
560559
/// Raise an error for the quit function. This error is not catchable.
561560
ExecutionStatus raiseQuitError();
562561

562+
/// Raise an error for execution timeout. This error is not catchable.
563+
ExecutionStatus raiseTimeoutError();
564+
565+
/// Utility function to raise a catchable JS error with \p errMessage.
566+
ExecutionStatus raiseUncatchableError(llvm::StringRef errMessage);
567+
563568
/// Interpret the current function until it returns or throws and return
564569
/// CallResult<HermesValue> or the thrown object in 'thrownObject'.
565570
CallResult<HermesValue> interpretFunction(CodeBlock *newCodeBlock);
@@ -989,44 +994,66 @@ class Runtime : public HandleRootOwner,
989994
/// A list of callbacks to call before runtime destruction.
990995
std::vector<DestructionCallback> destructionCallbacks_;
991996

992-
#if defined(HERMES_ENABLE_DEBUGGER) || defined(HERMESVM_TIMELIMIT)
993-
/// An atomic boolean set when an async pause is requested.
997+
/// Bit flags for async break request reasons.
998+
enum class AsyncBreakReasonBits : uint8_t {
999+
Debugger = 0x1,
1000+
Timeout = 0x2,
1001+
};
1002+
1003+
/// An atomic flag set when an async pause is requested.
1004+
/// It is a bits flag with each bit reserved for different clients
1005+
/// defined by AsyncBreakReasonBits.
9941006
/// This may be manipulated from multiple threads.
9951007
std::atomic<uint8_t> asyncBreakRequestFlag_{0};
9961008

997-
/// \return zero if no async pause was requsted, nonzero if an async pause was
998-
/// requested. If nonzero is returned, the flag is reset to 0.
999-
uint8_t testAndClearAsyncBreakRequest() {
1009+
#if defined(HERMES_ENABLE_DEBUGGER)
1010+
/// \return zero if no debugger async pause was requested, nonzero if an async
1011+
/// pause was requested. If nonzero is returned, the flag is reset to 0.
1012+
bool testAndClearDebuggerAsyncBreakRequest() {
1013+
return testAndClearAsyncBreakRequest(AsyncBreakReasonBits::Debugger);
1014+
}
1015+
1016+
Debugger debugger_{this};
1017+
#endif
1018+
1019+
/// \return whether any async break is requested or not.
1020+
bool hasAsyncBreak() const {
1021+
return asyncBreakRequestFlag_.load(std::memory_order_relaxed) != 0;
1022+
}
1023+
1024+
/// \return whether async break was requested or not for \p reasonBit. Clear
1025+
/// \p reasonBit request bit afterward.
1026+
bool testAndClearAsyncBreakRequest(AsyncBreakReasonBits reasonBit) {
1027+
/// Note that while the triggerTimeoutAsyncBreak() function may be called
1028+
/// from any thread, this one may only be called from within the Interpreter
1029+
/// loop.
10001030
uint8_t flag = asyncBreakRequestFlag_.load(std::memory_order_relaxed);
1001-
if (LLVM_UNLIKELY(flag)) {
1002-
/// Note that while the triggerAsyncBreak() function may be called from
1003-
/// any thread, this one may only be called from within the Interpreter
1004-
/// loop; thus there is no need for a compare-and-swap. We might race with
1005-
/// setting the flag again, but currently we do not allow two callers who
1006-
/// both see a true return from a single flag set.
1007-
asyncBreakRequestFlag_.store(0, std::memory_order_relaxed);
1031+
if (LLVM_LIKELY((flag & (uint8_t)reasonBit) == 0)) {
1032+
// Fast path.
1033+
return false;
10081034
}
1009-
return flag;
1035+
// Clear the flag using CAS.
1036+
uint8_t oldFlag = asyncBreakRequestFlag_.fetch_and(
1037+
~(uint8_t)reasonBit, std::memory_order_relaxed);
1038+
assert(oldFlag != 0 && "Why is oldFlag zero?");
1039+
(void)oldFlag;
1040+
return true;
10101041
}
1011-
#endif
10121042

1013-
#ifdef HERMESVM_TIMELIMIT
1014-
void notifyTimeout(const Inst *ip) {
1015-
char detailBuffer[400];
1016-
snprintf(
1017-
detailBuffer,
1018-
sizeof(detailBuffer),
1019-
// todo: include time out value.
1020-
"Javascript execution has timeout.");
1021-
throw JSTimeoutError(
1022-
std::string(detailBuffer) + "\ncall stack:\n" +
1023-
getCallStackNoAlloc(ip));
1043+
/// \return whether timeout async break was requsted or not. Clear the
1044+
/// timeout request bit afterward.
1045+
bool testAndClearTimeoutAsyncBreakRequest() {
1046+
return testAndClearAsyncBreakRequest(AsyncBreakReasonBits::Timeout);
10241047
}
1025-
#endif
10261048

1027-
#ifdef HERMES_ENABLE_DEBUGGER
1028-
Debugger debugger_{this};
1029-
#endif
1049+
/// Request the interpreter loop to take an asynchronous break
1050+
/// at a convenient point.
1051+
void triggerAsyncBreak(AsyncBreakReasonBits reason) {
1052+
asyncBreakRequestFlag_.fetch_or((uint8_t)reason, std::memory_order_relaxed);
1053+
}
1054+
1055+
/// Notify runtime execution has timeout.
1056+
ExecutionStatus notifyTimeout();
10301057

10311058
/// Holds references to persistent BC providers for the lifetime of the
10321059
/// Runtime. This is needed because the identifier table may contain pointers

include/hermes/VM/TimeLimitMonitor.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
* This source code is licensed under the MIT license found in the LICENSE
55
* file in the root directory of this source tree.
66
*/
7-
#ifdef HERMESVM_TIMELIMIT
8-
97
#ifndef HERMES_VM_TIMELIMITMONITOR_H
108
#define HERMES_VM_TIMELIMITMONITOR_H
119

@@ -60,7 +58,7 @@ class TimeLimitMonitor {
6058

6159
/// Notify \p runtime to check timeout.
6260
void notifyRuntimeTimeout(Runtime *runtime) {
63-
runtime->triggerAsyncBreak();
61+
runtime->triggerTimeoutAsyncBreak();
6462
}
6563

6664
private:
@@ -84,5 +82,3 @@ class TimeLimitMonitor {
8482
} // namespace hermes
8583

8684
#endif // HERMES_VM_TIMELIMITMONITOR_H
87-
88-
#endif // HERMESVM_TIMELIMIT

lib/CompilerDriver/CompilerDriver.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -813,11 +813,6 @@ bool validateFlags() {
813813
if (cl::DumpTarget != DumpBytecode)
814814
err("You can only dump bytecode for HBC bytecode file.");
815815
}
816-
817-
if (cl::EmitDebugInfo && cl::EmitAsyncBreakCheck) {
818-
err("Debugger and execution time limit can't be used at the same time");
819-
}
820-
821816
return !errored;
822817
}
823818

lib/ConsoleHost/ConsoleHost.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,15 @@ namespace {
167167

168168
template <typename F>
169169
auto maybeCatchException(const F &f) -> decltype(f()) {
170-
#if defined(HERMESVM_EXCEPTION_ON_OOM) || defined(HERMESVM_TIMELIMIT)
170+
#if defined(HERMESVM_EXCEPTION_ON_OOM)
171171
try {
172172
return f();
173173
} catch (const std::exception &ex) {
174174
// Report thrown exception and exit the process with failure code.
175175
llvm::errs() << ex.what();
176176
exit(1);
177177
}
178-
#else // HERMESVM_EXCEPTION_ON_OOM || HERMESVM_TIMELIMIT
178+
#else // HERMESVM_EXCEPTION_ON_OOM
179179
return f();
180180
#endif
181181
}
@@ -233,12 +233,10 @@ bool executeHBCBytecodeImpl(
233233
runtime->getJITContext().setDumpJITCode(options.dumpJITCode);
234234
runtime->getJITContext().setCrashOnError(options.jitCrashOnError);
235235

236-
#ifdef HERMESVM_TIMELIMIT
237236
if (options.timeLimit > 0) {
238237
vm::TimeLimitMonitor::getInstance().watchRuntime(
239238
runtime.get(), options.timeLimit);
240239
}
241-
#endif
242240

243241
if (shouldRecordGCStats) {
244242
statSampler = llvm::make_unique<vm::StatSamplingThread>(
@@ -291,11 +289,9 @@ bool executeHBCBytecodeImpl(
291289
llvm::errs(), runtime->makeHandle(runtime->getThrownValue()));
292290
}
293291

294-
#ifdef HERMESVM_TIMELIMIT
295292
if (options.timeLimit > 0) {
296293
vm::TimeLimitMonitor::getInstance().unwatchRuntime(runtime.get());
297294
}
298-
#endif
299295

300296
#ifdef HERMESVM_PROFILER_OPCODE
301297
runtime->dumpOpcodeStats(llvm::outs());

lib/VM/Debugger/Debugger.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ static llvm::Optional<ScopeChain> scopeChainForBlock(
8787
}
8888

8989
void Debugger::triggerAsyncPause() {
90-
runtime_->triggerAsyncBreak();
90+
runtime_->triggerDebuggerAsyncBreak();
9191
}
9292

9393
void Debugger::breakAtJumpTarget(InterpreterState &state) {

lib/VM/Interpreter.cpp

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ HERMES_SLOW_STATISTIC(
102102
PROFILER_SYMBOLS(INTERP_WRAPPER)
103103
#endif
104104

105-
#if defined(HERMES_ENABLE_DEBUGGER) && defined(HERMESVM_TIMELIMIT)
106-
static_assert(false, "Debugger and TimeLimit can't be used at the same time");
107-
#endif
108-
109105
namespace hermes {
110106
namespace vm {
111107

@@ -1485,7 +1481,7 @@ CallResult<HermesValue> Interpreter::interpretFunction(
14851481
doCall : {
14861482
#ifdef HERMES_ENABLE_DEBUGGER
14871483
// Check for an async debugger request.
1488-
if (LLVM_UNLIKELY(runtime->testAndClearAsyncBreakRequest())) {
1484+
if (LLVM_UNLIKELY(runtime->testAndClearDebuggerAsyncBreakRequest())) {
14891485
if (runDebuggerUpdatingState(
14901486
Debugger::RunReason::AsyncBreak,
14911487
runtime,
@@ -1561,7 +1557,7 @@ CallResult<HermesValue> Interpreter::interpretFunction(
15611557
CASE(CallDirectLongIndex) {
15621558
#ifdef HERMES_ENABLE_DEBUGGER
15631559
// Check for an async debugger request.
1564-
if (LLVM_UNLIKELY(runtime->testAndClearAsyncBreakRequest())) {
1560+
if (LLVM_UNLIKELY(runtime->testAndClearDebuggerAsyncBreakRequest())) {
15651561
if (runDebuggerUpdatingState(
15661562
Debugger::RunReason::AsyncBreak,
15671563
runtime,
@@ -1716,7 +1712,7 @@ CallResult<HermesValue> Interpreter::interpretFunction(
17161712
CASE(Ret) {
17171713
#ifdef HERMES_ENABLE_DEBUGGER
17181714
// Check for an async debugger request.
1719-
if (LLVM_UNLIKELY(runtime->testAndClearAsyncBreakRequest())) {
1715+
if (LLVM_UNLIKELY(runtime->testAndClearDebuggerAsyncBreakRequest())) {
17201716
if (runDebuggerUpdatingState(
17211717
Debugger::RunReason::AsyncBreak,
17221718
runtime,
@@ -1858,25 +1854,26 @@ CallResult<HermesValue> Interpreter::interpretFunction(
18581854
}
18591855

18601856
CASE(AsyncBreakCheck) {
1861-
#if defined(HERMES_ENABLE_DEBUGGER) || defined(HERMESVM_TIMELIMIT)
1862-
if (LLVM_UNLIKELY(runtime->testAndClearAsyncBreakRequest())) {
1857+
if (LLVM_UNLIKELY(runtime->hasAsyncBreak())) {
18631858
#ifdef HERMES_ENABLE_DEBUGGER
1864-
if (runDebuggerUpdatingState(
1865-
Debugger::RunReason::AsyncBreak,
1866-
runtime,
1867-
curCodeBlock,
1868-
ip,
1869-
frameRegs) == ExecutionStatus::EXCEPTION)
1870-
goto exception;
1871-
#endif
1872-
1873-
#ifdef HERMESVM_TIMELIMIT
1874-
// notifyTimeout does not return.
1875-
notifyTimeout(runtime, ip);
1859+
if (runtime->testAndClearDebuggerAsyncBreakRequest()) {
1860+
if (runDebuggerUpdatingState(
1861+
Debugger::RunReason::AsyncBreak,
1862+
runtime,
1863+
curCodeBlock,
1864+
ip,
1865+
frameRegs) == ExecutionStatus::EXCEPTION)
1866+
goto exception;
1867+
}
18761868
#endif
1877-
gcScope.flushToSmallCount(KEEP_HANDLES);
1869+
if (runtime->testAndClearTimeoutAsyncBreakRequest()) {
1870+
if (runtime->notifyTimeout() == ExecutionStatus::EXCEPTION) {
1871+
goto exception;
1872+
}
1873+
}
18781874
}
1879-
#endif
1875+
gcScope.flushToSmallCount(KEEP_HANDLES);
1876+
18801877
ip = NEXTINST(AsyncBreakCheck);
18811878
DISPATCH;
18821879
}

lib/VM/Runtime.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1033,13 +1033,22 @@ ExecutionStatus Runtime::raiseStackOverflow(StackOverflowKind kind) {
10331033
}
10341034

10351035
ExecutionStatus Runtime::raiseQuitError() {
1036+
return raiseUncatchableError("Quit");
1037+
}
1038+
1039+
ExecutionStatus Runtime::raiseTimeoutError() {
1040+
return raiseUncatchableError("Javascript execution has timeout.");
1041+
}
1042+
1043+
ExecutionStatus Runtime::raiseUncatchableError(llvm::StringRef errMessage) {
10361044
auto res = JSError::createUncatchable(
10371045
this, Handle<JSObject>::vmcast(&ErrorPrototype));
10381046
if (res == ExecutionStatus::EXCEPTION) {
10391047
return ExecutionStatus::EXCEPTION;
10401048
}
10411049
auto err = makeHandle<JSError>(*res);
1042-
res = StringPrimitive::create(this, llvm::ASCIIRef{"Quit"});
1050+
res = StringPrimitive::create(
1051+
this, llvm::ASCIIRef{errMessage.begin(), errMessage.end()});
10431052
if (res == ExecutionStatus::EXCEPTION) {
10441053
return ExecutionStatus::EXCEPTION;
10451054
}
@@ -1771,5 +1780,10 @@ void Runtime::deserializeImpl(
17711780
}
17721781
#endif
17731782

1783+
ExecutionStatus Runtime::notifyTimeout() {
1784+
// TODO: allow a vector of callbacks.
1785+
return raiseTimeoutError();
1786+
}
1787+
17741788
} // namespace vm
17751789
} // namespace hermes

0 commit comments

Comments
 (0)