Move var_dump() to HNI, kill off variable.idl.json
[hiphop-php.git] / hphp / runtime / base / program-functions.cpp
blob7546320654b542f27df24d087c43929d2897e683
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
73 #include <libgen.h>
74 #include <oniguruma.h>
75 #include <signal.h>
76 #include <libxml/parser.h>
77 #include <exception>
78 #include <iterator>
79 #include <map>
80 #include <memory>
81 #include <vector>
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))
93 #undef NOUSER
94 #include <windows.h>
95 #include <winuser.h>
96 #endif
98 using namespace boost::program_options;
99 using std::cout;
100 extern char **environ;
102 #define MAX_INPUT_NESTING_LEVEL 64
104 namespace HPHP {
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 ///////////////////////////////////////////////////////////////////////////////
118 // helpers
120 struct ProgramOptions {
121 string mode;
122 std::vector<std::string>
123 config;
124 std::vector<std::string>
125 confStrings;
126 std::vector<std::string>
127 iniStrings;
128 int port;
129 int portfd;
130 int sslportfd;
131 int admin_port;
132 string user;
133 string file;
134 string lint;
135 bool isTempFile;
136 int count;
137 bool noSafeAccessCheck;
138 std::vector<std::string>
139 args;
140 string buildId;
141 string instanceId;
142 int xhprofFlags;
143 string show;
144 string parse;
146 Eval::DebuggerClientOptions debugger_options;
149 class StartTime {
150 public:
151 StartTime() : startTime(time(nullptr)) {}
152 time_t startTime;
154 static StartTime s_startTime;
155 static string tempFile;
157 time_t start_time() {
158 return s_startTime.startTime;
161 const StaticString
162 s_HPHP("HPHP"),
163 s_HHVM("HHVM"),
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"),
173 s_argc("argc"),
174 s_argv("argv"),
175 s_PWD("PWD"),
176 s_HOSTNAME("HOSTNAME"),
177 s__SERVER("_SERVER"),
178 s__ENV("_ENV");
180 String k_PHP_BINARY;
181 String k_PHP_BINDIR;
182 String k_PHP_OS;
183 String k_PHP_SAPI;
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, '=');
200 if (p) {
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()) {
210 return;
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
232 char *var = name;
233 while (*var && *var == ' ') {
234 var++;
237 // ensure that we don't have spaces or dots in the variable name
238 // (not binary safe)
239 bool is_array = false;
240 char *ip = nullptr; // index pointer
241 char *p = var;
242 for (; *p; p++) {
243 if (*p == ' ' || *p == '.') {
244 *p = '_';
245 } else if (*p == '[') {
246 is_array = true;
247 ip = p;
248 *p = 0;
249 break;
252 int var_len = p - var;
253 if (var_len == 0) {
254 // empty variable name, or variable name with a space in it
255 return;
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
266 // gpc_elements).
267 Array* symtable = &variables;
269 char* index = var;
270 int index_len = var_len;
272 if (is_array) {
273 int nest_level = 0;
274 while (true) {
275 if (++nest_level > MAX_INPUT_NESTING_LEVEL) {
276 Logger::Warning("Input variable nesting level exceeded");
277 return;
280 ip++;
281 char *index_s = ip;
282 int new_idx_len = 0;
283 if (isspace(*ip)) {
284 ip++;
286 if (*ip == ']') {
287 index_s = nullptr;
288 } else {
289 ip = strchr(ip, ']');
290 if (!ip) {
291 // PHP variables cannot contain '[' in their names,
292 // so we replace the character with a '_'
293 *(index_s - 1) = '_';
295 index_len = 0;
296 if (index) {
297 index_len = strlen(index);
299 goto plain_var;
301 *ip = 0;
302 new_idx_len = strlen(index_s);
305 if (!index) {
306 auto& val = symtable->lvalAt();
307 val = Array::Create();
308 gpc_elements.push_back(uninit_null());
309 gpc_elements.back().assignRef(val);
310 } else {
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 */
321 index = index_s;
322 index_len = new_idx_len;
324 ip++;
325 if (*ip == '[') {
326 is_array = true;
327 *ip = 0;
328 } else {
329 goto plain_var;
332 } else {
333 plain_var:
334 if (!index) {
335 symtable->append(value);
336 } else {
337 String key(index, index_len, CopyString);
338 if (overwrite || !symtable->exists(key)) {
339 symtable->set(key, value);
345 enum class ContextOfException {
346 ReqInit = 1,
347 Invoke,
348 Handler,
351 static void handle_exception_append_bt(std::string& errorMsg,
352 const ExtendedException& e) {
353 Array bt = e.getBacktrace();
354 if (!bt.empty()) {
355 errorMsg += ExtendedLogger::StringOfStackTrace(bt);
359 void bump_counter_and_rethrow(bool isPsp) {
360 try {
361 throw;
362 } catch (const RequestTimeoutException& e) {
363 if (isPsp) {
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);
368 } else {
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);
374 throw;
375 } catch (const RequestMemoryExceededException& e) {
376 if (isPsp) {
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);
382 } else {
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);
389 #ifdef USE_JEMALLOC
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);
393 #endif
395 throw;
399 static void handle_exception_helper(bool& ret,
400 ExecutionContext* context,
401 std::string& errorMsg,
402 ContextOfException where,
403 bool& error,
404 bool richErrorMsg) {
405 try {
406 bump_counter_and_rethrow(false /* isPsp */);
407 } catch (const Eval::DebuggerException &e) {
408 throw;
409 } catch (const ExitException &e) {
410 if (where == ContextOfException::ReqInit) {
411 ret = false;
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) {
420 ret = false;
421 if (where != ContextOfException::Handler) {
422 raise_notice("%s", e.getMessage().c_str());
423 } else {
424 Logger::Error("%s", e.getMessage().c_str());
426 if (richErrorMsg) {
427 handle_exception_append_bt(errorMsg, e);
429 } catch (const Exception &e) {
430 bool oldRet = ret;
431 bool origError = error;
432 std::string origErrorMsg = errorMsg;
433 ret = false;
434 error = true;
435 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);
442 if (handlerRet) {
443 ret = oldRet;
444 error = origError;
445 errorMsg = origErrorMsg;
447 } else {
448 Logger::Error("%s", errorMsg.c_str());
450 if (richErrorMsg) {
451 const ExtendedException *ee = dynamic_cast<const ExtendedException *>(&e);
452 if (ee) {
453 handle_exception_append_bt(errorMsg, *ee);
456 } catch (const Object &e) {
457 bool oldRet = ret;
458 bool origError = error;
459 std::string origErrorMsg = errorMsg;
460 ret = false;
461 error = true;
462 errorMsg = "";
463 if (where == ContextOfException::Handler) {
464 errorMsg = "Exception handler threw an object exception: ";
466 try {
467 errorMsg += e.toString().data();
468 } catch (...) {
469 errorMsg += "(unable to call toString())";
471 if (where == ContextOfException::Invoke) {
472 bool handlerRet = context->onUnhandledException(e);
473 if (handlerRet) {
474 ret = oldRet;
475 error = origError;
476 errorMsg = origErrorMsg;
478 } else {
479 Logger::Error("%s", errorMsg.c_str());
481 } catch (...) {
482 ret = false;
483 error = true;
484 errorMsg = "(unknown exception was thrown)";
485 Logger::Error("%s", errorMsg.c_str());
489 static bool hphp_chdir_file(const string filename) {
490 bool ret = false;
491 String s = File::TranslatePath(filename);
492 char *buf = strndup(s.data(), s.size());
493 char *dir = dirname(buf);
494 assert(dir);
495 if (dir) {
496 if (File::IsVirtualDirectory(dir)) {
497 g_context->setCwd(String(dir, CopyString));
498 ret = true;
499 } else {
500 struct stat sb;
501 stat(dir, &sb);
502 if ((sb.st_mode & S_IFMT) == S_IFDIR) {
503 ret = true;
504 if (*dir != '.') {
505 g_context->setCwd(String(dir, CopyString));
510 free(buf);
511 return ret;
514 static void handle_resource_exceeded_exception() {
515 try {
516 throw;
517 } catch (RequestTimeoutException&) {
518 ThreadInfo::s_threadInfo->m_reqInjectionData.setTimedOutFlag();
519 } catch (RequestMemoryExceededException&) {
520 ThreadInfo::s_threadInfo->m_reqInjectionData.setMemExceededFlag();
521 } catch (...) {}
524 void handle_destructor_exception(const char* situation) {
525 string errorMsg;
527 try {
528 throw;
529 } catch (ExitException &e) {
530 // ExitException is fine, no need to show a warning.
531 ThreadInfo::s_threadInfo->setPendingException(e.clone());
532 return;
533 } catch (Object &e) {
534 // For user exceptions, invoke the user exception handler
535 errorMsg = situation;
536 errorMsg += " threw an object exception: ";
537 try {
538 errorMsg += e.toString().data();
539 } catch (...) {
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();
548 } catch (...) {
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.
555 try {
556 raise_debugging("%s", errorMsg.c_str());
557 } catch (...) {
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");
569 string args;
570 for (int i = 0; i < argc; i++) {
571 if (i) args += " ";
572 args += argv[i];
574 StackTraceNoHeap::AddExtraLogging("Arguments", args.c_str());
576 hphp_session_init();
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);
588 switch (arch()) {
589 case Arch::X64:
590 envArr.set(s_HHVM_ARCH, "x64");
591 break;
592 case Arch::ARM:
593 envArr.set(s_HHVM_ARCH, "arm");
594 break;
596 php_global_set(s__ENV, envArr);
599 process_cmd_arguments(argc, argv);
602 Array serverArr(Array::Create());
603 process_env_variables(serverArr);
604 time_t now;
605 struct timeval tp = {0};
606 double now_double;
607 if (!gettimeofday(&tp, nullptr)) {
608 now_double = (double)(tp.tv_sec + tp.tv_usec / 1000000.00);
609 now = tp.tv_sec;
610 } else {
611 now = time(nullptr);
612 now_double = (double)now;
614 String file = empty_string();
615 if (argc > 0) {
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());
628 char hostname[1024];
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);
640 if (xhprof) {
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) {
658 process_ini_file(c);
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();
672 if (xhprof) {
673 HHVM_FN(var_dump)(HHVM_FN(json_encode)(f_xhprof_disable()));
675 g_context->onShutdownPostSend();
676 Eval::Debugger::InterruptPSPEnded(program);
677 hphp_context_exit();
678 hphp_session_exit();
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;
688 #else
689 extern "C" {
690 void __attribute__((__weak__)) __hot_start();
691 void __attribute__((__weak__)) __hot_end();
693 #endif
695 #if FACEBOOK
696 # define AT_END_OF_TEXT __attribute__((__section__(".stub")))
697 #else
698 # define AT_END_OF_TEXT
699 #endif
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.
709 return;
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
718 mmap(from, sz,
719 PROT_READ | PROT_WRITE | PROT_EXEC,
720 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
721 -1, 0);
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);
732 free(mem);
733 mlock(from, to - from);
734 Debug::DebugInfo::setPidMapOverlay(from, to);
735 #endif
738 static void pagein_self(void) {
739 unsigned long begin, end, inode, pgoff;
740 char mapname[PATH_MAX];
741 char perm[5];
742 char dev[6];
743 char *buf;
744 int bufsz;
745 int r;
746 FILE *fp;
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);
751 if (buf == nullptr)
752 return;
754 Timer timer(Timer::WallTime, "mapping self");
755 fp = fopen("/proc/self/maps", "r");
756 if (fp != nullptr) {
757 while (!feof(fp)) {
758 if (fgets(buf, bufsz, fp) == 0)
759 break;
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
764 if (r != 7 ||
765 perm[0] != 'r' ||
766 perm[1] != '-' ||
767 access(mapname, F_OK) != 0) {
768 continue;
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 &&
779 __hot_start &&
780 __hot_end &&
781 hugePagesSupported() &&
782 beginPtr <= hotStart &&
783 hotEnd <= endPtr) {
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);
797 fclose(fp);
799 free(buf);
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;
815 } else {
816 // Undefined mode
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.
824 pagein_self();
826 set_execution_mode("server");
827 HttpRequestHandler::GetAccessLog().init
828 (RuntimeOption::AccessLogDefaultFormat, RuntimeOption::AccessLogs,
829 username);
830 AdminRequestHandler::GetAccessLog().init
831 (RuntimeOption::AdminLogFormat, RuntimeOption::AdminLogSymLink,
832 RuntimeOption::AdminLogFile,
833 username);
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();
844 #endif
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
851 // real connections
853 profileWarmupStart();
854 SCOPE_EXIT { profileWarmupEnd(); };
855 for (auto& file : RuntimeOption::ServerWarmupRequests) {
856 HttpRequestHandler handler(0);
857 ReplayTransport rt;
858 timespec start;
859 Timer::GetMonotonicTime(start);
860 std::string error;
861 Logger::Info("Replaying warmup request %s", file.c_str());
863 try {
864 rt.onRequestStart(start);
865 rt.replayInput(Hdf(file));
866 handler.handleRequest(&rt);
868 timespec stop;
869 Timer::GetMonotonicTime(stop);
870 Logger::Info("Finished successfully in %ld seconds",
871 stop.tv_sec - start.tv_sec);
872 } catch (std::exception& e) {
873 error = e.what();
876 if (error.size()) {
877 Logger::Info("Got exception during warmup: %s", error.c_str());
882 if (RuntimeOption::EvalEnableNuma) {
883 #ifdef USE_JEMALLOC
884 mallctl("arenas.purge", nullptr, nullptr, nullptr, 0);
885 #endif
886 enable_numa(RuntimeOption::EvalEnableNumaLocal);
889 HttpServer::Server->runOrExitProcess();
890 HttpServer::Server.reset();
891 return 0;
894 string translate_stack(const char *hexencoded, bool with_frame_numbers) {
895 if (!hexencoded || !*hexencoded) {
896 return "";
899 StackTrace st(hexencoded);
900 std::vector<std::shared_ptr<StackTrace::Frame>> frames;
901 st.get(frames);
903 std::ostringstream out;
904 for (unsigned int i = 0; i < frames.size(); i++) {
905 auto f = frames[i];
906 if (with_frame_numbers) {
907 out << "# " << (i < 10 ? " " : "") << i << ' ';
909 out << f->toString();
910 out << '\n';
912 return out.str();
915 ///////////////////////////////////////////////////////////////////////////////
917 static void prepare_args(int &argc,
918 char **&argv,
919 const std::vector<std::string> &args,
920 const char *file) {
921 argv = (char **)malloc((args.size() + 2) * sizeof(char*));
922 argc = 0;
923 if (file && *file) {
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) {
934 int ret_code = -1;
935 try {
936 initialize_repo();
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) {
941 fa.print();
942 StackTraceNoHeap::AddExtraLogging("Assertion failure", fa.summary);
943 abort();
944 } catch (const std::exception &e) {
945 Logger::Error("Uncaught exception: %s", e.what());
946 } catch (...) {
947 Logger::Error("Uncaught exception: (unknown)");
949 if (tempFile.length() && boost::filesystem::exists(tempFile)) {
950 boost::filesystem::remove(tempFile);
952 return ret_code;
955 /* -1 - cannot open file
956 * 0 - no need to open file
957 * 1 - fopen
958 * 2 - popen
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;
967 return 0;
968 } else {
969 Logger::Output = fopen(RuntimeOption::LogFile.c_str(), "a");
970 if (Logger::Output) return 1;
972 } else {
973 if (Logger::IsPipeOutput) {
974 Logger::Output = popen(RuntimeOption::LogFile.substr(1).c_str(), "w");
975 if (Logger::Output) return 2;
976 } else {
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());
982 return -1;
984 return 0;
987 static void close_server_log_file(int kind) {
988 if (kind == 1) {
989 fclose(Logger::Output);
990 } else if (kind == 2) {
991 pclose(Logger::Output);
992 } else {
993 always_assert(!Logger::Output);
997 static int compute_hhvm_argc(const options_description& desc,
998 int argc, char** argv) {
999 enum ArgCode {
1000 NO_ARG = 0,
1001 ARG_REQUIRED = 1,
1002 ARG_OPTIONAL = 2
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) {
1009 auto opt = vec[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;
1015 } else {
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
1028 int pos = 1;
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
1034 // PHP application
1035 ++pos;
1036 break;
1038 if (len >= 3 && str[0] == '-' && str[1] == '-') {
1039 // Handle long options
1040 ++pos;
1041 string s(str+2);
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] != '-')) {
1045 ++pos;
1047 } else if (len >= 2 && str[0] == '-') {
1048 // Handle short options
1049 ++pos;
1050 string s;
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] != '-')) {
1055 ++pos;
1057 } else {
1058 // We've found a non-option argument. This arg and all args
1059 // that follow are intended for the PHP application
1060 break;
1063 return pos;
1066 static int execute_program_impl(int argc, char** argv) {
1067 string usage = "Usage:\n\n ";
1068 usage += argv[0];
1069 usage += " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
1071 ProgramOptions po;
1072 options_description desc(usage.c_str());
1073 desc.add_options()
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")
1084 ("config-value,v",
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 "
1090 ".ini file")
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")
1102 ("debug-host,h",
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")
1112 ("debug-sandbox",
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")
1123 ("temp-file",
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(),
1131 "arguments")
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),
1139 "Set XHProf flags")
1142 positional_options_description p;
1143 p.add("arg", -1);
1144 variables_map vm;
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);
1154 try {
1155 // Invoke the boost command line parser to parse the args for HHVM.
1156 auto opts = command_line_parser(hhvm_argc, argv)
1157 .options(desc)
1158 .positional(p)
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)
1165 .run();
1166 // Manually append the args for the PHP application.
1167 int pos = 0;
1168 for (unsigned m = 0; m < opts.options.size(); ++m) {
1169 const auto& bo = opts.options[m];
1170 if (bo.string_key == "arg") {
1171 ++pos;
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
1186 store(opts, vm);
1187 notify(vm);
1188 if (vm.count("interactive") /* or -a */) {
1189 po.mode = "debug";
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);
1198 } else {
1199 Logger::Error("Error in command line: invalid mode: %s", po.mode.c_str());
1200 cout << desc << "\n";
1201 return -1;
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";
1218 return -1;
1219 } catch (...) {
1220 Logger::Error("Error in command line.");
1221 cout << desc << "\n";
1222 return -1;
1224 if (vm.count("help")) {
1225 cout << desc << "\n";
1226 return 0;
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";
1235 return 0;
1237 if (vm.count("compiler-id")) {
1238 cout << kCompilerId << "\n";
1239 return 0;
1242 if (vm.count("repo-schema")) {
1243 cout << kRepoSchemaId << "\n";
1244 return 0;
1247 if (!po.show.empty()) {
1248 PlainFile f;
1249 f.open(po.show, "r");
1250 if (!f.valid()) {
1251 Logger::Error("Unable to open file %s", po.show.c_str());
1252 return 1;
1254 f.print();
1255 f.close();
1256 return 0;
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
1265 pcre_init();
1267 MemoryManager::TlsWrapper::getCheck();
1269 IniSetting::Map ini = IniSetting::Map::object;
1270 Hdf config;
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
1325 // uncertain state.
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,
1334 inherited_fds);
1337 const size_t stackSizeMinimum = 8 * 1024 * 1024;
1338 struct rlimit rlim;
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;
1346 #ifdef __CYGWIN__
1347 Logger::Error("stack limit too small, use peflags -x to increase %zd\n",
1348 stackSizeMinimum);
1349 #else
1350 if (setrlimit(RLIMIT_STACK, &rlim)) {
1351 Logger::Error("failed to set stack limit to %zd\n", stackSizeMinimum);
1353 #endif
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) {
1369 tempFile = po.lint;
1372 hphp_process_init();
1373 try {
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;
1380 int line;
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()
1386 .withSelf()
1387 .setParserFrame(&parserFrame));
1388 throw FatalErrorException(msg->data(), bt);
1390 } catch (FileOpenException &e) {
1391 Logger::Error("%s", e.getMessage().c_str());
1392 return 1;
1393 } catch (const FatalErrorException& e) {
1394 RuntimeOption::CallUserHandlerOnFatals = false;
1395 RuntimeOption::AlwaysLogUnhandledExceptions = true;
1396 g_context->onFatalError(e);
1397 return 1;
1399 Logger::Info("No syntax errors detected in %s", po.lint.c_str());
1400 return 0;
1403 if (argc <= 1 || po.mode == "run" || po.mode == "debug") {
1404 if (po.isTempFile) {
1405 tempFile = po.file;
1408 set_execution_mode("run");
1410 int new_argc;
1411 char **new_argv;
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 "
1418 "use -m server\n";
1419 return 1;
1421 Repo::setCliFile(cliFile);
1423 int ret = 0;
1424 hphp_process_init();
1426 string file;
1427 if (new_argc > 0) {
1428 file = new_argv[0];
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);
1438 if (!localProxy) {
1439 Logger::Error("Failed to start debugger client\n\n");
1440 return 1;
1442 Eval::Debugger::RegisterSandbox(localProxy->getDummyInfo());
1443 std::shared_ptr<std::vector<std::string>> client_args;
1444 bool restart = false;
1445 ret = 0;
1446 while (true) {
1447 try {
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
1454 // debugger.
1455 g_context->setSandboxId(localProxy->getDummyInfo().id());
1456 Eval::Debugger::DebuggerSession(po.debugger_options, restart);
1457 restart = false;
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;
1465 free(new_argv);
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());
1471 restart = true;
1472 } catch (const Eval::DebuggerClientExitException &e) {
1473 execute_command_line_end(0, false, nullptr);
1474 break; // end user quitting debugger
1478 } else {
1479 ret = 0;
1480 for (int i = 0; i < po.count; i++) {
1481 execute_command_line_begin(new_argc, new_argv,
1482 po.xhprofFlags, po.config);
1483 ret = 255;
1484 if (hphp_invoke_simple(file, false /* warmup only */)) {
1485 ret = ExitException::ExitCode;
1487 execute_command_line_end(po.xhprofFlags, true, file.c_str());
1491 free(new_argv);
1492 hphp_process_exit();
1494 return ret;
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++) {
1509 ReplayTransport rt;
1510 rt.replayInput(po.args[j].c_str());
1511 handler.handleRequest(&rt);
1512 printf("%s\n", rt.getResponse().c_str());
1515 return 0;
1518 if (po.mode == "translate" && !po.args.empty()) {
1519 printf("%s", translate_stack(po.args[0].c_str()).c_str());
1520 return 0;
1523 cout << desc << "\n";
1524 return -1;
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);
1539 return path;
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);
1548 return slib;
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 &section /*= "systemlib" */,
1558 const string &filename /*= "" */) {
1559 if (filename.empty() && section == "systemlib") {
1560 if (char *file = getenv("HHVM_SYSTEMLIB")) {
1561 std::ifstream ifs(file);
1562 if (ifs.good()) {
1563 return systemlib_split(std::string(
1564 std::istreambuf_iterator<char>(ifs),
1565 std::istreambuf_iterator<char>()), hhas);
1570 embedded_data desc;
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),
1576 desc.m_len), hhas);
1577 #else
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);
1584 #endif
1585 return ret;
1588 ///////////////////////////////////////////////////////////////////////////////
1589 // C++ ffi
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;
1594 if (data) {
1595 data->onTimeout();
1596 } else {
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);
1607 #else
1608 pthread_attr_init(&attr);
1609 #endif
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);
1629 ClassInfo::Load();
1631 // reinitialize pcre table
1632 pcre_reinit();
1634 // the liboniguruma docs say this isnt needed,
1635 // but the implementation of init is not
1636 // thread safe due to bugs
1637 onig_init();
1639 // simple xml also needs one time init
1640 xmlInitParser();
1642 g_vmProcessInit();
1644 PageletServer::Restart();
1645 XboxServer::Restart();
1646 Stream::RegisterCoreWrappers();
1647 Extension::InitModules();
1648 for (InitFiniNode *in = extra_process_init; in; in = in->next) {
1649 in->func();
1651 int64_t save = RuntimeOption::SerializationSizeLimit;
1652 RuntimeOption::SerializationSizeLimit = StringData::MaxSize;
1653 apc_load(apcExtension::LoadThread);
1654 RuntimeOption::SerializationSizeLimit = save;
1656 RDS::requestExit();
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);
1668 try {
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().
1673 } catch (...) {
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,
1683 false);
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,
1690 richErrorMsg);
1693 static bool hphp_warmup(ExecutionContext *context,
1694 const string &reqInitFunc,
1695 const string &reqInitDoc, bool &error) {
1696 bool ret = true;
1697 error = false;
1698 std::string errorMsg;
1700 ServerStatsHelper ssh("reqinit");
1701 try {
1702 if (!reqInitDoc.empty()) {
1703 include_impl_invoke(reqInitDoc, true);
1705 if (!reqInitFunc.empty()) {
1706 invoke(reqInitFunc.c_str(), Array());
1708 context->backupSession();
1709 } catch (...) {
1710 handle_reqinit_exception(ret, context, errorMsg, error);
1713 return ret;
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;
1724 #endif
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();
1735 context->obStart();
1736 context->obProtect(true);
1737 return context;
1740 bool hphp_invoke_simple(const std::string& filename, bool warmupOnly) {
1741 bool error;
1742 string errorMsg;
1743 return hphp_invoke(g_context.getNoCheck(), filename, false, null_array,
1744 uninit_null(), "", "", error, errorMsg,
1745 true /* once */,
1746 warmupOnly,
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();
1757 error = false;
1759 String oldCwd;
1760 if (isServer) {
1761 oldCwd = context->getCwd();
1763 if (!hphp_warmup(context, reqInitFunc, reqInitDoc, error)) {
1764 if (isServer) context->setCwd(oldCwd);
1765 return false;
1768 LitstrTable::get().setReading();
1770 bool ret = true;
1771 if (!warmupOnly) {
1772 try {
1773 ServerStatsHelper ssh("invoke");
1774 if (!RuntimeOption::AutoPrependFile.empty() &&
1775 RuntimeOption::AutoPrependFile != "none") {
1776 require(RuntimeOption::AutoPrependFile, false,
1777 context->getCwd().data(), true);
1779 if (func) {
1780 funcRet->assign(invoke(cmd.c_str(), funcParams));
1781 } else {
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);
1790 } catch (...) {
1791 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
1795 try {
1796 context->onShutdownPreSend();
1797 } catch (...) {
1798 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
1801 if (isServer) context->setCwd(oldCwd);
1802 return ret;
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 */) {
1819 if (shutdown) {
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();
1842 auto& mm = MM();
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();
1850 mm.sweep();
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();
1866 assert(mm.empty());
1869 void hphp_process_exit() {
1870 Xenon::getInstance().stop();
1871 PageletServer::Stop();
1872 XboxServer::Stop();
1873 Eval::Debugger::Stop();
1874 Extension::ShutdownModules();
1875 LightProcess::Close();
1876 for (InitFiniNode *in = extra_process_exit; in; in = in->next) {
1877 in->func();
1879 delete JIT::mcg;
1880 JIT::mcg = nullptr;
1883 ///////////////////////////////////////////////////////////////////////////////