2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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/program-functions.h"
18 #include "hphp/runtime/base/arch.h"
19 #include "hphp/runtime/base/array-init.h"
20 #include "hphp/runtime/base/backtrace.h"
21 #include "hphp/runtime/base/builtin-functions.h"
22 #include "hphp/runtime/base/code-coverage.h"
23 #include "hphp/runtime/base/config.h"
24 #include "hphp/runtime/base/execution-context.h"
25 #include "hphp/runtime/base/extended-logger.h"
26 #include "hphp/runtime/base/externals.h"
27 #include "hphp/runtime/base/file-util.h"
28 #include "hphp/runtime/base/hhprof.h"
29 #include "hphp/runtime/base/ini-setting.h"
30 #include "hphp/runtime/base/memory-manager.h"
31 #include "hphp/runtime/base/php-globals.h"
32 #include "hphp/runtime/base/plain-file.h"
33 #include "hphp/runtime/base/runtime-option.h"
34 #include "hphp/runtime/base/simple-counter.h"
35 #include "hphp/runtime/base/stat-cache.h"
36 #include "hphp/runtime/base/stream-wrapper-registry.h"
37 #include "hphp/runtime/base/surprise-flags.h"
38 #include "hphp/runtime/base/init-fini-node.h"
39 #include "hphp/runtime/base/type-conversions.h"
40 #include "hphp/runtime/base/unit-cache.h"
41 #include "hphp/runtime/base/thread-safe-setlocale.h"
42 #include "hphp/runtime/base/variable-serializer.h"
43 #include "hphp/runtime/base/zend-math.h"
44 #include "hphp/runtime/base/zend-strtod.h"
45 #include "hphp/runtime/debugger/debugger.h"
46 #include "hphp/runtime/debugger/debugger_client.h"
47 #include "hphp/runtime/debugger/debugger_hook_handler.h"
48 #include "hphp/runtime/ext/apc/ext_apc.h"
49 #include "hphp/runtime/ext/xhprof/ext_xhprof.h"
50 #include "hphp/runtime/ext/extension-registry.h"
51 #include "hphp/runtime/ext/json/ext_json.h"
52 #include "hphp/runtime/ext/std/ext_std_file.h"
53 #include "hphp/runtime/ext/std/ext_std_function.h"
54 #include "hphp/runtime/ext/std/ext_std_variable.h"
55 #include "hphp/runtime/ext/xenon/ext_xenon.h"
56 #include "hphp/runtime/server/admin-request-handler.h"
57 #include "hphp/runtime/server/http-request-handler.h"
58 #include "hphp/runtime/server/log-writer.h"
59 #include "hphp/runtime/server/rpc-request-handler.h"
60 #include "hphp/runtime/server/http-server.h"
61 #include "hphp/runtime/server/pagelet-server.h"
62 #include "hphp/runtime/server/replay-transport.h"
63 #include "hphp/runtime/server/server-note.h"
64 #include "hphp/runtime/server/server-stats.h"
65 #include "hphp/runtime/server/xbox-server.h"
66 #include "hphp/runtime/vm/debug/debug.h"
67 #include "hphp/runtime/vm/jit/code-cache.h"
68 #include "hphp/runtime/vm/jit/mc-generator.h"
69 #include "hphp/runtime/vm/jit/translator.h"
70 #include "hphp/runtime/vm/repo.h"
71 #include "hphp/runtime/vm/runtime.h"
72 #include "hphp/runtime/vm/treadmill.h"
75 #include "hphp/util/abi-cxx.h"
76 #include "hphp/util/boot_timer.h"
77 #include "hphp/util/compatibility.h"
78 #include "hphp/util/capability.h"
79 #include "hphp/util/embedded-data.h"
80 #include "hphp/util/hardware-counter.h"
82 #include "hphp/util/light-process.h"
84 #include "hphp/util/process.h"
85 #include "hphp/util/build-info.h"
86 #include "hphp/util/service-data.h"
87 #include "hphp/util/shm-counter.h"
88 #include "hphp/util/stack-trace.h"
89 #include "hphp/util/timer.h"
91 #include <folly/Range.h>
92 #include <folly/Portability.h>
93 #include <folly/Singleton.h>
94 #include <folly/portability/Environment.h>
96 #include <boost/algorithm/string/replace.hpp>
97 #include <boost/program_options/options_description.hpp>
98 #include <boost/program_options/positional_options.hpp>
99 #include <boost/program_options/variables_map.hpp>
100 #include <boost/filesystem.hpp>
103 #include <oniguruma.h>
105 #include <libxml/parser.h>
116 #if (defined(__CYGWIN__) || defined(__MINGW__) || defined(_MSC_VER))
121 using namespace boost::program_options
;
124 constexpr auto MAX_INPUT_NESTING_LEVEL
= 64;
128 ///////////////////////////////////////////////////////////////////////////////
129 // Forward declarations.
131 void initialize_repo();
134 * XXX: VM process initialization is handled through a function
135 * pointer so libhphp_runtime.a can be linked into programs that don't
136 * actually initialize the VM.
138 void (*g_vmProcessInit
)();
140 void timezone_init();
145 ///////////////////////////////////////////////////////////////////////////////
148 struct ProgramOptions
{
150 std::vector
<std::string
> config
;
151 std::vector
<std::string
> confStrings
;
152 std::vector
<std::string
> iniStrings
;
162 bool noSafeAccessCheck
;
163 std::vector
<std::string
> args
;
165 std::string instanceId
;
170 Eval::DebuggerClientOptions debugger_options
;
174 StartTime() : startTime(time(nullptr)) {}
178 static StartTime s_startTime
;
179 static std::string tempFile
;
180 std::vector
<std::string
> s_config_files
;
182 time_t start_time() {
183 return s_startTime
.startTime
;
189 s_HHVM_JIT("HHVM_JIT"),
190 s_HHVM_ARCH("HHVM_ARCH"),
191 s_REQUEST_START_TIME("REQUEST_START_TIME"),
192 s_REQUEST_TIME("REQUEST_TIME"),
193 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
194 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
195 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
196 s_SCRIPT_NAME("SCRIPT_NAME"),
197 s_PHP_SELF("PHP_SELF"),
201 s_HOSTNAME("HOSTNAME"),
202 s__SERVER("_SERVER"),
205 static __thread
bool s_sessionInitialized
{false};
207 static void process_cmd_arguments(int argc
, char **argv
) {
208 php_global_set(s_argc
, Variant(argc
));
209 Array
argvArray(staticEmptyArray());
210 for (int i
= 0; i
< argc
; i
++) {
211 argvArray
.append(String(argv
[i
]));
213 php_global_set(s_argv
, argvArray
);
216 void process_env_variables(Array
& variables
) {
217 for (auto& kv
: RuntimeOption::EnvVariables
) {
218 variables
.set(String(kv
.first
), String(kv
.second
));
220 for (char **env
= environ
; env
&& *env
; env
++) {
221 char *p
= strchr(*env
, '=');
223 String
name(*env
, p
- *env
, CopyString
);
224 register_variable(variables
, (char*)name
.data(),
225 String(p
+ 1, CopyString
));
230 // Handle adding a variable to an array, supporting keys that look
231 // like array expressions (like 'FOO[][key1][k2]').
232 void register_variable(Array
& variables
, char *name
, const Variant
& value
,
233 bool overwrite
/* = true */) {
234 // ignore leading spaces in the variable name
236 while (*var
&& *var
== ' ') {
240 // ensure that we don't have spaces or dots in the variable name
242 bool is_array
= false;
243 char *ip
= nullptr; // index pointer
246 if (*p
== ' ' || *p
== '.') {
248 } else if (*p
== '[') {
255 int var_len
= p
- var
;
257 // empty variable name, or variable name with a space in it
261 // GPC elements holds Variants that are acting as smart pointers to
262 // RefDatas that we've created in the process of a multi-dim key.
263 std::vector
<Variant
> gpc_elements
;
264 if (is_array
) gpc_elements
.reserve(MAX_INPUT_NESTING_LEVEL
);
266 // The array pointer we're currently adding to. If we're doing a
267 // multi-dimensional set, this will point at the m_data.parr inside
268 // of a RefData sometimes (via toArrRef on the variants in
270 Array
* symtable
= &variables
;
273 int index_len
= var_len
;
278 if (++nest_level
> MAX_INPUT_NESTING_LEVEL
) {
279 Logger::Warning("Input variable nesting level exceeded");
292 ip
= strchr(ip
, ']');
294 // PHP variables cannot contain '[' in their names,
295 // so we replace the character with a '_'
296 *(index_s
- 1) = '_';
300 index_len
= strlen(index
);
305 new_idx_len
= strlen(index_s
);
309 auto& val
= symtable
->lvalAt();
310 val
= Array::Create();
311 gpc_elements
.push_back(uninit_null());
312 gpc_elements
.back().assignRef(val
);
314 String
key(index
, index_len
, CopyString
);
315 Variant v
= symtable
->rvalAt(key
);
316 if (v
.isNull() || !v
.isArray()) {
317 symtable
->set(key
, Array::Create());
319 gpc_elements
.push_back(uninit_null());
320 gpc_elements
.back().assignRef(symtable
->lvalAt(key
));
322 symtable
= &gpc_elements
.back().toArrRef();
323 /* ip pointed to the '[' character, now obtain the key */
325 index_len
= new_idx_len
;
338 symtable
->append(value
);
340 String
key(index
, index_len
, CopyString
);
341 if (overwrite
|| !symtable
->exists(key
)) {
342 symtable
->set(key
, value
);
348 enum class ContextOfException
{
354 static void handle_exception_append_bt(std::string
& errorMsg
,
355 const ExtendedException
& e
) {
356 Array bt
= e
.getBacktrace();
358 errorMsg
+= ExtendedLogger::StringOfStackTrace(bt
);
362 void bump_counter_and_rethrow(bool isPsp
) {
365 } catch (const RequestTimeoutException
& e
) {
367 static auto requestTimeoutPSPCounter
= ServiceData::createTimeseries(
368 "requests_timed_out_psp", {ServiceData::StatsType::COUNT
});
369 requestTimeoutPSPCounter
->addValue(1);
370 ServerStats::Log("request.timed_out.psp", 1);
372 static auto requestTimeoutCounter
= ServiceData::createTimeseries(
373 "requests_timed_out_non_psp", {ServiceData::StatsType::COUNT
});
374 requestTimeoutCounter
->addValue(1);
375 ServerStats::Log("request.timed_out.non_psp", 1);
378 } catch (const RequestCPUTimeoutException
& e
) {
380 static auto requestCPUTimeoutPSPCounter
= ServiceData::createTimeseries(
381 "requests_cpu_timed_out_psp", {ServiceData::StatsType::COUNT
});
382 requestCPUTimeoutPSPCounter
->addValue(1);
383 ServerStats::Log("request.cpu_timed_out.psp", 1);
385 static auto requestCPUTimeoutCounter
= ServiceData::createTimeseries(
386 "requests_cpu_timed_out_non_psp", {ServiceData::StatsType::COUNT
});
387 requestCPUTimeoutCounter
->addValue(1);
388 ServerStats::Log("request.cpu_timed_out.non_psp", 1);
391 } catch (const RequestMemoryExceededException
& e
) {
393 static auto requestMemoryExceededPSPCounter
=
394 ServiceData::createTimeseries(
395 "requests_memory_exceeded_psp", {ServiceData::StatsType::COUNT
});
396 requestMemoryExceededPSPCounter
->addValue(1);
397 ServerStats::Log("request.memory_exceeded.psp", 1);
399 static auto requestMemoryExceededCounter
= ServiceData::createTimeseries(
400 "requests_memory_exceeded_non_psp", {ServiceData::StatsType::COUNT
});
401 requestMemoryExceededCounter
->addValue(1);
402 ServerStats::Log("request.memory_exceeded.non_psp", 1);
406 // Capture a pprof (C++) dump when we OOM a request
407 // TODO: (t3753133) Should dump a PHP-instrumented pprof dump here as well
408 jemalloc_pprof_dump("", false);
415 static void handle_exception_helper(bool& ret
,
416 ExecutionContext
* context
,
417 std::string
& errorMsg
,
418 ContextOfException where
,
421 // Clear oom/timeout while handling exception and restore them afterwards.
422 auto& flags
= stackLimitAndSurprise();
423 auto const origFlags
= flags
.fetch_and(~ResourceFlags
) & ResourceFlags
;
426 flags
.fetch_or(origFlags
);
430 bump_counter_and_rethrow(false /* isPsp */);
431 } catch (const Eval::DebuggerException
&e
) {
433 } catch (const ExitException
&e
) {
434 if (where
== ContextOfException::ReqInit
) {
436 } else if (where
!= ContextOfException::Handler
&&
437 !context
->getExitCallback().isNull() &&
438 is_callable(context
->getExitCallback())) {
439 Array stack
= e
.getBacktrace();
440 Array argv
= make_packed_array(ExitException::ExitCode
.load(), stack
);
441 vm_call_user_func(context
->getExitCallback(), argv
);
443 } catch (const PhpFileDoesNotExistException
&e
) {
445 if (where
!= ContextOfException::Handler
) {
446 raise_notice("%s", e
.getMessage().c_str());
448 Logger::Error("%s", e
.getMessage().c_str());
451 handle_exception_append_bt(errorMsg
, e
);
453 } catch (const Exception
&e
) {
455 bool origError
= error
;
456 std::string origErrorMsg
= errorMsg
;
460 if (where
== ContextOfException::Handler
) {
461 errorMsg
= "Exception handler threw an exception: ";
463 errorMsg
+= e
.what();
464 if (where
== ContextOfException::Invoke
) {
465 bool handlerRet
= context
->onFatalError(e
);
469 errorMsg
= origErrorMsg
;
472 Logger::Error("%s", errorMsg
.c_str());
475 const ExtendedException
*ee
= dynamic_cast<const ExtendedException
*>(&e
);
477 handle_exception_append_bt(errorMsg
, *ee
);
480 } catch (const Object
&e
) {
482 bool origError
= error
;
483 auto const origErrorMsg
= errorMsg
;
487 if (where
== ContextOfException::Handler
) {
488 errorMsg
= "Exception handler threw an object exception: ";
491 errorMsg
+= e
.toString().data();
493 errorMsg
+= "(unable to call toString())";
495 if (where
== ContextOfException::Invoke
) {
496 bool handlerRet
= context
->onUnhandledException(e
);
500 errorMsg
= origErrorMsg
;
503 Logger::Error("%s", errorMsg
.c_str());
508 errorMsg
= "(unknown exception was thrown)";
509 Logger::Error("%s", errorMsg
.c_str());
513 static bool hphp_chdir_file(const std::string
& filename
) {
515 String s
= File::TranslatePath(filename
);
516 char *buf
= strndup(s
.data(), s
.size());
517 char *dir
= dirname(buf
);
520 if (File::IsVirtualDirectory(dir
)) {
521 g_context
->setCwd(String(dir
, CopyString
));
526 if ((sb
.st_mode
& S_IFMT
) == S_IFDIR
) {
529 g_context
->setCwd(String(dir
, CopyString
));
538 static void handle_resource_exceeded_exception() {
541 } catch (RequestTimeoutException
&) {
542 setSurpriseFlag(TimedOutFlag
);
543 } catch (RequestCPUTimeoutException
&) {
544 setSurpriseFlag(CPUTimedOutFlag
);
545 } catch (RequestMemoryExceededException
&) {
546 setSurpriseFlag(MemExceededFlag
);
550 void handle_destructor_exception(const char* situation
) {
551 std::string errorMsg
;
555 } catch (ExitException
&e
) {
556 // ExitException is fine, no need to show a warning.
557 TI().setPendingException(e
.clone());
559 } catch (Object
&e
) {
560 // For user exceptions, invoke the user exception handler
561 errorMsg
= situation
;
562 errorMsg
+= " threw an object exception: ";
564 errorMsg
+= e
.toString().data();
566 handle_resource_exceeded_exception();
567 errorMsg
+= "(unable to call toString())";
569 } catch (Exception
&e
) {
570 TI().setPendingException(e
.clone());
571 errorMsg
= situation
;
572 errorMsg
+= " raised a fatal error: ";
573 errorMsg
+= e
.what();
575 errorMsg
= situation
;
576 errorMsg
+= " threw an unknown exception";
578 // For fatal errors and unknown exceptions, we raise a warning.
579 // If there is a user error handler it will be invoked, otherwise
580 // the default error handler will be invoked.
582 raise_warning_unsampled("%s", errorMsg
.c_str());
584 handle_resource_exceeded_exception();
586 // The user error handler fataled or threw an exception,
587 // print out the error message directly to the log
588 Logger::Warning("%s", errorMsg
.c_str());
592 void execute_command_line_begin(int argc
, char **argv
, int xhprof
) {
593 StackTraceNoHeap::AddExtraLogging("ThreadType", "CLI");
595 for (int i
= 0; i
< argc
; i
++) {
599 StackTraceNoHeap::AddExtraLogging("Arguments", args
.c_str());
602 auto const context
= g_context
.getNoCheck();
603 context
->obSetImplicitFlush(true);
605 auto& variablesOrder
= RID().getVariablesOrder();
607 if (variablesOrder
.find('e') != std::string::npos
||
608 variablesOrder
.find('E') != std::string::npos
) {
609 Array
envArr(Array::Create());
610 process_env_variables(envArr
);
611 envArr
.set(s_HPHP
, 1);
612 envArr
.set(s_HHVM
, 1);
613 if (RuntimeOption::EvalJit
) {
614 envArr
.set(s_HHVM_JIT
, 1);
618 envArr
.set(s_HHVM_ARCH
, "x64");
621 envArr
.set(s_HHVM_ARCH
, "arm");
624 envArr
.set(s_HHVM_ARCH
, "ppc64");
627 php_global_set(s__ENV
, envArr
);
630 process_cmd_arguments(argc
, argv
);
632 if (variablesOrder
.find('s') != std::string::npos
||
633 variablesOrder
.find('S') != std::string::npos
) {
634 Array
serverArr(Array::Create());
635 process_env_variables(serverArr
);
637 struct timeval tp
= {0};
639 if (!gettimeofday(&tp
, nullptr)) {
640 now_double
= (double)(tp
.tv_sec
+ tp
.tv_usec
/ 1000000.00);
644 now_double
= (double)now
;
646 String file
= empty_string();
648 file
= String::attach(StringData::Make(argv
[0], CopyString
));
650 serverArr
.set(s_REQUEST_START_TIME
, now
);
651 serverArr
.set(s_REQUEST_TIME
, now
);
652 serverArr
.set(s_REQUEST_TIME_FLOAT
, now_double
);
653 serverArr
.set(s_DOCUMENT_ROOT
, empty_string_variant_ref
);
654 serverArr
.set(s_SCRIPT_FILENAME
, file
);
655 serverArr
.set(s_SCRIPT_NAME
, file
);
656 serverArr
.set(s_PHP_SELF
, file
);
657 serverArr
.set(s_argv
, php_global(s_argv
));
658 serverArr
.set(s_argc
, php_global(s_argc
));
659 serverArr
.set(s_PWD
, g_context
->getCwd());
661 if (RuntimeOption::ServerExecutionMode() &&
662 !gethostname(hostname
, sizeof(hostname
))) {
663 // gethostname may not null-terminate
664 hostname
[sizeof(hostname
) - 1] = '\0';
665 serverArr
.set(s_HOSTNAME
, String(hostname
, CopyString
));
668 for (auto& kv
: RuntimeOption::ServerVariables
) {
669 serverArr
.set(String(kv
.first
.c_str()), String(kv
.second
.c_str()));
672 php_global_set(s__SERVER
, serverArr
);
676 HHVM_FN(xhprof_enable
)(xhprof
, uninit_null().toArray());
679 if (RuntimeOption::RequestTimeoutSeconds
) {
680 RID().setTimeout(RuntimeOption::RequestTimeoutSeconds
);
683 if (RuntimeOption::XenonForceAlwaysOn
) {
684 Xenon::getInstance().surpriseAll();
687 InitFiniNode::GlobalsInit();
688 // Initialize the debugger
689 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestInitHook());
692 void execute_command_line_end(int xhprof
, bool coverage
, const char *program
) {
693 if (RuntimeOption::EvalDumpTC
||
694 RuntimeOption::EvalDumpIR
||
695 RuntimeOption::EvalDumpRegion
) {
696 if (jit::mcg
) jit::mcg
->dumpTC();
699 Variant profileData
= HHVM_FN(xhprof_disable
)();
700 if (!profileData
.isNull()) {
701 HHVM_FN(var_dump
)(HHVM_FN(json_encode
)(HHVM_FN(xhprof_disable
)()));
704 g_context
->onShutdownPostSend(); // runs more php
705 Eval::Debugger::InterruptPSPEnded(program
);
709 if (coverage
&& ti
.m_reqInjectionData
.getCoverage() &&
710 !RuntimeOption::CodeCoverageOutputFile
.empty()) {
711 ti
.m_coverage
->Report(RuntimeOption::CodeCoverageOutputFile
);
715 #if defined(__APPLE__) || defined(__CYGWIN__) || defined(_MSC_VER)
716 const void* __hot_start
= nullptr;
717 const void* __hot_end
= nullptr;
720 #if FACEBOOK && defined USE_SSECRC
721 // Overwrite the functiosn
722 NEVER_INLINE
void copyFunc(void* dst
, void* src
, uint32_t sz
= 64) {
723 if (dst
>= reinterpret_cast<void*>(__hot_start
) &&
724 dst
< reinterpret_cast<void*>(__hot_end
)) {
725 memcpy(dst
, src
, sz
);
726 Logger::Info("Successfully overwrite function at %p.", dst
);
728 Logger::Info("Failed to patch code at %p.", dst
);
732 NEVER_INLINE
void copyHashFuncs() {
734 if (IsSSEHashSupported()) {
735 copyFunc(getMethodPtr(&HPHP::StringData::hashHelper
),
736 reinterpret_cast<void*>(g_hashHelper_crc
), 64);
737 typedef strhash_t (*HashFunc
) (const char*, uint32_t);
738 auto hash_func
= [](HashFunc x
) {
739 return reinterpret_cast<void*>(x
);
741 copyFunc(hash_func(hash_string_cs_unsafe
),
742 hash_func(hash_string_cs_crc
), 48);
743 copyFunc(hash_func(hash_string_cs
),
744 hash_func(hash_string_cs_unaligned_crc
), 64);
745 copyFunc(hash_func(hash_string_i_unsafe
),
746 hash_func(hash_string_i_crc
), 64);
747 copyFunc(hash_func(hash_string_i
),
748 hash_func(hash_string_i_unaligned_crc
), 128);
755 # define AT_END_OF_TEXT __attribute__((__section__(".stub")))
757 # define AT_END_OF_TEXT
760 static void NEVER_INLINE AT_END_OF_TEXT
__attribute__((__optimize__("2")))
761 hugifyText(char* from
, char* to
) {
762 #if FACEBOOK && !defined FOLLY_SANITIZE_ADDRESS && defined MADV_HUGEPAGE
763 if (from
> to
|| (to
- from
) < sizeof(uint64_t)) {
764 // This shouldn't happen if HHVM is behaving correctly (I think),
765 // but if it does then there is nothing to do and we should bail
766 // out early because the call to wordcpy() below can't handle
767 // zero size or negative sizes.
770 size_t sz
= to
- from
;
771 void* mem
= malloc(sz
);
772 memcpy(mem
, from
, sz
);
774 // This maps out a portion of our executable
775 // We need to be very careful about what we do
776 // until we replace the original code
778 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
779 MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_FIXED
,
781 // This is in glibc, which isn't a problem, except for
782 // the trampoline code in .plt, which we dealt with
783 // in the linker script
784 madvise(from
, sz
, MADV_HUGEPAGE
);
785 // Don't use memcpy because its probably one of the
786 // functions thats been mapped out.
787 // Needs the attribute((optimize("2")) to prevent
788 // g++ from turning this back into memcpy(!)
789 wordcpy((uint64_t*)from
, (uint64_t*)mem
, sz
/ sizeof(uint64_t));
790 // When supported, string hash functions using SSE 4.2 CRC32 instruction will
791 // be used, so we don't have to check every time.
795 mprotect(from
, sz
, PROT_READ
| PROT_EXEC
);
797 mlock(from
, to
- from
);
798 Debug::DebugInfo::setPidMapOverlay(from
, to
);
802 static void pagein_self(void) {
803 unsigned long begin
, end
, inode
, pgoff
;
804 char mapname
[PATH_MAX
];
812 // pad due to the spaces between the inode number and the mapname
813 bufsz
= sizeof(unsigned long) * 4 + sizeof(mapname
) + sizeof(char) * 11 + 100;
814 buf
= (char *)malloc(bufsz
);
818 BootTimer::Block
timer("mapping self");
819 fp
= fopen("/proc/self/maps", "r");
822 if (fgets(buf
, bufsz
, fp
) == 0)
824 r
= sscanf(buf
, "%lx-%lx %4s %lx %5s %ld %s",
825 &begin
, &end
, perm
, &pgoff
, dev
, &inode
, mapname
);
827 // page in read-only segments that correspond to a file on disk
831 access(mapname
, F_OK
) != 0) {
835 auto beginPtr
= (char*)begin
;
836 auto endPtr
= (char*)end
;
837 auto hotStart
= (char*)__hot_start
;
838 auto hotEnd
= (char*)__hot_end
;
839 const size_t hugePageBytes
= 2L * 1024 * 1024;
841 if (mlock(beginPtr
, end
- begin
) == 0) {
842 if (RuntimeOption::EvalMaxHotTextHugePages
> 0 &&
845 hugePagesSupported() &&
846 beginPtr
<= hotStart
&&
849 char* from
= hotStart
- ((intptr_t)hotStart
& (hugePageBytes
- 1));
850 char* to
= hotEnd
+ (hugePageBytes
- 1);
851 to
-= (intptr_t)to
& (hugePageBytes
- 1);
852 const size_t maxHugeHotTextBytes
=
853 RuntimeOption::EvalMaxHotTextHugePages
* hugePageBytes
;
854 if (to
- from
> maxHugeHotTextBytes
) {
855 to
= from
+ maxHugeHotTextBytes
;
857 if (to
< (void*)hugifyText
) {
858 hugifyText(from
, to
);
861 if (!RuntimeOption::LockCodeMemory
) {
862 munlock(beginPtr
, end
- begin
);
871 /* Sets RuntimeOption::ExecutionMode according
872 * to commandline options prior to config load
874 static void set_execution_mode(folly::StringPiece mode
) {
875 if (mode
== "daemon" || mode
== "server" || mode
== "replay") {
876 RuntimeOption::ExecutionMode
= "srv";
877 Logger::Escape
= true;
878 } else if (mode
== "run" || mode
== "debug") {
879 RuntimeOption::ExecutionMode
= "cli";
880 Logger::Escape
= false;
881 } else if (mode
== "translate") {
882 RuntimeOption::ExecutionMode
= "";
883 Logger::Escape
= false;
886 always_assert(false);
890 /* Reads a file into the OS page cache, with rate limiting. */
891 static bool readahead_rate(const char* path
, int64_t mbPerSec
) {
892 int ret
= open(path
, O_RDONLY
);
893 if (ret
< 0) return false;
895 SCOPE_EXIT
{ close(fd
); };
897 constexpr size_t kReadaheadBytes
= 1 << 20;
898 std::unique_ptr
<char[]> buf(new char[kReadaheadBytes
]);
900 auto startTime
= std::chrono::steady_clock::now();
902 ret
= read(fd
, buf
.get(), kReadaheadBytes
);
905 // Unit math: bytes / (MB / seconds) = microseconds
906 auto endTime
= startTime
+ std::chrono::microseconds(total
/ mbPerSec
);
907 auto sleepT
= endTime
- std::chrono::steady_clock::now();
908 // Don't sleep too frequently.
909 if (sleepT
>= std::chrono::seconds(1)) {
910 Logger::Info(folly::sformat(
911 "readahead sleeping {}ms after total {}b",
912 std::chrono::duration_cast
<std::chrono::milliseconds
>(sleepT
).count(),
914 /* sleep override */ std::this_thread::sleep_for(sleepT
);
921 static int start_server(const std::string
&username
, int xhprof
) {
922 InitFiniNode::ServerPreInit();
925 // Before we start the webserver, make sure the entire
926 // binary is paged into memory.
928 BootTimer::mark("pagein_self");
930 set_execution_mode("server");
931 HttpRequestHandler::GetAccessLog().init
932 (RuntimeOption::AccessLogDefaultFormat
, RuntimeOption::AccessLogs
,
934 AdminRequestHandler::GetAccessLog().init
935 (RuntimeOption::AdminLogFormat
, RuntimeOption::AdminLogSymLink
,
936 RuntimeOption::AdminLogFile
,
938 RPCRequestHandler::GetAccessLog().init
939 (RuntimeOption::AccessLogDefaultFormat
, RuntimeOption::RPCLogs
,
941 SCOPE_EXIT
{ HttpRequestHandler::GetAccessLog().flushAllWriters(); };
942 SCOPE_EXIT
{ AdminRequestHandler::GetAccessLog().flushAllWriters(); };
943 SCOPE_EXIT
{ RPCRequestHandler::GetAccessLog().flushAllWriters(); };
944 SCOPE_EXIT
{ Logger::FlushAll(); };
946 #if !defined(SKIP_USER_CHANGE)
947 if (!username
.empty()) {
948 if (Logger::UseCronolog
) {
949 for (const auto& el
: RuntimeOption::ErrorLogs
) {
950 Cronolog::changeOwner(username
, el
.second
.symLink
);
953 Capability::ChangeUnixUser(username
);
954 LightProcess::ChangeUser(username
);
956 Capability::SetDumpable();
959 if (RuntimeOption::ServerInternalWarmupThreads
> 0) {
960 InitFiniNode::WarmupConcurrentStart(
961 RuntimeOption::ServerInternalWarmupThreads
);
964 // Create the HttpServer before any warmup requests to properly
965 // initialize the process
966 HttpServer::Server
= std::make_shared
<HttpServer
>();
969 HHVM_FN(xhprof_enable
)(xhprof
, uninit_null().toArray());
972 std::unique_ptr
<std::thread
> readaheadThread
;
974 if (RuntimeOption::RepoLocalReadaheadRate
> 0 &&
975 !RuntimeOption::RepoLocalPath
.empty()) {
976 readaheadThread
= folly::make_unique
<std::thread
>([&] {
977 BootTimer::Block
timer("Readahead Repo");
978 auto path
= RuntimeOption::RepoLocalPath
.c_str();
979 Logger::Info("readahead %s", path
);
980 const auto mbPerSec
= RuntimeOption::RepoLocalReadaheadRate
;
981 if (!readahead_rate(path
, mbPerSec
)) {
982 Logger::Error("readahead failed: %s", strerror(errno
));
985 if (!RuntimeOption::RepoLocalReadaheadConcurrent
) {
986 // TODO(10152762): Run this concurrently with non-disk warmup.
987 readaheadThread
->join();
988 readaheadThread
.reset();
992 if (RuntimeOption::ServerInternalWarmupThreads
> 0) {
993 BootTimer::Block
timer("concurrentWaitForEnd");
994 InitFiniNode::WarmupConcurrentWaitForEnd();
997 if (RuntimeOption::RepoPreload
) {
998 BootTimer::Block
timer("Preloading Repo");
999 profileWarmupStart();
1004 // If we have any warmup requests, replay them before listening for
1007 Logger::Info("Warming up");
1008 if (!RuntimeOption::EvalJitProfileWarmupRequests
) profileWarmupStart();
1009 SCOPE_EXIT
{ profileWarmupEnd(); };
1010 std::map
<std::string
, int> seen
;
1011 for (auto& file
: RuntimeOption::ServerWarmupRequests
) {
1012 // Take only the last part
1013 folly::StringPiece
f(file
);
1014 auto pos
= f
.rfind('/');
1015 std::string
str(pos
== f
.npos
? file
: f
.subpiece(pos
+ 1).str());
1016 auto count
= seen
[str
];
1017 BootTimer::Block
timer(folly::sformat("warmup:{}:{}", str
, count
++));
1020 HttpRequestHandler
handler(0);
1023 Timer::GetMonotonicTime(start
);
1025 Logger::Info("Replaying warmup request %s", file
.c_str());
1028 rt
.onRequestStart(start
);
1029 rt
.replayInput(Hdf(file
));
1033 Timer::GetMonotonicTime(stop
);
1034 Logger::Info("Finished successfully in %ld seconds",
1035 stop
.tv_sec
- start
.tv_sec
);
1036 } catch (std::exception
& e
) {
1041 Logger::Info("Got exception during warmup: %s", error
.c_str());
1045 BootTimer::mark("warmup");
1047 if (readaheadThread
.get()) {
1048 readaheadThread
->join();
1049 readaheadThread
.reset();
1052 if (RuntimeOption::EvalEnableNuma
) {
1057 if (mallctlWrite
<uint64_t>("epoch", 1, true) == 0 &&
1058 mallctlRead("arenas.narenas", &narenas
, true) == 0 &&
1059 mallctlnametomib("arena.0.purge", mib
, &miblen
) == 0) {
1060 mib
[1] = size_t(narenas
);
1061 mallctlbymib(mib
, miblen
, nullptr, nullptr, nullptr, 0);
1064 enable_numa(RuntimeOption::EvalEnableNumaLocal
);
1065 BootTimer::mark("enable_numa");
1068 HttpServer::Server
->runOrExitProcess();
1069 HttpServer::Server
.reset();
1073 std::string
translate_stack(const char *hexencoded
, bool with_frame_numbers
) {
1074 if (!hexencoded
|| !*hexencoded
) {
1078 StackTrace
st(hexencoded
);
1079 std::vector
<std::shared_ptr
<StackTrace::Frame
>> frames
;
1082 std::ostringstream out
;
1083 for (size_t i
= 0; i
< frames
.size(); i
++) {
1085 if (with_frame_numbers
) {
1086 out
<< "# " << (i
< 10 ? " " : "") << i
<< ' ';
1088 out
<< f
->toString();
1094 ///////////////////////////////////////////////////////////////////////////////
1096 static void prepare_args(int &argc
,
1098 const std::vector
<std::string
> &args
,
1100 argv
= (char **)malloc((args
.size() + 2) * sizeof(char*));
1102 if (file
&& *file
) {
1103 argv
[argc
++] = (char*)file
;
1105 for (int i
= 0; i
< (int)args
.size(); i
++) {
1106 argv
[argc
++] = (char*)args
[i
].c_str();
1108 argv
[argc
] = nullptr;
1111 static int execute_program_impl(int argc
, char **argv
);
1112 int execute_program(int argc
, char **argv
) {
1117 ret_code
= execute_program_impl(argc
, argv
);
1118 } catch (const Exception
&e
) {
1119 Logger::Error("Uncaught exception: %s", e
.what());
1121 } catch (const std::exception
&e
) {
1122 Logger::Error("Uncaught exception: %s", e
.what());
1125 Logger::Error("Uncaught exception: (unknown)");
1128 if (tempFile
.length() && boost::filesystem::exists(tempFile
)) {
1129 boost::filesystem::remove(tempFile
);
1132 if (HttpServer::Server
||
1133 folly::SingletonVault::singleton()->livingSingletonCount()) {
1134 // an exception was thrown that prevented proper shutdown. Its not
1135 // safe to destroy the globals, or run atexit handlers.
1136 // abort() so it shows up as a crash, and we can diagnose/fix the
1145 static bool open_server_log_files() {
1146 bool openedLog
= false;
1147 for (const auto& el
: RuntimeOption::ErrorLogs
) {
1149 const auto& name
= el
.first
;
1150 const auto& errlog
= el
.second
;
1151 if (!errlog
.logFile
.empty()) {
1152 if (errlog
.isPipeOutput()) {
1153 auto output
= popen(errlog
.logFile
.substr(1).c_str(), "w");
1154 ok
= (output
!= nullptr);
1155 Logger::SetOutput(name
, output
, true);
1156 } else if (Logger::UseCronolog
&& errlog
.hasTemplate()) {
1157 auto cronoLog
= Logger::CronoOutput(name
);
1158 always_assert(cronoLog
);
1159 cronoLog
->m_template
= errlog
.logFile
;
1160 cronoLog
->setPeriodicity();
1161 cronoLog
->m_linkName
= errlog
.symLink
;
1163 auto output
= fopen(errlog
.logFile
.c_str(), "a");
1164 ok
= (output
!= nullptr);
1165 Logger::SetOutput(name
, output
, false);
1167 if (!ok
) Logger::Error("Can't open log file: %s", errlog
.logFile
.c_str());
1174 static void close_server_log_files() {
1175 for (const auto& el
: RuntimeOption::ErrorLogs
) {
1176 const auto& logNameAndPipe
= Logger::GetOutput(el
.first
);
1177 const auto& errlog
= el
.second
;
1178 if (logNameAndPipe
.second
) {
1179 pclose(logNameAndPipe
.first
);
1180 } else if (Logger::UseCronolog
&& errlog
.hasTemplate()) {
1181 always_assert(!logNameAndPipe
.first
);
1183 fclose(logNameAndPipe
.first
);
1188 static int compute_hhvm_argc(const options_description
& desc
,
1189 int argc
, char** argv
) {
1195 const auto& vec
= desc
.options();
1196 std::map
<std::string
,ArgCode
> long_options
;
1197 std::map
<std::string
,ArgCode
> short_options
;
1198 // Build lookup maps for the short options and the long options
1199 for (unsigned i
= 0; i
< vec
.size(); ++i
) {
1201 auto long_name
= opt
->long_name();
1202 ArgCode code
= NO_ARG
;
1203 if (opt
->semantic()->max_tokens() == 1) {
1204 if (opt
->semantic()->min_tokens() == 1) {
1205 code
= ARG_REQUIRED
;
1207 code
= ARG_OPTIONAL
;
1210 long_options
[long_name
] = code
;
1211 auto format_name
= opt
->format_name();
1212 if (format_name
.size() >= 2 && format_name
[0] == '-' &&
1213 format_name
[1] != '-') {
1214 auto short_name
= format_name
.substr(1,1);
1215 short_options
[short_name
] = code
;
1218 // Loop over the args
1220 while (pos
< argc
) {
1221 const char* str
= argv
[pos
];
1222 int len
= strlen(str
);
1223 if (len
== 2 && memcmp(str
, "--", 2) == 0) {
1224 // We found "--". All args after this are intended for the
1229 if (len
>= 3 && str
[0] == '-' && str
[1] == '-') {
1230 // Handle long options
1232 std::string
s(str
+2);
1233 auto it
= long_options
.find(s
);
1234 if (it
!= long_options
.end() && it
->second
!= NO_ARG
&& pos
< argc
&&
1235 (it
->second
== ARG_REQUIRED
|| argv
[pos
][0] != '-')) {
1238 } else if (len
>= 2 && str
[0] == '-') {
1239 // Handle short options
1242 s
.append(1, str
[1]);
1243 auto it
= short_options
.find(s
);
1244 if (it
!= short_options
.end() && it
->second
!= 0 && len
== 2 &&
1245 pos
< argc
&& (it
->second
== ARG_REQUIRED
|| argv
[pos
][0] != '-')) {
1249 // We've found a non-option argument. This arg and all args
1250 // that follow are intended for the PHP application
1258 * AsyncFuncImpl defines a minimum C++ stack size but that only applies to
1259 * threads we manually create. When the main thread will be executing PHP
1260 * rather than just managing a server, make sure its stack is big enough.
1262 static void set_stack_size() {
1264 if (getrlimit(RLIMIT_STACK
, &rlim
) != 0) return;
1266 if (rlim
.rlim_cur
< AsyncFuncImpl::kStackSizeMinimum
1268 || rlim
.rlim_cur
== RLIM_INFINITY
1272 Logger::Error("stack limit too small, use peflags -x to increase %zd\n",
1273 AsyncFuncImpl::kStackSizeMinimum
);
1275 rlim
.rlim_cur
= AsyncFuncImpl::kStackSizeMinimum
;
1276 if (setrlimit(RLIMIT_STACK
, &rlim
)) {
1277 Logger::Error("failed to set stack limit to %zd\n",
1278 AsyncFuncImpl::kStackSizeMinimum
);
1284 #if defined(BOOST_VERSION) && BOOST_VERSION <= 105400
1285 std::string
get_right_option_name(const basic_parsed_options
<char>& opts
,
1286 std::string
& wrong_name
) {
1287 // Remove any - from the wrong name for better comparing
1288 // since it will probably come prepended with --
1290 std::remove(wrong_name
.begin(), wrong_name
.end(), '-'), wrong_name
.end());
1291 for (basic_option
<char> opt
: opts
.options
) {
1292 std::string s_opt
= opt
.string_key
;
1293 // We are only dealing with options that have a - in them.
1294 if (s_opt
.find("-") != std::string::npos
) {
1295 if (s_opt
.find(wrong_name
) != std::string::npos
) {
1305 // Note that confusingly there are two different implementations
1309 // hphp/runtime/ext_zend_compat/php-src/Zend/zend_strtod.h
1310 // does not wrap with HPHP namespace, and implements
1311 // functionality required by the zend extension compatibility layer.
1312 // Empirically, this zend_strtod.h file can't be included because
1313 // it includes <zend.h> which isn't on any search path when compiling this.
1315 // The zend_startup_strtod from
1316 // hphp/runtime/base/zend-strtod.h
1317 // uses the HPHP namespace, is used for other purposes,
1318 // and predates the EZC extensions.
1320 // Before we can call zend_strtod from zend compatibility extensions,
1321 // we need to initialize it. Since it doesn't seem
1322 // to work to include the .h file, just sleaze declare it here.
1324 // See the related issue https://github.com/facebook/hhvm/issues/5244
1327 static int execute_program_impl(int argc
, char** argv
) {
1328 std::string usage
= "Usage:\n\n ";
1330 usage
+= " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
1333 options_description
desc(usage
.c_str());
1335 ("help", "display this message")
1336 ("version", "display version number")
1337 ("modules", "display modules")
1338 ("php", "emulate the standard php command line")
1339 ("compiler-id", "display the git hash for the compiler")
1340 ("repo-schema", "display the repository schema id")
1341 ("mode,m", value
<std::string
>(&po
.mode
)->default_value("run"),
1342 "run | debug (d) | server (s) | daemon | replay | translate (t)")
1343 ("interactive,a", "Shortcut for --mode debug") // -a is from PHP5
1344 ("config,c", value
<std::vector
<std::string
>>(&po
.config
)->composing(),
1345 "load specified config file")
1347 value
<std::vector
<std::string
>>(&po
.confStrings
)->composing(),
1348 "individual configuration string in a format of name=value, where "
1349 "name can be any valid configuration for a config file")
1350 ("define,d", value
<std::vector
<std::string
>>(&po
.iniStrings
)->composing(),
1351 "define an ini setting in the same format ( foo[=bar] ) as provided in a "
1353 ("no-config", "don't use the default php.ini")
1354 ("port,p", value
<int>(&po
.port
)->default_value(-1),
1355 "start an HTTP server at specified port")
1356 ("port-fd", value
<int>(&po
.portfd
)->default_value(-1),
1357 "use specified fd instead of creating a socket")
1358 ("ssl-port-fd", value
<int>(&po
.sslportfd
)->default_value(-1),
1359 "use specified fd for SSL instead of creating a socket")
1360 ("admin-port", value
<int>(&po
.admin_port
)->default_value(-1),
1361 "start admin listener at specified port")
1362 ("debug-config", value
<std::string
>(&po
.debugger_options
.configFName
),
1363 "load specified debugger config file")
1365 value
<std::string
>(&po
.debugger_options
.host
)->implicit_value("localhost"),
1366 "connect to debugger server at specified address")
1367 ("debug-port", value
<int>(&po
.debugger_options
.port
)->default_value(-1),
1368 "connect to debugger server at specified port")
1369 ("debug-extension", value
<std::string
>(&po
.debugger_options
.extension
),
1370 "PHP file that extends command 'arg'")
1371 ("debug-cmd", value
<std::vector
<std::string
>>(
1372 &po
.debugger_options
.cmds
)->composing(),
1373 "executes this debugger command and returns its output in stdout")
1375 value
<std::string
>(&po
.debugger_options
.sandbox
)->default_value("default"),
1376 "initial sandbox to attach to when debugger is started")
1377 ("user,u", value
<std::string
>(&po
.user
),
1378 "run server under this user account")
1379 ("file,f", value
<std::string
>(&po
.file
),
1380 "execute specified file")
1381 ("lint,l", value
<std::string
>(&po
.lint
),
1382 "lint specified file")
1383 ("show,w", value
<std::string
>(&po
.show
),
1384 "output specified file and do nothing else")
1386 "file specified is temporary and removed after execution")
1387 ("count", value
<int>(&po
.count
)->default_value(1),
1388 "how many times to repeat execution")
1389 ("no-safe-access-check",
1390 value
<bool>(&po
.noSafeAccessCheck
)->default_value(false),
1391 "whether to ignore safe file access check")
1392 ("arg", value
<std::vector
<std::string
>>(&po
.args
)->composing(),
1394 ("extra-header", value
<std::string
>(&Logger::ExtraHeader
),
1395 "extra-header to add to log lines")
1396 ("build-id", value
<std::string
>(&po
.buildId
),
1397 "unique identifier of compiled server code")
1398 ("instance-id", value
<std::string
>(&po
.instanceId
),
1399 "unique identifier of server instance")
1400 ("xhprof-flags", value
<int>(&po
.xhprofFlags
)->default_value(0),
1404 positional_options_description p
;
1408 // Before invoking the boost command line parser, we do a manual pass
1409 // to find the first occurrence of either "--" or a non-option argument
1410 // in order to determine which arguments should be consumed by HHVM and
1411 // which arguments should be passed along to the PHP application. This
1412 // is necessary so that the boost command line parser doesn't choke on
1413 // args intended for the PHP application.
1414 int hhvm_argc
= compute_hhvm_argc(desc
, argc
, argv
);
1415 // Need to have a parent try for opts so I can use opts in the catch of
1416 // one of the sub-tries below.
1418 // Invoke the boost command line parser to parse the args for HHVM.
1419 auto opts
= command_line_parser(hhvm_argc
, argv
)
1422 // If these style options are changed, compute_hhvm_argc() will
1423 // need to be updated appropriately
1424 .style(command_line_style::default_style
&
1425 ~command_line_style::allow_guessing
&
1426 ~command_line_style::allow_sticky
&
1427 ~command_line_style::long_allow_adjacent
)
1430 // Manually append the args for the PHP application.
1432 for (unsigned m
= 0; m
< opts
.options
.size(); ++m
) {
1433 const auto& bo
= opts
.options
[m
];
1434 if (bo
.string_key
== "arg") {
1438 for (unsigned m
= hhvm_argc
; m
< argc
; ++m
) {
1439 std::string str
= argv
[m
];
1440 basic_option
<char> bo
;
1441 bo
.string_key
= "arg";
1442 bo
.position_key
= pos
++;
1443 bo
.value
.push_back(str
);
1444 bo
.original_tokens
.push_back(str
);
1445 bo
.unregistered
= false;
1446 bo
.case_insensitive
= false;
1447 opts
.options
.push_back(bo
);
1449 // Process the options
1452 if (vm
.count("interactive") /* or -a */) {
1455 if (po
.mode
== "d") po
.mode
= "debug";
1456 if (po
.mode
== "s") po
.mode
= "server";
1457 if (po
.mode
== "t") po
.mode
= "translate";
1458 if (po
.mode
== "") po
.mode
= "run";
1459 if (po
.mode
== "daemon" || po
.mode
== "server" || po
.mode
== "replay" ||
1460 po
.mode
== "run" || po
.mode
== "debug"|| po
.mode
== "translate") {
1461 set_execution_mode(po
.mode
);
1463 Logger::Error("Error in command line: invalid mode: %s",
1465 cout
<< desc
<< "\n";
1468 if (po
.config
.empty() && !vm
.count("no-config")) {
1469 auto file_callback
= [&po
] (const char *filename
) {
1470 Logger::Verbose("Using default config file: %s", filename
);
1471 po
.config
.push_back(filename
);
1473 add_default_config_files_globbed(DEFAULT_CONFIG_DIR
"/php*.ini",
1475 add_default_config_files_globbed(DEFAULT_CONFIG_DIR
"/config*.hdf",
1478 // When we upgrade boost, we can remove this and also get rid of the parent
1479 // try statement and move opts back into the original try block
1480 #if defined(BOOST_VERSION) && BOOST_VERSION >= 105000 && BOOST_VERSION <= 105400
1481 } catch (const error_with_option_name
&e
) {
1482 std::string wrong_name
= e
.get_option_name();
1483 std::string right_name
= get_right_option_name(opts
, wrong_name
);
1484 std::string message
= e
.what();
1485 if (right_name
!= "") {
1486 boost::replace_all(message
, wrong_name
, right_name
);
1488 Logger::Error("Error in command line: %s", message
.c_str());
1489 cout
<< desc
<< "\n";
1492 } catch (const error
&e
) {
1493 Logger::Error("Error in command line: %s", e
.what());
1494 cout
<< desc
<< "\n";
1497 Logger::Error("Error in command line.");
1498 cout
<< desc
<< "\n";
1501 } catch (const error
&e
) {
1502 Logger::Error("Error in command line: %s", e
.what());
1503 cout
<< desc
<< "\n";
1506 Logger::Error("Error in command line parsing.");
1507 cout
<< desc
<< "\n";
1510 // reuse -h for help command if possible
1511 if (vm
.count("help") || (vm
.count("debug-host") && po
.mode
!= "debug")) {
1512 cout
<< desc
<< "\n";
1515 if (vm
.count("version")) {
1516 cout
<< "HipHop VM";
1517 cout
<< " " << HHVM_VERSION
;
1518 cout
<< " (" << (debug
? "dbg" : "rel") << ")\n";
1519 cout
<< "Compiler: " << compilerId() << "\n";
1520 cout
<< "Repo schema: " << repoSchemaId() << "\n";
1523 if (vm
.count("modules")) {
1524 Array exts
= ExtensionRegistry::getLoaded();
1525 cout
<< "[PHP Modules]" << "\n";
1526 for (ArrayIter
iter(exts
); iter
; ++iter
) {
1527 cout
<< iter
.second().toString().toCppString() << "\n";
1531 if (vm
.count("compiler-id")) {
1532 cout
<< compilerId() << "\n";
1536 if (vm
.count("repo-schema")) {
1537 cout
<< repoSchemaId() << "\n";
1541 if (!po
.show
.empty()) {
1542 auto f
= req::make
<PlainFile
>();
1543 f
->open(po
.show
, "r");
1545 Logger::Error("Unable to open file %s", po
.show
.c_str());
1553 po
.isTempFile
= vm
.count("temp-file");
1555 // forget the source for systemlib.php unless we are debugging
1556 if (po
.mode
!= "debug") SystemLib::s_source
= "";
1558 // we need to initialize pcre cache table very early
1561 #ifdef ENABLE_ZEND_COMPAT
1563 // Initialize in the zend extension compatibility layer, as needed
1564 // before any calls from legacy zend extensions to zend_strtod. See
1565 // the extern "C" declaration of this function, above.
1567 zend_startup_strtod();
1570 MemoryManager::TlsWrapper::getCheck();
1571 if (RuntimeOption::ServerExecutionMode()) {
1572 // Create the hardware counter before reading options,
1573 // so that the main thread never has inherit set in server
1575 HardwareCounter::s_counter
.getCheck();
1577 std::vector
<std::string
> messages
;
1578 // We want the ini map to be freed after processing and loading the options
1579 // So put this in its own block
1581 IniSettingMap ini
= IniSettingMap();
1583 s_config_files
= po
.config
;
1584 // Start with .hdf and .ini files
1585 for (auto& filename
: s_config_files
) {
1586 if (boost::filesystem::exists(filename
)) {
1587 Config::ParseConfigFile(filename
, ini
, config
);
1590 "The configuration file %s does not exist",
1595 // Now, take care of CLI options and then officially load and bind things
1596 RuntimeOption::Load(ini
, config
, po
.iniStrings
, po
.confStrings
, &messages
);
1597 std::vector
<std::string
> badnodes
;
1598 config
.lint(badnodes
);
1599 for (const auto& badnode
: badnodes
) {
1600 const auto msg
= "Possible bad config node: " + badnode
;
1601 fprintf(stderr
, "%s\n", msg
.c_str());
1602 messages
.push_back(msg
);
1605 std::vector
<int> inherited_fds
;
1606 RuntimeOption::BuildId
= po
.buildId
;
1607 RuntimeOption::InstanceId
= po
.instanceId
;
1608 if (po
.port
!= -1) {
1609 RuntimeOption::ServerPort
= po
.port
;
1611 if (po
.portfd
!= -1) {
1612 RuntimeOption::ServerPortFd
= po
.portfd
;
1613 inherited_fds
.push_back(po
.portfd
);
1615 if (po
.sslportfd
!= -1) {
1616 RuntimeOption::SSLPortFd
= po
.sslportfd
;
1617 inherited_fds
.push_back(po
.sslportfd
);
1619 if (po
.admin_port
!= -1) {
1620 RuntimeOption::AdminServerPort
= po
.admin_port
;
1622 if (po
.noSafeAccessCheck
) {
1623 RuntimeOption::SafeFileAccess
= false;
1625 IniSetting::s_system_settings_are_set
= true;
1626 MM().resetRuntimeOptions();
1628 if (po
.mode
== "daemon") {
1629 if (!open_server_log_files()) {
1630 Logger::Error("Log file not specified under daemon mode.\n\n");
1632 Process::Daemonize();
1633 close_server_log_files();
1636 open_server_log_files();
1637 if (RuntimeOption::ServerExecutionMode()) {
1638 for (auto const& m
: messages
) {
1644 // Defer the initialization of light processes until the log file handle is
1645 // created, so that light processes can log to the right place. If we ever
1646 // lose a light process, stop the server instead of proceeding in an
1648 LightProcess::SetLostChildHandler([](pid_t child
) {
1649 if (!HttpServer::Server
) return;
1650 if (!HttpServer::Server
->isStopped()) {
1651 HttpServer::Server
->stop("lost light process child");
1654 LightProcess::Initialize(RuntimeOption::LightProcessFilePrefix
,
1655 RuntimeOption::LightProcessCount
,
1656 RuntimeOption::EvalRecordSubprocessTimes
,
1660 if (!ShmCounters::initialize(true, Logger::Error
)) {
1661 exit(HPHP_EXIT_FAILURE
);
1663 // Initialize compiler state
1664 compile_file(0, 0, MD5(), 0);
1666 if (!po
.lint
.empty()) {
1667 Logger::LogHeader
= false;
1668 Logger::LogLevel
= Logger::LogInfo
;
1669 Logger::UseCronolog
= false;
1670 Logger::UseLogFile
= true;
1671 // we're linting, reset whatever logger settings and write once to stdout
1672 Logger::ClearThreadLog();
1673 for (auto& el
: RuntimeOption::ErrorLogs
) {
1674 const auto& name
= el
.first
;
1675 Logger::SetTheLogger(name
, nullptr);
1677 Logger::SetTheLogger(Logger::DEFAULT
, new Logger());
1679 if (po
.isTempFile
) {
1683 hphp_process_init();
1685 auto const unit
= lookupUnit(
1686 makeStaticString(po
.lint
.c_str()), "", nullptr);
1687 if (unit
== nullptr) {
1688 throw FileOpenException(po
.lint
);
1690 const StringData
* msg
;
1692 if (unit
->compileTimeFatal(msg
, line
)) {
1693 VMParserFrame parserFrame
;
1694 parserFrame
.filename
= po
.lint
.c_str();
1695 parserFrame
.lineNumber
= line
;
1696 Array bt
= createBacktrace(BacktraceArgs()
1698 .setParserFrame(&parserFrame
));
1699 raise_fatal_error(msg
->data(), bt
);
1701 } catch (FileOpenException
&e
) {
1702 Logger::Error("%s", e
.getMessage().c_str());
1704 } catch (const FatalErrorException
& e
) {
1705 RuntimeOption::CallUserHandlerOnFatals
= false;
1706 RuntimeOption::AlwaysLogUnhandledExceptions
= false;
1707 g_context
->onFatalError(e
);
1710 Logger::Info("No syntax errors detected in %s", po
.lint
.c_str());
1714 if (argc
<= 1 || po
.mode
== "run" || po
.mode
== "debug") {
1717 if (po
.isTempFile
) {
1721 set_execution_mode("run");
1722 /* recreate the hardware counters for the main thread now that we know
1723 * whether to include subprocess times */
1724 HardwareCounter::s_counter
.destroy();
1725 HardwareCounter::s_counter
.getCheck();
1729 prepare_args(new_argc
, new_argv
, po
.args
, po
.file
.c_str());
1731 std::string
const cliFile
= !po
.file
.empty() ? po
.file
:
1732 new_argv
[0] ? new_argv
[0] : "";
1733 if (po
.mode
!= "debug" && cliFile
.empty()) {
1734 std::cerr
<< "Nothing to do. Either pass a .php file to run, or "
1738 Repo::setCliFile(cliFile
);
1741 hphp_process_init();
1748 if (po
.mode
== "debug") {
1749 StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
1750 RuntimeOption::EnableDebugger
= true;
1751 po
.debugger_options
.fileName
= file
;
1752 po
.debugger_options
.user
= po
.user
;
1753 Eval::DebuggerProxyPtr localProxy
=
1754 Eval::Debugger::StartClient(po
.debugger_options
);
1756 Logger::Error("Failed to start debugger client\n\n");
1759 Eval::Debugger::RegisterSandbox(localProxy
->getDummyInfo());
1760 std::shared_ptr
<std::vector
<std::string
>> client_args
;
1761 bool restart
= false;
1765 assert(po
.debugger_options
.fileName
== file
);
1766 execute_command_line_begin(new_argc
, new_argv
, po
.xhprofFlags
);
1767 // Set the proxy for this thread to be the localProxy we just
1768 // created. If we're script debugging, this will be the proxy that
1769 // does all of our work. If we're remote debugging, this proxy will
1770 // go unused until we finally stop it when the user quits the
1772 g_context
->setSandboxId(localProxy
->getDummyInfo().id());
1774 // Systemlib.php is not loaded again, so we need this if we
1775 // are to hit any breakpoints in systemlib.
1776 proxySetBreakPoints(localProxy
.get());
1778 Eval::Debugger::DebuggerSession(po
.debugger_options
, restart
);
1780 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1781 } catch (const Eval::DebuggerRestartException
&e
) {
1782 execute_command_line_end(0, false, nullptr);
1784 if (!e
.m_args
->empty()) {
1785 file
= e
.m_args
->at(0);
1786 po
.debugger_options
.fileName
= file
;
1787 client_args
= e
.m_args
;
1789 prepare_args(new_argc
, new_argv
, *client_args
, nullptr);
1792 } catch (const Eval::DebuggerClientExitException
&e
) {
1793 execute_command_line_end(0, false, nullptr);
1794 break; // end user quitting debugger
1800 for (int i
= 0; i
< po
.count
; i
++) {
1801 execute_command_line_begin(new_argc
, new_argv
, po
.xhprofFlags
);
1803 if (hphp_invoke_simple(file
, false /* warmup only */)) {
1804 ret
= ExitException::ExitCode
;
1806 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1811 hphp_process_exit();
1816 if (po
.mode
== "daemon" || po
.mode
== "server") {
1817 if (!po
.user
.empty()) RuntimeOption::ServerUser
= po
.user
;
1818 return start_server(RuntimeOption::ServerUser
, po
.xhprofFlags
);
1821 if (po
.mode
== "replay" && !po
.args
.empty()) {
1822 RuntimeOption::RecordInput
= false;
1823 set_execution_mode("server");
1824 HttpServer server
; // so we initialize runtime properly
1825 HttpRequestHandler
handler(0);
1826 for (int i
= 0; i
< po
.count
; i
++) {
1827 for (unsigned int j
= 0; j
< po
.args
.size(); j
++) {
1829 rt
.replayInput(po
.args
[j
].c_str());
1831 printf("%s\n", rt
.getResponse().c_str());
1837 if (po
.mode
== "translate" && !po
.args
.empty()) {
1838 printf("%s", translate_stack(po
.args
[0].c_str()).c_str());
1842 cout
<< desc
<< "\n";
1846 String
canonicalize_path(const String
& p
, const char* root
, int rootLen
) {
1847 String path
= FileUtil::canonicalize(p
);
1848 if (path
.charAt(0) == '/') {
1849 auto const& sourceRoot
= RuntimeOption::SourceRoot
;
1850 int len
= sourceRoot
.size();
1851 if (len
&& strncmp(path
.data(), sourceRoot
.c_str(), len
) == 0) {
1852 return path
.substr(len
);
1854 if (root
&& rootLen
&& strncmp(path
.data(), root
, rootLen
) == 0) {
1855 return path
.substr(rootLen
);
1861 static std::string
systemlib_split(const std::string
& slib
, std::string
* hhas
) {
1862 auto pos
= slib
.find("\n<?hhas\n");
1863 if (pos
!= std::string::npos
) {
1864 if (hhas
) *hhas
= slib
.substr(pos
+ 8);
1865 return slib
.substr(0, pos
);
1870 // Retrieve a systemlib (or mini systemlib) from the
1871 // current executable or another ELF object file.
1873 // Additionally, when retrieving the main systemlib
1874 // from the current executable, honor the
1875 // HHVM_SYSTEMLIB environment variable as an override.
1876 std::string
get_systemlib(std::string
* hhas
,
1877 const std::string
§ion
/*= "systemlib" */,
1878 const std::string
&filename
/*= "" */) {
1879 if (filename
.empty() && section
== "systemlib") {
1880 if (auto const file
= getenv("HHVM_SYSTEMLIB")) {
1881 std::ifstream
ifs(file
);
1883 return systemlib_split(std::string(
1884 std::istreambuf_iterator
<char>(ifs
),
1885 std::istreambuf_iterator
<char>()), hhas
);
1891 if (!get_embedded_data(section
.c_str(), &desc
, filename
)) return "";
1893 auto const data
= read_embedded_data(desc
);
1894 return systemlib_split(data
, hhas
);
1897 ///////////////////////////////////////////////////////////////////////////////
1901 static void on_timeout(int sig
, siginfo_t
* info
, void* context
) {
1902 if (sig
== SIGVTALRM
&& info
&& info
->si_code
== SI_TIMER
) {
1903 auto data
= (RequestTimer
*)info
->si_value
.sival_ptr
;
1907 Xenon::getInstance().onTimer();
1914 * Update constants to their real values and sync some runtime options
1916 static void update_constants_and_options() {
1917 assert(ExtensionRegistry::modulesInitialised());
1918 // If extension constants were used in the ini files (e.g., E_ALL) they
1919 // would have come out as 0 in the previous pass until we load and
1920 // initialize our extensions, which we do in RuntimeOption::Load() via
1921 // ExtensionRegistry::ModuleLoad() and in ExtensionRegistry::ModuleInit()
1922 // in hphp_process_init(). We will re-import and set only the constants that
1923 // have been now bound to their proper value.
1924 IniSettingMap ini
= IniSettingMap();
1925 for (auto& filename
: s_config_files
) {
1926 Config::ParseIniFile(filename
, ini
, true);
1928 // Reset, possibly, some request dependent runtime options based on certain
1929 // setting values. Do this here so we ensure the constants have been loaded
1930 // correctly (e.g., error_reporting E_ALL, etc.)
1932 if (IniSetting::GetSystem("error_reporting", sys
)) {
1933 RuntimeOption::RuntimeErrorReportingLevel
= sys
.toInt64();
1934 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
1936 if (IniSetting::GetSystem("memory_limit", sys
)) {
1937 RID().setMemoryLimit(sys
.toString().toCppString());
1938 RuntimeOption::RequestMemoryMaxBytes
= RID().GetMemoryLimitNumeric();
1942 void hphp_thread_init() {
1943 ServerStats::GetLogger();
1944 zend_get_bigint_data();
1945 zend_get_rand_data();
1947 MemoryManager::TlsWrapper::getCheck();
1949 assert(ThreadInfo::s_threadInfo
.isNull());
1950 ThreadInfo::s_threadInfo
.getCheck()->init();
1952 HardwareCounter::s_counter
.getCheck();
1953 ExtensionRegistry::threadInit();
1954 InitFiniNode::ThreadInit();
1956 // ensure that there's no request-allocated memory
1957 hphp_memory_cleanup();
1960 void hphp_thread_exit() {
1961 InitFiniNode::ThreadFini();
1962 ExtensionRegistry::threadShutdown();
1963 if (!g_context
.isNull()) g_context
.destroy();
1966 void hphp_process_init() {
1967 pthread_attr_t attr
;
1968 // Linux+GNU extension
1969 #if defined(_GNU_SOURCE) && (defined(__linux__) || defined(__CYGWIN__))
1970 pthread_getattr_np(pthread_self(), &attr
);
1972 pthread_attr_init(&attr
);
1974 init_stack_limits(&attr
);
1975 pthread_attr_destroy(&attr
);
1976 BootTimer::mark("pthread_init");
1978 Process::InitProcessStatics();
1979 BootTimer::mark("Process::InitProcessStatics");
1983 // initialize the tzinfo cache.
1985 BootTimer::mark("timezone_init");
1990 struct sigaction action
= {};
1991 action
.sa_sigaction
= on_timeout
;
1992 action
.sa_flags
= SA_SIGINFO
| SA_NODEFER
;
1993 sigaction(SIGVTALRM
, &action
, nullptr);
1995 // start takes milliseconds, Period is a double in seconds
1996 Xenon::getInstance().start(1000 * RuntimeOption::XenonPeriodSeconds
);
1997 BootTimer::mark("xenon");
2000 BootTimer::mark("ClassInfo::Load");
2002 // reinitialize pcre table
2004 BootTimer::mark("pcre_reinit");
2006 // the liboniguruma docs say this isnt needed,
2007 // but the implementation of init is not
2008 // thread safe due to bugs
2010 BootTimer::mark("onig_init");
2012 // simple xml also needs one time init
2014 BootTimer::mark("xmlInitParser");
2016 g_context
.getCheck();
2017 InitFiniNode::ProcessPreInit();
2018 // TODO(9795696): Race in thread map may trigger spurious logging at
2019 // thread exit, so for now, only spawn threads if we're a server.
2020 const uint32_t maxWorkers
= RuntimeOption::ServerExecutionMode() ? 3 : 0;
2021 InitFiniNode::ProcessInitConcurrentStart(maxWorkers
);
2023 InitFiniNode::ProcessInitConcurrentWaitForEnd();
2024 BootTimer::mark("extra_process_init_concurrent_wait");
2027 BootTimer::mark("g_vmProcessInit");
2029 PageletServer::Restart();
2030 BootTimer::mark("PageletServer::Restart");
2031 XboxServer::Restart();
2032 BootTimer::mark("XboxServer::Restart");
2033 Stream::RegisterCoreWrappers();
2034 BootTimer::mark("Stream::RegisterCoreWrappers");
2035 ExtensionRegistry::moduleInit();
2036 BootTimer::mark("ExtensionRegistry::moduleInit");
2038 // Now that constants have been bound we can update options using constants
2039 // in ini files (e.g., E_ALL) and sync some other options
2040 update_constants_and_options();
2042 InitFiniNode::ProcessInit();
2043 BootTimer::mark("extra_process_init");
2045 UnlimitSerializationScope unlimit
;
2046 // TODO(9755792): Add real execution mode for snapshot generation.
2047 if (apcExtension::PrimeLibraryUpgradeDest
!= "") {
2048 Timer
timer(Timer::WallTime
, "optimizeApcPrime");
2049 apc_load(apcExtension::LoadThread
);
2051 apc_load(apcExtension::LoadThread
);
2053 BootTimer::mark("apc_load");
2057 BootTimer::mark("rds::requestExit");
2058 // Reset the preloaded g_context
2059 ExecutionContext
*context
= g_context
.getNoCheck();
2060 context
->~ExecutionContext();
2061 new (context
) ExecutionContext();
2062 BootTimer::mark("ExecutionContext");
2064 // TODO(9755792): Add real execution mode for snapshot generation.
2065 if (apcExtension::PrimeLibraryUpgradeDest
!= "") {
2066 Logger::Info("APC PrimeLibrary upgrade mode completed; exiting.");
2067 hphp_process_exit();
2072 static void handle_exception(bool& ret
, ExecutionContext
* context
,
2073 std::string
& errorMsg
, ContextOfException where
,
2074 bool& error
, bool richErrorMsg
) {
2075 assert(where
== ContextOfException::Invoke
||
2076 where
== ContextOfException::ReqInit
);
2078 handle_exception_helper(ret
, context
, errorMsg
, where
, error
, richErrorMsg
);
2079 } catch (const ExitException
&e
) {
2080 // Got an ExitException during exception handling, handle
2081 // similarly to the case below but don't call obEndAll().
2083 handle_exception_helper(ret
, context
, errorMsg
, ContextOfException::Handler
,
2084 error
, richErrorMsg
);
2085 context
->obEndAll();
2089 static void handle_reqinit_exception(bool &ret
, ExecutionContext
*context
,
2090 std::string
&errorMsg
, bool &error
) {
2091 handle_exception(ret
, context
, errorMsg
, ContextOfException::ReqInit
, error
,
2095 static void handle_invoke_exception(bool &ret
, ExecutionContext
*context
,
2096 std::string
&errorMsg
, bool &error
,
2097 bool richErrorMsg
) {
2098 handle_exception(ret
, context
, errorMsg
, ContextOfException::Invoke
, error
,
2102 static bool hphp_warmup(ExecutionContext
*context
,
2103 const std::string
&reqInitFunc
,
2104 const std::string
&reqInitDoc
, bool &error
) {
2107 std::string errorMsg
;
2109 ServerStatsHelper
ssh("reqinit");
2111 if (!reqInitDoc
.empty()) {
2112 include_impl_invoke(reqInitDoc
, true);
2114 if (!reqInitFunc
.empty()) {
2115 invoke(reqInitFunc
.c_str(), Array());
2117 context
->backupSession();
2119 handle_reqinit_exception(ret
, context
, errorMsg
, error
);
2125 void hphp_session_init() {
2126 assert(!s_sessionInitialized
);
2127 g_context
.getCheck();
2128 AsioSession::Init();
2129 InitFiniNode::RequestInit();
2130 Socket::clearLastError();
2131 TI().onSessionInit();
2132 MM().resetExternalStats();
2134 g_thread_safe_locale_handler
->reset();
2135 Treadmill::startRequest();
2137 #ifdef ENABLE_SIMPLE_COUNTER
2138 SimpleCounter::Enabled
= true;
2139 StackTrace::Enabled
= true;
2142 // Ordering is sensitive; StatCache::requestInit produces work that
2143 // must be done in ExecutionContext::requestInit.
2144 StatCache::requestInit();
2146 g_context
->requestInit();
2147 s_sessionInitialized
= true;
2148 ExtensionRegistry::requestInit();
2151 ExecutionContext
*hphp_context_init() {
2152 return g_context
.getNoCheck();
2155 bool hphp_invoke_simple(const std::string
& filename
, bool warmupOnly
) {
2157 std::string errorMsg
;
2158 return hphp_invoke(g_context
.getNoCheck(), filename
, false, null_array
,
2159 uninit_null(), "", "", error
, errorMsg
,
2162 false /* richErrorMsg */);
2165 bool hphp_invoke(ExecutionContext
*context
, const std::string
&cmd
,
2166 bool func
, const Array
& funcParams
, VRefParam funcRet
,
2167 const std::string
&reqInitFunc
, const std::string
&reqInitDoc
,
2168 bool &error
, std::string
&errorMsg
,
2169 bool once
, bool warmupOnly
,
2170 bool richErrorMsg
) {
2171 bool isServer
= RuntimeOption::ServerExecutionMode();
2174 // Make sure we have the right current working directory within the repo
2175 // based on what server.source_root was set to (current process directory
2176 // being the default)
2177 if (RuntimeOption::RepoAuthoritative
) {
2178 context
->setCwd(RuntimeOption::SourceRoot
);
2183 oldCwd
= context
->getCwd();
2185 if (!hphp_warmup(context
, reqInitFunc
, reqInitDoc
, error
)) {
2186 if (isServer
) context
->setCwd(oldCwd
);
2190 MM().resetCouldOOM(isStandardRequest());
2193 LitstrTable::get().setReading();
2198 ServerStatsHelper
ssh("invoke");
2199 if (!RuntimeOption::AutoPrependFile
.empty() &&
2200 RuntimeOption::AutoPrependFile
!= "none") {
2201 require(RuntimeOption::AutoPrependFile
, false,
2202 context
->getCwd().data(), true);
2205 funcRet
.assignIfRef(invoke(cmd
.c_str(), funcParams
));
2207 if (isServer
) hphp_chdir_file(cmd
);
2208 include_impl_invoke(cmd
.c_str(), once
);
2210 if (!RuntimeOption::AutoAppendFile
.empty() &&
2211 RuntimeOption::AutoAppendFile
!= "none") {
2212 require(RuntimeOption::AutoAppendFile
, false,
2213 context
->getCwd().data(), true);
2216 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
2221 context
->onShutdownPreSend();
2223 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
2226 if (isServer
) context
->setCwd(oldCwd
);
2230 void hphp_context_shutdown() {
2231 // Run shutdown handlers. This may cause user code to run.
2232 g_thread_safe_locale_handler
->reset();
2234 auto const context
= g_context
.getNoCheck();
2235 context
->destructObjects();
2236 context
->onRequestShutdown();
2238 // Shutdown the debugger
2239 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestShutdownHook());
2241 // Extensions could have shutdown handlers
2242 ExtensionRegistry::requestShutdown();
2243 InitFiniNode::RequestFini();
2245 // Extension shutdown could have re-initialized some
2247 context
->onRequestShutdown();
2250 void hphp_context_exit(bool shutdown
/* = true */) {
2252 hphp_context_shutdown();
2255 // Clean up a bunch of request state. No user code after this point.
2256 MemoryManager::setExiting();
2257 auto const context
= g_context
.getNoCheck();
2258 context
->requestExit();
2259 context
->obProtect(false);
2260 context
->obEndAll();
2263 void hphp_memory_cleanup() {
2265 // sweep functions are allowed to access g_context,
2266 // so we can't destroy it yet
2269 // We should never have any registered RequestEventHandlers. If we do
2270 // something after onRequestShutdown registered a RequestEventHandler.
2271 // Its now too late to run the requestShutdown functions, but if we carry
2272 // on, requestInit and requestShutdown will never be called again.
2273 // I considered just clearing the inited flags; which works for some
2274 // RequestEventHandlers - but its a disaster for others. So just fail hard
2276 always_assert(g_context
.isNull() || !g_context
->hasRequestEventHandlers());
2278 // g_context is request allocated, and has some members that need
2279 // cleanup, so destroy it before its too late
2280 g_context
.destroy();
2282 mm
.resetAllocator();
2286 void hphp_session_exit() {
2287 assert(s_sessionInitialized
);
2288 // Server note has to live long enough for the access log to fire.
2289 // RequestLocal is too early.
2290 ServerNote::Reset();
2291 // Similarly, apc strings could be in the ServerNote array, and
2292 // its possible they are scheduled to be destroyed after this request
2294 Treadmill::finishRequest();
2296 TI().onSessionExit();
2299 ServerStatsHelper
ssh("rollback");
2301 hphp_memory_cleanup();
2302 // Do any post-sweep cleanup necessary for global variables
2303 free_global_variables_after_sweep();
2306 assert(MM().empty());
2308 s_sessionInitialized
= false;
2309 s_extra_request_microseconds
= 0;
2312 void hphp_process_exit() {
2313 Xenon::getInstance().stop();
2314 PageletServer::Stop();
2316 // Debugger::Stop() needs an execution context
2317 g_context
.getCheck();
2318 Eval::Debugger::Stop();
2319 g_context
.destroy();
2320 ExtensionRegistry::moduleShutdown();
2322 LightProcess::Close();
2324 InitFiniNode::ProcessFini();
2327 folly::SingletonVault::singleton()->destroyInstances();
2330 bool is_hphp_session_initialized() {
2331 return s_sessionInitialized
;
2334 static struct SetThreadInitFini
{
2335 SetThreadInitFini() {
2336 AsyncFuncImpl::SetThreadInitFunc([](void*) { hphp_thread_init(); },
2338 AsyncFuncImpl::SetThreadFiniFunc([](void*) { hphp_thread_exit(); },
2341 } s_SetThreadInitFini
;
2343 ///////////////////////////////////////////////////////////////////////////////