2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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/types.h"
19 #include "hphp/runtime/base/type-conversions.h"
20 #include "hphp/runtime/base/php-globals.h"
21 #include "hphp/runtime/base/builtin-functions.h"
22 #include "hphp/runtime/base/execution-context.h"
23 #include "hphp/runtime/base/thread-init-fini.h"
24 #include "hphp/runtime/base/code-coverage.h"
25 #include "hphp/runtime/base/runtime-option.h"
26 #include "hphp/runtime/base/pprof-server.h"
27 #include "hphp/runtime/base/ini-setting.h"
28 #include "hphp/runtime/server/pagelet-server.h"
29 #include "hphp/runtime/server/xbox-server.h"
30 #include "hphp/runtime/server/http-server.h"
31 #include "hphp/runtime/server/replay-transport.h"
32 #include "hphp/runtime/server/http-request-handler.h"
33 #include "hphp/runtime/server/admin-request-handler.h"
34 #include "hphp/runtime/server/server-stats.h"
35 #include "hphp/runtime/server/server-note.h"
36 #include "hphp/runtime/base/memory-manager.h"
37 #include "hphp/util/process.h"
38 #include "hphp/util/capability.h"
39 #include "hphp/util/embedded-data.h"
40 #include "hphp/util/timer.h"
41 #include "hphp/util/stack-trace.h"
42 #include "hphp/util/light-process.h"
43 #include "hphp/util/repo-schema.h"
44 #include "hphp/util/current-executable.h"
45 #include "hphp/util/service-data.h"
46 #include "hphp/runtime/base/file-util.h"
47 #include "hphp/runtime/base/stat-cache.h"
48 #include "hphp/runtime/ext/extension.h"
49 #include "hphp/runtime/ext/ext_fb.h"
50 #include "hphp/runtime/ext/json/ext_json.h"
51 #include "hphp/runtime/ext/std/ext_std_variable.h"
52 #include "hphp/runtime/ext/ext_apc.h"
53 #include "hphp/runtime/ext/ext_function.h"
54 #include "hphp/runtime/ext/std/ext_std_options.h"
55 #include "hphp/runtime/ext/ext_file.h"
56 #include "hphp/runtime/ext/ext_xenon.h"
57 #include "hphp/runtime/debugger/debugger.h"
58 #include "hphp/runtime/debugger/debugger_client.h"
59 #include "hphp/runtime/debugger/debugger_hook_handler.h"
60 #include "hphp/runtime/base/simple-counter.h"
61 #include "hphp/runtime/base/extended-logger.h"
62 #include "hphp/runtime/base/stream-wrapper-registry.h"
63 #include "hphp/runtime/vm/debug/debug.h"
64 #include "hphp/runtime/vm/jit/mc-generator.h"
65 #include "hphp/system/constants.h"
66 #include "hphp/runtime/base/config.h"
67 #include "hphp/runtime/base/backtrace.h"
69 #include <boost/program_options/options_description.hpp>
70 #include <boost/program_options/positional_options.hpp>
71 #include <boost/program_options/variables_map.hpp>
72 #include <boost/program_options/parsers.hpp>
74 #include <oniguruma.h>
76 #include <libxml/parser.h>
83 #include "hphp/runtime/base/arch.h"
84 #include "hphp/runtime/base/unit-cache.h"
86 #include "hphp/runtime/vm/runtime.h"
87 #include "hphp/runtime/vm/runtime-type-profiler.h"
88 #include "hphp/runtime/vm/repo.h"
89 #include "hphp/runtime/vm/jit/translator.h"
90 #include "hphp/compiler/builtin_symbols.h"
92 #if (defined(__CYGWIN__) || defined(__MINGW__) || defined(_MSC_VER))
98 using namespace boost::program_options
;
100 extern char **environ
;
102 #define MAX_INPUT_NESTING_LEVEL 64
106 extern InitFiniNode
*extra_process_init
, *extra_process_exit
;
108 void initialize_repo();
111 * XXX: VM process initialization is handled through a function
112 * pointer so libhphp_runtime.a can be linked into programs that don't
113 * actually initialize the VM.
115 void (*g_vmProcessInit
)();
117 ///////////////////////////////////////////////////////////////////////////////
120 struct ProgramOptions
{
122 std::vector
<std::string
>
124 std::vector
<std::string
>
126 std::vector
<std::string
>
137 bool noSafeAccessCheck
;
138 std::vector
<std::string
>
146 Eval::DebuggerClientOptions debugger_options
;
151 StartTime() : startTime(time(nullptr)) {}
154 static StartTime s_startTime
;
155 static string tempFile
;
157 time_t start_time() {
158 return s_startTime
.startTime
;
164 s_HHVM_JIT("HHVM_JIT"),
165 s_HHVM_ARCH("HHVM_ARCH"),
166 s_REQUEST_START_TIME("REQUEST_START_TIME"),
167 s_REQUEST_TIME("REQUEST_TIME"),
168 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
169 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
170 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
171 s_SCRIPT_NAME("SCRIPT_NAME"),
172 s_PHP_SELF("PHP_SELF"),
176 s_HOSTNAME("HOSTNAME"),
177 s__SERVER("_SERVER"),
185 static void process_cmd_arguments(int argc
, char **argv
) {
186 php_global_set(s_argc
, Variant(argc
));
187 Array
argvArray(staticEmptyArray());
188 for (int i
= 0; i
< argc
; i
++) {
189 argvArray
.append(String(argv
[i
]));
191 php_global_set(s_argv
, argvArray
);
194 void process_env_variables(Array
& variables
) {
195 for (auto& kv
: RuntimeOption::EnvVariables
) {
196 variables
.set(String(kv
.first
), String(kv
.second
));
198 for (char **env
= environ
; env
&& *env
; env
++) {
199 char *p
= strchr(*env
, '=');
201 String
name(*env
, p
- *env
, CopyString
);
202 register_variable(variables
, (char*)name
.data(),
203 String(p
+ 1, CopyString
));
208 void process_ini_file(const std::string
& filename
) {
209 if (filename
.empty()) {
212 std::ifstream
ifs(filename
);
213 const std::string
str((std::istreambuf_iterator
<char>(ifs
)),
214 std::istreambuf_iterator
<char>());
215 process_ini_settings(str
, filename
);
218 void process_ini_settings(const std::string
& ini_str
,
219 const std::string
& filename
/* = "" */) {
220 auto settings
= IniSetting::FromStringAsMap(ini_str
, filename
);
222 for (auto& item
: settings
.items()) {
223 IniSetting::Set(item
.first
.data(), item
.second
,
224 IniSetting::FollyDynamic());
227 // Handle adding a variable to an array, supporting keys that look
228 // like array expressions (like 'FOO[][key1][k2]').
229 void register_variable(Array
& variables
, char *name
, const Variant
& value
,
230 bool overwrite
/* = true */) {
231 // ignore leading spaces in the variable name
233 while (*var
&& *var
== ' ') {
237 // ensure that we don't have spaces or dots in the variable name
239 bool is_array
= false;
240 char *ip
= nullptr; // index pointer
243 if (*p
== ' ' || *p
== '.') {
245 } else if (*p
== '[') {
252 int var_len
= p
- var
;
254 // empty variable name, or variable name with a space in it
258 // GPC elements holds Variants that are acting as smart pointers to
259 // RefDatas that we've created in the process of a multi-dim key.
260 std::vector
<Variant
> gpc_elements
;
261 if (is_array
) gpc_elements
.reserve(MAX_INPUT_NESTING_LEVEL
);
263 // The array pointer we're currently adding to. If we're doing a
264 // multi-dimensional set, this will point at the m_data.parr inside
265 // of a RefData sometimes (via toArrRef on the variants in
267 Array
* symtable
= &variables
;
270 int index_len
= var_len
;
275 if (++nest_level
> MAX_INPUT_NESTING_LEVEL
) {
276 Logger::Warning("Input variable nesting level exceeded");
289 ip
= strchr(ip
, ']');
291 // PHP variables cannot contain '[' in their names,
292 // so we replace the character with a '_'
293 *(index_s
- 1) = '_';
297 index_len
= strlen(index
);
302 new_idx_len
= strlen(index_s
);
306 auto& val
= symtable
->lvalAt();
307 val
= Array::Create();
308 gpc_elements
.push_back(uninit_null());
309 gpc_elements
.back().assignRef(val
);
311 String
key(index
, index_len
, CopyString
);
312 Variant v
= symtable
->rvalAt(key
);
313 if (v
.isNull() || !v
.is(KindOfArray
)) {
314 symtable
->set(key
, Array::Create());
316 gpc_elements
.push_back(uninit_null());
317 gpc_elements
.back().assignRef(symtable
->lvalAt(key
));
319 symtable
= &gpc_elements
.back().toArrRef();
320 /* ip pointed to the '[' character, now obtain the key */
322 index_len
= new_idx_len
;
335 symtable
->append(value
);
337 String
key(index
, index_len
, CopyString
);
338 if (overwrite
|| !symtable
->exists(key
)) {
339 symtable
->set(key
, value
);
345 enum class ContextOfException
{
351 static void handle_exception_append_bt(std::string
& errorMsg
,
352 const ExtendedException
& e
) {
353 Array bt
= e
.getBacktrace();
355 errorMsg
+= ExtendedLogger::StringOfStackTrace(bt
);
359 void bump_counter_and_rethrow(bool isPsp
) {
362 } catch (const RequestTimeoutException
& e
) {
364 static auto requestTimeoutPSPCounter
= ServiceData::createTimeseries(
365 "requests_timed_out_psp", {ServiceData::StatsType::COUNT
});
366 requestTimeoutPSPCounter
->addValue(1);
367 ServerStats::Log("request.timed_out.psp", 1);
369 static auto requestTimeoutCounter
= ServiceData::createTimeseries(
370 "requests_timed_out_non_psp", {ServiceData::StatsType::COUNT
});
371 requestTimeoutCounter
->addValue(1);
372 ServerStats::Log("request.timed_out.non_psp", 1);
375 } catch (const RequestMemoryExceededException
& e
) {
377 static auto requestMemoryExceededPSPCounter
=
378 ServiceData::createTimeseries(
379 "requests_memory_exceeded_psp", {ServiceData::StatsType::COUNT
});
380 requestMemoryExceededPSPCounter
->addValue(1);
381 ServerStats::Log("request.memory_exceeded.psp", 1);
383 static auto requestMemoryExceededCounter
= ServiceData::createTimeseries(
384 "requests_memory_exceeded_non_psp", {ServiceData::StatsType::COUNT
});
385 requestMemoryExceededCounter
->addValue(1);
386 ServerStats::Log("request.memory_exceeded.non_psp", 1);
390 // Capture a pprof (C++) dump when we OOM a request
391 // TODO: (t3753133) Should dump a PHP-instrumented pprof dump here as well
392 jemalloc_pprof_dump("", false);
399 static void handle_exception_helper(bool& ret
,
400 ExecutionContext
* context
,
401 std::string
& errorMsg
,
402 ContextOfException where
,
406 bump_counter_and_rethrow(false /* isPsp */);
407 } catch (const Eval::DebuggerException
&e
) {
409 } catch (const ExitException
&e
) {
410 if (where
== ContextOfException::ReqInit
) {
412 } else if (where
!= ContextOfException::Handler
&&
413 !context
->getExitCallback().isNull() &&
414 f_is_callable(context
->getExitCallback())) {
415 Array stack
= e
.getBacktrace();
416 Array argv
= make_packed_array(ExitException::ExitCode
.load(), stack
);
417 vm_call_user_func(context
->getExitCallback(), argv
);
419 } catch (const PhpFileDoesNotExistException
&e
) {
421 if (where
!= ContextOfException::Handler
) {
422 raise_notice("%s", e
.getMessage().c_str());
424 Logger::Error("%s", e
.getMessage().c_str());
427 handle_exception_append_bt(errorMsg
, e
);
429 } catch (const Exception
&e
) {
431 bool origError
= error
;
432 std::string origErrorMsg
= errorMsg
;
436 if (where
== ContextOfException::Handler
) {
437 errorMsg
= "Exception handler threw an exception: ";
439 errorMsg
+= e
.what();
440 if (where
== ContextOfException::Invoke
) {
441 bool handlerRet
= context
->onFatalError(e
);
445 errorMsg
= origErrorMsg
;
448 Logger::Error("%s", errorMsg
.c_str());
451 const ExtendedException
*ee
= dynamic_cast<const ExtendedException
*>(&e
);
453 handle_exception_append_bt(errorMsg
, *ee
);
456 } catch (const Object
&e
) {
458 bool origError
= error
;
459 std::string origErrorMsg
= errorMsg
;
463 if (where
== ContextOfException::Handler
) {
464 errorMsg
= "Exception handler threw an object exception: ";
467 errorMsg
+= e
.toString().data();
469 errorMsg
+= "(unable to call toString())";
471 if (where
== ContextOfException::Invoke
) {
472 bool handlerRet
= context
->onUnhandledException(e
);
476 errorMsg
= origErrorMsg
;
479 Logger::Error("%s", errorMsg
.c_str());
484 errorMsg
= "(unknown exception was thrown)";
485 Logger::Error("%s", errorMsg
.c_str());
489 static bool hphp_chdir_file(const string filename
) {
491 String s
= File::TranslatePath(filename
);
492 char *buf
= strndup(s
.data(), s
.size());
493 char *dir
= dirname(buf
);
496 if (File::IsVirtualDirectory(dir
)) {
497 g_context
->setCwd(String(dir
, CopyString
));
502 if ((sb
.st_mode
& S_IFMT
) == S_IFDIR
) {
505 g_context
->setCwd(String(dir
, CopyString
));
514 static void handle_resource_exceeded_exception() {
517 } catch (RequestTimeoutException
&) {
518 ThreadInfo::s_threadInfo
->m_reqInjectionData
.setTimedOutFlag();
519 } catch (RequestMemoryExceededException
&) {
520 ThreadInfo::s_threadInfo
->m_reqInjectionData
.setMemExceededFlag();
524 void handle_destructor_exception(const char* situation
) {
529 } catch (ExitException
&e
) {
530 // ExitException is fine, no need to show a warning.
531 ThreadInfo::s_threadInfo
->setPendingException(e
.clone());
533 } catch (Object
&e
) {
534 // For user exceptions, invoke the user exception handler
535 errorMsg
= situation
;
536 errorMsg
+= " threw an object exception: ";
538 errorMsg
+= e
.toString().data();
540 handle_resource_exceeded_exception();
541 errorMsg
+= "(unable to call toString())";
543 } catch (Exception
&e
) {
544 ThreadInfo::s_threadInfo
->setPendingException(e
.clone());
545 errorMsg
= situation
;
546 errorMsg
+= " raised a fatal error: ";
547 errorMsg
+= e
.what();
549 errorMsg
= situation
;
550 errorMsg
+= " threw an unknown exception";
552 // For fatal errors and unknown exceptions, we raise a warning.
553 // If there is a user error handler it will be invoked, otherwise
554 // the default error handler will be invoked.
556 raise_debugging("%s", errorMsg
.c_str());
558 handle_resource_exceeded_exception();
560 // The user error handler fataled or threw an exception,
561 // print out the error message directly to the log
562 Logger::Warning("%s", errorMsg
.c_str());
566 void execute_command_line_begin(int argc
, char **argv
, int xhprof
,
567 const std::vector
<std::string
>& config
) {
568 StackTraceNoHeap::AddExtraLogging("ThreadType", "CLI");
570 for (int i
= 0; i
< argc
; i
++) {
574 StackTraceNoHeap::AddExtraLogging("Arguments", args
.c_str());
577 auto const context
= g_context
.getNoCheck();
578 context
->obSetImplicitFlush(true);
581 Array
envArr(Array::Create());
582 process_env_variables(envArr
);
583 envArr
.set(s_HPHP
, 1);
584 envArr
.set(s_HHVM
, 1);
585 if (RuntimeOption::EvalJit
) {
586 envArr
.set(s_HHVM_JIT
, 1);
590 envArr
.set(s_HHVM_ARCH
, "x64");
593 envArr
.set(s_HHVM_ARCH
, "arm");
596 php_global_set(s__ENV
, envArr
);
599 process_cmd_arguments(argc
, argv
);
602 Array
serverArr(Array::Create());
603 process_env_variables(serverArr
);
605 struct timeval tp
= {0};
607 if (!gettimeofday(&tp
, nullptr)) {
608 now_double
= (double)(tp
.tv_sec
+ tp
.tv_usec
/ 1000000.00);
612 now_double
= (double)now
;
614 String file
= empty_string();
616 file
= StringData::Make(argv
[0], CopyString
);
618 serverArr
.set(s_REQUEST_START_TIME
, now
);
619 serverArr
.set(s_REQUEST_TIME
, now
);
620 serverArr
.set(s_REQUEST_TIME_FLOAT
, now_double
);
621 serverArr
.set(s_DOCUMENT_ROOT
, empty_string_variant_ref
);
622 serverArr
.set(s_SCRIPT_FILENAME
, file
);
623 serverArr
.set(s_SCRIPT_NAME
, file
);
624 serverArr
.set(s_PHP_SELF
, file
);
625 serverArr
.set(s_argv
, php_global(s_argv
));
626 serverArr
.set(s_argc
, php_global(s_argc
));
627 serverArr
.set(s_PWD
, g_context
->getCwd());
629 if (RuntimeOption::ServerExecutionMode() && !gethostname(hostname
, 1024)) {
630 serverArr
.set(s_HOSTNAME
, String(hostname
, CopyString
));
633 for (auto& kv
: RuntimeOption::ServerVariables
) {
634 serverArr
.set(String(kv
.first
.c_str()), String(kv
.second
.c_str()));
637 php_global_set(s__SERVER
, serverArr
);
641 f_xhprof_enable(xhprof
, uninit_null().toArray());
644 if (RuntimeOption::RequestTimeoutSeconds
) {
645 ThreadInfo::s_threadInfo
->m_reqInjectionData
.setTimeout(
646 RuntimeOption::RequestTimeoutSeconds
);
649 if (RuntimeOption::XenonForceAlwaysOn
) {
650 Xenon::getInstance().surpriseAll();
653 Extension::RequestInitModules();
654 // If extension constants were used in the in ini files, they would have come
655 // out as 0 in the previous pass. Lets re-import the ini files. We could be
656 // more clever, but that would be harder and this works.
657 for (auto& c
: config
) {
661 // Initialize the debugger
662 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestInitHook());
665 void execute_command_line_end(int xhprof
, bool coverage
, const char *program
) {
666 ThreadInfo
*ti
= ThreadInfo::s_threadInfo
.getNoCheck();
668 if (RuntimeOption::EvalDumpTC
) {
669 HPHP::JIT::tc_dump();
673 HHVM_FN(var_dump
)(HHVM_FN(json_encode
)(f_xhprof_disable()));
675 g_context
->onShutdownPostSend();
676 Eval::Debugger::InterruptPSPEnded(program
);
679 if (coverage
&& ti
->m_reqInjectionData
.getCoverage() &&
680 !RuntimeOption::CodeCoverageOutputFile
.empty()) {
681 ti
->m_coverage
->Report(RuntimeOption::CodeCoverageOutputFile
);
685 #if defined(__APPLE__) || defined(__CYGWIN__)
686 const void* __hot_start
= nullptr;
687 const void* __hot_end
= nullptr;
690 void __attribute__((__weak__
)) __hot_start();
691 void __attribute__((__weak__
)) __hot_end();
696 # define AT_END_OF_TEXT __attribute__((__section__(".stub")))
698 # define AT_END_OF_TEXT
701 static void NEVER_INLINE AT_END_OF_TEXT
__attribute__((__optimize__("2")))
702 hugifyText(char* from
, char* to
) {
703 #if FACEBOOK && !defined FOLLY_SANITIZE_ADDRESS && defined MADV_HUGEPAGE
704 if (from
> to
|| (to
- from
) < sizeof(uint64_t)) {
705 // This shouldn't happen if HHVM is behaving correctly (I think),
706 // but if it does then there is nothing to do and we should bail
707 // out early because the call to wordcpy() below can't handle
708 // zero size or negative sizes.
711 size_t sz
= to
- from
;
712 void* mem
= malloc(sz
);
713 memcpy(mem
, from
, sz
);
715 // This maps out a portion of our executable
716 // We need to be very careful about what we do
717 // until we replace the original code
719 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
720 MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_FIXED
,
722 // This is in glibc, which isn't a problem, except for
723 // the trampoline code in .plt, which we dealt with
724 // in the linker script
725 madvise(from
, sz
, MADV_HUGEPAGE
);
726 // Don't use memcpy because its probably one of the
727 // functions thats been mapped out.
728 // Needs the attribute((optimize("2")) to prevent
729 // g++ from turning this back into memcpy(!)
730 wordcpy((uint64_t*)from
, (uint64_t*)mem
, sz
/ sizeof(uint64_t));
731 mprotect(from
, sz
, PROT_READ
| PROT_EXEC
);
733 mlock(from
, to
- from
);
734 Debug::DebugInfo::setPidMapOverlay(from
, to
);
738 static void pagein_self(void) {
739 unsigned long begin
, end
, inode
, pgoff
;
740 char mapname
[PATH_MAX
];
748 // pad due to the spaces between the inode number and the mapname
749 bufsz
= sizeof(unsigned long) * 4 + sizeof(mapname
) + sizeof(char) * 11 + 100;
750 buf
= (char *)malloc(bufsz
);
754 Timer
timer(Timer::WallTime
, "mapping self");
755 fp
= fopen("/proc/self/maps", "r");
758 if (fgets(buf
, bufsz
, fp
) == 0)
760 r
= sscanf(buf
, "%lx-%lx %4s %lx %5s %ld %s",
761 &begin
, &end
, perm
, &pgoff
, dev
, &inode
, mapname
);
763 // page in read-only segments that correspond to a file on disk
767 access(mapname
, F_OK
) != 0) {
771 auto beginPtr
= (char*)begin
;
772 auto endPtr
= (char*)end
;
773 auto hotStart
= (char*)__hot_start
;
774 auto hotEnd
= (char*)__hot_end
;
775 const size_t hugeBytes
= 2L * 1024 * 1024;
777 if (mlock(beginPtr
, end
- begin
) == 0) {
778 if (RuntimeOption::EvalMapHotTextHuge
&&
781 hugePagesSupported() &&
782 beginPtr
<= hotStart
&&
785 char* from
= hotStart
- ((intptr_t)hotStart
& (hugeBytes
- 1));
786 char* to
= hotEnd
+ (hugeBytes
- 1);
787 to
-= (intptr_t)to
& (hugeBytes
- 1);
788 if (to
< (void*)hugifyText
) {
789 hugifyText(from
, to
);
792 if (!RuntimeOption::LockCodeMemory
) {
793 munlock(beginPtr
, end
- begin
);
802 /* Sets RuntimeOption::ExecutionMode according
803 * to commandline options prior to config load
805 static void set_execution_mode(string mode
) {
806 if (mode
== "daemon" || mode
== "server" || mode
== "replay") {
807 RuntimeOption::ExecutionMode
= "srv";
808 Logger::Escape
= true;
809 } else if (mode
== "run" || mode
== "debug") {
810 RuntimeOption::ExecutionMode
= "cli";
811 Logger::Escape
= false;
812 } else if (mode
== "translate") {
813 RuntimeOption::ExecutionMode
= "";
814 Logger::Escape
= false;
817 always_assert(false);
821 static int start_server(const std::string
&username
) {
822 // Before we start the webserver, make sure the entire
823 // binary is paged into memory.
826 set_execution_mode("server");
827 HttpRequestHandler::GetAccessLog().init
828 (RuntimeOption::AccessLogDefaultFormat
, RuntimeOption::AccessLogs
,
830 AdminRequestHandler::GetAccessLog().init
831 (RuntimeOption::AdminLogFormat
, RuntimeOption::AdminLogSymLink
,
832 RuntimeOption::AdminLogFile
,
835 #if !defined(SKIP_USER_CHANGE)
836 if (!username
.empty()) {
837 if (Logger::UseCronolog
) {
838 Cronolog::changeOwner(username
, RuntimeOption::LogFileSymLink
);
840 Capability::ChangeUnixUser(username
);
841 LightProcess::ChangeUser(username
);
843 Capability::SetDumpable();
846 // Create the HttpServer before any warmup requests to properly
847 // initialize the process
848 HttpServer::Server
= std::make_shared
<HttpServer
>();
850 // If we have any warmup requests, replay them before listening for
853 profileWarmupStart();
854 SCOPE_EXIT
{ profileWarmupEnd(); };
855 for (auto& file
: RuntimeOption::ServerWarmupRequests
) {
856 HttpRequestHandler
handler(0);
859 Timer::GetMonotonicTime(start
);
861 Logger::Info("Replaying warmup request %s", file
.c_str());
864 rt
.onRequestStart(start
);
865 rt
.replayInput(Hdf(file
));
866 handler
.handleRequest(&rt
);
869 Timer::GetMonotonicTime(stop
);
870 Logger::Info("Finished successfully in %ld seconds",
871 stop
.tv_sec
- start
.tv_sec
);
872 } catch (std::exception
& e
) {
877 Logger::Info("Got exception during warmup: %s", error
.c_str());
882 if (RuntimeOption::EvalEnableNuma
) {
884 mallctl("arenas.purge", nullptr, nullptr, nullptr, 0);
886 enable_numa(RuntimeOption::EvalEnableNumaLocal
);
889 HttpServer::Server
->runOrExitProcess();
890 HttpServer::Server
.reset();
894 string
translate_stack(const char *hexencoded
, bool with_frame_numbers
) {
895 if (!hexencoded
|| !*hexencoded
) {
899 StackTrace
st(hexencoded
);
900 std::vector
<std::shared_ptr
<StackTrace::Frame
>> frames
;
903 std::ostringstream out
;
904 for (unsigned int i
= 0; i
< frames
.size(); i
++) {
906 if (with_frame_numbers
) {
907 out
<< "# " << (i
< 10 ? " " : "") << i
<< ' ';
909 out
<< f
->toString();
915 ///////////////////////////////////////////////////////////////////////////////
917 static void prepare_args(int &argc
,
919 const std::vector
<std::string
> &args
,
921 argv
= (char **)malloc((args
.size() + 2) * sizeof(char*));
924 argv
[argc
++] = (char*)file
;
926 for (int i
= 0; i
< (int)args
.size(); i
++) {
927 argv
[argc
++] = (char*)args
[i
].c_str();
929 argv
[argc
] = nullptr;
932 static int execute_program_impl(int argc
, char **argv
);
933 int execute_program(int argc
, char **argv
) {
937 ret_code
= execute_program_impl(argc
, argv
);
938 } catch (const Exception
&e
) {
939 Logger::Error("Uncaught exception: %s", e
.what());
940 } catch (const FailedAssertion
& fa
) {
942 StackTraceNoHeap::AddExtraLogging("Assertion failure", fa
.summary
);
944 } catch (const std::exception
&e
) {
945 Logger::Error("Uncaught exception: %s", e
.what());
947 Logger::Error("Uncaught exception: (unknown)");
949 if (tempFile
.length() && boost::filesystem::exists(tempFile
)) {
950 boost::filesystem::remove(tempFile
);
955 /* -1 - cannot open file
956 * 0 - no need to open file
960 static int open_server_log_file() {
961 if (!RuntimeOption::LogFile
.empty()) {
962 if (Logger::UseCronolog
) {
963 if (strchr(RuntimeOption::LogFile
.c_str(), '%')) {
964 Logger::cronOutput
.m_template
= RuntimeOption::LogFile
;
965 Logger::cronOutput
.setPeriodicity();
966 Logger::cronOutput
.m_linkName
= RuntimeOption::LogFileSymLink
;
969 Logger::Output
= fopen(RuntimeOption::LogFile
.c_str(), "a");
970 if (Logger::Output
) return 1;
973 if (Logger::IsPipeOutput
) {
974 Logger::Output
= popen(RuntimeOption::LogFile
.substr(1).c_str(), "w");
975 if (Logger::Output
) return 2;
977 Logger::Output
= fopen(RuntimeOption::LogFile
.c_str(), "a");
978 if (Logger::Output
) return 1;
981 Logger::Error("Cannot open log file: %s", RuntimeOption::LogFile
.c_str());
987 static void close_server_log_file(int kind
) {
989 fclose(Logger::Output
);
990 } else if (kind
== 2) {
991 pclose(Logger::Output
);
993 always_assert(!Logger::Output
);
997 static int compute_hhvm_argc(const options_description
& desc
,
998 int argc
, char** argv
) {
1004 const auto& vec
= desc
.options();
1005 std::map
<string
,ArgCode
> long_options
;
1006 std::map
<string
,ArgCode
> short_options
;
1007 // Build lookup maps for the short options and the long options
1008 for (unsigned i
= 0; i
< vec
.size(); ++i
) {
1010 auto long_name
= opt
->long_name();
1011 ArgCode code
= NO_ARG
;
1012 if (opt
->semantic()->max_tokens() == 1) {
1013 if (opt
->semantic()->min_tokens() == 1) {
1014 code
= ARG_REQUIRED
;
1016 code
= ARG_OPTIONAL
;
1019 long_options
[long_name
] = code
;
1020 auto format_name
= opt
->format_name();
1021 if (format_name
.size() >= 2 && format_name
[0] == '-' &&
1022 format_name
[1] != '-') {
1023 auto short_name
= format_name
.substr(1,1);
1024 short_options
[short_name
] = code
;
1027 // Loop over the args
1029 while (pos
< argc
) {
1030 const char* str
= argv
[pos
];
1031 int len
= strlen(str
);
1032 if (len
== 2 && memcmp(str
, "--", 2) == 0) {
1033 // We found "--". All args after this are intended for the
1038 if (len
>= 3 && str
[0] == '-' && str
[1] == '-') {
1039 // Handle long options
1042 auto it
= long_options
.find(s
);
1043 if (it
!= long_options
.end() && it
->second
!= NO_ARG
&& pos
< argc
&&
1044 (it
->second
== ARG_REQUIRED
|| argv
[pos
][0] != '-')) {
1047 } else if (len
>= 2 && str
[0] == '-') {
1048 // Handle short options
1051 s
.append(1, str
[1]);
1052 auto it
= short_options
.find(s
);
1053 if (it
!= short_options
.end() && it
->second
!= 0 && len
== 2 &&
1054 pos
< argc
&& (it
->second
== ARG_REQUIRED
|| argv
[pos
][0] != '-')) {
1058 // We've found a non-option argument. This arg and all args
1059 // that follow are intended for the PHP application
1066 static int execute_program_impl(int argc
, char** argv
) {
1067 string usage
= "Usage:\n\n ";
1069 usage
+= " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
1072 options_description
desc(usage
.c_str());
1074 ("help", "display this message")
1075 ("version", "display version number")
1076 ("php", "emulate the standard php command line")
1077 ("compiler-id", "display the git hash for the compiler")
1078 ("repo-schema", "display the repository schema id")
1079 ("mode,m", value
<string
>(&po
.mode
)->default_value("run"),
1080 "run | debug (d) | server (s) | daemon | replay | translate (t)")
1081 ("interactive,a", "Shortcut for --mode debug") // -a is from PHP5
1082 ("config,c", value
<vector
<string
> >(&po
.config
)->composing(),
1083 "load specified config file")
1085 value
<std::vector
<std::string
>>(&po
.confStrings
)->composing(),
1086 "individual configuration string in a format of name=value, where "
1087 "name can be any valid configuration for a config file")
1088 ("define,d", value
<std::vector
<std::string
>>(&po
.iniStrings
)->composing(),
1089 "define an ini setting in the same format ( foo[=bar] ) as provided in a "
1091 ("no-config", "don't use the default php.ini")
1092 ("port,p", value
<int>(&po
.port
)->default_value(-1),
1093 "start an HTTP server at specified port")
1094 ("port-fd", value
<int>(&po
.portfd
)->default_value(-1),
1095 "use specified fd instead of creating a socket")
1096 ("ssl-port-fd", value
<int>(&po
.sslportfd
)->default_value(-1),
1097 "use specified fd for SSL instead of creating a socket")
1098 ("admin-port", value
<int>(&po
.admin_port
)->default_value(-1),
1099 "start admin listener at specified port")
1100 ("debug-config", value
<string
>(&po
.debugger_options
.configFName
),
1101 "load specified debugger config file")
1103 value
<string
>(&po
.debugger_options
.host
)->implicit_value("localhost"),
1104 "connect to debugger server at specified address")
1105 ("debug-port", value
<int>(&po
.debugger_options
.port
)->default_value(-1),
1106 "connect to debugger server at specified port")
1107 ("debug-extension", value
<string
>(&po
.debugger_options
.extension
),
1108 "PHP file that extends command 'arg'")
1109 ("debug-cmd", value
<std::vector
<std::string
>>(
1110 &po
.debugger_options
.cmds
)->composing(),
1111 "executes this debugger command and returns its output in stdout")
1113 value
<string
>(&po
.debugger_options
.sandbox
)->default_value("default"),
1114 "initial sandbox to attach to when debugger is started")
1115 ("user,u", value
<string
>(&po
.user
),
1116 "run server under this user account")
1117 ("file,f", value
<string
>(&po
.file
),
1118 "execute specified file")
1119 ("lint,l", value
<string
>(&po
.lint
),
1120 "lint specified file")
1121 ("show,w", value
<string
>(&po
.show
),
1122 "output specified file and do nothing else")
1124 "file specified is temporary and removed after execution")
1125 ("count", value
<int>(&po
.count
)->default_value(1),
1126 "how many times to repeat execution")
1127 ("no-safe-access-check",
1128 value
<bool>(&po
.noSafeAccessCheck
)->default_value(false),
1129 "whether to ignore safe file access check")
1130 ("arg", value
<std::vector
<std::string
>>(&po
.args
)->composing(),
1132 ("extra-header", value
<string
>(&Logger::ExtraHeader
),
1133 "extra-header to add to log lines")
1134 ("build-id", value
<string
>(&po
.buildId
),
1135 "unique identifier of compiled server code")
1136 ("instance-id", value
<string
>(&po
.instanceId
),
1137 "unique identifier of server instance")
1138 ("xhprof-flags", value
<int>(&po
.xhprofFlags
)->default_value(0),
1142 positional_options_description p
;
1146 // Before invoking the boost command line parser, we do a manual pass
1147 // to find the first occurrence of either "--" or a non-option argument
1148 // in order to determine which arguments should be consumed by HHVM and
1149 // which arguments should be passed along to the PHP application. This
1150 // is necessary so that the boost command line parser doesn't choke on
1151 // args intended for the PHP application.
1152 int hhvm_argc
= compute_hhvm_argc(desc
, argc
, argv
);
1155 // Invoke the boost command line parser to parse the args for HHVM.
1156 auto opts
= command_line_parser(hhvm_argc
, argv
)
1159 // If these style options are changed, compute_hhvm_argc() will
1160 // need to be updated appropriately
1161 .style(command_line_style::default_style
&
1162 ~command_line_style::allow_guessing
&
1163 ~command_line_style::allow_sticky
&
1164 ~command_line_style::long_allow_adjacent
)
1166 // Manually append the args for the PHP application.
1168 for (unsigned m
= 0; m
< opts
.options
.size(); ++m
) {
1169 const auto& bo
= opts
.options
[m
];
1170 if (bo
.string_key
== "arg") {
1174 for (unsigned m
= hhvm_argc
; m
< argc
; ++m
) {
1175 string str
= argv
[m
];
1176 basic_option
<char> bo
;
1177 bo
.string_key
= "arg";
1178 bo
.position_key
= pos
++;
1179 bo
.value
.push_back(str
);
1180 bo
.original_tokens
.push_back(str
);
1181 bo
.unregistered
= false;
1182 bo
.case_insensitive
= false;
1183 opts
.options
.push_back(bo
);
1185 // Process the options
1188 if (vm
.count("interactive") /* or -a */) {
1191 if (po
.mode
== "d") po
.mode
= "debug";
1192 if (po
.mode
== "s") po
.mode
= "server";
1193 if (po
.mode
== "t") po
.mode
= "translate";
1194 if (po
.mode
== "") po
.mode
= "run";
1195 if (po
.mode
== "daemon" || po
.mode
== "server" || po
.mode
== "replay" ||
1196 po
.mode
== "run" || po
.mode
== "debug"|| po
.mode
== "translate") {
1197 set_execution_mode(po
.mode
);
1199 Logger::Error("Error in command line: invalid mode: %s", po
.mode
.c_str());
1200 cout
<< desc
<< "\n";
1203 if (po
.config
.empty() && !vm
.count("no-config")) {
1204 auto default_config_file
= INSTALL_PREFIX
"/etc/hhvm/php.ini";
1205 if (access(default_config_file
, R_OK
) != -1) {
1206 Logger::Verbose("Using default config file: %s", default_config_file
);
1207 po
.config
.push_back(default_config_file
);
1209 default_config_file
= INSTALL_PREFIX
"/etc/hhvm/config.hdf";
1210 if (access(default_config_file
, R_OK
) != -1) {
1211 Logger::Verbose("Using default config file: %s", default_config_file
);
1212 po
.config
.push_back(default_config_file
);
1215 } catch (error
&e
) {
1216 Logger::Error("Error in command line: %s", e
.what());
1217 cout
<< desc
<< "\n";
1220 Logger::Error("Error in command line.");
1221 cout
<< desc
<< "\n";
1224 if (vm
.count("help")) {
1225 cout
<< desc
<< "\n";
1228 if (vm
.count("version")) {
1229 cout
<< "HipHop VM";
1230 cout
<< " " << k_HHVM_VERSION
.c_str();
1231 cout
<< " (" << (debug
? "dbg" : "rel") << ")\n";
1232 cout
<< "Compiler: " << kCompilerId
<< "\n";
1233 cout
<< "Repo schema: " << kRepoSchemaId
<< "\n";
1234 cout
<< "Extension API: " << std::to_string(HHVM_API_VERSION
) << "\n";
1237 if (vm
.count("compiler-id")) {
1238 cout
<< kCompilerId
<< "\n";
1242 if (vm
.count("repo-schema")) {
1243 cout
<< kRepoSchemaId
<< "\n";
1247 if (!po
.show
.empty()) {
1249 f
.open(po
.show
, "r");
1251 Logger::Error("Unable to open file %s", po
.show
.c_str());
1259 po
.isTempFile
= vm
.count("temp-file");
1261 // forget the source for systemlib.php unless we are debugging
1262 if (po
.mode
!= "debug") SystemLib::s_source
= "";
1264 // we need to initialize pcre cache table very early
1267 MemoryManager::TlsWrapper::getCheck();
1269 IniSetting::Map ini
= IniSetting::Map::object
;
1271 for (auto& c
: po
.config
) {
1272 Config::Parse(c
, ini
, config
);
1274 RuntimeOption::Load(ini
, config
, &po
.confStrings
);
1275 for (auto& c
: po
.config
) {
1276 process_ini_file(c
);
1278 for (auto& istr
: po
.iniStrings
) {
1279 process_ini_settings(istr
, "");
1282 vector
<string
> badnodes
;
1283 config
.lint(badnodes
);
1284 for (unsigned int i
= 0; i
< badnodes
.size(); i
++) {
1285 Logger::Error("Possible bad config node: %s", badnodes
[i
].c_str());
1287 if (RuntimeOption::EvalRuntimeTypeProfile
) {
1288 HPHP::initTypeProfileStructure();
1290 vector
<int> inherited_fds
;
1291 RuntimeOption::BuildId
= po
.buildId
;
1292 RuntimeOption::InstanceId
= po
.instanceId
;
1293 if (po
.port
!= -1) {
1294 RuntimeOption::ServerPort
= po
.port
;
1296 if (po
.portfd
!= -1) {
1297 RuntimeOption::ServerPortFd
= po
.portfd
;
1298 inherited_fds
.push_back(po
.portfd
);
1300 if (po
.sslportfd
!= -1) {
1301 RuntimeOption::SSLPortFd
= po
.sslportfd
;
1302 inherited_fds
.push_back(po
.sslportfd
);
1304 if (po
.admin_port
!= -1) {
1305 RuntimeOption::AdminServerPort
= po
.admin_port
;
1307 if (po
.noSafeAccessCheck
) {
1308 RuntimeOption::SafeFileAccess
= false;
1311 if (po
.mode
== "daemon") {
1312 if (RuntimeOption::LogFile
.empty()) {
1313 Logger::Error("Log file not specified under daemon mode.\n\n");
1315 int ret
= open_server_log_file();
1316 Process::Daemonize();
1317 close_server_log_file(ret
);
1320 open_server_log_file();
1322 // Defer the initialization of light processes until the log file handle is
1323 // created, so that light processes can log to the right place. If we ever
1324 // lose a light process, stop the server instead of proceeding in an
1326 LightProcess::SetLostChildHandler([](pid_t child
) {
1327 if (!HttpServer::Server
) return;
1328 if (!HttpServer::Server
->isStopped()) {
1329 HttpServer::Server
->stop("lost light process child");
1332 LightProcess::Initialize(RuntimeOption::LightProcessFilePrefix
,
1333 RuntimeOption::LightProcessCount
,
1337 const size_t stackSizeMinimum
= 8 * 1024 * 1024;
1339 if (getrlimit(RLIMIT_STACK
, &rlim
) == 0 &&
1340 (rlim
.rlim_cur
== RLIM_INFINITY
||
1341 rlim
.rlim_cur
< stackSizeMinimum
)) {
1342 rlim
.rlim_cur
= stackSizeMinimum
;
1343 if (stackSizeMinimum
> rlim
.rlim_max
) {
1344 rlim
.rlim_max
= stackSizeMinimum
;
1347 Logger::Error("stack limit too small, use peflags -x to increase %zd\n",
1350 if (setrlimit(RLIMIT_STACK
, &rlim
)) {
1351 Logger::Error("failed to set stack limit to %zd\n", stackSizeMinimum
);
1357 ShmCounters::initialize(true, Logger::Error
);
1358 // Initialize compiler state
1359 compile_file(0, 0, MD5(), 0);
1361 if (!po
.lint
.empty()) {
1362 Logger::LogHeader
= false;
1363 Logger::LogLevel
= Logger::LogInfo
;
1364 Logger::UseCronolog
= false;
1365 Logger::UseLogFile
= true;
1366 Logger::SetNewOutput(nullptr);
1368 if (po
.isTempFile
) {
1372 hphp_process_init();
1374 auto const unit
= lookupUnit(
1375 makeStaticString(po
.lint
.c_str()), "", nullptr);
1376 if (unit
== nullptr) {
1377 throw FileOpenException(po
.lint
);
1379 const StringData
* msg
;
1381 if (unit
->compileTimeFatal(msg
, line
)) {
1382 VMParserFrame parserFrame
;
1383 parserFrame
.filename
= po
.lint
.c_str();
1384 parserFrame
.lineNumber
= line
;
1385 Array bt
= createBacktrace(BacktraceArgs()
1387 .setParserFrame(&parserFrame
));
1388 throw FatalErrorException(msg
->data(), bt
);
1390 } catch (FileOpenException
&e
) {
1391 Logger::Error("%s", e
.getMessage().c_str());
1393 } catch (const FatalErrorException
& e
) {
1394 RuntimeOption::CallUserHandlerOnFatals
= false;
1395 RuntimeOption::AlwaysLogUnhandledExceptions
= true;
1396 g_context
->onFatalError(e
);
1399 Logger::Info("No syntax errors detected in %s", po
.lint
.c_str());
1403 if (argc
<= 1 || po
.mode
== "run" || po
.mode
== "debug") {
1404 if (po
.isTempFile
) {
1408 set_execution_mode("run");
1412 prepare_args(new_argc
, new_argv
, po
.args
, po
.file
.c_str());
1414 std::string
const cliFile
= !po
.file
.empty() ? po
.file
:
1415 new_argv
[0] ? new_argv
[0] : "";
1416 if (po
.mode
!= "debug" && cliFile
.empty()) {
1417 std::cerr
<< "Nothing to do. Either pass a .php file to run, or "
1421 Repo::setCliFile(cliFile
);
1424 hphp_process_init();
1431 if (po
.mode
== "debug") {
1432 StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
1433 RuntimeOption::EnableDebugger
= true;
1434 po
.debugger_options
.fileName
= file
;
1435 po
.debugger_options
.user
= po
.user
;
1436 Eval::DebuggerProxyPtr localProxy
=
1437 Eval::Debugger::StartClient(po
.debugger_options
);
1439 Logger::Error("Failed to start debugger client\n\n");
1442 Eval::Debugger::RegisterSandbox(localProxy
->getDummyInfo());
1443 std::shared_ptr
<std::vector
<std::string
>> client_args
;
1444 bool restart
= false;
1448 execute_command_line_begin(new_argc
, new_argv
,
1449 po
.xhprofFlags
, po
.config
);
1450 // Set the proxy for this thread to be the localProxy we just
1451 // created. If we're script debugging, this will be the proxy that
1452 // does all of our work. If we're remote debugging, this proxy will
1453 // go unused until we finally stop it when the user quits the
1455 g_context
->setSandboxId(localProxy
->getDummyInfo().id());
1456 Eval::Debugger::DebuggerSession(po
.debugger_options
, restart
);
1458 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1459 } catch (const Eval::DebuggerRestartException
&e
) {
1460 execute_command_line_end(0, false, nullptr);
1462 if (!e
.m_args
->empty()) {
1463 file
= e
.m_args
->at(0);
1464 client_args
= e
.m_args
;
1466 prepare_args(new_argc
, new_argv
, *client_args
, nullptr);
1468 // Systemlib.php is not loaded again, so we need this if we
1469 // are to hit any breakpoints in systemlib.
1470 proxySetBreakPoints(localProxy
.get());
1472 } catch (const Eval::DebuggerClientExitException
&e
) {
1473 execute_command_line_end(0, false, nullptr);
1474 break; // end user quitting debugger
1480 for (int i
= 0; i
< po
.count
; i
++) {
1481 execute_command_line_begin(new_argc
, new_argv
,
1482 po
.xhprofFlags
, po
.config
);
1484 if (hphp_invoke_simple(file
, false /* warmup only */)) {
1485 ret
= ExitException::ExitCode
;
1487 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1492 hphp_process_exit();
1497 if (po
.mode
== "daemon" || po
.mode
== "server") {
1498 if (!po
.user
.empty()) RuntimeOption::ServerUser
= po
.user
;
1499 return start_server(RuntimeOption::ServerUser
);
1502 if (po
.mode
== "replay" && !po
.args
.empty()) {
1503 RuntimeOption::RecordInput
= false;
1504 set_execution_mode("server");
1505 HttpServer server
; // so we initialize runtime properly
1506 HttpRequestHandler
handler(0);
1507 for (int i
= 0; i
< po
.count
; i
++) {
1508 for (unsigned int j
= 0; j
< po
.args
.size(); j
++) {
1510 rt
.replayInput(po
.args
[j
].c_str());
1511 handler
.handleRequest(&rt
);
1512 printf("%s\n", rt
.getResponse().c_str());
1518 if (po
.mode
== "translate" && !po
.args
.empty()) {
1519 printf("%s", translate_stack(po
.args
[0].c_str()).c_str());
1523 cout
<< desc
<< "\n";
1527 String
canonicalize_path(const String
& p
, const char* root
, int rootLen
) {
1528 String path
= FileUtil::canonicalize(p
);
1529 if (path
.charAt(0) == '/') {
1530 const string
&sourceRoot
= RuntimeOption::SourceRoot
;
1531 int len
= sourceRoot
.size();
1532 if (len
&& strncmp(path
.data(), sourceRoot
.c_str(), len
) == 0) {
1533 return path
.substr(len
);
1535 if (root
&& rootLen
&& strncmp(path
.data(), root
, rootLen
) == 0) {
1536 return path
.substr(rootLen
);
1542 static string
systemlib_split(string slib
, string
* hhas
) {
1543 auto pos
= slib
.find("\n<?hhas\n");
1544 if (pos
!= string::npos
) {
1545 if (hhas
) *hhas
= slib
.substr(pos
+ 8);
1546 return slib
.substr(0, pos
);
1551 // Retrieve a systemlib (or mini systemlib) from the
1552 // current executable or another ELF object file.
1554 // Additionally, when retrieving the main systemlib
1555 // from the current executable, honor the
1556 // HHVM_SYSTEMLIB environment variable as an override.
1557 string
get_systemlib(string
* hhas
, const string
§ion
/*= "systemlib" */,
1558 const string
&filename
/*= "" */) {
1559 if (filename
.empty() && section
== "systemlib") {
1560 if (char *file
= getenv("HHVM_SYSTEMLIB")) {
1561 std::ifstream
ifs(file
);
1563 return systemlib_split(std::string(
1564 std::istreambuf_iterator
<char>(ifs
),
1565 std::istreambuf_iterator
<char>()), hhas
);
1571 if (!get_embedded_data(section
.c_str(), &desc
, filename
)) return "";
1573 #if (defined(__CYGWIN__) || defined(__MINGW__) || defined(_MSC_VER))
1574 string ret
= systemlib_split(std::string(
1575 (const char*)LockResource(desc
.m_handle
),
1578 std::ifstream
ifs(desc
.m_filename
);
1579 if (!ifs
.good()) return "";
1580 ifs
.seekg(desc
.m_start
, std::ios::beg
);
1581 std::unique_ptr
<char[]> data(new char[desc
.m_len
]);
1582 ifs
.read(data
.get(), desc
.m_len
);
1583 string ret
= systemlib_split(string(data
.get(), desc
.m_len
), hhas
);
1588 ///////////////////////////////////////////////////////////////////////////////
1591 static void on_timeout(int sig
, siginfo_t
* info
, void* context
) {
1592 if (sig
== SIGVTALRM
&& info
&& info
->si_code
== SI_TIMER
) {
1593 auto data
= (RequestInjectionData
*)info
->si_value
.sival_ptr
;
1597 Xenon::getInstance().onTimer();
1602 void hphp_process_init() {
1603 pthread_attr_t attr
;
1604 // Linux+GNU extension
1605 #if defined(_GNU_SOURCE) && (defined(__linux__) || defined(__CYGWIN__))
1606 pthread_getattr_np(pthread_self(), &attr
);
1608 pthread_attr_init(&attr
);
1610 init_stack_limits(&attr
);
1611 pthread_attr_destroy(&attr
);
1613 struct sigaction action
= {};
1614 action
.sa_sigaction
= on_timeout
;
1615 action
.sa_flags
= SA_SIGINFO
| SA_NODEFER
;
1616 sigaction(SIGVTALRM
, &action
, nullptr);
1617 // start takes milliseconds, Period is a double in seconds
1618 Xenon::getInstance().start(1000 * RuntimeOption::XenonPeriodSeconds
);
1620 Process::InitProcessStatics();
1621 init_thread_locals();
1623 // Initialize per-process dynamic PHP-visible consts before ClassInfo::Load()
1624 k_PHP_BINARY
= makeStaticString(current_executable_path());
1625 k_PHP_BINDIR
= makeStaticString(current_executable_directory());
1626 k_PHP_OS
= makeStaticString(HHVM_FN(php_uname
)("s").toString());
1627 k_PHP_SAPI
= makeStaticString(RuntimeOption::ExecutionMode
);
1631 // reinitialize pcre table
1634 // the liboniguruma docs say this isnt needed,
1635 // but the implementation of init is not
1636 // thread safe due to bugs
1639 // simple xml also needs one time init
1644 PageletServer::Restart();
1645 XboxServer::Restart();
1646 Stream::RegisterCoreWrappers();
1647 Extension::InitModules();
1648 for (InitFiniNode
*in
= extra_process_init
; in
; in
= in
->next
) {
1651 int64_t save
= RuntimeOption::SerializationSizeLimit
;
1652 RuntimeOption::SerializationSizeLimit
= StringData::MaxSize
;
1653 apc_load(apcExtension::LoadThread
);
1654 RuntimeOption::SerializationSizeLimit
= save
;
1657 // Reset the preloaded g_context
1658 ExecutionContext
*context
= g_context
.getNoCheck();
1659 context
->~ExecutionContext();
1660 new (context
) ExecutionContext();
1663 static void handle_exception(bool& ret
, ExecutionContext
* context
,
1664 std::string
& errorMsg
, ContextOfException where
,
1665 bool& error
, bool richErrorMsg
) {
1666 assert(where
== ContextOfException::Invoke
||
1667 where
== ContextOfException::ReqInit
);
1669 handle_exception_helper(ret
, context
, errorMsg
, where
, error
, richErrorMsg
);
1670 } catch (const ExitException
&e
) {
1671 // Got an ExitException during exception handling, handle
1672 // similarly to the case below but don't call obEndAll().
1674 handle_exception_helper(ret
, context
, errorMsg
, ContextOfException::Handler
,
1675 error
, richErrorMsg
);
1676 context
->obEndAll();
1680 static void handle_reqinit_exception(bool &ret
, ExecutionContext
*context
,
1681 std::string
&errorMsg
, bool &error
) {
1682 handle_exception(ret
, context
, errorMsg
, ContextOfException::ReqInit
, error
,
1686 static void handle_invoke_exception(bool &ret
, ExecutionContext
*context
,
1687 std::string
&errorMsg
, bool &error
,
1688 bool richErrorMsg
) {
1689 handle_exception(ret
, context
, errorMsg
, ContextOfException::Invoke
, error
,
1693 static bool hphp_warmup(ExecutionContext
*context
,
1694 const string
&reqInitFunc
,
1695 const string
&reqInitDoc
, bool &error
) {
1698 std::string errorMsg
;
1700 ServerStatsHelper
ssh("reqinit");
1702 if (!reqInitDoc
.empty()) {
1703 include_impl_invoke(reqInitDoc
, true);
1705 if (!reqInitFunc
.empty()) {
1706 invoke(reqInitFunc
.c_str(), Array());
1708 context
->backupSession();
1710 handle_reqinit_exception(ret
, context
, errorMsg
, error
);
1716 void hphp_session_init() {
1717 init_thread_locals();
1718 ThreadInfo::s_threadInfo
->onSessionInit();
1719 MM().resetExternalStats();
1721 #ifdef ENABLE_SIMPLE_COUNTER
1722 SimpleCounter::Enabled
= true;
1723 StackTrace::Enabled
= true;
1726 // Ordering is sensitive; StatCache::requestInit produces work that
1727 // must be done in ExecutionContext::requestInit.
1728 StatCache::requestInit();
1730 g_context
->requestInit();
1733 ExecutionContext
*hphp_context_init() {
1734 ExecutionContext
*context
= g_context
.getNoCheck();
1736 context
->obProtect(true);
1740 bool hphp_invoke_simple(const std::string
& filename
, bool warmupOnly
) {
1743 return hphp_invoke(g_context
.getNoCheck(), filename
, false, null_array
,
1744 uninit_null(), "", "", error
, errorMsg
,
1747 false /* richErrorMsg */);
1750 bool hphp_invoke(ExecutionContext
*context
, const std::string
&cmd
,
1751 bool func
, const Array
& funcParams
, VRefParam funcRet
,
1752 const string
&reqInitFunc
, const string
&reqInitDoc
,
1753 bool &error
, string
&errorMsg
,
1754 bool once
, bool warmupOnly
,
1755 bool richErrorMsg
) {
1756 bool isServer
= RuntimeOption::ServerExecutionMode();
1761 oldCwd
= context
->getCwd();
1763 if (!hphp_warmup(context
, reqInitFunc
, reqInitDoc
, error
)) {
1764 if (isServer
) context
->setCwd(oldCwd
);
1768 LitstrTable::get().setReading();
1773 ServerStatsHelper
ssh("invoke");
1774 if (!RuntimeOption::AutoPrependFile
.empty() &&
1775 RuntimeOption::AutoPrependFile
!= "none") {
1776 require(RuntimeOption::AutoPrependFile
, false,
1777 context
->getCwd().data(), true);
1780 funcRet
->assign(invoke(cmd
.c_str(), funcParams
));
1782 if (isServer
) hphp_chdir_file(cmd
);
1783 include_impl_invoke(cmd
.c_str(), once
);
1785 if (!RuntimeOption::AutoAppendFile
.empty() &&
1786 RuntimeOption::AutoAppendFile
!= "none") {
1787 require(RuntimeOption::AutoAppendFile
, false,
1788 context
->getCwd().data(), true);
1791 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
1796 context
->onShutdownPreSend();
1798 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
1801 if (isServer
) context
->setCwd(oldCwd
);
1805 void hphp_context_shutdown() {
1806 // Run shutdown handlers. This may cause user code to run.
1807 auto const context
= g_context
.getNoCheck();
1808 context
->destructObjects();
1809 context
->onRequestShutdown();
1811 // Shutdown the debugger
1812 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestShutdownHook());
1814 // Extensions could have shutdown handlers
1815 Extension::RequestShutdownModules();
1818 void hphp_context_exit(bool shutdown
/* = true */) {
1820 hphp_context_shutdown();
1823 // Clean up a bunch of request state. No user code after this point.
1824 auto const context
= g_context
.getNoCheck();
1825 context
->requestExit();
1826 context
->obProtect(false);
1827 context
->obEndAll();
1830 void hphp_thread_exit() {
1831 finish_thread_locals();
1834 void hphp_session_exit() {
1835 // Server note has to live long enough for the access log to fire.
1836 // RequestLocal is too early.
1837 ServerNote::Reset();
1838 g_context
.destroy();
1840 ThreadInfo::s_threadInfo
->clearPendingException();
1845 ServerStatsHelper
ssh("rollback");
1846 // sweep functions are allowed to call g_context->, so we need to
1847 // reinitialize g_context here.
1848 g_context
.getCheck();
1852 // Destroy g_context again because ExecutionContext has
1853 // SmartAllocated data members. These members cannot survive over
1854 // resetAllocator(), so we need to destroy g_context before
1855 // calling resetAllocator().
1856 g_context
.destroy();
1858 mm
.resetAllocator();
1860 // Do any post-sweep cleanup necessary for global variables
1861 free_global_variables_after_sweep();
1862 g_context
.getCheck();
1865 ThreadInfo::s_threadInfo
->onSessionExit();
1869 void hphp_process_exit() {
1870 Xenon::getInstance().stop();
1871 PageletServer::Stop();
1873 Eval::Debugger::Stop();
1874 Extension::ShutdownModules();
1875 LightProcess::Close();
1876 for (InitFiniNode
*in
= extra_process_exit
; in
; in
= in
->next
) {
1883 ///////////////////////////////////////////////////////////////////////////////