2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/execution-context.h"
18 #define __STDC_LIMIT_MACROS
25 #include <folly/MapUtil.h>
26 #include <folly/Format.h>
27 #include <folly/Likely.h>
29 #include "hphp/util/logger.h"
30 #include "hphp/util/process.h"
31 #include "hphp/util/text-color.h"
32 #include "hphp/util/service-data.h"
33 #include "hphp/runtime/base/request-event-handler.h"
34 #include "hphp/runtime/base/array-init.h"
35 #include "hphp/runtime/base/debuggable.h"
36 #include "hphp/runtime/base/array-iterator.h"
37 #include "hphp/runtime/base/memory-manager.h"
38 #include "hphp/runtime/base/sweepable.h"
39 #include "hphp/runtime/base/backtrace.h"
40 #include "hphp/runtime/base/builtin-functions.h"
41 #include "hphp/runtime/base/comparisons.h"
42 #include "hphp/runtime/base/program-functions.h"
43 #include "hphp/runtime/base/runtime-option.h"
44 #include "hphp/runtime/base/tv-refcount.h"
45 #include "hphp/runtime/base/tv-type.h"
46 #include "hphp/runtime/base/type-variant.h"
47 #include "hphp/runtime/base/unit-cache.h"
48 #include "hphp/runtime/base/system-profiler.h"
49 #include "hphp/runtime/base/container-functions.h"
50 #include "hphp/runtime/base/hhprof.h"
51 #include "hphp/runtime/base/apc-stats.h"
52 #include "hphp/runtime/base/apc-typed-value.h"
53 #include "hphp/runtime/base/extended-logger.h"
54 #include "hphp/runtime/base/implicit-context.h"
55 #include "hphp/runtime/debugger/debugger.h"
56 #include "hphp/runtime/ext/std/ext_std_output.h"
57 #include "hphp/runtime/ext/string/ext_string.h"
58 #include "hphp/runtime/ext/reflection/ext_reflection.h"
59 #include "hphp/runtime/ext/apc/ext_apc.h"
60 #include "hphp/runtime/server/cli-server.h"
61 #include "hphp/runtime/server/server-stats.h"
62 #include "hphp/runtime/vm/debug/debug.h"
63 #include "hphp/runtime/vm/jit/enter-tc.h"
64 #include "hphp/runtime/vm/jit/tc.h"
65 #include "hphp/runtime/vm/jit/translator-inline.h"
66 #include "hphp/runtime/vm/jit/translator.h"
67 #include "hphp/runtime/vm/jit/unwind-itanium.h"
68 #include "hphp/runtime/vm/act-rec-defs.h"
69 #include "hphp/runtime/vm/debugger-hook.h"
70 #include "hphp/runtime/vm/event-hook.h"
71 #include "hphp/runtime/vm/hh-utils.h"
72 #include "hphp/runtime/vm/interp-helpers.h"
73 #include "hphp/runtime/vm/resumable.h"
74 #include "hphp/runtime/vm/runtime.h"
75 #include "hphp/runtime/vm/runtime-compiler.h"
76 #include "hphp/runtime/vm/treadmill.h"
77 #include "hphp/runtime/vm/unwind.h"
78 #include "hphp/runtime/base/php-globals.h"
79 #include "hphp/runtime/base/exceptions.h"
80 #include "hphp/zend/zend-math.h"
83 ///////////////////////////////////////////////////////////////////////////////
84 TRACE_SET_MOD(bcinterp
);
86 rds::local::AliasedRDSLocal
<ExecutionContext
,
87 rds::local::Initialize::Explicitly
,
88 &rds::local::detail::HotRDSLocals::g_context
91 ExecutionContext::ExecutionContext()
92 : m_transport(nullptr)
94 , m_implicitFlush(false)
96 , m_stdoutBytesWritten(0)
97 , m_errorState(ExecutionContext::ErrorState::NoError
)
99 , m_deferredErrors(nullptr)
100 , m_throwAllErrors(false)
101 , m_pageletTasksStarted(0)
103 , m_globalNVTable(nullptr)
106 , m_dbgNoBreak(false)
107 , m_lastErrorPath(staticEmptyString())
109 , m_executingSetprofileCallback(false)
110 , m_logger_hook(*this)
112 ARRPROV_USE_RUNTIME_LOCATION();
113 m_deferredErrors
= staticEmptyVec();
114 resetCoverageCounters();
115 // We don't want a new execution context to cause any request-heap
116 // allocations (because it will cause us to hold a slab, even while idle).
117 static auto s_cwd
= makeStaticString(Process::CurrentWorkingDirectory
);
119 RID().setMemoryLimit(std::to_string(RuntimeOption::RequestMemoryMaxBytes
));
120 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
122 VariableSerializer::serializationSizeLimit
->value
=
123 RuntimeOption::SerializationSizeLimit
;
124 tvWriteUninit(m_headerCallback
);
126 m_shutdowns
[ShutdownType::ShutDown
] = empty_vec_array();
127 m_shutdowns
[ShutdownType::PostSend
] = empty_vec_array();
128 m_shutdownsBackup
[ShutdownType::ShutDown
] = empty_vec_array();
129 m_shutdownsBackup
[ShutdownType::PostSend
] = empty_vec_array();
132 namespace rds
{ namespace local
{
133 // See header for why this is required.
137 void rds::local::RDSLocal
<ExecutionContext
,
138 rds::local::Initialize::Explicitly
>::destroy() {
140 getNoCheck()->sweep();
147 void ExecutionContext::cleanup() {
149 auto dead
= std::move(m_stdoutHooks
);
152 void ExecutionContext::sweep() {
156 ExecutionContext::~ExecutionContext() {
160 void ExecutionContext::backupSession() {
161 m_shutdownsBackup
= m_shutdowns
;
162 m_userErrorHandlersBackup
= m_userErrorHandlers
;
163 m_userExceptionHandlersBackup
= m_userExceptionHandlers
;
166 void ExecutionContext::restoreSession() {
167 m_shutdowns
= m_shutdownsBackup
;
168 m_userErrorHandlers
= m_userErrorHandlersBackup
;
169 m_userExceptionHandlers
= m_userExceptionHandlersBackup
;
172 ///////////////////////////////////////////////////////////////////////////////
175 String
ExecutionContext::getMimeType() const {
178 mimetype
= m_transport
->getMimeType();
181 if (strncasecmp(mimetype
.data(), "text/", 5) == 0) {
182 int pos
= mimetype
.find(';');
183 if (pos
!= String::npos
) {
184 mimetype
= mimetype
.substr(0, pos
);
186 } else if (m_transport
&& m_transport
->getUseDefaultContentType()) {
187 mimetype
= RID().getDefaultMimeType();
192 std::string
ExecutionContext::getRequestUrl(size_t szLimit
) {
193 Transport
* t
= getTransport();
194 std::string ret
= t
? t
->getUrl() : "";
195 if (szLimit
!= std::string::npos
) {
196 ret
= ret
.substr(0, szLimit
);
201 void ExecutionContext::setContentType(const String
& mimetype
,
202 const String
& charset
) {
204 String contentType
= mimetype
;
206 contentType
+= "charset=";
207 contentType
+= charset
;
208 m_transport
->addHeader("Content-Type", contentType
.c_str());
209 m_transport
->setUseDefaultContentType(false);
213 ///////////////////////////////////////////////////////////////////////////////
216 void ExecutionContext::write(const String
& s
) {
217 write(s
.data(), s
.size());
220 void ExecutionContext::addStdoutHook(StdoutHook
* hook
) {
221 if (hook
!= nullptr) {
222 m_stdoutHooks
.insert(hook
);
226 bool ExecutionContext::removeStdoutHook(StdoutHook
* hook
) {
227 if (hook
== nullptr) {
231 return m_stdoutHooks
.erase(hook
) != 0;
234 static void safe_stdout(const void *ptr
, size_t size
) {
235 write(fileno(stdout
), ptr
, size
);
238 void ExecutionContext::writeStdout(const char *s
, int len
) {
240 if (m_stdoutHooks
.empty()) {
241 if (s_stdout_color
) {
242 safe_stdout(s_stdout_color
, strlen(s_stdout_color
));
244 safe_stdout(ANSI_COLOR_END
, strlen(ANSI_COLOR_END
));
248 m_stdoutBytesWritten
+= len
;
250 for (auto const hook
: m_stdoutHooks
) {
251 assertx(hook
!= nullptr);
257 void ExecutionContext::writeTransport(const char *s
, int len
) {
259 m_transport
->sendRaw(s
, len
, 200, false, true);
265 size_t ExecutionContext::getStdoutBytesWritten() const {
266 return m_stdoutBytesWritten
;
269 void ExecutionContext::write(const char *s
, int len
) {
271 m_sb
->append(s
, len
);
272 if (m_out
&& m_out
->chunk_size
> 0) {
273 if (m_sb
->size() >= m_out
->chunk_size
) {
277 if (m_implicitFlush
) flush();
279 writeTransport(s
, len
);
283 ///////////////////////////////////////////////////////////////////////////////
286 void ExecutionContext::obProtect(bool on
) {
287 m_protectedLevel
= on
? m_buffers
.size() : 0;
290 void ExecutionContext::obStart(const Variant
& handler
/* = null */,
291 int chunk_size
/* = 0 */,
292 OBFlags flags
/* = OBFlags::Default */) {
293 if (m_insideOBHandler
) {
294 raise_error("ob_start(): Cannot use output buffering "
295 "in output buffering display handlers");
297 m_buffers
.emplace_back(Variant(handler
), chunk_size
, flags
);
298 resetCurrentBuffer();
301 String
ExecutionContext::obCopyContents() {
302 if (!m_buffers
.empty()) {
303 StringBuffer
&oss
= m_buffers
.back().oss
;
308 return empty_string();
311 String
ExecutionContext::obDetachContents() {
312 if (!m_buffers
.empty()) {
313 StringBuffer
&oss
= m_buffers
.back().oss
;
318 return empty_string();
321 int ExecutionContext::obGetContentLength() {
322 if (m_buffers
.empty()) {
325 return m_buffers
.back().oss
.size();
328 void ExecutionContext::obClean(int handler_flag
) {
329 if (!m_buffers
.empty()) {
330 OutputBuffer
*last
= &m_buffers
.back();
331 if (!last
->handler
.isNull()) {
332 m_insideOBHandler
= true;
333 SCOPE_EXIT
{ m_insideOBHandler
= false; };
334 vm_call_user_func(last
->handler
,
335 make_vec_array(last
->oss
.detach(), handler_flag
));
341 bool ExecutionContext::obFlush(bool force
/*= false*/) {
342 assertx(m_protectedLevel
>= 0);
344 if ((int)m_buffers
.size() <= m_protectedLevel
) {
348 auto iter
= m_buffers
.end();
349 OutputBuffer
& last
= *(--iter
);
350 if (!force
&& !(last
.flags
& OBFlags::Flushable
)) {
353 if (any(last
.flags
& OBFlags::OutputDisabled
)) {
357 const int flag
= k_PHP_OUTPUT_HANDLER_START
| k_PHP_OUTPUT_HANDLER_END
;
359 if (iter
!= m_buffers
.begin()) {
360 OutputBuffer
& prev
= *(--iter
);
361 if (last
.handler
.isNull()) {
362 prev
.oss
.absorb(last
.oss
);
364 auto str
= last
.oss
.detach();
368 ARRPROV_USE_RUNTIME_LOCATION();
369 m_insideOBHandler
= true;
370 SCOPE_EXIT
{ m_insideOBHandler
= false; };
371 tout
= vm_call_user_func(
372 last
.handler
, make_vec_array(str
, flag
)
375 prev
.oss
.append(tout
.toString());
377 prev
.oss
.append(str
);
384 auto str
= last
.oss
.detach();
385 if (!last
.handler
.isNull()) {
389 ARRPROV_USE_RUNTIME_LOCATION();
390 m_insideOBHandler
= true;
391 SCOPE_EXIT
{ m_insideOBHandler
= false; };
392 tout
= vm_call_user_func(
393 last
.handler
, make_vec_array(str
, flag
)
396 str
= tout
.toString();
398 writeTransport(str
.data(), str
.size());
403 writeTransport(str
.data(), str
.size());
407 void ExecutionContext::obFlushAll() {
413 bool ExecutionContext::obEnd() {
414 assertx(m_protectedLevel
>= 0);
415 if ((int)m_buffers
.size() > m_protectedLevel
) {
416 m_buffers
.pop_back();
417 resetCurrentBuffer();
418 if (m_implicitFlush
) flush();
421 if (m_implicitFlush
) flush();
425 void ExecutionContext::obEndAll() {
429 int ExecutionContext::obGetLevel() {
430 assertx((int)m_buffers
.size() >= m_protectedLevel
);
431 return m_buffers
.size() - m_protectedLevel
;
440 s_chunk_size("chunk_size"),
441 s_buffer_used("buffer_used"),
442 s_default_output_handler("default output handler");
444 Array
ExecutionContext::obGetStatus(bool full
) {
445 Array ret
= empty_varray();
447 for (auto& buffer
: m_buffers
) {
448 Array status
= empty_darray();
449 if (level
< m_protectedLevel
|| buffer
.handler
.isNull()) {
450 status
.set(s_name
, s_default_output_handler
);
451 status
.set(s_type
, 0);
453 status
.set(s_name
, buffer
.handler
);
454 status
.set(s_type
, 1);
458 if (any(buffer
.flags
& OBFlags::Cleanable
)) {
459 flags
|= k_PHP_OUTPUT_HANDLER_CLEANABLE
;
461 if (any(buffer
.flags
& OBFlags::Flushable
)) {
462 flags
|= k_PHP_OUTPUT_HANDLER_FLUSHABLE
;
464 if (any(buffer
.flags
& OBFlags::Removable
)) {
465 flags
|= k_PHP_OUTPUT_HANDLER_REMOVABLE
;
467 status
.set(s_flags
, flags
);
469 status
.set(s_level
, level
);
470 status
.set(s_chunk_size
, buffer
.chunk_size
);
471 status
.set(s_buffer_used
, static_cast<uint64_t>(buffer
.oss
.size()));
476 ret
= std::move(status
);
483 String
ExecutionContext::obGetBufferName() {
484 if (m_buffers
.empty()) {
486 } else if (m_buffers
.size() <= m_protectedLevel
) {
487 return s_default_output_handler
;
489 auto iter
= m_buffers
.end();
490 OutputBuffer
& buffer
= *(--iter
);
491 if (buffer
.handler
.isNull()) {
492 return s_default_output_handler
;
494 return buffer
.handler
.toString();
499 void ExecutionContext::obSetImplicitFlush(bool on
) {
500 m_implicitFlush
= on
;
503 Array
ExecutionContext::obGetHandlers() {
504 Array ret
= empty_varray();
505 for (auto& ob
: m_buffers
) {
506 auto& handler
= ob
.handler
;
507 ret
.append(handler
.isNull() ? s_default_output_handler
: handler
);
512 void ExecutionContext::flush() {
513 if (!m_buffers
.empty() &&
514 RuntimeOption::EnableEarlyFlush
&& m_protectedLevel
&&
515 !(m_buffers
.front().flags
& OBFlags::OutputDisabled
)) {
516 OutputBuffer
&buffer
= m_buffers
.front();
517 StringBuffer
&oss
= buffer
.oss
;
519 if (any(buffer
.flags
& OBFlags::WriteToStdout
)) {
520 writeStdout(oss
.data(), oss
.size());
522 writeTransport(oss
.data(), oss
.size());
529 void ExecutionContext::resetCurrentBuffer() {
530 if (m_buffers
.empty()) {
534 m_sb
= &m_buffers
.back().oss
;
535 m_out
= &m_buffers
.back();
539 ///////////////////////////////////////////////////////////////////////////////
540 // program executions
542 void ExecutionContext::registerShutdownFunction(const Variant
& function
,
544 auto& funcs
= m_shutdowns
[type
];
545 assertx(funcs
.isVec());
546 funcs
.append(function
);
549 Variant
ExecutionContext::pushUserErrorHandler(const Variant
& function
,
552 if (!m_userErrorHandlers
.empty()) {
553 ret
= m_userErrorHandlers
.back().first
;
555 m_userErrorHandlers
.push_back(std::pair
<Variant
,int>(function
, error_types
));
559 Variant
ExecutionContext::pushUserExceptionHandler(const Variant
& function
) {
561 if (!m_userExceptionHandlers
.empty()) {
562 ret
= m_userExceptionHandlers
.back();
564 m_userExceptionHandlers
.push_back(function
);
568 void ExecutionContext::popUserErrorHandler() {
569 if (!m_userErrorHandlers
.empty()) {
570 m_userErrorHandlers
.pop_back();
574 void ExecutionContext::clearUserErrorHandlers() {
575 while (!m_userErrorHandlers
.empty()) m_userErrorHandlers
.pop_back();
578 void ExecutionContext::popUserExceptionHandler() {
579 if (!m_userExceptionHandlers
.empty()) {
580 m_userExceptionHandlers
.pop_back();
584 void ExecutionContext::acceptRequestEventHandlers(bool enable
) {
585 m_acceptRequestEventHandlers
= enable
;
588 std::size_t ExecutionContext::registerRequestEventHandler(
589 RequestEventHandler
*handler
) {
590 assertx(handler
&& handler
->getInited());
591 assertx(m_acceptRequestEventHandlers
);
592 m_requestEventHandlers
.push_back(handler
);
593 return m_requestEventHandlers
.size()-1;
596 void ExecutionContext::unregisterRequestEventHandler(
597 RequestEventHandler
* handler
,
599 assertx(index
< m_requestEventHandlers
.size() &&
600 m_requestEventHandlers
[index
] == handler
);
601 assertx(!handler
->getInited());
602 if (index
== m_requestEventHandlers
.size()-1) {
603 m_requestEventHandlers
.pop_back();
605 m_requestEventHandlers
[index
] = nullptr;
609 static bool requestEventHandlerPriorityComp(RequestEventHandler
*a
,
610 RequestEventHandler
*b
) {
612 else if (!b
) return false;
613 else return a
->priority() < b
->priority();
616 void ExecutionContext::onRequestShutdown() {
617 while (!m_requestEventHandlers
.empty()) {
618 // handlers could cause other handlers to be registered,
619 // so need to repeat until done
620 decltype(m_requestEventHandlers
) tmp
;
621 tmp
.swap(m_requestEventHandlers
);
623 // Sort handlers by priority so that lower priority values get shutdown
625 sort(tmp
.begin(), tmp
.end(),
626 requestEventHandlerPriorityComp
);
627 for (auto* handler
: tmp
) {
628 if (!handler
) continue;
629 assertx(handler
->getInited());
630 handler
->requestShutdown();
631 handler
->setInited(false);
636 void ExecutionContext::executeFunctions(ShutdownType type
) {
637 ARRPROV_USE_RUNTIME_LOCATION();
639 RuntimeOption::PspTimeoutSeconds
,
640 RuntimeOption::PspCpuTimeoutSeconds
643 if (RO::EvalEnableImplicitContext
) {
644 // If the main request terminated with a C++ exception, we would not
645 // have cleared the implicit context since that logic is
646 // done in a PHP try-finally. Let's clear the implicit context here.
647 *ImplicitContext::activeCtx
= nullptr;
650 // We mustn't destroy any callbacks until we're done with all
651 // of them. So hold them in tmp.
652 // XXX still true in a world without destructors?
653 auto tmp
= empty_vec_array();
655 Array funcs
= m_shutdowns
[type
];
656 if (funcs
.empty()) break;
657 m_shutdowns
[type
] = empty_vec_array();
661 vm_call_user_func(VarNR
{v
}, init_null_variant
);
662 // Implicit context should not have leaked between each call
663 assertx(!RO::EvalEnableImplicitContext
||
664 !(*ImplicitContext::activeCtx
));
671 void ExecutionContext::onShutdownPreSend() {
672 // in case obStart was called without obFlush
674 try { obFlushAll(); } catch (...) {}
677 // When host is OOMing, abort abruptly.
678 if (RID().shouldOOMAbort()) return;
680 tracing::Block _
{"shutdown-pre-send"};
682 tl_heap
->resetCouldOOM(isStandardRequest());
683 executeFunctions(ShutDown
);
686 void ExecutionContext::debuggerExecutePsps() {
688 executeFunctions(PostSend
);
689 } catch (const ExitException
& e
) {
691 } catch (const Exception
& e
) {
693 } catch (const Object
& e
) {
694 onUnhandledException(e
);
699 void ExecutionContext::onShutdownPostSend() {
700 // When host is OOMing, abort abruptly.
701 if (RID().shouldOOMAbort()) return;
703 tracing::Block _
{"shutdown-post-send"};
705 ServerStats::SetThreadMode(ServerStats::ThreadMode::PostProcessing
);
706 tl_heap
->resetCouldOOM(isStandardRequest());
709 ServerStatsHelper
ssh("psp", ServerStatsHelper::TRACK_HWINST
);
710 executeFunctions(PostSend
);
713 bump_counter_and_rethrow(true /* isPsp */);
714 } catch (const ExitException
& e
) {
716 } catch (const Exception
& e
) {
718 } catch (const Object
& e
) {
719 onUnhandledException(e
);
723 Logger::Error("unknown exception was thrown from psp");
726 ServerStats::SetThreadMode(ServerStats::ThreadMode::Idling
);
729 ///////////////////////////////////////////////////////////////////////////////
732 bool ExecutionContext::errorNeedsHandling(int errnum
,
733 bool callUserHandler
,
734 ErrorThrowMode mode
) {
735 if (UNLIKELY(m_throwAllErrors
)) {
736 throw Exception(folly::sformat("throwAllErrors: {}", errnum
));
738 if (mode
!= ErrorThrowMode::Never
|| errorNeedsLogging(errnum
)) {
741 if (callUserHandler
) {
742 if (!m_userErrorHandlers
.empty() &&
743 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
750 bool ExecutionContext::errorNeedsLogging(int errnum
) {
752 RID().getErrorReportingLevel() |
753 RuntimeOption::ForceErrorReportingLevel
;
754 return RuntimeOption::NoSilencer
|| (level
& errnum
) != 0;
757 struct ErrorStateHelper
{
758 ErrorStateHelper(ExecutionContext
*context
,
759 ExecutionContext::ErrorState state
) {
761 m_originalState
= m_context
->getErrorState();
762 m_context
->setErrorState(state
);
764 ~ErrorStateHelper() {
765 m_context
->setErrorState(m_originalState
);
768 ExecutionContext
*m_context
;
769 ExecutionContext::ErrorState m_originalState
;
775 s_function("function"),
777 s_error_num("error-num"),
778 s_error_string("error-string"),
779 s_error_file("error-file"),
780 s_error_line("error-line"),
781 s_error_backtrace("error-backtrace"),
782 s_overflow("overflow");
784 void ExecutionContext::handleError(const std::string
& msg
,
786 bool callUserHandler
,
788 const std::string
& prefix
,
789 bool skipFrame
/* = false */) {
790 SYNC_VM_REGS_SCOPED();
792 auto newErrorState
= ErrorState::ErrorRaised
;
793 switch (getErrorState()) {
794 case ErrorState::ErrorRaised
:
795 case ErrorState::ErrorRaisedByUserHandler
:
797 case ErrorState::ExecutingUserHandler
:
798 newErrorState
= ErrorState::ErrorRaisedByUserHandler
;
804 // Potentially upgrade the error to E_USER_ERROR
805 if (errnum
& RuntimeOption::ErrorUpgradeLevel
&
806 static_cast<int>(ErrorMode::UPGRADEABLE_ERROR
)) {
807 errnum
= static_cast<int>(ErrorMode::USER_ERROR
);
808 mode
= ErrorThrowMode::IfUnhandled
;
811 auto const ee
= skipFrame
?
812 ExtendedException(ExtendedException::SkipFrame
{}, msg
) :
813 ExtendedException(msg
);
814 bool handled
= false;
816 ErrorStateHelper
esh(this, newErrorState
);
817 if (callUserHandler
) {
818 handled
= callUserErrorHandler(ee
, errnum
, false);
822 recordLastError(ee
, errnum
);
825 if (g_system_profiler
) {
826 g_system_profiler
->errorCallBack(ee
, errnum
, msg
);
830 if (mode
== ErrorThrowMode::Always
||
831 (mode
== ErrorThrowMode::IfUnhandled
&& !handled
)) {
832 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee
, errnum
, msg
));
834 errnum
== static_cast<int>(ErrorMode::RECOVERABLE_ERROR
);
835 raise_fatal_error(msg
.c_str(), ee
.getBacktrace(), isRecoverable
,
836 !errorNeedsLogging(errnum
) /* silent */);
843 // If we're inside an error handler already, queue it up on the deferred
845 if (getErrorState() == ErrorState::ExecutingUserHandler
) {
846 auto& deferred
= m_deferredErrors
;
847 if (deferred
.size() < RuntimeOption::EvalMaxDeferredErrors
) {
848 auto fileAndLine
= ee
.getFileAndLine();
853 s_error_file
, std::move(fileAndLine
.first
),
854 s_error_line
, fileAndLine
.second
,
855 s_error_backtrace
, ee
.getBacktrace()
858 } else if (!deferred
.empty()) {
859 auto const last
= deferred
.lval(int64_t{deferred
.size() - 1});
860 if (isDictType(type(last
))) {
861 asArrRef(last
).set(s_overflow
, true);
866 if (errorNeedsLogging(errnum
)) {
867 DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee
, errnum
, ee
.getMessage()));
868 auto fileAndLine
= ee
.getFileAndLine();
869 Logger::Log(Logger::LogError
, prefix
.c_str(), ee
,
870 fileAndLine
.first
.c_str(), fileAndLine
.second
);
875 bool ExecutionContext::callUserErrorHandler(const Exception
& e
, int errnum
,
876 bool swallowExceptions
) {
877 switch (getErrorState()) {
878 case ErrorState::ExecutingUserHandler
:
879 case ErrorState::ErrorRaisedByUserHandler
:
884 if (!m_userErrorHandlers
.empty() &&
885 (m_userErrorHandlers
.back().second
& errnum
) != 0) {
886 auto fileAndLine
= std::make_pair(empty_string(), 0);
888 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
889 fileAndLine
= ee
->getFileAndLine();
890 backtrace
= ee
->getBacktrace();
893 ARRPROV_USE_RUNTIME_LOCATION();
894 ErrorStateHelper
esh(this, ErrorState::ExecutingUserHandler
);
895 m_deferredErrors
= empty_vec_array();
896 SCOPE_EXIT
{ m_deferredErrors
= empty_vec_array(); };
897 if (!same(vm_call_user_func
898 (m_userErrorHandlers
.back().first
,
899 make_vec_array(errnum
, String(e
.getMessage()),
900 fileAndLine
.first
, fileAndLine
.second
, empty_darray(),
905 } catch (const RequestTimeoutException
&) {
906 static auto requestErrorHandlerTimeoutCounter
=
907 ServiceData::createTimeSeries("requests_timed_out_error_handler",
908 {ServiceData::StatsType::COUNT
});
909 requestErrorHandlerTimeoutCounter
->addValue(1);
910 ServerStats::Log("request.timed_out.error_handler", 1);
912 if (!swallowExceptions
) throw;
913 } catch (const RequestCPUTimeoutException
&) {
914 static auto requestErrorHandlerCPUTimeoutCounter
=
915 ServiceData::createTimeSeries("requests_cpu_timed_out_error_handler",
916 {ServiceData::StatsType::COUNT
});
917 requestErrorHandlerCPUTimeoutCounter
->addValue(1);
918 ServerStats::Log("request.cpu_timed_out.error_handler", 1);
920 if (!swallowExceptions
) throw;
921 } catch (const RequestMemoryExceededException
&) {
922 static auto requestErrorHandlerMemoryExceededCounter
=
923 ServiceData::createTimeSeries(
924 "requests_memory_exceeded_error_handler",
925 {ServiceData::StatsType::COUNT
});
926 requestErrorHandlerMemoryExceededCounter
->addValue(1);
927 ServerStats::Log("request.memory_exceeded.error_handler", 1);
929 if (!swallowExceptions
) throw;
931 static auto requestErrorHandlerOtherExceptionCounter
=
932 ServiceData::createTimeSeries(
933 "requests_other_exception_error_handler",
934 {ServiceData::StatsType::COUNT
});
935 requestErrorHandlerOtherExceptionCounter
->addValue(1);
936 ServerStats::Log("request.other_exception.error_handler", 1);
938 if (!swallowExceptions
) throw;
944 bool ExecutionContext::onFatalError(const Exception
& e
) {
945 tl_heap
->resetCouldOOM(isStandardRequest());
947 // need to restore the error reporting level, because the fault
948 // handler for silencers won't be run on fatals, and we might be
949 // about to run a user error handler (and psp/shutdown code).
950 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
952 auto prefix
= "\nFatal error: ";
953 auto errnum
= static_cast<int>(ErrorMode::FATAL_ERROR
);
954 auto const fatal
= dynamic_cast<const FatalErrorException
*>(&e
);
955 if (fatal
&& fatal
->isRecoverable()) {
956 prefix
= "\nCatchable fatal error: ";
957 errnum
= static_cast<int>(ErrorMode::RECOVERABLE_ERROR
);
960 recordLastError(e
, errnum
);
962 bool silenced
= false;
963 auto fileAndLine
= std::make_pair(empty_string(), 0);
964 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
965 silenced
= ee
->isSilent();
966 fileAndLine
= ee
->getFileAndLine();
968 // need to silence even with the AlwaysLogUnhandledExceptions flag set
969 if (!silenced
&& RuntimeOption::AlwaysLogUnhandledExceptions
) {
970 Logger::Log(Logger::LogError
, prefix
, e
, fileAndLine
.first
.c_str(),
973 bool handled
= false;
974 if (RuntimeOption::CallUserHandlerOnFatals
) {
975 handled
= callUserErrorHandler(e
, errnum
, true);
977 if (!handled
&& !silenced
&& !RuntimeOption::AlwaysLogUnhandledExceptions
) {
978 Logger::Log(Logger::LogError
, prefix
, e
, fileAndLine
.first
.c_str(),
984 bool ExecutionContext::onUnhandledException(Object e
) {
985 String err
= throwable_to_string(e
.get());
986 if (RuntimeOption::AlwaysLogUnhandledExceptions
) {
987 Logger::Error("\nFatal error: Uncaught %s", err
.data());
990 if (e
.instanceof(SystemLib::s_ThrowableClass
)) {
991 ARRPROV_USE_RUNTIME_LOCATION();
992 // user thrown exception
993 if (!m_userExceptionHandlers
.empty()) {
994 if (!same(vm_call_user_func
995 (m_userExceptionHandlers
.back(),
1006 if (!RuntimeOption::AlwaysLogUnhandledExceptions
) {
1007 Logger::Error("\nFatal error: Uncaught %s", err
.data());
1012 ///////////////////////////////////////////////////////////////////////////////
1014 void ExecutionContext::debuggerInfo(
1015 std::vector
<std::pair
<const char*,std::string
>>& info
) {
1016 int64_t newInt
= convert_bytes_to_long(IniSetting::Get("memory_limit"));
1018 newInt
= std::numeric_limits
<int64_t>::max();
1020 if (newInt
== std::numeric_limits
<int64_t>::max()) {
1021 info
.emplace_back("Max Memory", "(unlimited)");
1023 info
.emplace_back("Max Memory", IDebuggable::FormatSize(newInt
));
1025 info
.emplace_back("Max Time",
1026 IDebuggable::FormatTime(RID().getTimeout() * 1000));
1029 void ExecutionContext::setenv(const String
& name
, const String
& value
) {
1030 m_envs
.set(m_envs
.convertKey
<IntishCast::Cast
>(name
),
1031 make_tv
<KindOfString
>(value
.get()));
1034 void ExecutionContext::unsetenv(const String
& name
) {
1035 m_envs
.remove(name
);
1038 String
ExecutionContext::getenv(const String
& name
) const {
1039 if (m_envs
.exists(name
)) {
1040 return m_envs
[name
].toString();
1042 if (is_cli_server_mode()) {
1043 auto envs
= cli_env();
1044 if (envs
.exists(name
)) return envs
[name
].toString();
1047 if (auto value
= ::getenv(name
.data())) {
1048 return String(value
, CopyString
);
1050 if (RuntimeOption::EnvVariables
.find(name
.c_str()) != RuntimeOption::EnvVariables
.end()) {
1051 return String(RuntimeOption::EnvVariables
[name
.c_str()].data(), CopyString
);
1056 TypedValue
ExecutionContext::lookupClsCns(const NamedEntity
* ne
,
1057 const StringData
* cls
,
1058 const StringData
* cns
) {
1059 Class
* class_
= nullptr;
1061 class_
= Unit::loadClass(ne
, cls
);
1062 } catch (Object
& ex
) {
1063 // For compatibility with php, throwing through a constant lookup has
1064 // different behavior inside a property initializer (86pinit/86sinit).
1065 auto ar
= getStackFrame();
1066 if (ar
&& ar
->func() && Func::isSpecial(ar
->func()->name())) {
1067 raise_warning("Uncaught %s", throwable_to_string(ex
.get()).data());
1068 raise_error("Couldn't find constant %s::%s", cls
->data(), cns
->data());
1072 if (class_
== nullptr) {
1073 raise_error(Strings::UNKNOWN_CLASS
, cls
->data());
1075 TypedValue clsCns
= class_
->clsCnsGet(cns
);
1076 if (clsCns
.m_type
== KindOfUninit
) {
1077 raise_error("Couldn't find constant %s::%s", cls
->data(), cns
->data());
1082 static Class
* loadClass(StringData
* clsName
) {
1083 Class
* class_
= Unit::loadClass(clsName
);
1084 if (class_
== nullptr) {
1085 raise_error(Strings::UNKNOWN_CLASS
, clsName
->data());
1090 ObjectData
* ExecutionContext::createObject(StringData
* clsName
,
1091 const Variant
& params
,
1092 bool init
/* = true */) {
1093 return createObject(loadClass(clsName
), params
, init
);
1096 ObjectData
* ExecutionContext::createObject(const Class
* class_
,
1097 const Variant
& params
,
1099 callerDynamicConstructChecks(class_
);
1100 auto o
= Object::attach(ObjectData::newInstance(const_cast<Class
*>(class_
)));
1102 initObject(class_
, params
, o
.get());
1108 ObjectData
* ExecutionContext::createObjectOnly(StringData
* clsName
) {
1109 return createObject(clsName
, init_null_variant
, false);
1112 ObjectData
* ExecutionContext::initObject(StringData
* clsName
,
1113 const Variant
& params
,
1115 return initObject(loadClass(clsName
), params
, o
);
1118 ObjectData
* ExecutionContext::initObject(const Class
* class_
,
1119 const Variant
& params
,
1121 auto ctor
= class_
->getCtor();
1122 if (!(ctor
->attrs() & AttrPublic
)) {
1123 std::string msg
= "Access to non-public constructor of class ";
1124 msg
+= class_
->name()->data();
1125 Reflection::ThrowReflectionExceptionObject(msg
);
1128 if (!isContainerOrNull(params
)) {
1129 throw_param_is_not_container();
1131 tvDecRefGen(invokeFunc(ctor
, params
, o
, nullptr, true, false, true));
1135 ActRec
* ExecutionContext::getStackFrame() {
1140 ObjectData
* ExecutionContext::getThis() {
1142 ActRec
* fp
= vmfp();
1143 if (fp
->skipFrame()) fp
= getPrevVMStateSkipFrame(fp
);
1144 if (fp
&& fp
->func()->cls() && fp
->hasThis()) {
1145 return fp
->getThis();
1150 const RepoOptions
& ExecutionContext::getRepoOptionsForCurrentFrame() const {
1153 if (auto const ar
= vmfp()) {
1154 auto const path
= ar
->func()->unit()->filepath();
1155 return RepoOptions::forFile(path
->data());
1157 return RepoOptions::defaults();
1160 void ExecutionContext::onLoadWithOptions(
1161 const char* f
, const RepoOptions
& opts
1163 if (!RuntimeOption::EvalFatalOnParserOptionMismatch
) return;
1164 if (!m_requestOptions
) {
1165 m_requestOptions
.emplace(opts
);
1168 if (m_requestOptions
!= opts
) {
1169 // The data buffer has to stay alive for the call to raise_error.
1170 auto const path_str
= opts
.path();
1171 auto const path
= path_str
.empty() ? "{default options}" : path_str
.data();
1173 "Attempting to load file %s with incompatible parser settings from %s, "
1174 "this request is using parser settings from %s",
1175 f
, path
, m_requestOptions
->path().data()
1180 StringData
* ExecutionContext::getContainingFileName() {
1182 ActRec
* ar
= vmfp();
1183 if (ar
== nullptr) return staticEmptyString();
1184 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
);
1185 if (ar
== nullptr) return staticEmptyString();
1186 Unit
* unit
= ar
->m_func
->unit();
1187 auto const path
= ar
->m_func
->originalFilename() ?
1188 ar
->m_func
->originalFilename() : unit
->filepath();
1189 return const_cast<StringData
*>(path
);
1192 int ExecutionContext::getLine() {
1194 ActRec
* ar
= vmfp();
1195 Unit
* unit
= ar
? ar
->m_func
->unit() : nullptr;
1196 Offset pc
= unit
? pcOff() : 0;
1197 if (ar
== nullptr) return -1;
1198 if (ar
->skipFrame()) ar
= getPrevVMStateSkipFrame(ar
, &pc
);
1199 if (ar
== nullptr || (unit
= ar
->m_func
->unit()) == nullptr) return -1;
1200 return unit
->getLineNumber(pc
);
1203 ActRec
* ExecutionContext::getFrameAtDepthForDebuggerUnsafe(int frameDepth
) {
1204 ActRec
* ret
= nullptr;
1205 walkStack([&] (ActRec
* fp
, Offset
) {
1206 if (frameDepth
== 0) {
1207 if (fp
&& !fp
->localsDecRefd()) {
1219 Array
ExecutionContext::getLocalDefinedVariablesDebugger(int frame
) {
1220 const auto fp
= getFrameAtDepthForDebuggerUnsafe(frame
);
1221 return getDefinedVariables(fp
);
1224 bool ExecutionContext::setHeaderCallback(const Variant
& callback
) {
1225 if (tvAsVariant(g_context
->m_headerCallback
).toBoolean()) {
1226 // return false if a callback has already been set.
1229 tvAsVariant(g_context
->m_headerCallback
) = callback
;
1233 const static StaticString
1234 s_enter_async_entry_point("__SystemLib\\enter_async_entry_point");
1236 TypedValue
ExecutionContext::invokeUnit(const Unit
* unit
,
1237 bool callByHPHPInvoke
) {
1238 checkHHConfig(unit
);
1240 auto ret
= invokePseudoMain(unit
->getMain(nullptr, false));
1242 auto it
= unit
->getCachedEntryPoint();
1243 if (callByHPHPInvoke
&& it
!= nullptr) {
1244 if (it
->isAsync()) {
1246 Unit::lookupFunc(s_enter_async_entry_point
.get()),
1247 make_vec_array_tagged(ARRPROV_HERE(), Variant
{it
}),
1248 nullptr, nullptr, false
1251 invokeFunc(it
, init_null_variant
, nullptr, nullptr, false);
1257 void ExecutionContext::syncGdbState() {
1258 if (RuntimeOption::EvalJit
&& !RuntimeOption::EvalJitNoGdb
) {
1259 Debug::DebugInfo::Get()->debugSync();
1263 void ExecutionContext::pushVMState(TypedValue
* savedSP
) {
1264 if (UNLIKELY(!vmfp())) {
1266 assertx(m_nestedVMs
.size() == 0);
1270 TRACE(3, "savedVM: %p %p %p %p\n", vmpc(), vmfp(), vmFirstAR(), savedSP
);
1272 auto& savedVM
= m_nestedVMs
.emplace_back(
1281 jit::g_unwind_rds
->exn
,
1282 jit::g_unwind_rds
->sideEnter
1285 jit::g_unwind_rds
->exn
= nullptr;
1286 jit::g_unwind_rds
->sideEnter
= false;
1290 if (debug
&& savedVM
.fp
&&
1291 savedVM
.fp
->m_func
&&
1292 savedVM
.fp
->m_func
->unit()) {
1293 // Some asserts and tracing.
1294 const Func
* func
= savedVM
.fp
->m_func
;
1295 /* bound-check asserts in offsetOf */
1296 func
->unit()->offsetOf(savedVM
.pc
);
1297 TRACE(3, "pushVMState: saving frame %s pc %p off %d fp %p\n",
1298 func
->name()->data(),
1300 func
->unit()->offsetOf(savedVM
.pc
),
1305 void ExecutionContext::popVMState() {
1306 if (UNLIKELY(m_nestedVMs
.empty())) {
1310 vmFirstAR() = nullptr;
1314 assertx(m_nestedVMs
.size() >= 1);
1316 VMState
&savedVM
= m_nestedVMs
.back();
1317 vmpc() = savedVM
.pc
;
1318 vmfp() = savedVM
.fp
;
1319 vmFirstAR() = savedVM
.firstAR
;
1320 vmStack().top() = savedVM
.sp
;
1321 vmMInstrState() = savedVM
.mInstrState
;
1322 vmJitCalledFrame() = savedVM
.jitCalledFrame
;
1323 vmJitReturnAddr() = savedVM
.jitReturnAddr
;
1324 jit::g_unwind_rds
->exn
= savedVM
.exn
;
1325 jit::g_unwind_rds
->sideEnter
= savedVM
.unwinderSideEnter
;
1329 savedVM
.fp
->m_func
&&
1330 savedVM
.fp
->m_func
->unit()) {
1331 const Func
* func
= savedVM
.fp
->m_func
;
1332 (void) /* bound-check asserts in offsetOf */
1333 func
->unit()->offsetOf(savedVM
.pc
);
1334 TRACE(3, "popVMState: restoring frame %s pc %p off %d fp %p\n",
1335 func
->name()->data(),
1337 func
->unit()->offsetOf(savedVM
.pc
),
1342 m_nestedVMs
.pop_back();
1345 TRACE(1, "Reentry: exit fp %p pc %p\n", vmfp(), vmpc());
1348 void ExecutionContext::ExcLoggerHook::operator()(
1349 const char* header
, const char* msg
, const char* ending
1359 s_namespace("namespace "),
1360 s_curly_start(" { "),
1362 s_function_start("<<__DynamicallyCallable>> function "),
1363 s_evaluate_default_argument("evaluate_default_argument"),
1364 s_function_middle("() { return "),
1366 s_stdclass("stdclass");
1368 void ExecutionContext::requestInit() {
1369 assertx(SystemLib::s_unit
);
1372 createGlobalNVTable();
1373 vmStack().requestInit();
1374 ResourceHdr::resetMaxId();
1375 jit::tc::requestInit();
1377 if (RuntimeOption::EvalJitEnableRenameFunction
) {
1378 assertx(SystemLib::s_anyNonPersistentBuiltins
);
1382 * The normal case for production mode is that all builtins are
1383 * persistent, and every systemlib unit is accordingly going to be
1386 * However, if we have rename_function generally enabled, or if any
1387 * builtin functions were specified as interceptable at
1388 * repo-generation time, we'll actually need to merge systemlib on
1389 * every request because some of the builtins will not be marked
1392 if (UNLIKELY(SystemLib::s_anyNonPersistentBuiltins
)) {
1393 SystemLib::s_unit
->merge();
1394 SystemLib::mergePersistentUnits();
1395 if (SystemLib::s_hhas_unit
) SystemLib::s_hhas_unit
->merge();
1397 // System units are merge only, and everything is persistent.
1398 assertx(SystemLib::s_unit
->isEmpty());
1399 assertx(!SystemLib::s_hhas_unit
|| SystemLib::s_hhas_unit
->isEmpty());
1402 if (RO::EvalEnableImplicitContext
) *ImplicitContext::activeCtx
= nullptr;
1404 profileRequestStart();
1406 HHProf::Request::StartProfiling();
1409 Class
* cls
= NamedEntity::get(s_stdclass
.get())->clsList();
1411 assertx(cls
== SystemLib::s_stdclassClass
);
1414 if (Logger::UseRequestLog
) Logger::SetThreadHook(&m_logger_hook
);
1416 // Needs to be last (or nearly last): might cause unit merging to call an
1417 // extension function in the VM; this is bad if systemlib itself hasn't been
1419 autoTypecheckRequestInit();
1422 void ExecutionContext::requestExit() {
1423 apcExtension::purgeDeferred(std::move(m_apcDeferredExpire
));
1425 autoTypecheckRequestExit();
1426 HHProf::Request::FinishProfiling();
1430 vmStack().requestExit();
1431 profileRequestEnd();
1432 EventHook::Disable();
1436 if (m_globalNVTable
) {
1437 req::destroy_raw(m_globalNVTable
);
1438 m_globalNVTable
= nullptr;
1441 if (!m_lastError
.isNull()) {
1446 ARRPROV_USE_RUNTIME_LOCATION();
1447 m_deferredErrors
= empty_vec_array();
1450 if (Logger::UseRequestLog
) Logger::SetThreadHook(nullptr);
1451 if (m_requestTrace
) record_trace(std::move(*m_requestTrace
));
1454 template<class Action
>
1455 static inline void enterVM(ActRec
* ar
, Action action
) {
1456 enterVMCustomHandler(ar
, [&] { exception_handler(action
); });
1460 * Shared implementation for invokeFunc{,Few}().
1462 * The `doEnterVM' callback take an ActRec* argument corresponding to
1463 * the reentry frame.
1465 template<class FEnterVM
>
1467 TypedValue
ExecutionContext::invokeFuncImpl(const Func
* f
,
1468 ObjectData
* thiz
, Class
* cls
,
1469 uint32_t numArgsInclUnpack
,
1470 FEnterVM doEnterVM
) {
1472 // If `f' is a regular function, `thiz' and `cls' must be null.
1473 assertx(IMPLIES(!f
->implCls(), (!thiz
&& !cls
)));
1474 // If `f' is a method, either `thiz' or `cls' must be non-null.
1475 assertx(IMPLIES(f
->preClass(), thiz
|| cls
));
1476 // If `f' is a static method, thiz must be null.
1477 assertx(IMPLIES(f
->isStaticInPrologue(), !thiz
));
1479 if (thiz
!= nullptr) thiz
->incRefCount();
1481 ActRec
* ar
= vmStack().indA(numArgsInclUnpack
);
1482 ar
->setReturnVMExit();
1491 ar
->setNumArgs(numArgsInclUnpack
);
1494 if (vmfp() == nullptr) {
1495 TRACE(1, "Reentry: enter %s(%p) from top-level\n",
1496 f
->name()->data(), ar
);
1498 TRACE(1, "Reentry: enter %s(pc %p ar %p) from %s(%p)\n",
1499 f
->name()->data(), vmpc(), ar
,
1500 vmfp()->m_func
? vmfp()->m_func
->name()->data()
1506 auto const reentrySP
=
1507 vmStack().top() + numArgsInclUnpack
+ kNumActRecCells
+ f
->numInOutParams();
1508 pushVMState(reentrySP
);
1511 vmStack().top() == reentrySP
,
1512 "vmsp() mismatch around reentry: before @ {}, after @ {}",
1513 reentrySP
, vmStack().top()
1518 enterVM(ar
, [&] { doEnterVM(ar
); });
1520 if (UNLIKELY(f
->takesInOutParams())) {
1521 // This is OK (albeit ugly) since the return value should only be readable
1522 // from user code via e.g. call_user_func and we will tagTV the result from
1523 // the native wrapper and getting the correct frame pointer here (or from
1524 // the normal arrprov::tagFromPC) is awkward.
1525 ARRPROV_USE_RUNTIME_LOCATION();
1527 VArrayInit
varr(f
->numInOutParams() + 1);
1528 for (uint32_t i
= 0; i
< f
->numInOutParams() + 1; ++i
) {
1529 varr
.append(*vmStack().topTV());
1532 auto arr
= varr
.toArray();
1533 return make_array_like_tv(arr
.detach());
1535 auto const retval
= *vmStack().topTV();
1536 vmStack().discard();
1542 * Enter VM by calling action(), which invokes a function or resumes
1543 * an async function. The 'ar' argument points to an ActRec of the
1544 * invoked/resumed function.
1546 template<class Action
>
1547 static inline void enterVMCustomHandler(ActRec
* ar
, Action action
) {
1549 assertx(!ar
->sfp());
1550 assertx(isReturnHelper(reinterpret_cast<void*>(ar
->m_savedRip
)));
1551 assertx(ar
->callOffset() == 0);
1554 vmJitCalledFrame() = nullptr;
1555 vmJitReturnAddr() = 0;
1560 exception_handler(enterVMAtCurPC
);
1564 TypedValue
ExecutionContext::invokePseudoMain(const Func
* f
,
1565 ObjectData
* thiz
/* = NULL */,
1566 Class
* cls
/* = NULL */) {
1567 assertx(f
->isPseudoMain());
1568 auto toMerge
= f
->unit();
1570 if (toMerge
->isMergeOnly()) {
1571 Stats::inc(Stats::PseudoMain_Skipped
);
1572 return make_tv
<KindOfInt64
>(1);
1575 Stats::inc(Stats::PseudoMain_Executed
);
1578 // We must do a stack overflow check for leaf functions on re-entry,
1579 // because we won't have checked that the stack is deep enough for a
1580 // leaf function /after/ re-entry, and the prologue for the leaf
1581 // function will not make a check.
1582 if (f
->isPhpLeafFn()) {
1583 // Check both the native stack and VM stack for overflow.
1584 checkStack(vmStack(), f
, kNumActRecCells
);
1586 // invokePseudoMain() must always check the native stack for overflow no
1591 // Reserve space for ActRec.
1592 for (auto i
= kNumActRecCells
; i
> 0; --i
) vmStack().pushUninit();
1594 auto const doEnterVM
= [&] (ActRec
* ar
) {
1595 enterVMAtPseudoMain(ar
);
1598 return invokeFuncImpl(f
, thiz
, cls
, 0, doEnterVM
);
1601 TypedValue
ExecutionContext::invokeFunc(const Func
* f
,
1602 const Variant
& args_
,
1603 ObjectData
* thiz
/* = NULL */,
1604 Class
* cls
/* = NULL */,
1605 bool dynamic
/* = true */,
1606 bool checkRefAnnot
/* = false */,
1607 bool allowDynCallNoPointer
1609 Array
&& reifiedGenerics
1613 // We must do a stack overflow check for leaf functions on re-entry,
1614 // because we won't have checked that the stack is deep enough for a
1615 // leaf function /after/ re-entry, and the prologue for the leaf
1616 // function will not make a check.
1617 if (f
->isPhpLeafFn() ||
1618 !(f
->numParams() <= kStackCheckReenterPadding
- kNumActRecCells
)) {
1619 // Check both the native stack and VM stack for overflow, numParams
1620 // is already included in f->maxStackCells().
1621 checkStack(vmStack(), f
, f
->numInOutParams() + kNumActRecCells
);
1623 // invokeFunc() must always check the native stack for overflow no
1628 // Reserve space for inout outputs and ActRec.
1629 for (auto i
= f
->numInOutParams(); i
> 0; --i
) vmStack().pushUninit();
1630 for (auto i
= kNumActRecCells
; i
> 0; --i
) vmStack().pushUninit();
1633 auto const& args
= *args_
.asTypedValue();
1634 assertx(isContainerOrNull(args
));
1635 auto numArgs
= tvIsNull(args
) ? 0 : getContainerSize(args
);
1637 assertx(isContainer(args
));
1638 tvDup(args
, *vmStack().allocC());
1639 numArgs
= prepareUnpackArgs(f
, 0, checkRefAnnot
);
1643 if (dynamic
) callerDynamicCallChecks(f
, allowDynCallNoPointer
);
1645 auto const doEnterVM
= [&] (ActRec
* ar
) {
1646 enterVMAtFunc(ar
, std::move(reifiedGenerics
), f
->takesInOutParams(),
1647 dynamic
, allowDynCallNoPointer
);
1650 return invokeFuncImpl(f
, thiz
, cls
, numArgs
, doEnterVM
);
1653 TypedValue
ExecutionContext::invokeFuncFew(
1655 ExecutionContext::ThisOrClass thisOrCls
,
1657 const TypedValue
* argv
,
1658 bool dynamic
/* = true */,
1659 bool allowDynCallNoPointer
1663 auto& stack
= vmStack();
1665 // See comments in invokeFunc().
1666 if (f
->isPhpLeafFn() ||
1667 !(numArgs
<= kStackCheckReenterPadding
- kNumActRecCells
)) {
1668 checkStack(stack
, f
, f
->numInOutParams() + kNumActRecCells
);
1673 // Reserve space for inout outputs and ActRec.
1674 for (auto i
= f
->numInOutParams(); i
> 0; --i
) stack
.pushUninit();
1675 for (auto i
= kNumActRecCells
; i
> 0; --i
) stack
.pushUninit();
1677 // Push non-variadic arguments.
1678 auto const numParams
= f
->numNonVariadicParams();
1679 for (auto i
= 0; i
< numArgs
&& i
< numParams
; ++i
) {
1680 const TypedValue
*from
= &argv
[i
];
1681 TypedValue
* to
= stack
.allocTV();
1685 // Push variadic arguments.
1686 if (UNLIKELY(numParams
< numArgs
)) {
1687 VArrayInit ai
{numArgs
- numParams
};
1688 for (auto i
= numParams
; i
< numArgs
; ++i
) ai
.append(argv
[i
]);
1689 if (RuntimeOption::EvalHackArrDVArrs
) {
1690 stack
.pushVecNoRc(ai
.create());
1692 stack
.pushArrayNoRc(ai
.create());
1694 numArgs
= numParams
+ 1;
1698 if (dynamic
) callerDynamicCallChecks(f
, allowDynCallNoPointer
);
1700 auto const doEnterVM
= [&] (ActRec
* ar
) {
1701 enterVMAtFunc(ar
, Array(), f
->takesInOutParams(), dynamic
, false);
1704 return invokeFuncImpl(f
,
1707 numArgs
, doEnterVM
);
1710 static void prepareAsyncFuncEntry(ActRec
* enterFnAr
,
1711 Resumable
* resumable
,
1714 assertx(enterFnAr
->func()->isAsync());
1715 assertx(isResumed(enterFnAr
));
1719 // If we're going to unwind, we need to resume at the offset we
1720 // suspended at, so we look up the correct catch trace (not the one
1721 // for the next op).
1722 vmpc() = enterFnAr
->func()->unit()->at(
1724 ? resumable
->suspendOffset()
1725 : resumable
->resumeFromAwaitOffset()
1727 EventHook::FunctionResumeAwait(enterFnAr
);
1730 void ExecutionContext::resumeAsyncFunc(Resumable
* resumable
,
1731 ObjectData
* freeObj
,
1732 const TypedValue awaitResult
) {
1733 assertx(tl_regState
== VMRegState::CLEAN
);
1734 SCOPE_EXIT
{ assertx(tl_regState
== VMRegState::CLEAN
); };
1736 auto fp
= resumable
->actRec();
1738 if (RO::EvalEnableImplicitContext
) {
1739 *ImplicitContext::activeCtx
= [&] {
1740 if (!fp
->func()->isGenerator()) return frame_afwh(fp
)->m_implicitContext
;
1741 auto gen
= frame_async_generator(fp
);
1742 return gen
->getWaitHandle()->m_implicitContext
;
1746 // We don't need to check for space for the ActRec (unlike generally
1747 // in normal re-entry), because the ActRec isn't on the stack.
1748 checkStack(vmStack(), fp
->func(), 0);
1750 TypedValue
* savedSP
= vmStack().top();
1751 tvDup(awaitResult
, *vmStack().allocC());
1753 // decref after awaitResult is on the stack
1756 pushVMState(savedSP
);
1757 SCOPE_EXIT
{ popVMState(); };
1760 prepareAsyncFuncEntry(fp
, resumable
, false);
1762 const bool useJit
= RID().getJit();
1763 if (LIKELY(useJit
&& resumable
->resumeAddr())) {
1764 Stats::inc(Stats::VMEnter
);
1765 jit::enterTC(resumable
->resumeAddr());
1772 void ExecutionContext::resumeAsyncFuncThrow(Resumable
* resumable
,
1773 ObjectData
* freeObj
,
1774 ObjectData
* exception
) {
1776 assertx(exception
->instanceof(SystemLib::s_ThrowableClass
));
1777 assertx(tl_regState
== VMRegState::CLEAN
);
1778 SCOPE_EXIT
{ assertx(tl_regState
== VMRegState::CLEAN
); };
1780 auto fp
= resumable
->actRec();
1782 if (RO::EvalEnableImplicitContext
) {
1783 *ImplicitContext::activeCtx
= [&] {
1784 if (!fp
->func()->isGenerator()) return frame_afwh(fp
)->m_implicitContext
;
1785 auto gen
= frame_async_generator(fp
);
1786 return gen
->getWaitHandle()->m_implicitContext
;
1790 checkStack(vmStack(), fp
->func(), 0);
1792 // decref after we hold reference to the exception
1793 Object
e(exception
);
1796 pushVMState(vmStack().top());
1797 SCOPE_EXIT
{ popVMState(); };
1799 enterVMCustomHandler(fp
, [&] {
1800 prepareAsyncFuncEntry(fp
, resumable
, true);
1801 unwindVM(exception
);
1806 ActRec
* ExecutionContext::getPrevVMState(const ActRec
* fp
,
1807 Offset
* prevPc
/* = NULL */,
1808 TypedValue
** prevSp
/* = NULL */,
1809 bool* fromVMEntry
/* = NULL */,
1810 uint64_t* jitReturnAddr
/* = NULL */) {
1811 if (fp
== nullptr) {
1814 ActRec
* prevFp
= fp
->sfp();
1815 if (LIKELY(prevFp
!= nullptr)) {
1817 if (UNLIKELY(isResumed(fp
))) {
1818 assertx(fp
->func()->isGenerator());
1819 *prevSp
= (TypedValue
*)prevFp
- prevFp
->func()->numSlotsInFrame();
1821 *prevSp
= (TypedValue
*)(fp
+ 1);
1824 if (prevPc
) *prevPc
= prevFp
->func()->base() + fp
->callOffset();
1825 if (fromVMEntry
) *fromVMEntry
= false;
1828 // Linear search from end of m_nestedVMs. In practice, we're probably
1829 // looking for something recently pushed.
1830 int i
= m_nestedVMs
.size() - 1;
1831 ActRec
* firstAR
= vmFirstAR();
1832 while (i
>= 0 && firstAR
!= fp
) {
1833 firstAR
= m_nestedVMs
[i
--].firstAR
;
1835 if (i
== -1) return nullptr;
1836 const VMState
& vmstate
= m_nestedVMs
[i
];
1837 prevFp
= vmstate
.fp
;
1839 assertx(prevFp
->func()->unit());
1840 if (prevSp
) *prevSp
= vmstate
.sp
;
1842 *prevPc
= prevFp
->func()->unit()->offsetOf(vmstate
.pc
);
1844 if (fromVMEntry
) *fromVMEntry
= true;
1845 if (jitReturnAddr
) *jitReturnAddr
= (uint64_t)vmstate
.jitReturnAddr
;
1850 Instantiate hoistable classes and functions.
1851 If there is any more work left to do, setup a
1852 new frame ready to execute the pseudomain.
1854 return true iff the pseudomain needs to be executed.
1856 bool ExecutionContext::evalUnit(Unit
* unit
, PC callPC
, PC
& pc
, int funcType
) {
1859 if (unit
->isMergeOnly()) {
1860 Stats::inc(Stats::PseudoMain_Skipped
);
1861 *vmStack().allocTV() = make_tv
<KindOfInt64
>(1);
1865 Stats::inc(Stats::PseudoMain_Executed
);
1867 ActRec
* ar
= vmStack().allocA();
1868 auto const cls
= vmfp()->func()->cls();
1869 auto const hasThis
= cls
&& !vmfp()->func()->isStatic();
1870 auto const func
= unit
->getMain(cls
, hasThis
);
1871 assertx(!func
->isCPPBuiltin());
1874 ar
->setThisOrClass(vmfp()->getThisOrClass());
1875 if (hasThis
) ar
->getThis()->incRefCount();
1881 ar
->setReturn(vmfp(), callPC
, jit::tc::ustubs().retHelper
, false);
1882 pushFrameSlots(func
);
1884 auto prevFp
= vmfp();
1885 if (UNLIKELY(prevFp
->skipFrame())) {
1886 prevFp
= g_context
->getPrevVMStateSkipFrame(prevFp
);
1891 pc
= func
->getEntry();
1893 bool ret
= EventHook::FunctionCall(vmfp(), funcType
);
1895 checkStack(vmStack(), func
, 0);
1899 Variant
ExecutionContext::getEvaledArg(const StringData
* val
,
1900 const String
& namespacedName
,
1901 const Unit
* funcUnit
) {
1902 auto key
= StrNR(val
);
1904 if (m_evaledArgs
.get()) {
1905 auto const arg
= m_evaledArgs
.get()->get(key
);
1906 if (arg
.is_init()) return Variant::wrap(arg
);
1911 int pos
= namespacedName
.rfind('\\');
1913 auto ns
= namespacedName
.substr(0, pos
);
1914 code
= s_hh
+ s_namespace
+ ns
+ s_curly_start
+ s_function_start
+
1915 s_evaluate_default_argument
+ s_function_middle
+ key
+
1916 s_semicolon
+ s_curly_end
+ s_curly_end
;
1917 funcName
= ns
+ "\\" + s_evaluate_default_argument
;
1919 code
= s_hh
+ s_function_start
+ s_evaluate_default_argument
+
1920 s_function_middle
+ key
+ s_semicolon
+ s_curly_end
;
1921 funcName
= s_evaluate_default_argument
;
1924 Unit
* unit
= compileEvalString(code
.get());
1925 assertx(unit
!= nullptr);
1926 unit
->setInterpretOnly();
1928 // The evaluate_default_argument function should be the last one
1929 auto func
= *(unit
->funcs().end() - 1);
1930 assertx(func
->name()->equal(funcName
.get()) &&
1931 "We expecting the evaluate_default_argument func");
1933 // Default arg values are not currently allowed to depend on class context.
1934 auto v
= Variant::attach(
1935 g_context
->invokeFuncFew(func
, nullptr, 0, nullptr, true, true)
1937 auto const lv
= m_evaledArgs
.lvalForce(key
, AccessFlags::Key
);
1938 tvSet(*v
.asTypedValue(), lv
);
1939 return Variant::wrap(lv
.tv());
1942 void ExecutionContext::recordLastError(const Exception
& e
, int errnum
) {
1943 m_lastError
= String(e
.getMessage());
1944 m_lastErrorNum
= errnum
;
1945 m_lastErrorPath
= String::attach(getContainingFileName());
1946 m_lastErrorLine
= getLine();
1947 if (auto const ee
= dynamic_cast<const ExtendedException
*>(&e
)) {
1948 m_lastErrorPath
= ee
->getFileAndLine().first
;
1949 m_lastErrorLine
= ee
->getFileAndLine().second
;
1953 void ExecutionContext::clearLastError() {
1954 m_lastError
= String();
1956 m_lastErrorPath
= staticEmptyString();
1957 m_lastErrorLine
= 0;
1960 void ExecutionContext::enqueueAPCDeferredExpire(const String
& key
) {
1961 auto keyStr
= key
.get();
1962 keyStr
->incRefCount();
1963 m_apcDeferredExpire
.push_back(keyStr
);
1966 void ExecutionContext::enqueueAPCHandle(APCHandle
* handle
, size_t size
) {
1967 assertx(handle
->isUncounted());
1968 m_apcHandles
.push_back(handle
);
1969 m_apcMemSize
+= size
;
1972 // Treadmill solution for the SharedVariant memory management
1974 struct FreedAPCHandle
{
1975 explicit FreedAPCHandle(std::vector
<APCHandle
*>&& shandles
, size_t size
)
1976 : m_memSize(size
), m_apcHandles(std::move(shandles
))
1979 for (auto handle
: m_apcHandles
) {
1980 APCTypedValue::fromHandle(handle
)->deleteUncounted();
1982 APCStats::getAPCStats().removePendingDelete(m_memSize
);
1986 std::vector
<APCHandle
*> m_apcHandles
;
1990 void ExecutionContext::manageAPCHandle() {
1991 assertx(apcExtension::UseUncounted
|| m_apcHandles
.size() == 0);
1992 if (m_apcHandles
.size() > 0) {
1994 FreedAPCHandle(std::move(m_apcHandles
), m_apcMemSize
)
1996 APCStats::getAPCStats().addPendingDelete(m_apcMemSize
);
2000 // Evaled units have a footprint in the TC and translation metadata. The
2001 // applications we care about tend to have few, short, stereotyped evals,
2002 // where the same code keeps getting eval'ed over and over again; so we
2003 // keep around units for each eval'ed string, so that the TC space isn't
2004 // wasted on each eval.
2005 typedef RankedCHM
<StringData
*, HPHP::Unit
*,
2006 StringDataHashCompare
,
2007 RankEvaledUnits
> EvaledUnitsMap
;
2008 static EvaledUnitsMap s_evaledUnits
;
2009 Unit
* ExecutionContext::compileEvalString(
2011 const char* evalFilename
/* = nullptr */) {
2012 EvaledUnitsMap::accessor acc
;
2013 // Promote this to a static string; otherwise it may get swept
2015 code
= makeStaticString(code
);
2016 if (s_evaledUnits
.insert(acc
, code
)) {
2017 acc
->second
= compile_string(
2021 Native::s_noNativeFuncs
,
2022 getRepoOptionsForCurrentFrame()
2028 ExecutionContext::EvaluationResult
2029 ExecutionContext::evalPHPDebugger(StringData
* code
, int frame
) {
2030 // The code has "<?hh" prepended already
2031 auto unit
= compile_debugger_string(code
->data(), code
->size(),
2032 getRepoOptionsForCurrentFrame());
2033 if (unit
== nullptr) {
2034 raise_error("Syntax error");
2035 return {true, init_null_variant
, "Syntax error"};
2038 return evalPHPDebugger(unit
, frame
);
2042 s_DebuggerMainAttr("__DebuggerMain"),
2043 s_uninitClsName("__uninitSentinel");
2045 ExecutionContext::EvaluationResult
2046 ExecutionContext::evalPHPDebugger(Unit
* unit
, int frame
) {
2047 always_assert(!RuntimeOption::RepoAuthoritative
);
2049 // Do not JIT this unit, we are using it exactly once.
2050 unit
->setInterpretOnly();
2054 auto fp
= getFrameAtDepthForDebuggerUnsafe(frame
);
2056 // Continue walking up the stack until we find a frame that can have
2057 // a variable environment context attached to it, or we run out out frames.
2058 while (fp
&& (fp
->skipFrame() || fp
->isInlined())) {
2059 fp
= getPrevVMStateSkipFrame(fp
);
2062 ObjectData
*this_
= nullptr;
2063 // NB: the ActRec and function within the AR may have different classes. The
2064 // class in the ActRec is the type used when invoking the function (i.e.,
2065 // Derived in Derived::Foo()) while the class obtained from the function is
2066 // the type that declared the function Foo, which may be Base. We need both
2067 // the class to match any object that this function may have been invoked on,
2068 // and we need the class from the function execution is stopped in.
2069 Class
*frameClass
= nullptr;
2070 Class
*functionClass
= nullptr;
2072 functionClass
= fp
->m_func
->cls();
2073 if (functionClass
) {
2074 if (fp
->hasThis()) {
2075 this_
= fp
->getThis();
2076 } else if (fp
->hasClass()) {
2077 frameClass
= fp
->getClass();
2080 phpDebuggerEvalHook(fp
->m_func
);
2083 const static StaticString
s_cppException("Hit an exception");
2084 const static StaticString
s_phpException("Hit a php exception");
2085 const static StaticString
s_exit("Hit exit");
2086 const static StaticString
s_fatal("Hit fatal");
2087 std::ostringstream errorString
;
2090 // Find a suitable PC to use when switching to the target frame. If the target
2091 // is the current frame, this is just vmpc(). For other cases, this will
2092 // generally be the address of a call from that frame's function. If we can't
2093 // find the target frame (because it lies deeper in the stack), then just use
2094 // the target frame's func's entry point.
2095 auto const findSuitablePC
= [this](const ActRec
* target
){
2096 if (auto fp
= vmfp()) {
2097 if (fp
== target
) return vmpc();
2099 auto prevFp
= getPrevVMState(fp
);
2101 if (prevFp
== target
) {
2102 return prevFp
->func()->getEntry() + fp
->callOffset();
2107 return target
->func()->getEntry();
2111 // Start with the correct parent FP so that VarEnv can properly exitFP().
2112 // Note that if the same VarEnv is used across multiple frames, the most
2113 // recent FP must be used. This can happen if we are trying to debug
2114 // an eval() call or a call issued by debugger itself.
2116 // We also need to change vmpc() to match, since we assert in a few places
2117 // that the vmpc() lies within vmfp()'s code.
2118 auto savedFP
= vmfp();
2119 auto savedPC
= vmpc();
2121 vmpc() = findSuitablePC(fp
);
2124 SCOPE_EXIT
{ vmpc() = savedPC
; vmfp() = savedFP
; };
2127 unit
->getMain(functionClass
, this_
),
2132 enum VarAction
{ StoreFrame
, StoreEnv
};
2134 auto const uninit_cls
= Unit::loadClass(s_uninitClsName
.get());
2136 auto& env
= m_debuggerEnv
;
2138 auto const ctx
= fp
? fp
->func()->cls() : nullptr;
2139 auto const f
= [&] () -> Func
* {
2140 for (auto orig_f
: unit
->funcs()) {
2141 if (orig_f
->userAttributes().count(s_DebuggerMainAttr
.get())) {
2142 auto const f
= ctx
? orig_f
->clone(ctx
) : orig_f
;
2146 if (fp
&& fp
->hasThis()) {
2147 f
->setAttrs(Attr(f
->attrs() & ~AttrStatic
));
2148 } else if (fp
&& fp
->hasClass()) {
2149 f
->setAttrs(Attr(f
->attrs() | AttrStatic
));
2152 assertx(f
->numParams() >= 1 && f
->numParams() == f
->numInOutParams());
2160 VArrayInit args
{f
->numParams()};
2161 std::vector
<VarAction
> actions
{f
->numParams(), StoreEnv
};
2162 std::vector
<Id
> frameIds
;
2163 frameIds
.resize(f
->numParams(), 0);
2164 auto const appendUninit
= [&] {
2165 args
.append(make_tv
<KindOfObject
>(Object
{uninit_cls
}.detach()));
2167 for (Id id
= 0; id
< f
->numParams() - 1; id
++) {
2168 assertx(id
< f
->numNamedLocals());
2169 assertx(f
->params()[id
].isInOut());
2171 auto const idx
= fp
->func()->lookupVarId(f
->localVarName(id
));
2172 if (idx
!= kInvalidId
) {
2173 Variant var
{tvAsCVarRef(*frame_local(fp
, idx
))};
2174 if (var
.isInitialized()) args
.append(var
);
2175 else appendUninit();
2176 actions
[id
] = StoreFrame
;
2181 auto const val
= env
.lookup(StrNR
{f
->localVarName(id
)});
2182 if (val
.is_init()) args
.append(val
);
2183 else appendUninit();
2185 args
.append(make_tv
<KindOfNull
>()); // $__debugger_exn$output
2187 auto const obj
= ctx
&& fp
->hasThis() ? fp
->getThis() : nullptr;
2188 auto const cls
= ctx
&& fp
->hasClass() ? fp
->getClass() : nullptr;
2189 auto const arr_tv
= invokeFunc(f
, args
.toArray(), obj
, cls
, false);
2190 assertx(isArrayLikeType(type(arr_tv
)));
2191 assertx(val(arr_tv
).parr
->size() == f
->numParams() + 1);
2192 Array arr
= Array::attach(val(arr_tv
).parr
);
2193 for (Id id
= 0; id
< f
->numParams() - 1; id
++) {
2194 auto const tv
= arr
.lookup(id
+ 1);
2195 if (isObjectType(type(tv
)) &&
2196 val(tv
).pobj
->instanceof(uninit_cls
)) {
2197 switch (actions
[id
]) {
2199 variant_ref
{frame_local(fp
, frameIds
[id
])}.unset();
2202 env
.remove(StrNR
{f
->localVarName(id
)});
2207 switch (actions
[id
]) {
2209 variant_ref
{frame_local(fp
, frameIds
[id
])} = arr
[id
+ 1];
2212 env
.set(StrNR
{f
->localVarName(id
)}, tv
);
2216 auto const exn
= arr
[int64_t(f
->numParams())];
2217 if (exn
.isObject()) {
2218 assertx(exn
.toObject().instanceof("Throwable"));
2219 throw_object(exn
.toObject());
2221 assertx(exn
.isNull());
2223 return {false, arr
[0], ""};
2224 } catch (FatalErrorException
& e
) {
2225 errorString
<< s_fatal
.data();
2226 errorString
<< " : ";
2227 errorString
<< e
.getMessage().c_str();
2228 errorString
<< "\n";
2229 stack
= ExtendedLogger::StringOfStackTrace(e
.getBacktrace());
2230 } catch (ExitException
& e
) {
2231 errorString
<< s_exit
.data();
2232 errorString
<< " : ";
2233 errorString
<< *rl_exit_code
;
2234 } catch (Eval::DebuggerException
& e
) {
2235 } catch (Exception
& e
) {
2236 errorString
<< s_cppException
.data();
2237 errorString
<< " : ";
2238 errorString
<< e
.getMessage().c_str();
2239 ExtendedException
* ee
= dynamic_cast<ExtendedException
*>(&e
);
2241 errorString
<< "\n";
2242 stack
= ExtendedLogger::StringOfStackTrace(ee
->getBacktrace());
2244 } catch (Object
&e
) {
2245 errorString
<< s_phpException
.data();
2246 errorString
<< " : ";
2248 errorString
<< throwable_to_string(e
.get()).data();
2250 errorString
<< e
->getVMClass()->name()->data();
2253 errorString
<< s_cppException
.data();
2256 auto errorStr
= errorString
.str();
2257 g_context
->write(errorStr
);
2258 if (!stack
.empty()) {
2259 g_context
->write(stack
.c_str());
2262 return {true, init_null_variant
, errorStr
};
2265 void ExecutionContext::enterDebuggerDummyEnv() {
2266 static Unit
* s_debuggerDummy
= compile_debugger_string(
2267 "<?hh", 4, RepoOptions::defaults()
2269 // Ensure that the VM stack is completely empty (vmfp() should be null)
2270 // and that we're not in a nested VM (reentrancy)
2271 assertx(vmfp() == nullptr);
2272 assertx(m_nestedVMs
.size() == 0);
2273 assertx(m_nesting
== 0);
2274 assertx(vmStack().count() == 0);
2275 ActRec
* ar
= vmStack().allocA();
2276 ar
->m_func
= s_debuggerDummy
->getMain(nullptr, false);
2279 ar
->setReturnVMExit();
2281 vmpc() = s_debuggerDummy
->entry();
2285 void ExecutionContext::exitDebuggerDummyEnv() {
2286 assertx(m_globalNVTable
);
2287 // Ensure that vmfp() is valid
2288 assertx(vmfp() != nullptr);
2289 // Ensure that vmfp() points to the only frame on the call stack.
2290 // In other words, make sure there are no VM frames directly below
2291 // this one and that we are not in a nested VM (reentrancy)
2292 assertx(!vmfp()->sfp());
2293 assertx(m_nestedVMs
.size() == 0);
2294 assertx(m_nesting
== 0);
2295 // Teardown the frame we erected by enterDebuggerDummyEnv()
2296 const Func
* func
= vmfp()->m_func
;
2298 vmfp()->setLocalsDecRefd();
2299 frame_free_locals_no_hook(vmfp());
2301 vmStack().ndiscard(func
->numSlotsInFrame());
2302 vmStack().discardAR();
2303 // After tearing down this frame, the VM stack should be completely empty
2304 assertx(vmStack().count() == 0);
2309 ThrowAllErrorsSetter::ThrowAllErrorsSetter() {
2310 m_throwAllErrors
= g_context
->getThrowAllErrors();
2311 g_context
->setThrowAllErrors(true);
2314 ThrowAllErrorsSetter::~ThrowAllErrorsSetter() {
2315 g_context
->setThrowAllErrors(m_throwAllErrors
);