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/program-functions.h"
18 #include "hphp/runtime/base/array-init.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/builtin-functions.h"
21 #include "hphp/runtime/base/code-coverage.h"
22 #include "hphp/runtime/base/config.h"
23 #include "hphp/runtime/base/execution-context.h"
24 #include "hphp/runtime/base/extended-logger.h"
25 #include "hphp/runtime/base/externals.h"
26 #include "hphp/runtime/base/file-util.h"
27 #include "hphp/runtime/base/hhprof.h"
28 #include "hphp/runtime/base/ini-setting.h"
29 #include "hphp/runtime/base/member-reflection.h"
30 #include "hphp/runtime/base/memory-manager.h"
31 #include "hphp/runtime/base/perf-mem-event.h"
32 #include "hphp/runtime/base/php-globals.h"
33 #include "hphp/runtime/base/plain-file.h"
34 #include "hphp/runtime/base/runtime-error.h"
35 #include "hphp/runtime/base/runtime-option.h"
36 #include "hphp/runtime/base/stat-cache.h"
37 #include "hphp/runtime/base/stream-wrapper-registry.h"
38 #include "hphp/runtime/base/surprise-flags.h"
39 #include "hphp/runtime/base/init-fini-node.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/xdebug/status.h"
56 #include "hphp/runtime/ext/xenon/ext_xenon.h"
57 #include "hphp/runtime/server/admin-request-handler.h"
58 #include "hphp/runtime/server/cli-server.h"
59 #include "hphp/runtime/server/http-request-handler.h"
60 #include "hphp/runtime/server/log-writer.h"
61 #include "hphp/runtime/server/rpc-request-handler.h"
62 #include "hphp/runtime/server/http-server.h"
63 #include "hphp/runtime/server/pagelet-server.h"
64 #include "hphp/runtime/server/replay-transport.h"
65 #include "hphp/runtime/server/server-note.h"
66 #include "hphp/runtime/server/server-stats.h"
67 #include "hphp/runtime/server/xbox-server.h"
68 #include "hphp/runtime/vm/debug/debug.h"
69 #include "hphp/runtime/vm/jit/code-cache.h"
70 #include "hphp/runtime/vm/jit/tc.h"
71 #include "hphp/runtime/vm/jit/mcgen.h"
72 #include "hphp/runtime/vm/jit/translator.h"
73 #include "hphp/runtime/vm/extern-compiler.h"
74 #include "hphp/runtime/vm/repo.h"
75 #include "hphp/runtime/vm/runtime.h"
76 #include "hphp/runtime/vm/treadmill.h"
78 #include "hphp/util/abi-cxx.h"
79 #include "hphp/util/arch.h"
80 #include "hphp/util/boot-stats.h"
81 #include "hphp/util/build-info.h"
82 #include "hphp/util/compatibility.h"
83 #include "hphp/util/capability.h"
84 #include "hphp/util/embedded-data.h"
85 #include "hphp/util/hardware-counter.h"
86 #include "hphp/util/hphp-config.h"
87 #include "hphp/util/kernel-version.h"
89 #include "hphp/util/light-process.h"
91 #include "hphp/util/perf-event.h"
92 #include "hphp/util/process-exec.h"
93 #include "hphp/util/process.h"
94 #include "hphp/util/service-data.h"
95 #include "hphp/util/shm-counter.h"
96 #include "hphp/util/stack-trace.h"
97 #include "hphp/util/timer.h"
98 #include "hphp/util/type-scan.h"
100 #include "hphp/zend/zend-string.h"
102 #include <folly/Random.h>
103 #include <folly/Range.h>
104 #include <folly/Portability.h>
105 #include <folly/Singleton.h>
106 #include <folly/portability/Fcntl.h>
107 #include <folly/portability/Libgen.h>
108 #include <folly/portability/Stdlib.h>
109 #include <folly/portability/Unistd.h>
111 #include <boost/algorithm/string/replace.hpp>
112 #include <boost/program_options/options_description.hpp>
113 #include <boost/program_options/positional_options.hpp>
114 #include <boost/program_options/variables_map.hpp>
115 #include <boost/filesystem.hpp>
117 #include <oniguruma.h>
119 #include <libxml/parser.h>
135 using namespace boost::program_options
;
138 constexpr auto MAX_INPUT_NESTING_LEVEL
= 64;
142 ///////////////////////////////////////////////////////////////////////////////
143 // Forward declarations.
145 void initialize_repo();
148 * XXX: VM process initialization is handled through a function
149 * pointer so libhphp_runtime.a can be linked into programs that don't
150 * actually initialize the VM.
152 void (*g_vmProcessInit
)();
154 void timezone_init();
159 ///////////////////////////////////////////////////////////////////////////////
162 struct ProgramOptions
{
164 std::vector
<std::string
> config
;
165 std::vector
<std::string
> confStrings
;
166 std::vector
<std::string
> iniStrings
;
176 bool noSafeAccessCheck
;
177 std::vector
<std::string
> args
;
179 std::string instanceId
;
184 Eval::DebuggerClientOptions debugger_options
;
188 StartTime() : startTime(time(nullptr)) {}
192 static StartTime s_startTime
;
193 static std::string tempFile
;
194 std::vector
<std::string
> s_config_files
;
195 std::vector
<std::string
> s_ini_strings
;
197 time_t start_time() {
198 return s_startTime
.startTime
;
204 s_HHVM_JIT("HHVM_JIT"),
205 s_HHVM_ARCH("HHVM_ARCH"),
206 s_REQUEST_START_TIME("REQUEST_START_TIME"),
207 s_REQUEST_TIME("REQUEST_TIME"),
208 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
209 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
210 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
211 s_SCRIPT_NAME("SCRIPT_NAME"),
212 s_PHP_SELF("PHP_SELF"),
216 s_HOSTNAME("HOSTNAME"),
217 s__SERVER("_SERVER"),
220 static __thread
bool s_sessionInitialized
{false};
222 static void process_cmd_arguments(int argc
, char **argv
) {
223 php_global_set(s_argc
, Variant(argc
));
224 Array
argvArray(staticEmptyArray());
225 for (int i
= 0; i
< argc
; i
++) {
226 argvArray
.append(String(argv
[i
]));
228 php_global_set(s_argv
, argvArray
);
231 static void process_env_variables(Array
& variables
, char** envp
,
232 std::map
<std::string
, std::string
>& envVariables
) {
233 for (auto& kv
: envVariables
) {
234 variables
.set(String(kv
.first
), String(kv
.second
));
236 for (char **env
= envp
; env
&& *env
; env
++) {
237 char *p
= strchr(*env
, '=');
239 String
name(*env
, p
- *env
, CopyString
);
240 register_variable(variables
, (char*)name
.data(),
241 String(p
+ 1, CopyString
));
246 void process_env_variables(Array
& variables
) {
247 process_env_variables(variables
, environ
, RuntimeOption::EnvVariables
);
250 // Handle adding a variable to an array, supporting keys that look
251 // like array expressions (like 'FOO[][key1][k2]').
252 void register_variable(Array
& variables
, char *name
, const Variant
& value
,
253 bool overwrite
/* = true */) {
254 // ignore leading spaces in the variable name
256 while (*var
&& *var
== ' ') {
260 // ensure that we don't have spaces or dots in the variable name
262 bool is_array
= false;
263 char *ip
= nullptr; // index pointer
266 if (*p
== ' ' || *p
== '.') {
268 } else if (*p
== '[') {
275 int var_len
= p
- var
;
277 // empty variable name, or variable name with a space in it
281 // GPC elements holds Variants that are acting as smart pointers to
282 // RefDatas that we've created in the process of a multi-dim key.
283 std::vector
<Variant
> gpc_elements
;
284 if (is_array
) gpc_elements
.reserve(MAX_INPUT_NESTING_LEVEL
);
286 // The array pointer we're currently adding to. If we're doing a
287 // multi-dimensional set, this will point at the m_data.parr inside
288 // of a RefData sometimes (via toArrRef on the variants in
290 Array
* symtable
= &variables
;
293 int index_len
= var_len
;
298 if (++nest_level
> MAX_INPUT_NESTING_LEVEL
) {
299 Logger::Warning("Input variable nesting level exceeded");
312 ip
= strchr(ip
, ']');
314 // PHP variables cannot contain '[' in their names,
315 // so we replace the character with a '_'
316 *(index_s
- 1) = '_';
320 index_len
= strlen(index
);
325 new_idx_len
= strlen(index_s
);
329 auto lval
= symtable
->lvalAt();
330 lval
.type() = KindOfPersistentArray
;
331 lval
.val().parr
= staticEmptyArray();
332 gpc_elements
.push_back(uninit_null());
333 gpc_elements
.back().assignRef(tvAsVariant(lval
.tv_ptr()));
335 String
key(index
, index_len
, CopyString
);
336 auto const v
= tvToCell(symtable
->rvalAt(key
));
337 if (isNullType(v
.type()) || !isArrayLikeType(v
.type())) {
338 symtable
->set(key
, Array::Create());
340 gpc_elements
.push_back(uninit_null());
341 gpc_elements
.back().assignRef(symtable
->lvalAt(key
));
343 symtable
= &gpc_elements
.back().toArrRef();
344 /* ip pointed to the '[' character, now obtain the key */
346 index_len
= new_idx_len
;
359 symtable
->append(value
);
361 String
key(index
, index_len
, CopyString
);
362 if (overwrite
|| !symtable
->exists(key
)) {
363 symtable
->set(key
, value
);
369 enum class ContextOfException
{
375 static void handle_exception_append_bt(std::string
& errorMsg
,
376 const ExtendedException
& e
) {
377 Array bt
= e
.getBacktrace();
379 errorMsg
+= ExtendedLogger::StringOfStackTrace(bt
);
383 void bump_counter_and_rethrow(bool isPsp
) {
386 } catch (const RequestTimeoutException
& e
) {
388 static auto requestTimeoutPSPCounter
= ServiceData::createTimeSeries(
389 "requests_timed_out_psp", {ServiceData::StatsType::COUNT
});
390 requestTimeoutPSPCounter
->addValue(1);
391 ServerStats::Log("request.timed_out.psp", 1);
393 static auto requestTimeoutCounter
= ServiceData::createTimeSeries(
394 "requests_timed_out_non_psp", {ServiceData::StatsType::COUNT
});
395 requestTimeoutCounter
->addValue(1);
396 ServerStats::Log("request.timed_out.non_psp", 1);
399 } catch (const RequestCPUTimeoutException
& e
) {
401 static auto requestCPUTimeoutPSPCounter
= ServiceData::createTimeSeries(
402 "requests_cpu_timed_out_psp", {ServiceData::StatsType::COUNT
});
403 requestCPUTimeoutPSPCounter
->addValue(1);
404 ServerStats::Log("request.cpu_timed_out.psp", 1);
406 static auto requestCPUTimeoutCounter
= ServiceData::createTimeSeries(
407 "requests_cpu_timed_out_non_psp", {ServiceData::StatsType::COUNT
});
408 requestCPUTimeoutCounter
->addValue(1);
409 ServerStats::Log("request.cpu_timed_out.non_psp", 1);
412 } catch (const RequestMemoryExceededException
& e
) {
414 static auto requestMemoryExceededPSPCounter
=
415 ServiceData::createTimeSeries(
416 "requests_memory_exceeded_psp", {ServiceData::StatsType::COUNT
});
417 requestMemoryExceededPSPCounter
->addValue(1);
418 ServerStats::Log("request.memory_exceeded.psp", 1);
420 static auto requestMemoryExceededCounter
= ServiceData::createTimeSeries(
421 "requests_memory_exceeded_non_psp", {ServiceData::StatsType::COUNT
});
422 requestMemoryExceededCounter
->addValue(1);
423 ServerStats::Log("request.memory_exceeded.non_psp", 1);
427 // Capture a pprof (C++) dump when we OOM a request
428 // TODO: (t3753133) Should dump a PHP-instrumented pprof dump here as well
429 jemalloc_pprof_dump("", false);
436 static void handle_exception_helper(bool& ret
,
437 ExecutionContext
* context
,
438 std::string
& errorMsg
,
439 ContextOfException where
,
442 // Clear oom/timeout while handling exception and restore them afterwards.
443 auto& flags
= stackLimitAndSurprise();
444 auto const origFlags
= flags
.fetch_and(~ResourceFlags
) & ResourceFlags
;
447 flags
.fetch_or(origFlags
);
451 bump_counter_and_rethrow(false /* isPsp */);
452 } catch (const Eval::DebuggerException
&e
) {
454 } catch (const ExitException
&e
) {
455 if (where
== ContextOfException::ReqInit
) {
457 } else if (where
!= ContextOfException::Handler
&&
458 !context
->getExitCallback().isNull() &&
459 is_callable(context
->getExitCallback())) {
460 Array stack
= e
.getBacktrace();
461 Array argv
= make_packed_array(ExitException::ExitCode
.load(), stack
);
462 vm_call_user_func(context
->getExitCallback(), argv
);
464 } catch (const XDebugExitExn
&) {
465 // Do nothing, this is normal behavior.
466 } catch (const PhpFileDoesNotExistException
&e
) {
468 if (where
!= ContextOfException::Handler
) {
469 raise_notice("%s", e
.getMessage().c_str());
471 Logger::Error("%s", e
.getMessage().c_str());
474 handle_exception_append_bt(errorMsg
, e
);
476 } catch (const Exception
&e
) {
478 bool origError
= error
;
479 std::string origErrorMsg
= errorMsg
;
483 if (where
== ContextOfException::Handler
) {
484 errorMsg
= "Exception handler threw an exception: ";
486 errorMsg
+= e
.what();
487 if (where
== ContextOfException::Invoke
) {
488 bool handlerRet
= context
->onFatalError(e
);
492 errorMsg
= origErrorMsg
;
495 Logger::Error("%s", errorMsg
.c_str());
498 const ExtendedException
*ee
= dynamic_cast<const ExtendedException
*>(&e
);
500 handle_exception_append_bt(errorMsg
, *ee
);
503 } catch (const Object
&e
) {
505 bool origError
= error
;
506 auto const origErrorMsg
= errorMsg
;
510 if (where
== ContextOfException::Handler
) {
511 errorMsg
= "Exception handler threw an object exception: ";
514 errorMsg
+= e
.toString().data();
516 errorMsg
+= "(unable to call toString())";
518 if (where
== ContextOfException::Invoke
) {
519 bool handlerRet
= context
->onUnhandledException(e
);
523 errorMsg
= origErrorMsg
;
526 Logger::Error("%s", errorMsg
.c_str());
531 errorMsg
= "(unknown exception was thrown)";
532 Logger::Error("%s", errorMsg
.c_str());
536 static bool hphp_chdir_file(const std::string
& filename
) {
538 String s
= File::TranslatePath(filename
);
539 char *buf
= strndup(s
.data(), s
.size());
540 char *dir
= dirname(buf
);
543 if (File::IsVirtualDirectory(dir
)) {
544 g_context
->setCwd(String(dir
, CopyString
));
549 if ((sb
.st_mode
& S_IFMT
) == S_IFDIR
) {
552 g_context
->setCwd(String(dir
, CopyString
));
561 static void handle_resource_exceeded_exception() {
564 } catch (RequestTimeoutException
&) {
565 setSurpriseFlag(TimedOutFlag
);
566 } catch (RequestCPUTimeoutException
&) {
567 setSurpriseFlag(CPUTimedOutFlag
);
568 } catch (RequestMemoryExceededException
&) {
569 setSurpriseFlag(MemExceededFlag
);
573 void handle_destructor_exception(const char* situation
) {
574 std::string errorMsg
;
578 } catch (ExitException
&e
) {
579 // ExitException is fine, no need to show a warning.
580 TI().setPendingException(e
.clone());
582 } catch (Object
&e
) {
583 // For user exceptions, invoke the user exception handler
584 errorMsg
= situation
;
585 errorMsg
+= " threw an object exception: ";
587 errorMsg
+= e
.toString().data();
589 handle_resource_exceeded_exception();
590 errorMsg
+= "(unable to call toString())";
592 } catch (Exception
&e
) {
593 TI().setPendingException(e
.clone());
594 errorMsg
= situation
;
595 errorMsg
+= " raised a fatal error: ";
596 errorMsg
+= e
.what();
598 errorMsg
= situation
;
599 errorMsg
+= " threw an unknown exception";
601 // For fatal errors and unknown exceptions, we raise a warning.
602 // If there is a user error handler it will be invoked, otherwise
603 // the default error handler will be invoked.
605 raise_warning_unsampled("%s", errorMsg
.c_str());
607 handle_resource_exceeded_exception();
609 // The user error handler fataled or threw an exception,
610 // print out the error message directly to the log
611 Logger::Warning("%s", errorMsg
.c_str());
615 void init_command_line_session(int argc
, char** argv
) {
616 StackTraceNoHeap::AddExtraLogging("ThreadType", "CLI");
618 for (int i
= 0; i
< argc
; i
++) {
622 StackTraceNoHeap::AddExtraLogging("Arguments", args
.c_str());
625 auto const context
= g_context
.getNoCheck();
626 context
->obSetImplicitFlush(true);
630 init_command_line_globals(int argc
, char** argv
, char** envp
,
632 std::map
<std::string
, std::string
>& serverVariables
,
633 std::map
<std::string
, std::string
>& envVariables
) {
634 auto& variablesOrder
= RID().getVariablesOrder();
636 if (variablesOrder
.find('e') != std::string::npos
||
637 variablesOrder
.find('E') != std::string::npos
) {
638 Array
envArr(Array::Create());
639 process_env_variables(envArr
, envp
, envVariables
);
640 envArr
.set(s_HPHP
, 1);
641 envArr
.set(s_HHVM
, 1);
642 if (RuntimeOption::EvalJit
) {
643 envArr
.set(s_HHVM_JIT
, 1);
647 envArr
.set(s_HHVM_ARCH
, "x64");
650 envArr
.set(s_HHVM_ARCH
, "arm");
653 envArr
.set(s_HHVM_ARCH
, "ppc64");
656 php_global_set(s__ENV
, envArr
);
659 process_cmd_arguments(argc
, argv
);
661 if (variablesOrder
.find('s') != std::string::npos
||
662 variablesOrder
.find('S') != std::string::npos
) {
663 Array
serverArr(Array::Create());
664 process_env_variables(serverArr
, envp
, envVariables
);
666 struct timeval tp
= {0};
668 if (!gettimeofday(&tp
, nullptr)) {
669 now_double
= (double)(tp
.tv_sec
+ tp
.tv_usec
/ 1000000.00);
673 now_double
= (double)now
;
675 String file
= empty_string();
677 file
= String::attach(StringData::Make(argv
[0], CopyString
));
679 serverArr
.set(s_REQUEST_START_TIME
, now
);
680 serverArr
.set(s_REQUEST_TIME
, now
);
681 serverArr
.set(s_REQUEST_TIME_FLOAT
, now_double
);
682 serverArr
.set(s_DOCUMENT_ROOT
, empty_string_variant_ref
);
683 serverArr
.set(s_SCRIPT_FILENAME
, file
);
684 serverArr
.set(s_SCRIPT_NAME
, file
);
685 serverArr
.set(s_PHP_SELF
, file
);
686 serverArr
.set(s_argv
, php_global(s_argv
));
687 serverArr
.set(s_argc
, php_global(s_argc
));
688 serverArr
.set(s_PWD
, g_context
->getCwd());
690 if (RuntimeOption::ServerExecutionMode() &&
692 !gethostname(hostname
, sizeof(hostname
))) {
693 // gethostname may not null-terminate
694 hostname
[sizeof(hostname
) - 1] = '\0';
695 serverArr
.set(s_HOSTNAME
, String(hostname
, CopyString
));
698 for (auto& kv
: serverVariables
) {
699 serverArr
.set(String(kv
.first
.c_str()), String(kv
.second
.c_str()));
702 php_global_set(s__SERVER
, serverArr
);
706 HHVM_FN(xhprof_enable
)(xhprof
, uninit_null().toArray());
709 if (RuntimeOption::RequestTimeoutSeconds
) {
710 RID().setTimeout(RuntimeOption::RequestTimeoutSeconds
);
713 if (RuntimeOption::XenonForceAlwaysOn
) {
714 Xenon::getInstance().surpriseAll();
717 InitFiniNode::GlobalsInit();
718 // Initialize the debugger
719 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestInitHook());
722 void execute_command_line_begin(int argc
, char **argv
, int xhprof
) {
723 init_command_line_session(argc
, argv
);
724 init_command_line_globals(argc
, argv
, environ
, xhprof
,
725 RuntimeOption::ServerVariables
,
726 RuntimeOption::EnvVariables
);
729 void execute_command_line_end(int xhprof
, bool coverage
, const char *program
) {
730 if (RuntimeOption::EvalDumpTC
||
731 RuntimeOption::EvalDumpIR
||
732 RuntimeOption::EvalDumpRegion
) {
733 jit::mcgen::joinWorkerThreads();
737 Variant profileData
= HHVM_FN(xhprof_disable
)();
738 if (!profileData
.isNull()) {
739 HHVM_FN(var_dump
)(Variant::attach(
740 HHVM_FN(json_encode
)(HHVM_FN(xhprof_disable
)())
744 g_context
->onShutdownPostSend(); // runs more php
745 Eval::Debugger::InterruptPSPEnded(program
);
749 if (coverage
&& ti
.m_reqInjectionData
.getCoverage() &&
750 !RuntimeOption::CodeCoverageOutputFile
.empty()) {
751 ti
.m_coverage
->Report(RuntimeOption::CodeCoverageOutputFile
);
755 #if defined(__APPLE__) || defined(_MSC_VER)
756 const void* __hot_start
= nullptr;
757 const void* __hot_end
= nullptr;
758 #define AT_END_OF_TEXT
760 #define AT_END_OF_TEXT __attribute__((__section__(".stub")))
763 #define ALIGN_HUGE_PAGE __attribute__((__aligned__(2 * 1024 * 1024)))
766 NEVER_INLINE AT_END_OF_TEXT ALIGN_HUGE_PAGE
__attribute__((__optimize__("2")))
767 hugifyText(char* from
, char* to
) {
768 #if !defined FOLLY_SANITIZE_ADDRESS && defined MADV_HUGEPAGE
769 if (from
> to
|| (to
- from
) < sizeof(uint64_t)) {
770 // This shouldn't happen if HHVM is behaving correctly (I think),
771 // but if it does then there is nothing to do and we should bail
772 // out early because the call to wordcpy() below can't handle
773 // zero size or negative sizes.
776 size_t sz
= to
- from
;
777 void* mem
= malloc(sz
);
778 memcpy(mem
, from
, sz
);
780 // This maps out a portion of our executable
781 // We need to be very careful about what we do
782 // until we replace the original code
784 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
785 MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_FIXED
,
787 // This is in glibc, which isn't a problem, except for
788 // the trampoline code in .plt, which we dealt with
789 // in the linker script
790 madvise(from
, sz
, MADV_HUGEPAGE
);
791 // Don't use memcpy because its probably one of the
792 // functions thats been mapped out.
793 // Needs the attribute((optimize("2")) to prevent
794 // g++ from turning this back into memcpy(!)
795 wordcpy((uint64_t*)from
, (uint64_t*)mem
, sz
/ sizeof(uint64_t));
796 mprotect(from
, sz
, PROT_READ
| PROT_EXEC
);
798 mlock(from
, to
- from
);
799 Debug::DebugInfo::setPidMapOverlay(from
, to
);
800 std::stringstream ss
;
801 ss
<< "Mapped text section onto huge pages from " <<
802 std::hex
<< (uint64_t*)from
<< " to " << (uint64_t*)to
;
803 Logger::Info(ss
.str());
807 static void pagein_self(void) {
808 unsigned long begin
, end
, inode
, pgoff
;
809 char mapname
[PATH_MAX
];
817 // pad due to the spaces between the inode number and the mapname
818 bufsz
= sizeof(unsigned long) * 4 + sizeof(mapname
) + sizeof(char) * 11 + 100;
819 buf
= (char *)malloc(bufsz
);
823 BootStats::Block
timer("mapping self");
824 fp
= fopen("/proc/self/maps", "r");
827 if (fgets(buf
, bufsz
, fp
) == 0)
829 r
= sscanf(buf
, "%lx-%lx %4s %lx %5s %ld %s",
830 &begin
, &end
, perm
, &pgoff
, dev
, &inode
, mapname
);
832 // page in read-only segments that correspond to a file on disk
836 access(mapname
, F_OK
) != 0) {
840 auto beginPtr
= (char*)begin
;
841 auto endPtr
= (char*)end
;
842 auto hotStart
= (char*)__hot_start
;
843 auto hotEnd
= (char*)__hot_end
;
844 const size_t hugePageBytes
= 2L * 1024 * 1024;
846 if (mlock(beginPtr
, end
- begin
) == 0) {
847 if (RuntimeOption::EvalMaxHotTextHugePages
> 0 &&
850 hugePagesSupported() &&
851 beginPtr
<= hotStart
&&
854 char* from
= hotStart
- ((intptr_t)hotStart
& (hugePageBytes
- 1));
855 char* to
= hotEnd
+ (hugePageBytes
- 1);
856 to
-= (intptr_t)to
& (hugePageBytes
- 1);
857 const size_t maxHugeHotTextBytes
=
858 RuntimeOption::EvalMaxHotTextHugePages
* hugePageBytes
;
859 if (to
- from
> maxHugeHotTextBytes
) {
860 to
= from
+ maxHugeHotTextBytes
;
862 if (to
<= (void*)hugifyText
) {
863 hugifyText(from
, to
);
866 if (!RuntimeOption::LockCodeMemory
) {
867 munlock(beginPtr
, end
- begin
);
876 /* Sets RuntimeOption::ExecutionMode according to commandline options prior to
877 * config load. Returns false upon unrecognized mode.
879 static bool set_execution_mode(folly::StringPiece mode
) {
880 if (mode
== "daemon" || mode
== "server" || mode
== "replay") {
881 RuntimeOption::ServerMode
= true;
882 Logger::Escape
= true;
884 } else if (mode
== "run" || mode
== "debug" || mode
== "translate" ||
885 mode
== "dumphhas" || mode
== "verify") {
886 // We don't run PHP in "translate" mode, so just treat it like cli mode.
887 RuntimeOption::ServerMode
= false;
888 Logger::Escape
= false;
895 /* Reads a file into the OS page cache, with rate limiting. */
896 static bool readahead_rate(const char* path
, int64_t mbPerSec
) {
897 int ret
= open(path
, O_RDONLY
);
898 if (ret
< 0) return false;
900 SCOPE_EXIT
{ close(fd
); };
902 constexpr size_t kReadaheadBytes
= 1 << 20;
903 std::unique_ptr
<char[]> buf(new char[kReadaheadBytes
]);
905 auto startTime
= std::chrono::steady_clock::now();
907 ret
= read(fd
, buf
.get(), kReadaheadBytes
);
910 // Unit math: bytes / (MB / seconds) = microseconds
911 auto endTime
= startTime
+ std::chrono::microseconds(total
/ mbPerSec
);
912 auto sleepT
= endTime
- std::chrono::steady_clock::now();
913 // Don't sleep too frequently.
914 if (sleepT
>= std::chrono::seconds(1)) {
915 Logger::Info(folly::sformat(
916 "readahead sleeping {}ms after total {}b",
917 std::chrono::duration_cast
<std::chrono::milliseconds
>(sleepT
).count(),
919 /* sleep override */ std::this_thread::sleep_for(sleepT
);
926 static int start_server(const std::string
&username
, int xhprof
) {
928 HttpServer::CheckMemAndWait();
929 InitFiniNode::ServerPreInit();
931 if (!RuntimeOption::EvalUnixServerPath
.empty()) {
932 init_cli_server(RuntimeOption::EvalUnixServerPath
.c_str());
935 // Before we start the webserver, make sure the entire
936 // binary is paged into memory.
938 BootStats::mark("pagein_self");
940 set_execution_mode("server");
942 #if !defined(SKIP_USER_CHANGE)
943 if (!username
.empty()) {
944 if (Logger::UseCronolog
) {
945 for (const auto& el
: RuntimeOption::ErrorLogs
) {
946 Cronolog::changeOwner(username
, el
.second
.symLink
);
949 if (!Capability::ChangeUnixUser(username
, RuntimeOption::AllowRunAsRoot
)) {
952 LightProcess::ChangeUser(username
);
953 compilers_set_user(username
);
954 } else if (getuid() == 0 && !RuntimeOption::AllowRunAsRoot
) {
955 Logger::Error("hhvm not allowed to run as root unless "
956 "-vServer.AllowRunAsRoot=1 is used.");
959 Capability::SetDumpable();
960 // Include hugetlb pages in core dumps.
961 Process::SetCoreDumpHugePages();
967 try { Logger::Info("all servers stopped"); } catch(...) {}
970 HttpRequestHandler::GetAccessLog().init
971 (RuntimeOption::AccessLogDefaultFormat
, RuntimeOption::AccessLogs
,
973 AdminRequestHandler::GetAccessLog().init
974 (RuntimeOption::AdminLogFormat
, RuntimeOption::AdminLogSymLink
,
975 RuntimeOption::AdminLogFile
,
977 RPCRequestHandler::GetAccessLog().init
978 (RuntimeOption::AccessLogDefaultFormat
, RuntimeOption::RPCLogs
,
980 SCOPE_EXIT
{ HttpRequestHandler::GetAccessLog().flushAllWriters(); };
981 SCOPE_EXIT
{ AdminRequestHandler::GetAccessLog().flushAllWriters(); };
982 SCOPE_EXIT
{ RPCRequestHandler::GetAccessLog().flushAllWriters(); };
983 SCOPE_EXIT
{ Logger::FlushAll(); };
985 if (RuntimeOption::ServerInternalWarmupThreads
> 0) {
986 HttpServer::CheckMemAndWait();
987 InitFiniNode::WarmupConcurrentStart(
988 RuntimeOption::ServerInternalWarmupThreads
);
991 HttpServer::CheckMemAndWait();
992 // Create the HttpServer before any warmup requests to properly
993 // initialize the process
994 HttpServer::Server
= std::make_shared
<HttpServer
>();
997 HHVM_FN(xhprof_enable
)(xhprof
, uninit_null().toArray());
1000 std::unique_ptr
<std::thread
> readaheadThread
;
1002 if (RuntimeOption::RepoLocalReadaheadRate
> 0 &&
1003 !RuntimeOption::RepoLocalPath
.empty()) {
1004 HttpServer::CheckMemAndWait();
1005 readaheadThread
= std::make_unique
<std::thread
>([&] {
1006 BootStats::Block
timer("Readahead Repo");
1007 auto path
= RuntimeOption::RepoLocalPath
.c_str();
1008 Logger::Info("readahead %s", path
);
1010 // glibc doesn't have a wrapper for ioprio_set(), so we need to use
1011 // syscall(). The constants here are consistent with the kernel source.
1012 // See http://lxr.free-electrons.com/source/include/linux/ioprio.h
1013 auto constexpr IOPRIO_CLASS_SHIFT
= 13;
1020 // Set to lowest IO priority.
1021 constexpr int ioprio
= (IOPRIO_CLASS_IDLE
<< IOPRIO_CLASS_SHIFT
);
1023 // ioprio_set() is available starting kernel 2.6.13
1024 KernelVersion version
;
1025 if (version
.m_major
> 2 ||
1026 (version
.m_major
== 2 &&
1027 (version
.m_minor
> 6 ||
1028 (version
.m_minor
== 6 && version
.m_release
>= 13)))) {
1029 syscall(SYS_ioprio_set
,
1030 1 /* IOPRIO_WHO_PROCESS, in fact, it is this thread */,
1031 0 /* current thread */,
1035 const auto mbPerSec
= RuntimeOption::RepoLocalReadaheadRate
;
1036 if (!readahead_rate(path
, mbPerSec
)) {
1037 Logger::Error("readahead failed: %s", strerror(errno
));
1040 if (!RuntimeOption::RepoLocalReadaheadConcurrent
) {
1041 // TODO(10152762): Run this concurrently with non-disk warmup.
1042 readaheadThread
->join();
1043 readaheadThread
.reset();
1047 if (RuntimeOption::ServerInternalWarmupThreads
> 0) {
1048 BootStats::Block
timer("concurrentWaitForEnd");
1049 InitFiniNode::WarmupConcurrentWaitForEnd();
1052 if (RuntimeOption::RepoPreload
) {
1053 HttpServer::CheckMemAndWait();
1054 BootStats::Block
timer("Preloading Repo");
1055 profileWarmupStart();
1060 // If we have any warmup requests, replay them before listening for
1063 Logger::Info("Warming up");
1064 if (!RuntimeOption::EvalJitProfileWarmupRequests
) profileWarmupStart();
1065 SCOPE_EXIT
{ profileWarmupEnd(); };
1066 std::map
<std::string
, int> seen
;
1067 for (auto& file
: RuntimeOption::ServerWarmupRequests
) {
1068 HttpServer::CheckMemAndWait();
1069 // Take only the last part
1070 folly::StringPiece
f(file
);
1071 auto pos
= f
.rfind('/');
1072 std::string
str(pos
== f
.npos
? file
: f
.subpiece(pos
+ 1).str());
1073 auto count
= seen
[str
];
1074 BootStats::Block
timer(folly::sformat("warmup:{}:{}", str
, count
++));
1077 HttpRequestHandler
handler(0);
1080 Timer::GetMonotonicTime(start
);
1082 Logger::Info("Replaying warmup request %s", file
.c_str());
1085 rt
.onRequestStart(start
);
1086 rt
.replayInput(Hdf(file
));
1090 Timer::GetMonotonicTime(stop
);
1091 Logger::Info("Finished successfully in %ld seconds",
1092 stop
.tv_sec
- start
.tv_sec
);
1093 } catch (std::exception
& e
) {
1098 Logger::Info("Got exception during warmup: %s", error
.c_str());
1102 BootStats::mark("warmup");
1104 if (RuntimeOption::StopOldServer
) HttpServer::StopOldServer();
1106 if (RuntimeOption::EvalEnableNuma
&& !getenv("HHVM_DISABLE_NUMA")) {
1111 if (mallctlWrite
<uint64_t>("epoch", 1, true) == 0 &&
1112 mallctlRead("arenas.narenas", &narenas
, true) == 0 &&
1113 mallctlnametomib("arena.0.purge", mib
, &miblen
) == 0) {
1114 mib
[1] = size_t(narenas
);
1115 mallctlbymib(mib
, miblen
, nullptr, nullptr, nullptr, 0);
1118 enable_numa(RuntimeOption::EvalEnableNumaLocal
);
1119 BootStats::mark("enable_numa");
1123 // we're serving real traffic now, start batching log output
1124 if (RuntimeOption::UseThriftLogger
) {
1125 Logger::Info("Turning logger batching on, batch size %ld",
1126 RuntimeOption::LoggerBatchSize
);
1127 Logger::SetBatchSize(RuntimeOption::LoggerBatchSize
);
1128 Logger::SetFlushTimeout(
1129 std::chrono::milliseconds(RuntimeOption::LoggerFlushTimeout
));
1133 HttpServer::CheckMemAndWait(true); // Final wait
1134 if (readaheadThread
.get()) {
1135 readaheadThread
->join();
1136 readaheadThread
.reset();
1139 if (!RuntimeOption::EvalUnixServerPath
.empty()) {
1143 HttpServer::Server
->runOrExitProcess();
1144 HttpServer::Server
.reset();
1149 static void logSettings() {
1150 if (RuntimeOption::ServerLogSettingsOnStartup
) {
1151 Logger::Info("Settings: %s\n", IniSetting::GetAllAsJSON().c_str());
1155 static InitFiniNode
s_logSettings(logSettings
, InitFiniNode::When::ServerInit
);
1157 std::string
translate_stack(const char *hexencoded
, bool with_frame_numbers
) {
1158 if (!hexencoded
|| !*hexencoded
) {
1162 StackTrace
st(hexencoded
);
1163 std::vector
<std::shared_ptr
<StackFrameExtra
>> frames
;
1166 std::ostringstream out
;
1167 for (size_t i
= 0; i
< frames
.size(); i
++) {
1169 if (with_frame_numbers
) {
1170 out
<< "# " << (i
< 10 ? " " : "") << i
<< ' ';
1172 out
<< f
->toString();
1178 ///////////////////////////////////////////////////////////////////////////////
1180 static void prepare_args(int &argc
,
1182 const std::vector
<std::string
> &args
,
1184 argv
= (char **)malloc((args
.size() + 2) * sizeof(char*));
1186 if (file
&& *file
) {
1187 argv
[argc
++] = (char*)file
;
1189 for (int i
= 0; i
< (int)args
.size(); i
++) {
1190 argv
[argc
++] = (char*)args
[i
].c_str();
1192 argv
[argc
] = nullptr;
1195 static int execute_program_impl(int argc
, char **argv
);
1196 int execute_program(int argc
, char **argv
) {
1201 ret_code
= execute_program_impl(argc
, argv
);
1202 } catch (const Exception
&e
) {
1203 Logger::Error("Uncaught exception: %s", e
.what());
1205 } catch (const std::exception
&e
) {
1206 Logger::Error("Uncaught exception: %s", e
.what());
1209 Logger::Error("Uncaught exception: (unknown)");
1212 if (tempFile
.length() && boost::filesystem::exists(tempFile
)) {
1213 boost::filesystem::remove(tempFile
);
1216 if (HttpServer::Server
||
1217 folly::SingletonVault::singleton()->livingSingletonCount()) {
1218 // an exception was thrown that prevented proper shutdown. Its not
1219 // safe to destroy the globals, or run atexit handlers.
1220 // abort() so it shows up as a crash, and we can diagnose/fix the
1229 static bool open_server_log_files() {
1230 bool openedLog
= false;
1231 for (const auto& el
: RuntimeOption::ErrorLogs
) {
1233 const auto& name
= el
.first
;
1234 const auto& errlog
= el
.second
;
1235 if (!errlog
.logFile
.empty()) {
1236 if (errlog
.isPipeOutput()) {
1237 auto output
= popen(errlog
.logFile
.substr(1).c_str(), "w");
1238 ok
= (output
!= nullptr);
1239 Logger::SetOutput(name
, output
, true);
1240 } else if (Logger::UseCronolog
&& errlog
.hasTemplate()) {
1241 auto cronoLog
= Logger::CronoOutput(name
);
1242 always_assert(cronoLog
);
1243 cronoLog
->m_template
= errlog
.logFile
;
1244 cronoLog
->setPeriodicity();
1245 if (errlog
.periodMultiplier
) {
1246 cronoLog
->m_periodMultiple
= errlog
.periodMultiplier
;
1248 cronoLog
->m_linkName
= errlog
.symLink
;
1250 auto output
= fopen(errlog
.logFile
.c_str(), "a");
1251 ok
= (output
!= nullptr);
1252 Logger::SetOutput(name
, output
, false);
1254 if (!ok
) Logger::Error("Can't open log file: %s", errlog
.logFile
.c_str());
1261 static int compute_hhvm_argc(const options_description
& desc
,
1262 int argc
, char** argv
) {
1268 const auto& vec
= desc
.options();
1269 std::map
<std::string
,ArgCode
> long_options
;
1270 std::map
<std::string
,ArgCode
> short_options
;
1271 // Build lookup maps for the short options and the long options
1272 for (unsigned i
= 0; i
< vec
.size(); ++i
) {
1274 auto long_name
= opt
->long_name();
1275 ArgCode code
= NO_ARG
;
1276 if (opt
->semantic()->max_tokens() == 1) {
1277 if (opt
->semantic()->min_tokens() == 1) {
1278 code
= ARG_REQUIRED
;
1280 code
= ARG_OPTIONAL
;
1283 long_options
[long_name
] = code
;
1284 auto format_name
= opt
->format_name();
1285 if (format_name
.size() >= 2 && format_name
[0] == '-' &&
1286 format_name
[1] != '-') {
1287 auto short_name
= format_name
.substr(1,1);
1288 short_options
[short_name
] = code
;
1291 // Loop over the args
1293 while (pos
< argc
) {
1294 const char* str
= argv
[pos
];
1295 int len
= strlen(str
);
1296 if (len
== 2 && memcmp(str
, "--", 2) == 0) {
1297 // We found "--". All args after this are intended for the
1302 if (len
>= 3 && str
[0] == '-' && str
[1] == '-') {
1303 // Handle long options
1305 std::string
s(str
+2);
1306 auto it
= long_options
.find(s
);
1307 if (it
!= long_options
.end() && it
->second
!= NO_ARG
&& pos
< argc
&&
1308 (it
->second
== ARG_REQUIRED
|| argv
[pos
][0] != '-')) {
1311 } else if (len
>= 2 && str
[0] == '-') {
1312 // Handle short options
1315 s
.append(1, str
[1]);
1316 auto it
= short_options
.find(s
);
1317 if (it
!= short_options
.end() && it
->second
!= 0 && len
== 2 &&
1318 pos
< argc
&& (it
->second
== ARG_REQUIRED
|| argv
[pos
][0] != '-')) {
1322 // We've found a non-option argument. This arg and all args
1323 // that follow are intended for the PHP application
1331 * alloc.h defines a minimum C++ stack size but that only applies to threads we
1332 * manually create. When the main thread will be executing PHP rather than just
1333 * managing a server, make sure its stack is big enough.
1335 static void set_stack_size() {
1337 if (getrlimit(RLIMIT_STACK
, &rlim
) != 0) return;
1339 if (rlim
.rlim_cur
< kStackSizeMinimum
|| rlim
.rlim_cur
== RLIM_INFINITY
) {
1341 Logger::Error("stack limit too small, use peflags -x to increase %zd\n",
1344 rlim
.rlim_cur
= kStackSizeMinimum
;
1345 if (setrlimit(RLIMIT_STACK
, &rlim
)) {
1346 Logger::Error("failed to set stack limit to %zd\n", kStackSizeMinimum
);
1352 #if defined(BOOST_VERSION) && BOOST_VERSION <= 105400
1353 std::string
get_right_option_name(const basic_parsed_options
<char>& opts
,
1354 std::string
& wrong_name
) {
1355 // Remove any - from the wrong name for better comparing
1356 // since it will probably come prepended with --
1358 std::remove(wrong_name
.begin(), wrong_name
.end(), '-'), wrong_name
.end());
1359 for (basic_option
<char> opt
: opts
.options
) {
1360 std::string s_opt
= opt
.string_key
;
1361 // We are only dealing with options that have a - in them.
1362 if (s_opt
.find("-") != std::string::npos
) {
1363 if (s_opt
.find(wrong_name
) != std::string::npos
) {
1373 // Note that confusingly there are two different implementations
1377 // hphp/runtime/ext_zend_compat/php-src/Zend/zend_strtod.h
1378 // does not wrap with HPHP namespace, and implements
1379 // functionality required by the zend extension compatibility layer.
1380 // Empirically, this zend_strtod.h file can't be included because
1381 // it includes <zend.h> which isn't on any search path when compiling this.
1383 // The zend_startup_strtod from
1384 // hphp/runtime/base/zend-strtod.h
1385 // uses the HPHP namespace, is used for other purposes,
1386 // and predates the EZC extensions.
1388 // Before we can call zend_strtod from zend compatibility extensions,
1389 // we need to initialize it. Since it doesn't seem
1390 // to work to include the .h file, just sleaze declare it here.
1392 // See the related issue https://github.com/facebook/hhvm/issues/5244
1395 static int execute_program_impl(int argc
, char** argv
) {
1396 std::string usage
= "Usage:\n\n ";
1398 usage
+= " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
1401 options_description
desc(usage
.c_str());
1403 ("help", "display this message")
1404 ("version", "display version number")
1405 ("modules", "display modules")
1406 ("info", "PHP information")
1407 ("php", "emulate the standard php command line")
1408 ("compiler-id", "display the git hash for the compiler")
1409 ("repo-schema", "display the repository schema id")
1410 ("mode,m", value
<std::string
>(&po
.mode
)->default_value("run"),
1411 "run | debug (d) | server (s) | daemon | replay | translate (t) | verify")
1412 ("interactive,a", "Shortcut for --mode debug") // -a is from PHP5
1413 ("config,c", value
<std::vector
<std::string
>>(&po
.config
)->composing(),
1414 "load specified config file")
1416 value
<std::vector
<std::string
>>(&po
.confStrings
)->composing(),
1417 "individual configuration string in a format of name=value, where "
1418 "name can be any valid configuration for a config file")
1419 ("define,d", value
<std::vector
<std::string
>>(&po
.iniStrings
)->composing(),
1420 "define an ini setting in the same format ( foo[=bar] ) as provided in a "
1422 ("no-config", "don't use the default php.ini")
1423 ("port,p", value
<int>(&po
.port
)->default_value(-1),
1424 "start an HTTP server at specified port")
1425 ("port-fd", value
<int>(&po
.portfd
)->default_value(-1),
1426 "use specified fd instead of creating a socket")
1427 ("ssl-port-fd", value
<int>(&po
.sslportfd
)->default_value(-1),
1428 "use specified fd for SSL instead of creating a socket")
1429 ("admin-port", value
<int>(&po
.admin_port
)->default_value(-1),
1430 "start admin listener at specified port")
1431 ("debug-config", value
<std::string
>(&po
.debugger_options
.configFName
),
1432 "load specified debugger config file")
1434 value
<std::string
>(&po
.debugger_options
.host
)->implicit_value("localhost"),
1435 "connect to debugger server at specified address")
1436 ("debug-port", value
<int>(&po
.debugger_options
.port
)->default_value(-1),
1437 "connect to debugger server at specified port")
1438 ("debug-extension", value
<std::string
>(&po
.debugger_options
.extension
),
1439 "PHP file that extends command 'arg'")
1440 ("debug-cmd", value
<std::vector
<std::string
>>(
1441 &po
.debugger_options
.cmds
)->composing(),
1442 "executes this debugger command and returns its output in stdout")
1444 value
<std::string
>(&po
.debugger_options
.sandbox
)->default_value("default"),
1445 "initial sandbox to attach to when debugger is started")
1446 ("user,u", value
<std::string
>(&po
.user
),
1447 "run server under this user account")
1448 ("file,f", value
<std::string
>(&po
.file
),
1449 "execute specified file")
1450 ("lint,l", value
<std::string
>(&po
.lint
),
1451 "lint specified file")
1452 ("show,w", value
<std::string
>(&po
.show
),
1453 "output specified file and do nothing else")
1455 "file specified is temporary and removed after execution")
1456 ("count", value
<int>(&po
.count
)->default_value(1),
1457 "how many times to repeat execution")
1458 ("no-safe-access-check",
1459 value
<bool>(&po
.noSafeAccessCheck
)->default_value(false),
1460 "whether to ignore safe file access check")
1461 ("arg", value
<std::vector
<std::string
>>(&po
.args
)->composing(),
1463 ("extra-header", value
<std::string
>(&Logger::ExtraHeader
),
1464 "extra-header to add to log lines")
1465 ("build-id", value
<std::string
>(&po
.buildId
),
1466 "unique identifier of compiled server code")
1467 ("instance-id", value
<std::string
>(&po
.instanceId
),
1468 "unique identifier of server instance")
1469 ("xhprof-flags", value
<int>(&po
.xhprofFlags
)->default_value(0),
1473 positional_options_description p
;
1477 // Before invoking the boost command line parser, we do a manual pass
1478 // to find the first occurrence of either "--" or a non-option argument
1479 // in order to determine which arguments should be consumed by HHVM and
1480 // which arguments should be passed along to the PHP application. This
1481 // is necessary so that the boost command line parser doesn't choke on
1482 // args intended for the PHP application.
1483 int hhvm_argc
= compute_hhvm_argc(desc
, argc
, argv
);
1484 // Need to have a parent try for opts so I can use opts in the catch of
1485 // one of the sub-tries below.
1487 // Invoke the boost command line parser to parse the args for HHVM.
1488 auto opts
= command_line_parser(hhvm_argc
, argv
)
1491 // If these style options are changed, compute_hhvm_argc() will
1492 // need to be updated appropriately
1493 .style(command_line_style::default_style
&
1494 ~command_line_style::allow_guessing
&
1495 ~command_line_style::allow_sticky
&
1496 ~command_line_style::long_allow_adjacent
)
1499 // Manually append the args for the PHP application.
1501 for (unsigned m
= 0; m
< opts
.options
.size(); ++m
) {
1502 const auto& bo
= opts
.options
[m
];
1503 if (bo
.string_key
== "arg") {
1507 for (unsigned m
= hhvm_argc
; m
< argc
; ++m
) {
1508 std::string str
= argv
[m
];
1509 basic_option
<char> bo
;
1510 bo
.string_key
= "arg";
1511 bo
.position_key
= pos
++;
1512 bo
.value
.push_back(str
);
1513 bo
.original_tokens
.push_back(str
);
1514 bo
.unregistered
= false;
1515 bo
.case_insensitive
= false;
1516 opts
.options
.push_back(bo
);
1518 // Process the options
1521 if (vm
.count("interactive") /* or -a */) po
.mode
= "debug";
1522 else if (po
.mode
.empty()) po
.mode
= "run";
1523 else if (po
.mode
== "d") po
.mode
= "debug";
1524 else if (po
.mode
== "s") po
.mode
= "server";
1525 else if (po
.mode
== "t") po
.mode
= "translate";
1527 if (!set_execution_mode(po
.mode
)) {
1528 Logger::Error("Error in command line: invalid mode: %s",
1530 cout
<< desc
<< "\n";
1533 if (po
.config
.empty() && !vm
.count("no-config")
1534 && ::getenv("HHVM_NO_DEFAULT_CONFIGS") == nullptr) {
1535 auto file_callback
= [&po
] (const char *filename
) {
1536 Logger::Verbose("Using default config file: %s", filename
);
1537 po
.config
.push_back(filename
);
1539 add_default_config_files_globbed(DEFAULT_CONFIG_DIR
"/php*.ini",
1541 add_default_config_files_globbed(DEFAULT_CONFIG_DIR
"/config*.hdf",
1544 const auto env_config
= ::getenv("HHVM_CONFIG_FILE");
1545 if (env_config
!= nullptr) {
1546 add_default_config_files_globbed(
1548 [&po
](const char* filename
) {
1549 Logger::Verbose("Using config file from environment: %s", filename
);
1550 po
.config
.push_back(filename
);
1554 // When we upgrade boost, we can remove this and also get rid of the parent
1555 // try statement and move opts back into the original try block
1556 #if defined(BOOST_VERSION) && BOOST_VERSION >= 105000 && BOOST_VERSION <= 105400
1557 } catch (const error_with_option_name
&e
) {
1558 std::string wrong_name
= e
.get_option_name();
1559 std::string right_name
= get_right_option_name(opts
, wrong_name
);
1560 std::string message
= e
.what();
1561 if (right_name
!= "") {
1562 boost::replace_all(message
, wrong_name
, right_name
);
1564 Logger::Error("Error in command line: %s", message
.c_str());
1565 cout
<< desc
<< "\n";
1568 } catch (const error
&e
) {
1569 Logger::Error("Error in command line: %s", e
.what());
1570 cout
<< desc
<< "\n";
1573 Logger::Error("Error in command line.");
1574 cout
<< desc
<< "\n";
1577 } catch (const error
&e
) {
1578 Logger::Error("Error in command line: %s", e
.what());
1579 cout
<< desc
<< "\n";
1582 Logger::Error("Error in command line parsing.");
1583 cout
<< desc
<< "\n";
1586 // reuse -h for help command if possible
1587 if (vm
.count("help") || (vm
.count("debug-host") && po
.mode
!= "debug")) {
1588 cout
<< desc
<< "\n";
1591 if (vm
.count("version")) {
1592 cout
<< "HipHop VM";
1593 cout
<< " " << HHVM_VERSION
;
1594 cout
<< " (" << (debug
? "dbg" : "rel") << ")\n";
1595 cout
<< "Compiler: " << compilerId() << "\n";
1596 cout
<< "Repo schema: " << repoSchemaId() << "\n";
1599 if (vm
.count("modules")) {
1600 Array exts
= ExtensionRegistry::getLoaded();
1601 cout
<< "[PHP Modules]" << "\n";
1602 for (ArrayIter
iter(exts
); iter
; ++iter
) {
1603 cout
<< iter
.second().toString().toCppString() << "\n";
1607 if (vm
.count("compiler-id")) {
1608 cout
<< compilerId() << "\n";
1612 if (vm
.count("repo-schema")) {
1613 cout
<< repoSchemaId() << "\n";
1617 if (!po
.show
.empty()) {
1618 auto f
= req::make
<PlainFile
>();
1619 f
->open(po
.show
, "r");
1621 Logger::Error("Unable to open file %s", po
.show
.c_str());
1629 po
.isTempFile
= vm
.count("temp-file");
1631 // forget the source for systemlib.php unless we are debugging
1632 if (po
.mode
!= "debug") SystemLib::s_source
= "";
1634 // we need to initialize pcre cache table very early
1637 #ifdef ENABLE_ZEND_COMPAT
1639 // Initialize in the zend extension compatibility layer, as needed
1640 // before any calls from legacy zend extensions to zend_strtod. See
1641 // the extern "C" declaration of this function, above.
1643 zend_startup_strtod();
1646 MemoryManager::TlsWrapper::getCheck();
1647 if (RuntimeOption::ServerExecutionMode()) {
1648 // Create the hardware counter before reading options,
1649 // so that the main thread never has inherit set in server
1651 HardwareCounter::s_counter
.getCheck();
1653 std::vector
<std::string
> messages
;
1654 // We want the ini map to be freed after processing and loading the options
1655 // So put this in its own block
1657 IniSettingMap ini
= IniSettingMap();
1659 s_config_files
= po
.config
;
1660 // Start with .hdf and .ini files
1661 for (auto& filename
: s_config_files
) {
1662 if (boost::filesystem::exists(filename
)) {
1663 Config::ParseConfigFile(filename
, ini
, config
);
1666 "The configuration file %s does not exist",
1671 // Now, take care of CLI options and then officially load and bind things
1672 s_ini_strings
= po
.iniStrings
;
1673 RuntimeOption::Load(ini
, config
, po
.iniStrings
, po
.confStrings
, &messages
);
1674 std::vector
<std::string
> badnodes
;
1675 config
.lint(badnodes
);
1676 for (const auto& badnode
: badnodes
) {
1677 const auto msg
= "Possible bad config node: " + badnode
;
1678 fprintf(stderr
, "%s\n", msg
.c_str());
1679 messages
.push_back(msg
);
1683 std::vector
<int> inherited_fds
;
1684 RuntimeOption::BuildId
= po
.buildId
;
1685 RuntimeOption::InstanceId
= po
.instanceId
;
1687 // Do this as early as possible to avoid creating temp files and spawing
1688 // light processes. Correct compilation still requires loading all of the
1689 // ini/hdf/cli options.
1690 if (po
.mode
== "dumphhas" || po
.mode
== "verify") {
1691 if (po
.file
.empty() && po
.args
.empty()) {
1692 std::cerr
<< "Nothing to do. Pass a php file to compile.\n";
1696 auto const file
= [] (std::string file
) -> std::string
{
1697 if (!FileUtil::isAbsolutePath(file
)) {
1698 return SourceRootInfo::GetCurrentSourceRoot() + std::move(file
);
1701 }(po
.file
.empty() ? po
.args
[0] : po
.file
);
1703 RuntimeOption::RepoCommit
= false; // avoid initializing a repo
1705 std::fstream
fs(file
, std::ios::in
);
1707 std::cerr
<< "Unable to open \"" << file
<< "\"\n";
1710 std::stringstream contents
;
1711 contents
<< fs
.rdbuf();
1713 auto const str
= contents
.str();
1714 auto const md5
= MD5
{mangleUnitMd5(string_md5(str
))};
1718 g_context
.getCheck();
1719 SCOPE_EXIT
{ hphp_thread_exit(); };
1721 // Initialize compiler state
1722 compile_file(0, 0, MD5(), 0);
1724 if (po
.mode
== "dumphhas") RuntimeOption::EvalDumpHhas
= true;
1725 else RuntimeOption::EvalVerifyOnly
= true;
1726 SystemLib::s_inited
= true;
1728 auto compiled
= compile_file(str
.c_str(), str
.size(), md5
, file
.c_str(),
1731 if (po
.mode
== "verify") {
1735 // This will dump the hhas for file as EvalDumpHhas was set
1737 std::cerr
<< "Unable to compile \"" << file
<< "\"\n";
1744 if (po
.port
!= -1) {
1745 RuntimeOption::ServerPort
= po
.port
;
1747 if (po
.portfd
!= -1) {
1748 RuntimeOption::ServerPortFd
= po
.portfd
;
1749 inherited_fds
.push_back(po
.portfd
);
1751 if (po
.sslportfd
!= -1) {
1752 RuntimeOption::SSLPortFd
= po
.sslportfd
;
1753 inherited_fds
.push_back(po
.sslportfd
);
1755 if (po
.admin_port
!= -1) {
1756 RuntimeOption::AdminServerPort
= po
.admin_port
;
1758 if (po
.noSafeAccessCheck
) {
1759 RuntimeOption::SafeFileAccess
= false;
1761 IniSetting::s_system_settings_are_set
= true;
1762 MM().resetRuntimeOptions();
1764 auto opened_logs
= open_server_log_files();
1765 if (po
.mode
== "daemon") {
1767 Logger::Error("Log file not specified under daemon mode.\n\n");
1772 if (RuntimeOption::ServerExecutionMode()) {
1773 for (auto const& m
: messages
) {
1779 // Defer the initialization of light processes until the log file handle is
1780 // created, so that light processes can log to the right place. If we ever
1781 // lose a light process, stop the server instead of proceeding in an
1782 // uncertain state. Don't start them in DumpHhas mode because
1783 // it _Exit()s after loading the first non-systemlib unit.
1784 if (!RuntimeOption::EvalDumpHhas
) {
1785 LightProcess::SetLostChildHandler([](pid_t
/*child*/) {
1786 if (!HttpServer::Server
) return;
1787 if (!HttpServer::Server
->isStopped()) {
1788 HttpServer::Server
->stopOnSignal(SIGCHLD
);
1791 LightProcess::Initialize(RuntimeOption::LightProcessFilePrefix
,
1792 RuntimeOption::LightProcessCount
,
1793 RuntimeOption::EvalRecordSubprocessTimes
,
1796 // HackC initialization should happen immediately prior to LightProcess
1797 // configuration as it will create a private delegate process to deal with
1803 // We want to do this as early as possible because any allocations before-hand
1804 // will get a generic unknown type type-index.
1807 } catch (const type_scan::InitException
& exn
) {
1808 Logger::Error("Unable to initialize GC type-scanners: %s", exn
.what());
1809 exit(HPHP_EXIT_FAILURE
);
1811 ThreadLocalManager::GetManager().initTypeIndices();
1813 // It's okay if this fails.
1814 init_member_reflection();
1816 if (!ShmCounters::initialize(true, Logger::Error
)) {
1817 exit(HPHP_EXIT_FAILURE
);
1819 // Initialize compiler state
1820 compile_file(0, 0, MD5(), 0);
1822 if (!po
.lint
.empty()) {
1823 Logger::LogHeader
= false;
1824 Logger::LogLevel
= Logger::LogInfo
;
1825 Logger::UseCronolog
= false;
1826 Logger::UseLogFile
= true;
1827 // we're linting, reset whatever logger settings and write once to stdout
1828 Logger::ClearThreadLog();
1829 for (auto& el
: RuntimeOption::ErrorLogs
) {
1830 const auto& name
= el
.first
;
1831 Logger::SetTheLogger(name
, nullptr);
1833 Logger::SetTheLogger(Logger::DEFAULT
, new Logger());
1835 if (po
.isTempFile
) {
1839 hphp_process_init();
1840 SCOPE_EXIT
{ hphp_process_exit(); };
1843 auto const unit
= lookupUnit(
1844 makeStaticString(po
.lint
.c_str()), "", nullptr);
1845 if (unit
== nullptr) {
1846 throw FileOpenException(po
.lint
);
1848 const StringData
* msg
;
1850 if (unit
->compileTimeFatal(msg
, line
)) {
1851 VMParserFrame parserFrame
;
1852 parserFrame
.filename
= po
.lint
.c_str();
1853 parserFrame
.lineNumber
= line
;
1854 Array bt
= createBacktrace(BacktraceArgs()
1856 .setParserFrame(&parserFrame
));
1857 raise_fatal_error(msg
->data(), bt
);
1859 } catch (FileOpenException
&e
) {
1860 Logger::Error("%s", e
.getMessage().c_str());
1862 } catch (const FatalErrorException
& e
) {
1863 RuntimeOption::CallUserHandlerOnFatals
= false;
1864 RuntimeOption::AlwaysLogUnhandledExceptions
= false;
1865 g_context
->onFatalError(e
);
1868 Logger::Info("No syntax errors detected in %s", po
.lint
.c_str());
1872 if (argc
<= 1 || po
.mode
== "run" || po
.mode
== "debug") {
1875 if (po
.isTempFile
) {
1879 set_execution_mode("run");
1880 /* recreate the hardware counters for the main thread now that we know
1881 * whether to include subprocess times */
1882 HardwareCounter::s_counter
.destroy();
1883 HardwareCounter::s_counter
.getCheck();
1887 prepare_args(new_argc
, new_argv
, po
.args
, po
.file
.c_str());
1889 std::string
const cliFile
= !po
.file
.empty() ? po
.file
:
1890 new_argv
[0] ? new_argv
[0] : "";
1891 if (po
.mode
!= "debug" && cliFile
.empty()) {
1892 std::cerr
<< "Nothing to do. Either pass a .php file to run, or "
1896 Repo::setCliFile(cliFile
);
1899 hphp_process_init();
1900 SCOPE_EXIT
{ hphp_process_exit(); };
1902 if (RuntimeOption::EvalUseRemoteUnixServer
!= "no" &&
1903 !RuntimeOption::EvalUnixServerPath
.empty() &&
1904 (!po
.file
.empty() || !po
.args
.empty())) {
1905 std::vector
<std::string
> args
;
1906 if (!po
.file
.empty()) {
1907 args
.emplace_back(po
.file
);
1909 args
.insert(args
.end(), po
.args
.begin(), po
.args
.end());
1910 run_command_on_cli_server(
1911 RuntimeOption::EvalUnixServerPath
.c_str(), args
1913 if (RuntimeOption::EvalUseRemoteUnixServer
== "only") {
1914 Logger::Error("Failed to connect to unix server.");
1924 if (po
.mode
== "debug") {
1925 StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
1926 RuntimeOption::EnableDebugger
= true;
1927 po
.debugger_options
.fileName
= file
;
1928 po
.debugger_options
.user
= po
.user
;
1929 Eval::DebuggerProxyPtr localProxy
=
1930 Eval::Debugger::StartClient(po
.debugger_options
);
1932 Logger::Error("Failed to start debugger client\n\n");
1935 Eval::Debugger::RegisterSandbox(localProxy
->getDummyInfo());
1936 std::shared_ptr
<std::vector
<std::string
>> client_args
;
1937 bool restart
= false;
1941 assert(po
.debugger_options
.fileName
== file
);
1942 execute_command_line_begin(new_argc
, new_argv
, po
.xhprofFlags
);
1943 // Set the proxy for this thread to be the localProxy we just
1944 // created. If we're script debugging, this will be the proxy that
1945 // does all of our work. If we're remote debugging, this proxy will
1946 // go unused until we finally stop it when the user quits the
1948 g_context
->setSandboxId(localProxy
->getDummyInfo().id());
1950 // Systemlib.php is not loaded again, so we need this if we
1951 // are to hit any breakpoints in systemlib.
1952 proxySetBreakPoints(localProxy
.get());
1954 Eval::Debugger::DebuggerSession(po
.debugger_options
, restart
);
1956 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1957 } catch (const Eval::DebuggerRestartException
&e
) {
1958 execute_command_line_end(0, false, nullptr);
1960 if (!e
.m_args
->empty()) {
1961 file
= e
.m_args
->at(0);
1962 po
.debugger_options
.fileName
= file
;
1963 client_args
= e
.m_args
;
1965 prepare_args(new_argc
, new_argv
, *client_args
, nullptr);
1968 } catch (const Eval::DebuggerClientExitException
&e
) {
1969 execute_command_line_end(0, false, nullptr);
1970 break; // end user quitting debugger
1976 for (int i
= 0; i
< po
.count
; i
++) {
1977 execute_command_line_begin(new_argc
, new_argv
, po
.xhprofFlags
);
1979 if (hphp_invoke_simple(file
, false /* warmup only */)) {
1980 ret
= ExitException::ExitCode
;
1982 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1991 if (po
.mode
== "daemon" || po
.mode
== "server") {
1992 if (!po
.user
.empty()) RuntimeOption::ServerUser
= po
.user
;
1993 return start_server(RuntimeOption::ServerUser
, po
.xhprofFlags
);
1996 if (po
.mode
== "replay" && !po
.args
.empty()) {
1997 RuntimeOption::RecordInput
= false;
1998 set_execution_mode("server");
1999 HttpServer server
; // so we initialize runtime properly
2000 HttpRequestHandler
handler(0);
2001 for (int i
= 0; i
< po
.count
; i
++) {
2002 for (unsigned int j
= 0; j
< po
.args
.size(); j
++) {
2004 rt
.replayInput(po
.args
[j
].c_str());
2006 printf("%s\n", rt
.getResponse().c_str());
2012 if (po
.mode
== "translate" && !po
.args
.empty()) {
2013 printf("%s", translate_stack(po
.args
[0].c_str()).c_str());
2017 cout
<< desc
<< "\n";
2021 String
canonicalize_path(const String
& p
, const char* root
, int rootLen
) {
2022 String path
= FileUtil::canonicalize(p
);
2023 if (path
.charAt(0) == '/') {
2024 auto const& sourceRoot
= RuntimeOption::SourceRoot
;
2025 int len
= sourceRoot
.size();
2026 if (len
&& strncmp(path
.data(), sourceRoot
.c_str(), len
) == 0) {
2027 return path
.substr(len
);
2029 if (root
&& rootLen
&& strncmp(path
.data(), root
, rootLen
) == 0) {
2030 return path
.substr(rootLen
);
2036 static std::string
systemlib_split(const std::string
& slib
, std::string
* hhas
) {
2037 auto pos
= slib
.find("\n<?hhas\n");
2038 if (pos
!= std::string::npos
) {
2039 if (hhas
) *hhas
= slib
.substr(pos
+ 8);
2040 return slib
.substr(0, pos
);
2045 // Retrieve a systemlib (or mini systemlib) from the
2046 // current executable or another ELF object file.
2048 // Additionally, when retrieving the main systemlib
2049 // from the current executable, honor the
2050 // HHVM_SYSTEMLIB environment variable as an override.
2051 std::string
get_systemlib(std::string
* hhas
,
2052 const std::string
§ion
/*= "systemlib" */,
2053 const std::string
&filename
/*= "" */) {
2054 if (filename
.empty() && section
== "systemlib") {
2055 if (auto const file
= getenv("HHVM_SYSTEMLIB")) {
2056 std::ifstream
ifs(file
);
2058 return systemlib_split(std::string(
2059 std::istreambuf_iterator
<char>(ifs
),
2060 std::istreambuf_iterator
<char>()), hhas
);
2066 if (!get_embedded_data(section
.c_str(), &desc
, filename
)) return "";
2068 auto const data
= read_embedded_data(desc
);
2069 return systemlib_split(data
, hhas
);
2072 ///////////////////////////////////////////////////////////////////////////////
2076 static void on_timeout(int sig
, siginfo_t
* info
, void* /*context*/) {
2077 if (sig
== SIGVTALRM
&& info
&& info
->si_code
== SI_TIMER
) {
2078 auto data
= (RequestTimer
*)info
->si_value
.sival_ptr
;
2082 Xenon::getInstance().onTimer();
2089 * Update constants to their real values and sync some runtime options
2091 static void update_constants_and_options() {
2092 assert(ExtensionRegistry::modulesInitialised());
2093 // If extension constants were used in the ini files (e.g., E_ALL) they
2094 // would have come out as 0 in the previous pass until we load and
2095 // initialize our extensions, which we do in RuntimeOption::Load() via
2096 // ExtensionRegistry::ModuleLoad() and in ExtensionRegistry::ModuleInit()
2097 // in hphp_process_init(). We will re-import and set only the constants that
2098 // have been now bound to their proper value.
2099 IniSettingMap ini
= IniSettingMap();
2100 for (auto& filename
: s_config_files
) {
2101 SuppressHackArrCompatNotices shacn
;
2102 Config::ParseIniFile(filename
, ini
, true);
2104 // Reset the INI settings from the CLI.
2105 for (auto& iniStr
: s_ini_strings
) {
2106 SuppressHackArrCompatNotices shacn
;
2107 Config::ParseIniString(iniStr
, ini
, true);
2110 // Reset, possibly, some request dependent runtime options based on certain
2111 // setting values. Do this here so we ensure the constants have been loaded
2112 // correctly (e.g., error_reporting E_ALL, etc.)
2114 if (IniSetting::GetSystem("error_reporting", sys
)) {
2115 RuntimeOption::RuntimeErrorReportingLevel
= sys
.toInt64();
2116 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel
);
2118 if (IniSetting::GetSystem("memory_limit", sys
)) {
2119 RID().setMemoryLimit(sys
.toString().toCppString());
2120 RuntimeOption::RequestMemoryMaxBytes
= RID().getMemoryLimitNumeric();
2124 void hphp_thread_init() {
2125 #ifdef USE_JEMALLOC_CUSTOM_HOOKS
2126 thread_huge_tcache_create();
2128 ServerStats::GetLogger();
2129 zend_get_bigint_data();
2132 MemoryManager::TlsWrapper::getCheck();
2134 assert(ThreadInfo::s_threadInfo
.isNull());
2135 ThreadInfo::s_threadInfo
.getCheck()->init();
2137 HardwareCounter::s_counter
.getCheck();
2138 ExtensionRegistry::threadInit();
2139 InitFiniNode::ThreadInit();
2141 // ensure that there's no request-allocated memory
2142 hphp_memory_cleanup();
2145 void hphp_thread_exit() {
2146 InitFiniNode::ThreadFini();
2147 ExtensionRegistry::threadShutdown();
2148 if (!g_context
.isNull()) g_context
.destroy();
2149 #ifdef USE_JEMALLOC_CUSTOM_HOOKS
2150 thread_huge_tcache_destroy();
2154 void hphp_process_init() {
2155 pthread_attr_t attr
;
2156 // Linux+GNU extension
2157 #if defined(_GNU_SOURCE) && defined(__linux__)
2158 if (pthread_getattr_np(pthread_self(), &attr
) != 0 ) {
2159 Logger::Error("pthread_getattr_np failed before checking stack limits");
2163 if (pthread_attr_init(&attr
) != 0 ) {
2164 Logger::Error("pthread_attr_init failed before checking stack limits");
2168 init_stack_limits(&attr
);
2169 if (pthread_attr_destroy(&attr
) != 0 ) {
2170 Logger::Error("pthread_attr_destroy failed after checking stack limits");
2173 BootStats::mark("pthread_init");
2175 Process::InitProcessStatics();
2176 BootStats::mark("Process::InitProcessStatics");
2181 (void)type_scan::getIndexForMalloc
<MArrayIter
>();
2182 MIterTable::TlsWrapper tls
;
2185 // initialize the tzinfo cache.
2187 BootStats::mark("timezone_init");
2189 // start any external compilers
2191 BootStats::mark("compilers_start");
2196 struct sigaction action
= {};
2197 action
.sa_sigaction
= on_timeout
;
2198 action
.sa_flags
= SA_SIGINFO
| SA_NODEFER
;
2199 sigaction(SIGVTALRM
, &action
, nullptr);
2201 // start takes milliseconds, Period is a double in seconds
2202 Xenon::getInstance().start(1000 * RuntimeOption::XenonPeriodSeconds
);
2203 BootStats::mark("xenon");
2205 // reinitialize pcre table
2207 BootStats::mark("pcre_reinit");
2209 // the liboniguruma docs say this isnt needed,
2210 // but the implementation of init is not
2211 // thread safe due to bugs
2213 BootStats::mark("onig_init");
2215 // simple xml also needs one time init
2217 BootStats::mark("xmlInitParser");
2219 g_context
.getCheck();
2220 // Some event handlers are registered during the startup process.
2221 g_context
->acceptRequestEventHandlers(true);
2222 InitFiniNode::ProcessPreInit();
2223 // TODO(9795696): Race in thread map may trigger spurious logging at
2224 // thread exit, so for now, only spawn threads if we're a server.
2225 const uint32_t maxWorkers
= RuntimeOption::ServerExecutionMode() ? 3 : 0;
2226 folly::SingletonVault::singleton()->registrationComplete();
2227 InitFiniNode::ProcessInitConcurrentStart(maxWorkers
);
2229 InitFiniNode::ProcessInitConcurrentWaitForEnd();
2230 BootStats::mark("extra_process_init_concurrent_wait");
2233 BootStats::mark("g_vmProcessInit");
2235 PageletServer::Restart();
2236 BootStats::mark("PageletServer::Restart");
2237 XboxServer::Restart();
2238 BootStats::mark("XboxServer::Restart");
2239 Stream::RegisterCoreWrappers();
2240 BootStats::mark("Stream::RegisterCoreWrappers");
2241 ExtensionRegistry::moduleInit();
2242 BootStats::mark("ExtensionRegistry::moduleInit");
2244 // Now that constants have been bound we can update options using constants
2245 // in ini files (e.g., E_ALL) and sync some other options
2246 update_constants_and_options();
2248 InitFiniNode::ProcessInit();
2249 BootStats::mark("extra_process_init");
2251 UnlimitSerializationScope unlimit
;
2252 // TODO(9755792): Add real execution mode for snapshot generation.
2253 if (apcExtension::PrimeLibraryUpgradeDest
!= "") {
2254 Timer
timer(Timer::WallTime
, "optimizeApcPrime");
2255 apc_load(apcExtension::LoadThread
);
2257 apc_load(apcExtension::LoadThread
);
2259 BootStats::mark("apc_load");
2263 BootStats::mark("rds::requestExit");
2264 // Reset the preloaded g_context
2265 ExecutionContext
*context
= g_context
.getNoCheck();
2266 context
->onRequestShutdown(); // TODO T20898959 kill early REH usage.
2267 context
->~ExecutionContext();
2268 new (context
) ExecutionContext();
2269 BootStats::mark("ExecutionContext");
2271 // TODO(9755792): Add real execution mode for snapshot generation.
2272 if (apcExtension::PrimeLibraryUpgradeDest
!= "") {
2273 Logger::Info("APC PrimeLibrary upgrade mode completed; exiting.");
2274 hphp_process_exit();
2279 static void handle_exception(bool& ret
, ExecutionContext
* context
,
2280 std::string
& errorMsg
, ContextOfException where
,
2281 bool& error
, bool richErrorMsg
) {
2282 assert(where
== ContextOfException::Invoke
||
2283 where
== ContextOfException::ReqInit
);
2285 handle_exception_helper(ret
, context
, errorMsg
, where
, error
, richErrorMsg
);
2286 } catch (const ExitException
&e
) {
2287 // Got an ExitException during exception handling, handle
2288 // similarly to the case below but don't call obEndAll().
2290 handle_exception_helper(ret
, context
, errorMsg
, ContextOfException::Handler
,
2291 error
, richErrorMsg
);
2292 context
->obEndAll();
2296 static void handle_reqinit_exception(bool &ret
, ExecutionContext
*context
,
2297 std::string
&errorMsg
, bool &error
) {
2298 handle_exception(ret
, context
, errorMsg
, ContextOfException::ReqInit
, error
,
2302 static void handle_invoke_exception(bool &ret
, ExecutionContext
*context
,
2303 std::string
&errorMsg
, bool &error
,
2304 bool richErrorMsg
) {
2305 handle_exception(ret
, context
, errorMsg
, ContextOfException::Invoke
, error
,
2309 static bool hphp_warmup(ExecutionContext
*context
,
2310 const std::string
&reqInitFunc
,
2311 const std::string
&reqInitDoc
, bool &error
) {
2314 std::string errorMsg
;
2316 ServerStatsHelper
ssh("reqinit");
2318 if (!reqInitDoc
.empty()) {
2319 include_impl_invoke(reqInitDoc
, true);
2321 if (!reqInitFunc
.empty()) {
2322 invoke(reqInitFunc
.c_str(), Array());
2324 context
->backupSession();
2326 handle_reqinit_exception(ret
, context
, errorMsg
, error
);
2332 void hphp_session_init() {
2333 assert(!s_sessionInitialized
);
2334 g_context
.getCheck();
2335 AsioSession::Init();
2336 Socket::clearLastError();
2337 TI().onSessionInit();
2338 MM().resetExternalStats();
2340 g_thread_safe_locale_handler
->reset();
2341 Treadmill::startRequest();
2343 #ifdef ENABLE_SIMPLE_COUNTER
2344 SimpleCounter::Enabled
= true;
2345 StackTrace::Enabled
= true;
2348 // Ordering is sensitive; StatCache::requestInit produces work that
2349 // must be done in ExecutionContext::requestInit.
2350 StatCache::requestInit();
2352 // Allow request event handlers to be created now that a new request has
2354 g_context
->acceptRequestEventHandlers(true);
2356 g_context
->requestInit();
2357 s_sessionInitialized
= true;
2358 ExtensionRegistry::requestInit();
2360 // Sample function calls for this request
2361 if (RID().logFunctionCalls()) {
2362 EventHook::Enable();
2365 auto const pme_freq
= RuntimeOption::EvalPerfMemEventRequestFreq
;
2366 if (pme_freq
> 0 && folly::Random::rand32(pme_freq
) == 0) {
2367 // Enable memory access sampling for this request.
2369 RuntimeOption::EvalPerfMemEventSampleFreq
,
2370 [] (PerfEvent
) { setSurpriseFlag(PendingPerfEventFlag
); }
2375 bool hphp_invoke_simple(const std::string
& filename
, bool warmupOnly
) {
2377 std::string errorMsg
;
2378 return hphp_invoke(g_context
.getNoCheck(), filename
, false, null_array
,
2379 uninit_null(), "", "", error
, errorMsg
,
2382 false /* richErrorMsg */);
2385 bool hphp_invoke(ExecutionContext
*context
, const std::string
&cmd
,
2386 bool func
, const Array
& funcParams
, VRefParam funcRet
,
2387 const std::string
&reqInitFunc
, const std::string
&reqInitDoc
,
2388 bool &error
, std::string
&errorMsg
,
2389 bool once
, bool warmupOnly
,
2390 bool richErrorMsg
) {
2392 RuntimeOption::ServerExecutionMode() && !is_cli_mode();
2395 // Make sure we have the right current working directory within the repo
2396 // based on what server.source_root was set to (current process directory
2397 // being the default)
2398 if (RuntimeOption::RepoAuthoritative
) {
2399 context
->setCwd(RuntimeOption::SourceRoot
);
2404 oldCwd
= context
->getCwd();
2406 if (!hphp_warmup(context
, reqInitFunc
, reqInitDoc
, error
)) {
2407 if (isServer
) context
->setCwd(oldCwd
);
2411 MM().resetCouldOOM(isStandardRequest());
2417 ServerStatsHelper
ssh("invoke");
2418 if (!RuntimeOption::AutoPrependFile
.empty() &&
2419 RuntimeOption::AutoPrependFile
!= "none") {
2420 require(RuntimeOption::AutoPrependFile
, false,
2421 context
->getCwd().data(), true);
2424 funcRet
.assignIfRef(invoke(cmd
.c_str(), funcParams
));
2426 if (isServer
) hphp_chdir_file(cmd
);
2427 include_impl_invoke(cmd
.c_str(), once
);
2429 if (!RuntimeOption::AutoAppendFile
.empty() &&
2430 RuntimeOption::AutoAppendFile
!= "none") {
2431 require(RuntimeOption::AutoAppendFile
, false,
2432 context
->getCwd().data(), true);
2435 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
2440 context
->onShutdownPreSend();
2442 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
2445 if (isServer
) context
->setCwd(oldCwd
);
2449 void hphp_context_shutdown() {
2450 // Run shutdown handlers. This may cause user code to run.
2451 g_thread_safe_locale_handler
->reset();
2453 auto const context
= g_context
.getNoCheck();
2454 context
->destructObjects();
2455 context
->onRequestShutdown();
2458 // Shutdown the debugger. This can throw, but we don't care about what the
2460 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestShutdownHook());
2462 // Gotta catch 'em all!
2465 // Extensions could have shutdown handlers
2466 ExtensionRegistry::requestShutdown();
2467 InitFiniNode::RequestFini();
2469 // Extension shutdown could have re-initialized some
2471 context
->onRequestShutdown();
2473 // This causes request event handler registration to fail until the next
2475 context
->acceptRequestEventHandlers(false);
2478 void hphp_context_exit(bool shutdown
/* = true */) {
2480 hphp_context_shutdown();
2483 // Clean up a bunch of request state. No user code after this point.
2484 MemoryManager::setExiting();
2485 auto const context
= g_context
.getNoCheck();
2486 context
->requestExit();
2487 context
->obProtect(false);
2488 context
->obEndAll();
2491 void hphp_memory_cleanup() {
2493 // sweep functions are allowed to access g_context,
2494 // so we can't destroy it yet
2497 // We should never have any registered RequestEventHandlers. If we do
2498 // something after onRequestShutdown registered a RequestEventHandler.
2499 // Its now too late to run the requestShutdown functions, but if we carry
2500 // on, requestInit and requestShutdown will never be called again.
2501 // I considered just clearing the inited flags; which works for some
2502 // RequestEventHandlers - but its a disaster for others. So just fail hard
2504 always_assert(g_context
.isNull() || !g_context
->hasRequestEventHandlers());
2506 // g_context is request allocated, and has some members that need
2507 // cleanup, so destroy it before its too late
2508 g_context
.destroy();
2511 mm
.resetAllocator();
2515 void hphp_session_exit(const Transport
* transport
) {
2516 assert(s_sessionInitialized
);
2517 // Server note and INI have to live long enough for the access log to fire.
2518 // RequestLocal is too early.
2519 ServerNote::Reset();
2520 IniSetting::ResetSavedDefaults();
2521 // In JitPGO mode, check if it's time to schedule the retranslation of all
2522 // profiled functions and, if so, schedule it.
2523 jit::mcgen::checkRetranslateAll();
2524 jit::tc::requestExit();
2525 // Similarly, apc strings could be in the ServerNote array, and
2526 // it's possible they are scheduled to be destroyed after this request
2528 Treadmill::finishRequest();
2530 TI().onSessionExit();
2533 std::unique_ptr
<StructuredLogEntry
> entry
;
2534 if (RuntimeOption::EvalProfileHWStructLog
) {
2535 entry
= std::make_unique
<StructuredLogEntry
>();
2536 entry
->setInt("response_code", transport
->getResponseCode());
2538 HardwareCounter::UpdateServiceData(transport
->getCpuTime(),
2539 transport
->getWallTime(),
2542 if (entry
) StructuredLog::log("hhvm_request_perf", *entry
);
2545 // We might have events from after the final surprise flag check of the
2546 // request, so consume them here.
2547 perf_event_consume(record_perf_mem_event
);
2548 perf_event_disable();
2551 ServerStatsHelper
ssh("rollback");
2553 hphp_memory_cleanup();
2556 assert(MM().empty());
2558 s_sessionInitialized
= false;
2559 s_extra_request_nanoseconds
= 0;
2562 void hphp_process_exit() noexcept
{
2563 // We want to do clean up on a best-effort basis: don't skip later steps if
2564 // an earlier step fails, and don't propagate exceptions ouf of this function
2565 #define LOG_AND_IGNORE(voidexpr) try { voidexpr; } catch (...) { \
2566 Logger::Error("got exception in cleanup step: " #voidexpr); }
2567 LOG_AND_IGNORE(teardown_cli_server())
2568 LOG_AND_IGNORE(Xenon::getInstance().stop())
2569 LOG_AND_IGNORE(jit::mcgen::joinWorkerThreads())
2570 LOG_AND_IGNORE(jit::tc::processExit())
2571 LOG_AND_IGNORE(PageletServer::Stop())
2572 LOG_AND_IGNORE(XboxServer::Stop())
2573 // Debugger::Stop() needs an execution context
2574 LOG_AND_IGNORE(g_context
.getCheck())
2575 LOG_AND_IGNORE(Eval::Debugger::Stop())
2576 LOG_AND_IGNORE(g_context
.destroy())
2577 LOG_AND_IGNORE(ExtensionRegistry::moduleShutdown())
2578 LOG_AND_IGNORE(compilers_shutdown())
2580 LOG_AND_IGNORE(LightProcess::Close())
2582 LOG_AND_IGNORE(InitFiniNode::ProcessFini())
2583 LOG_AND_IGNORE(folly::SingletonVault::singleton()->destroyInstances())
2584 LOG_AND_IGNORE(embedded_data_cleanup())
2585 #undef LOG_AND_IGNORE
2588 bool is_hphp_session_initialized() {
2589 return s_sessionInitialized
;
2592 static struct SetThreadInitFini
{
2593 template<class ThreadT
> static typename
std::enable_if
<
2594 std::is_integral
<ThreadT
>::value
|| std::is_pointer
<ThreadT
>::value
>::type
2595 recordThreadAddr(ThreadT threadId
, char* stackAddr
, size_t stackSize
) {
2596 // In the current glibc implementation, pthread_t is a 64-bit unsigned
2597 // integer, whose value equals the address of the thread control block
2598 // (TCB). In x64_64, this is right above the TLS block. In addition,
2599 // TLS and TCB sits at the high end of the stack, i.e.,
2601 // stackAddr + stackSize ----> +---------------+
2603 // threadId ----> +---------------+
2605 // +---------------+
2609 // stackAddr ----> +---------------+
2610 auto const tcbBase
= reinterpret_cast<char*>(threadId
);
2611 auto stackEnd
= stackAddr
+ stackSize
;
2612 if (tcbBase
> stackAddr
&& tcbBase
< stackEnd
) { // the expected layout
2614 Debug::DebugInfo::recordDataMap(
2616 folly::sformat("Thread-{}", static_cast<void*>(tcbBase
)));
2618 auto const tlsRange
= getCppTdata();
2619 auto const tlsSize
= (tlsRange
.second
+ 15) / 16 * 16;
2620 stackEnd
= tcbBase
- tlsSize
;
2622 Debug::DebugInfo::recordDataMap(
2624 folly::sformat("TLS-{}", static_cast<void*>(tcbBase
)));
2627 Debug::DebugInfo::recordDataMap(
2628 stackAddr
, stackEnd
,
2629 folly::sformat("Stack-{}", static_cast<void*>(tcbBase
)));
2631 template <class ThreadT
>
2632 static typename
std::enable_if
<!std::is_integral
<ThreadT
>::value
&&
2633 !std::is_pointer
<ThreadT
>::value
>::type
2634 recordThreadAddr(ThreadT
/*threadId*/, char* stackAddr
, size_t stackSize
) {
2635 // pthread_t is not an integer or pointer to TCB in this pthread
2636 // implementation. But we can still figure out where TLS is.
2637 auto const tlsRange
= getCppTdata();
2638 auto const tlsSize
= (tlsRange
.second
+ 15) / 16 * 16;
2639 auto const tlsBaseAddr
= reinterpret_cast<char*>(tlsBase());
2640 Debug::DebugInfo::recordDataMap(
2641 tlsBaseAddr
, tlsBaseAddr
+ tlsSize
,
2642 folly::sformat("TLS-{}", static_cast<void*>(stackAddr
)));
2643 Debug::DebugInfo::recordDataMap(
2644 stackAddr
, stackAddr
+ stackSize
,
2645 folly::sformat("Stack-{}", static_cast<void*>(stackAddr
)));
2648 SetThreadInitFini() {
2649 AsyncFuncImpl::SetThreadInitFunc(
2651 #if defined(_GNU_SOURCE) && defined(__linux__)
2652 if (RuntimeOption::EvalPerfDataMap
) {
2653 pthread_t threadId
= pthread_self();
2654 pthread_attr_t attr
;
2655 pthread_getattr_np(threadId
, &attr
);
2656 void* stackAddr
{nullptr};
2657 size_t stackSize
{0};
2658 pthread_attr_getstack(&attr
, &stackAddr
, &stackSize
);
2659 pthread_attr_destroy(&attr
);
2660 recordThreadAddr(threadId
, static_cast<char*>(stackAddr
), stackSize
);
2666 AsyncFuncImpl::SetThreadFiniFunc([](void*) { hphp_thread_exit(); },
2669 } s_SetThreadInitFini
;
2671 ///////////////////////////////////////////////////////////////////////////////