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