Use folly::dynamic::object and folly::dynamic::string exclusivly
[hiphop-php.git] / hphp / runtime / base / program-functions.cpp
blob52ef89fff74a29826fc2934b784a91e14420c5a6
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/builtin-functions.h"
21 #include "hphp/runtime/base/execution-context.h"
22 #include "hphp/runtime/base/thread-init-fini.h"
23 #include "hphp/runtime/base/code-coverage.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/base/pprof-server.h"
26 #include "hphp/runtime/base/ini-setting.h"
27 #include "hphp/runtime/server/pagelet-server.h"
28 #include "hphp/runtime/server/xbox-server.h"
29 #include "hphp/runtime/server/http-server.h"
30 #include "hphp/runtime/server/replay-transport.h"
31 #include "hphp/runtime/server/http-request-handler.h"
32 #include "hphp/runtime/server/admin-request-handler.h"
33 #include "hphp/runtime/server/server-stats.h"
34 #include "hphp/runtime/server/server-note.h"
35 #include "hphp/runtime/base/memory-manager.h"
36 #include "hphp/util/process.h"
37 #include "hphp/util/capability.h"
38 #include "hphp/util/embedded-data.h"
39 #include "hphp/util/timer.h"
40 #include "hphp/util/stack-trace.h"
41 #include "hphp/util/light-process.h"
42 #include "hphp/util/repo-schema.h"
43 #include "hphp/util/current-executable.h"
44 #include "hphp/util/service-data.h"
45 #include "hphp/runtime/base/stat-cache.h"
46 #include "hphp/runtime/ext/extension.h"
47 #include "hphp/runtime/ext/ext_fb.h"
48 #include "hphp/runtime/ext/json/ext_json.h"
49 #include "hphp/runtime/ext/ext_variable.h"
50 #include "hphp/runtime/ext/ext_apc.h"
51 #include "hphp/runtime/ext/ext_function.h"
52 #include "hphp/runtime/ext/ext_options.h"
53 #include "hphp/runtime/ext/ext_file.h"
54 #include "hphp/runtime/debugger/debugger.h"
55 #include "hphp/runtime/debugger/debugger_client.h"
56 #include "hphp/runtime/base/simple-counter.h"
57 #include "hphp/runtime/base/extended-logger.h"
58 #include "hphp/runtime/base/stream-wrapper-registry.h"
59 #include "hphp/runtime/vm/debug/debug.h"
61 #include <boost/program_options/options_description.hpp>
62 #include <boost/program_options/positional_options.hpp>
63 #include <boost/program_options/variables_map.hpp>
64 #include <boost/program_options/parsers.hpp>
65 #include <libgen.h>
66 #include <oniguruma.h>
67 #include <signal.h>
68 #include <libxml/parser.h>
70 #include "hphp/runtime/base/file-repository.h"
72 #include "hphp/runtime/vm/runtime.h"
73 #include "hphp/runtime/vm/runtime-type-profiler.h"
74 #include "hphp/runtime/vm/repo.h"
75 #include "hphp/runtime/vm/jit/arch.h"
76 #include "hphp/runtime/vm/jit/translator.h"
77 #include "hphp/compiler/builtin_symbols.h"
79 using namespace boost::program_options;
80 using std::cout;
81 extern char **environ;
83 #define MAX_INPUT_NESTING_LEVEL 64
85 namespace HPHP {
87 extern InitFiniNode *extra_process_init, *extra_process_exit;
89 void initialize_repo();
92 * XXX: VM process initialization is handled through a function
93 * pointer so libhphp_runtime.a can be linked into programs that don't
94 * actually initialize the VM.
96 void (*g_vmProcessInit)();
98 ///////////////////////////////////////////////////////////////////////////////
99 // helpers
101 struct ProgramOptions {
102 string mode;
103 string config;
104 std::vector<std::string>
105 confStrings;
106 int port;
107 int portfd;
108 int sslportfd;
109 int admin_port;
110 string user;
111 string file;
112 string lint;
113 bool isTempFile;
114 int count;
115 bool noSafeAccessCheck;
116 std::vector<std::string>
117 args;
118 string buildId;
119 string instanceId;
120 int xhprofFlags;
121 string show;
122 string parse;
124 Eval::DebuggerClientOptions debugger_options;
127 class StartTime {
128 public:
129 StartTime() : startTime(time(nullptr)) {}
130 time_t startTime;
132 static StartTime s_startTime;
133 static string tempFile;
135 time_t start_time() {
136 return s_startTime.startTime;
139 const StaticString
140 s_HPHP("HPHP"),
141 s_HHVM("HHVM"),
142 s_HHVM_JIT("HHVM_JIT"),
143 s_HHVM_ARCH("HHVM_ARCH"),
144 s_REQUEST_START_TIME("REQUEST_START_TIME"),
145 s_REQUEST_TIME("REQUEST_TIME"),
146 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
147 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
148 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
149 s_SCRIPT_NAME("SCRIPT_NAME"),
150 s_PHP_SELF("PHP_SELF"),
151 s_argc("argc"),
152 s_argv("argv"),
153 s_PWD("PWD"),
154 s_HOSTNAME("HOSTNAME"),
155 s__SERVER("_SERVER"),
156 s__ENV("_ENV");
158 String k_PHP_BINARY;
159 String k_PHP_BINDIR;
160 String k_PHP_OS;
161 String k_PHP_SAPI;
163 static void process_cmd_arguments(int argc, char **argv) {
164 GlobalVariables *g = get_global_variables();
165 g->set(s_argc, Variant(argc), false);
166 Array argvArray = HphpArray::GetStaticEmptyArray();
167 for (int i = 0; i < argc; i++) {
168 argvArray.append(String(argv[i]));
170 g->set(s_argv, argvArray, false);
173 void process_env_variables(Variant &variables) {
174 for (std::map<string, string>::const_iterator iter =
175 RuntimeOption::EnvVariables.begin();
176 iter != RuntimeOption::EnvVariables.end(); ++iter) {
177 variables.set(String(iter->first), String(iter->second));
179 for (char **env = environ; env && *env; env++) {
180 char *p = strchr(*env, '=');
181 if (p) {
182 String name(*env, p - *env, CopyString);
183 register_variable(variables, (char*)name.data(),
184 String(p + 1, CopyString));
189 void process_ini_settings() {
190 if (RuntimeOption::IniFile.empty()) {
191 return;
193 auto settings = f_parse_ini_file(String(RuntimeOption::IniFile));
194 for (ArrayIter iter(settings); iter; ++iter) {
195 IniSetting::Set(iter.first(), iter.second());
199 void register_variable(Variant &variables, char *name, CVarRef value,
200 bool overwrite /* = true */) {
201 // ignore leading spaces in the variable name
202 char *var = name;
203 while (*var && *var == ' ') {
204 var++;
207 // ensure that we don't have spaces or dots in the variable name
208 // (not binary safe)
209 bool is_array = false;
210 char *ip = nullptr; // index pointer
211 char *p = var;
212 for (; *p; p++) {
213 if (*p == ' ' || *p == '.') {
214 *p = '_';
215 } else if (*p == '[') {
216 is_array = true;
217 ip = p;
218 *p = 0;
219 break;
222 int var_len = p - var;
223 if (var_len == 0) {
224 // empty variable name, or variable name with a space in it
225 return;
228 vector<Variant> gpc_elements;
229 gpc_elements.reserve(MAX_INPUT_NESTING_LEVEL); // important, so no resize
230 Variant *symtable = &variables;
231 char *index = var;
232 int index_len = var_len;
234 if (is_array) {
235 int nest_level = 0;
236 while (true) {
237 if (++nest_level > MAX_INPUT_NESTING_LEVEL) {
238 Logger::Warning("Input variable nesting level exceeded");
239 return;
242 ip++;
243 char *index_s = ip;
244 int new_idx_len = 0;
245 if (isspace(*ip)) {
246 ip++;
248 if (*ip == ']') {
249 index_s = nullptr;
250 } else {
251 ip = strchr(ip, ']');
252 if (!ip) {
253 // PHP variables cannot contain '[' in their names,
254 // so we replace the character with a '_'
255 *(index_s - 1) = '_';
257 index_len = 0;
258 if (index) {
259 index_len = strlen(index);
261 goto plain_var;
263 *ip = 0;
264 new_idx_len = strlen(index_s);
267 if (!index) {
268 symtable->append(Array::Create());
269 gpc_elements.push_back(uninit_null());
270 gpc_elements.back().assignRef(
271 symtable->lvalAt((int)symtable->toArray().size() - 1));
272 } else {
273 String key(index, index_len, CopyString);
274 Variant v = symtable->rvalAt(key);
275 if (v.isNull() || !v.is(KindOfArray)) {
276 symtable->set(key, Array::Create());
278 gpc_elements.push_back(uninit_null());
279 gpc_elements.back().assignRef(symtable->lvalAt(key));
281 symtable = &gpc_elements.back();
282 /* ip pointed to the '[' character, now obtain the key */
283 index = index_s;
284 index_len = new_idx_len;
286 ip++;
287 if (*ip == '[') {
288 is_array = true;
289 *ip = 0;
290 } else {
291 goto plain_var;
294 } else {
295 plain_var:
296 if (!index) {
297 symtable->append(value);
298 } else {
299 String key(index, index_len, CopyString);
300 if (overwrite || !symtable->toArray().exists(key)) {
301 symtable->set(key, value);
307 enum class ContextOfException {
308 ReqInit = 1,
309 Invoke,
310 Handler,
313 static void handle_exception_append_bt(std::string& errorMsg,
314 const ExtendedException& e) {
315 Array bt = e.getBackTrace();
316 if (!bt.empty()) {
317 errorMsg += ExtendedLogger::StringOfStackTrace(bt);
321 static void bump_counter_and_rethrow() {
322 try {
323 throw;
324 } catch (const RequestTimeoutException& e) {
325 static auto requestTimeoutCounter = ServiceData::createTimeseries(
326 "requests_timed_out", {ServiceData::StatsType::COUNT});
327 requestTimeoutCounter->addValue(1);
328 throw;
329 } catch (const RequestMemoryExceededException& e) {
330 static auto requestMemoryExceededCounter = ServiceData::createTimeseries(
331 "requests_memory_exceeded", {ServiceData::StatsType::COUNT});
332 requestMemoryExceededCounter->addValue(1);
334 #ifdef USE_JEMALLOC
335 // Capture a pprof (C++) dump when we OOM a request
336 // TODO: (t3753133) Should dump a PHP-instrumented pprof dump here as well
337 jemalloc_pprof_dump("", false);
338 #endif
340 throw;
344 static void handle_exception_helper(bool& ret,
345 ExecutionContext* context,
346 std::string& errorMsg,
347 ContextOfException where,
348 bool& error,
349 bool richErrorMsg) {
350 try {
351 bump_counter_and_rethrow();
352 } catch (const Eval::DebuggerException &e) {
353 throw;
354 } catch (const ExitException &e) {
355 if (where == ContextOfException::ReqInit) {
356 ret = false;
357 } else if (where != ContextOfException::Handler &&
358 !context->getExitCallback().isNull() &&
359 f_is_callable(context->getExitCallback())) {
360 Array stack = e.getBackTrace();
361 Array argv = make_packed_array(e.ExitCode, stack);
362 vm_call_user_func(context->getExitCallback(), argv);
364 } catch (const PhpFileDoesNotExistException &e) {
365 ret = false;
366 if (where != ContextOfException::Handler) {
367 raise_notice("%s", e.getMessage().c_str());
368 } else {
369 Logger::Error("%s", e.getMessage().c_str());
371 if (richErrorMsg) {
372 handle_exception_append_bt(errorMsg, e);
374 } catch (const UncatchableException &e) {
375 ret = false;
376 error = true;
377 errorMsg = "";
378 if (RuntimeOption::ServerStackTrace) {
379 errorMsg = e.what();
380 } else if (RuntimeOption::InjectedStackTrace) {
381 errorMsg = e.getMessage();
382 errorMsg += "\n";
383 errorMsg += ExtendedLogger::StringOfStackTrace(e.getBackTrace());
385 Logger::Error("%s", errorMsg.c_str());
386 if (richErrorMsg) {
387 handle_exception_append_bt(errorMsg, e);
389 } catch (const Exception &e) {
390 bool oldRet = ret;
391 bool origError = error;
392 std::string origErrorMsg = errorMsg;
393 ret = false;
394 error = true;
395 errorMsg = "";
396 if (where == ContextOfException::Handler) {
397 errorMsg = "Exception handler threw an exception: ";
399 errorMsg += e.what();
400 if (where == ContextOfException::Invoke) {
401 bool handlerRet = context->onFatalError(e);
402 if (handlerRet) {
403 ret = oldRet;
404 error = origError;
405 errorMsg = origErrorMsg;
407 } else {
408 Logger::Error("%s", errorMsg.c_str());
410 if (richErrorMsg) {
411 const ExtendedException *ee = dynamic_cast<const ExtendedException *>(&e);
412 if (ee) {
413 handle_exception_append_bt(errorMsg, *ee);
416 } catch (const Object &e) {
417 bool oldRet = ret;
418 bool origError = error;
419 std::string origErrorMsg = errorMsg;
420 ret = false;
421 error = true;
422 errorMsg = "";
423 if (where == ContextOfException::Handler) {
424 errorMsg = "Exception handler threw an object exception: ";
426 try {
427 errorMsg += e.toString().data();
428 } catch (...) {
429 errorMsg += "(unable to call toString())";
431 if (where == ContextOfException::Invoke) {
432 bool handlerRet = context->onUnhandledException(e);
433 if (handlerRet) {
434 ret = oldRet;
435 error = origError;
436 errorMsg = origErrorMsg;
438 } else {
439 Logger::Error("%s", errorMsg.c_str());
441 } catch (...) {
442 ret = false;
443 error = true;
444 errorMsg = "(unknown exception was thrown)";
445 Logger::Error("%s", errorMsg.c_str());
449 static bool hphp_chdir_file(const string filename) {
450 bool ret = false;
451 String s = File::TranslatePath(filename);
452 char *buf = strndup(s.data(), s.size());
453 char *dir = dirname(buf);
454 assert(dir);
455 if (dir) {
456 if (File::IsVirtualDirectory(dir)) {
457 g_context->setCwd(String(dir, CopyString));
458 ret = true;
459 } else {
460 struct stat sb;
461 stat(dir, &sb);
462 if ((sb.st_mode & S_IFMT) == S_IFDIR) {
463 ret = true;
464 if (*dir != '.') {
465 g_context->setCwd(String(dir, CopyString));
470 free(buf);
471 return ret;
474 void handle_destructor_exception(const char* situation) {
475 string errorMsg;
477 try {
478 throw;
479 } catch (ExitException &e) {
480 // ExitException is fine, no need to show a warning.
481 ThreadInfo::s_threadInfo->setPendingException(e.clone());
482 return;
483 } catch (Object &e) {
484 // For user exceptions, invoke the user exception handler
485 errorMsg = situation;
486 errorMsg += " threw an object exception: ";
487 try {
488 errorMsg += e.toString().data();
489 } catch (...) {
490 errorMsg += "(unable to call toString())";
492 } catch (Exception &e) {
493 ThreadInfo::s_threadInfo->setPendingException(e.clone());
494 errorMsg = situation;
495 errorMsg += " raised a fatal error: ";
496 errorMsg += e.what();
497 } catch (...) {
498 errorMsg = situation;
499 errorMsg += " threw an unknown exception";
501 // For fatal errors and unknown exceptions, we raise a warning.
502 // If there is a user error handler it will be invoked, otherwise
503 // the default error handler will be invoked.
504 try {
505 raise_debugging("%s", errorMsg.c_str());
506 } catch (...) {
507 // The user error handler fataled or threw an exception,
508 // print out the error message directly to the log
509 Logger::Warning("%s", errorMsg.c_str());
513 void execute_command_line_begin(int argc, char **argv, int xhprof) {
514 StackTraceNoHeap::AddExtraLogging("ThreadType", "CLI");
515 string args;
516 for (int i = 0; i < argc; i++) {
517 if (i) args += " ";
518 args += argv[i];
520 StackTraceNoHeap::AddExtraLogging("Arguments", args.c_str());
522 hphp_session_init();
523 ExecutionContext *context = g_context.getNoCheck();
524 context->obSetImplicitFlush(true);
526 GlobalVariables *g = get_global_variables();
528 Variant& env = g->getRef(s__ENV);
529 process_env_variables(env);
530 env.set(s_HPHP, 1);
531 env.set(s_HHVM, 1);
532 if (RuntimeOption::EvalJit) {
533 env.set(s_HHVM_JIT, 1);
535 switch (JIT::arch()) {
536 case JIT::Arch::X64:
537 env.set(s_HHVM_ARCH, "x64");
538 break;
539 case JIT::Arch::ARM:
540 env.set(s_HHVM_ARCH, "arm");
541 break;
544 process_cmd_arguments(argc, argv);
546 Variant& server = g->getRef(s__SERVER);
547 process_env_variables(server);
548 time_t now;
549 struct timeval tp = {0};
550 double now_double;
551 if (!gettimeofday(&tp, nullptr)) {
552 now_double = (double)(tp.tv_sec + tp.tv_usec / 1000000.00);
553 now = tp.tv_sec;
554 } else {
555 now = time(nullptr);
556 now_double = (double)now;
558 String file = empty_string;
559 if (argc > 0) {
560 file = StringData::Make(argv[0], CopyString);
562 server.set(s_REQUEST_START_TIME, now);
563 server.set(s_REQUEST_TIME, now);
564 server.set(s_REQUEST_TIME_FLOAT, now_double);
565 server.set(s_DOCUMENT_ROOT, empty_string);
566 server.set(s_SCRIPT_FILENAME, file);
567 server.set(s_SCRIPT_NAME, file);
568 server.set(s_PHP_SELF, file);
569 server.set(s_argv, g->get(s_argv));
570 server.set(s_argc, g->get(s_argc));
571 server.set(s_PWD, g_context->getCwd());
572 char hostname[1024];
573 if (!gethostname(hostname, 1024)) {
574 server.set(s_HOSTNAME, String(hostname, CopyString));
577 for(std::map<string,string>::iterator it =
578 RuntimeOption::ServerVariables.begin(),
579 end = RuntimeOption::ServerVariables.end(); it != end; ++it) {
580 server.set(String(it->first.c_str()), String(it->second.c_str()));
583 if (xhprof) {
584 f_xhprof_enable(xhprof, uninit_null().toArray());
587 if (RuntimeOption::RequestTimeoutSeconds) {
588 ThreadInfo::s_threadInfo->m_reqInjectionData.setTimeout(
589 RuntimeOption::RequestTimeoutSeconds);
592 Extension::RequestInitModules();
593 process_ini_settings();
596 void execute_command_line_end(int xhprof, bool coverage, const char *program) {
597 ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
599 if (RuntimeOption::EvalDumpTC) {
600 HPHP::JIT::tc_dump();
603 if (xhprof) {
604 f_var_dump(HHVM_FN(json_encode)(f_xhprof_disable()));
606 hphp_context_exit(g_context.getNoCheck(), true, true, program);
607 hphp_session_exit();
608 if (coverage && ti->m_reqInjectionData.getCoverage() &&
609 !RuntimeOption::CodeCoverageOutputFile.empty()) {
610 ti->m_coverage->Report(RuntimeOption::CodeCoverageOutputFile);
614 #ifdef __APPLE__
615 const void* __hot_start = nullptr;
616 const void* __hot_end = nullptr;
617 #else
618 extern "C" {
619 void __attribute__((weak)) __hot_start();
620 void __attribute__((weak)) __hot_end();
622 #endif
624 #if FACEBOOK
625 # define AT_END_OF_TEXT __attribute__((__section__(".stub")))
626 #else
627 # define AT_END_OF_TEXT
628 #endif
630 static void NEVER_INLINE AT_END_OF_TEXT __attribute__((optimize("2")))
631 hugifyText(char* from, char* to) {
632 #if FACEBOOK && !defined FOLLY_SANITIZE_ADDRESS && defined MADV_HUGEPAGE
633 size_t sz = to - from;
634 void* mem = malloc(sz);
635 memcpy(mem, from, sz);
637 // This maps out a portion of our executable
638 // We need to be very careful about what we do
639 // until we replace the original code
640 mmap(from, sz,
641 PROT_READ | PROT_WRITE | PROT_EXEC,
642 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
643 -1, 0);
644 // This is in glibc, which isn't a problem, except for
645 // the trampoline code in .plt, which we dealt with
646 // in the linker script
647 madvise(from, sz, MADV_HUGEPAGE);
648 // Don't use memcpy because its probably one of the
649 // functions thats been mapped out.
650 // Needs the attribute((optimize("2")) to prevent
651 // g++ from turning this back into memcpy(!)
652 wordcpy((uint64_t*)from, (uint64_t*)mem, sz / sizeof(uint64_t));
653 mprotect(from, sz, PROT_READ | PROT_EXEC);
654 free(mem);
655 mlock(from, to - from);
656 Debug::DebugInfo::setPidMapOverlay(from, to);
657 #endif
660 static void pagein_self(void) {
661 unsigned long begin, end, inode, pgoff;
662 char mapname[PATH_MAX];
663 char perm[5];
664 char dev[6];
665 char *buf;
666 int bufsz;
667 int r;
668 FILE *fp;
670 // pad due to the spaces between the inode number and the mapname
671 bufsz = sizeof(unsigned long) * 4 + sizeof(mapname) + sizeof(char) * 11 + 100;
672 buf = (char *)malloc(bufsz);
673 if (buf == nullptr)
674 return;
676 Timer timer(Timer::WallTime, "mapping self");
677 fp = fopen("/proc/self/maps", "r");
678 if (fp != nullptr) {
679 while (!feof(fp)) {
680 if (fgets(buf, bufsz, fp) == 0)
681 break;
682 r = sscanf(buf, "%lx-%lx %4s %lx %5s %ld %s",
683 &begin, &end, perm, &pgoff, dev, &inode, mapname);
685 // page in read-only segments that correspond to a file on disk
686 if (r != 7 ||
687 perm[0] != 'r' ||
688 perm[1] != '-' ||
689 access(mapname, F_OK) != 0) {
690 continue;
693 auto beginPtr = (char*)begin;
694 auto endPtr = (char*)end;
695 auto hotStart = (char*)__hot_start;
696 auto hotEnd = (char*)__hot_end;
697 const size_t hugeBytes = 2L * 1024 * 1024;
699 if (mlock(beginPtr, end - begin) == 0) {
700 if (RuntimeOption::EvalMapHotTextHuge &&
701 __hot_start &&
702 __hot_end &&
703 hugePagesSupported() &&
704 beginPtr <= hotStart &&
705 hotEnd <= endPtr) {
707 char* from = hotStart - ((intptr_t)hotStart & (hugeBytes - 1));
708 char* to = hotEnd + (hugeBytes - 1);
709 to -= (intptr_t)to & (hugeBytes - 1);
710 if (to < (void*)hugifyText) {
711 hugifyText(from, to);
714 if (!RuntimeOption::LockCodeMemory) {
715 munlock(beginPtr, end - begin);
719 fclose(fp);
721 free(buf);
724 /* Sets RuntimeOption::ExecutionMode according
725 * to commandline options prior to config load
727 static void set_execution_mode(string mode) {
728 if (mode == "daemon" || mode == "server" || mode == "replay") {
729 RuntimeOption::ExecutionMode = "srv";
730 Logger::Escape = true;
731 } else if (mode == "run" || mode == "debug") {
732 RuntimeOption::ExecutionMode = "cli";
733 Logger::Escape = false;
734 } else if (mode == "translate") {
735 RuntimeOption::ExecutionMode = "";
736 Logger::Escape = false;
737 } else {
738 // Undefined mode
739 always_assert(false);
743 static int start_server(const std::string &username) {
744 // Before we start the webserver, make sure the entire
745 // binary is paged into memory.
746 pagein_self();
748 set_execution_mode("server");
749 HttpRequestHandler::GetAccessLog().init
750 (RuntimeOption::AccessLogDefaultFormat, RuntimeOption::AccessLogs,
751 username);
752 AdminRequestHandler::GetAccessLog().init
753 (RuntimeOption::AdminLogFormat, RuntimeOption::AdminLogSymLink,
754 RuntimeOption::AdminLogFile,
755 username);
757 #if !defined(SKIP_USER_CHANGE)
758 if (!username.empty()) {
759 if (Logger::UseCronolog) {
760 Cronolog::changeOwner(username, RuntimeOption::LogFileSymLink);
762 Capability::ChangeUnixUser(username);
763 LightProcess::ChangeUser(username);
765 Capability::SetDumpable();
766 #endif
768 // Create the HttpServer before any warmup requests to properly
769 // initialize the process
770 HttpServer::Server = std::make_shared<HttpServer>();
772 // If we have any warmup requests, replay them before listening for
773 // real connections
774 for (auto& file : RuntimeOption::ServerWarmupRequests) {
775 HttpRequestHandler handler(0);
776 ReplayTransport rt;
777 timespec start;
778 Timer::GetMonotonicTime(start);
779 std::string error;
780 Logger::Info("Replaying warmup request %s", file.c_str());
782 try {
783 rt.onRequestStart(start);
784 rt.replayInput(Hdf(file));
785 handler.handleRequest(&rt);
787 timespec stop;
788 Timer::GetMonotonicTime(stop);
789 Logger::Info("Finished successfully in %ld seconds",
790 stop.tv_sec - start.tv_sec);
791 } catch (std::exception& e) {
792 error = e.what();
795 if (error.size()) {
796 Logger::Info("Got exception during warmup: %s", error.c_str());
800 if (RuntimeOption::EvalEnableNuma) {
801 #ifdef USE_JEMALLOC
802 mallctl("arenas.purge", nullptr, nullptr, nullptr, 0);
803 #endif
804 enable_numa(RuntimeOption::EvalEnableNumaLocal);
808 HttpServer::Server->runOrExitProcess();
809 return 0;
812 string translate_stack(const char *hexencoded, bool with_frame_numbers) {
813 if (!hexencoded || !*hexencoded) {
814 return "";
817 StackTrace st(hexencoded);
818 std::vector<std::shared_ptr<StackTrace::Frame>> frames;
819 st.get(frames);
821 std::ostringstream out;
822 for (unsigned int i = 0; i < frames.size(); i++) {
823 auto f = frames[i];
824 if (with_frame_numbers) {
825 out << "# " << (i < 10 ? " " : "") << i << ' ';
827 out << f->toString();
828 out << '\n';
830 return out.str();
833 ///////////////////////////////////////////////////////////////////////////////
835 static void prepare_args(int &argc,
836 char **&argv,
837 const std::vector<std::string> &args,
838 const char *file) {
839 argv = (char **)malloc((args.size() + 2) * sizeof(char*));
840 argc = 0;
841 if (file && *file) {
842 argv[argc++] = (char*)file;
844 for (int i = 0; i < (int)args.size(); i++) {
845 argv[argc++] = (char*)args[i].c_str();
847 argv[argc] = nullptr;
850 static int execute_program_impl(int argc, char **argv);
851 int execute_program(int argc, char **argv) {
852 int ret_code = -1;
853 try {
854 initialize_repo();
855 init_thread_locals();
856 ret_code = execute_program_impl(argc, argv);
857 } catch (const Exception &e) {
858 Logger::Error("Uncaught exception: %s", e.what());
859 } catch (const FailedAssertion& fa) {
860 fa.print();
861 StackTraceNoHeap::AddExtraLogging("Assertion failure", fa.summary);
862 abort();
863 } catch (const std::exception &e) {
864 Logger::Error("Uncaught exception: %s", e.what());
865 } catch (...) {
866 Logger::Error("Uncaught exception: (unknown)");
868 if (tempFile.length() && boost::filesystem::exists(tempFile)) {
869 boost::filesystem::remove(tempFile);
871 return ret_code;
874 /* -1 - cannot open file
875 * 0 - no need to open file
876 * 1 - fopen
877 * 2 - popen
879 static int open_server_log_file() {
880 if (!RuntimeOption::LogFile.empty()) {
881 if (Logger::UseCronolog) {
882 if (strchr(RuntimeOption::LogFile.c_str(), '%')) {
883 Logger::cronOutput.m_template = RuntimeOption::LogFile;
884 Logger::cronOutput.setPeriodicity();
885 Logger::cronOutput.m_linkName = RuntimeOption::LogFileSymLink;
886 return 0;
887 } else {
888 Logger::Output = fopen(RuntimeOption::LogFile.c_str(), "a");
889 if (Logger::Output) return 1;
891 } else {
892 if (Logger::IsPipeOutput) {
893 Logger::Output = popen(RuntimeOption::LogFile.substr(1).c_str(), "w");
894 if (Logger::Output) return 2;
895 } else {
896 Logger::Output = fopen(RuntimeOption::LogFile.c_str(), "a");
897 if (Logger::Output) return 1;
900 Logger::Error("Cannot open log file: %s", RuntimeOption::LogFile.c_str());
901 return -1;
903 return 0;
906 static void close_server_log_file(int kind) {
907 if (kind == 1) {
908 fclose(Logger::Output);
909 } else if (kind == 2) {
910 pclose(Logger::Output);
911 } else {
912 always_assert(!Logger::Output);
916 static int compute_hhvm_argc(const options_description& desc,
917 int argc, char** argv) {
918 enum ArgCode {
919 NO_ARG = 0,
920 ARG_REQUIRED = 1,
921 ARG_OPTIONAL = 2
923 const auto& vec = desc.options();
924 std::map<string,ArgCode> long_options;
925 std::map<string,ArgCode> short_options;
926 // Build lookup maps for the short options and the long options
927 for (unsigned i = 0; i < vec.size(); ++i) {
928 auto opt = vec[i];
929 auto long_name = opt->long_name();
930 ArgCode code = NO_ARG;
931 if (opt->semantic()->max_tokens() == 1) {
932 if (opt->semantic()->min_tokens() == 1) {
933 code = ARG_REQUIRED;
934 } else {
935 code = ARG_OPTIONAL;
938 long_options[long_name] = code;
939 auto format_name = opt->format_name();
940 if (format_name.size() >= 2 && format_name[0] == '-' &&
941 format_name[1] != '-') {
942 auto short_name = format_name.substr(1,1);
943 short_options[short_name] = code;
946 // Loop over the args
947 int pos = 1;
948 while (pos < argc) {
949 const char* str = argv[pos];
950 int len = strlen(str);
951 if (len == 2 && memcmp(str, "--", 2) == 0) {
952 // We found "--". All args after this are intended for the
953 // PHP application
954 ++pos;
955 break;
957 if (len >= 3 && str[0] == '-' && str[1] == '-') {
958 // Handle long options
959 ++pos;
960 string s(str+2);
961 auto it = long_options.find(s);
962 if (it != long_options.end() && it->second != NO_ARG && pos < argc &&
963 (it->second == ARG_REQUIRED || argv[pos][0] != '-')) {
964 ++pos;
966 } else if (len >= 2 && str[0] == '-') {
967 // Handle short options
968 ++pos;
969 string s;
970 s.append(1, str[1]);
971 auto it = short_options.find(s);
972 if (it != short_options.end() && it->second != 0 && len == 2 &&
973 pos < argc && (it->second == ARG_REQUIRED || argv[pos][0] != '-')) {
974 ++pos;
976 } else {
977 // We've found a non-option argument. This arg and all args
978 // that follow are intended for the PHP application
979 break;
982 return pos;
985 static int execute_program_impl(int argc, char** argv) {
986 string usage = "Usage:\n\n ";
987 usage += argv[0];
988 usage += " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
990 ProgramOptions po;
991 options_description desc(usage.c_str());
992 desc.add_options()
993 ("help", "display this message")
994 ("version", "display version number")
995 ("php", "emulate the standard php command line")
996 ("compiler-id", "display the git hash for the compiler")
997 ("repo-schema", "display the repository schema id")
998 ("mode,m", value<string>(&po.mode)->default_value("run"),
999 "run | debug (d) | server (s) | daemon | replay | translate (t)")
1000 ("config,c", value<string>(&po.config),
1001 "load specified config file")
1002 ("config-value,v", value<std::vector<std::string>>(&po.confStrings)->composing(),
1003 "individual configuration string in a format of name=value, where "
1004 "name can be any valid configuration for a config file")
1005 ("port,p", value<int>(&po.port)->default_value(-1),
1006 "start an HTTP server at specified port")
1007 ("port-fd", value<int>(&po.portfd)->default_value(-1),
1008 "use specified fd instead of creating a socket")
1009 ("ssl-port-fd", value<int>(&po.sslportfd)->default_value(-1),
1010 "use specified fd for SSL instead of creating a socket")
1011 ("admin-port", value<int>(&po.admin_port)->default_value(-1),
1012 "start admin listener at specified port")
1013 ("debug-config", value<string>(&po.debugger_options.configFName),
1014 "load specified debugger config file")
1015 ("debug-host,h",
1016 value<string>(&po.debugger_options.host)->implicit_value("localhost"),
1017 "connect to debugger server at specified address")
1018 ("debug-port", value<int>(&po.debugger_options.port)->default_value(-1),
1019 "connect to debugger server at specified port")
1020 ("debug-extension", value<string>(&po.debugger_options.extension),
1021 "PHP file that extends y command")
1022 ("debug-cmd", value<std::vector<std::string>>(
1023 &po.debugger_options.cmds)->composing(),
1024 "executes this debugger command and returns its output in stdout")
1025 ("debug-sandbox",
1026 value<string>(&po.debugger_options.sandbox)->default_value("default"),
1027 "initial sandbox to attach to when debugger is started")
1028 ("user,u", value<string>(&po.user),
1029 "run server under this user account")
1030 ("file,f", value<string>(&po.file),
1031 "executing specified file")
1032 ("lint,l", value<string>(&po.lint),
1033 "lint specified file")
1034 ("show,w", value<string>(&po.show),
1035 "output specified file and do nothing else")
1036 ("parse", value<string>(&po.parse),
1037 "parse specified file and dump the AST")
1038 ("temp-file",
1039 "file specified is temporary and removed after execution")
1040 ("count", value<int>(&po.count)->default_value(1),
1041 "how many times to repeat execution")
1042 ("no-safe-access-check",
1043 value<bool>(&po.noSafeAccessCheck)->default_value(false),
1044 "whether to ignore safe file access check")
1045 ("arg", value<std::vector<std::string>>(&po.args)->composing(),
1046 "arguments")
1047 ("extra-header", value<string>(&Logger::ExtraHeader),
1048 "extra-header to add to log lines")
1049 ("build-id", value<string>(&po.buildId),
1050 "unique identifier of compiled server code")
1051 ("instance-id", value<string>(&po.instanceId),
1052 "unique identifier of server instance")
1053 ("xhprof-flags", value<int>(&po.xhprofFlags)->default_value(0),
1054 "Set XHProf flags")
1057 positional_options_description p;
1058 p.add("arg", -1);
1059 variables_map vm;
1061 // Before invoking the boost command line parser, we do a manual pass
1062 // to find the first occurrence of either "--" or a non-option argument
1063 // in order to determine which arguments should be consumed by HHVM and
1064 // which arguments should be passed along to the PHP application. This
1065 // is necessary so that the boost command line parser doesn't choke on
1066 // args intended for the PHP application.
1067 int hhvm_argc = compute_hhvm_argc(desc, argc, argv);
1069 try {
1070 // Invoke the boost command line parser to parse the args for HHVM.
1071 auto opts = command_line_parser(hhvm_argc, argv)
1072 .options(desc)
1073 .positional(p)
1074 // If these style options are changed, compute_hhvm_argc() will
1075 // need to be updated appropriately
1076 .style(command_line_style::default_style &
1077 ~command_line_style::allow_guessing &
1078 ~command_line_style::allow_sticky &
1079 ~command_line_style::long_allow_adjacent)
1080 .run();
1081 // Manually append the args for the PHP application.
1082 int pos = 0;
1083 for (unsigned m = 0; m < opts.options.size(); ++m) {
1084 const auto& bo = opts.options[m];
1085 if (bo.string_key == "arg") {
1086 ++pos;
1089 for (unsigned m = hhvm_argc; m < argc; ++m) {
1090 string str = argv[m];
1091 basic_option<char> bo;
1092 bo.string_key = "arg";
1093 bo.position_key = pos++;
1094 bo.value.push_back(str);
1095 bo.original_tokens.push_back(str);
1096 bo.unregistered = false;
1097 bo.case_insensitive = false;
1098 opts.options.push_back(bo);
1100 // Process the options
1101 store(opts, vm);
1102 notify(vm);
1103 if (po.mode == "d") po.mode = "debug";
1104 if (po.mode == "s") po.mode = "server";
1105 if (po.mode == "t") po.mode = "translate";
1106 if (po.mode == "") po.mode = "run";
1107 if (po.mode == "daemon" || po.mode == "server" || po.mode == "replay" ||
1108 po.mode == "run" || po.mode == "debug"|| po.mode == "translate") {
1109 set_execution_mode(po.mode);
1110 } else {
1111 Logger::Error("Error in command line: invalid mode: %s", po.mode.c_str());
1112 cout << desc << "\n";
1113 return -1;
1115 if (po.config.empty()) {
1116 auto default_config_file = "/etc/hhvm/config.hdf";
1117 if (access(default_config_file, R_OK) != -1) {
1118 Logger::Verbose("Using default config file: %s", default_config_file);
1119 po.config = default_config_file;
1122 } catch (error &e) {
1123 Logger::Error("Error in command line: %s", e.what());
1124 cout << desc << "\n";
1125 return -1;
1126 } catch (...) {
1127 Logger::Error("Error in command line.");
1128 cout << desc << "\n";
1129 return -1;
1131 if (vm.count("help")) {
1132 cout << desc << "\n";
1133 return 0;
1135 if (vm.count("version")) {
1136 cout << "HipHop VM";
1137 cout << " " << k_HHVM_VERSION.c_str();
1138 cout << " (" << (debug ? "dbg" : "rel") << ")\n";
1139 cout << "Compiler: " << kCompilerId << "\n";
1140 cout << "Repo schema: " << kRepoSchemaId << "\n";
1141 return 0;
1143 if (vm.count("compiler-id")) {
1144 cout << kCompilerId << "\n";
1145 return 0;
1148 if (vm.count("repo-schema")) {
1149 cout << kRepoSchemaId << "\n";
1150 return 0;
1153 if (!po.show.empty()) {
1154 PlainFile f;
1155 f.open(po.show, "r");
1156 if (!f.valid()) {
1157 Logger::Error("Unable to open file %s", po.show.c_str());
1158 return 1;
1160 f.print();
1161 f.close();
1162 return 0;
1165 po.isTempFile = vm.count("temp-file");
1167 // forget the source for systemlib.php unless we are debugging
1168 if (po.mode != "debug") SystemLib::s_source = "";
1170 // we need to initialize pcre cache table very early
1171 pcre_init();
1173 Hdf config;
1174 if (!po.config.empty()) {
1175 config.open(po.config);
1177 RuntimeOption::Load(config, &po.confStrings);
1178 vector<string> badnodes;
1179 config.lint(badnodes);
1180 for (unsigned int i = 0; i < badnodes.size(); i++) {
1181 Logger::Error("Possible bad config node: %s", badnodes[i].c_str());
1183 if (RuntimeOption::EvalRuntimeTypeProfile) {
1184 HPHP::initTypeProfileStructure();
1186 vector<int> inherited_fds;
1187 RuntimeOption::BuildId = po.buildId;
1188 RuntimeOption::InstanceId = po.instanceId;
1189 if (po.port != -1) {
1190 RuntimeOption::ServerPort = po.port;
1192 if (po.portfd != -1) {
1193 RuntimeOption::ServerPortFd = po.portfd;
1194 inherited_fds.push_back(po.portfd);
1196 if (po.sslportfd != -1) {
1197 RuntimeOption::SSLPortFd = po.sslportfd;
1198 inherited_fds.push_back(po.sslportfd);
1200 if (po.admin_port != -1) {
1201 RuntimeOption::AdminServerPort = po.admin_port;
1203 if (po.noSafeAccessCheck) {
1204 RuntimeOption::SafeFileAccess = false;
1207 if (po.mode == "daemon") {
1208 if (RuntimeOption::LogFile.empty()) {
1209 Logger::Error("Log file not specified under daemon mode.\n\n");
1211 int ret = open_server_log_file();
1212 Process::Daemonize();
1213 close_server_log_file(ret);
1216 open_server_log_file();
1218 // Defer the initialization of light processes until the log file handle is
1219 // created, so that light processes can log to the right place. If we ever
1220 // lose a light process, stop the server instead of proceeding in an
1221 // uncertain state.
1222 LightProcess::SetLostChildHandler([](pid_t child) {
1223 if (!HttpServer::Server) return;
1224 if (!HttpServer::Server->isStopped()) {
1225 HttpServer::Server->stop("lost light process child");
1228 LightProcess::Initialize(RuntimeOption::LightProcessFilePrefix,
1229 RuntimeOption::LightProcessCount,
1230 inherited_fds);
1233 const size_t stackSizeMinimum = 8 * 1024 * 1024;
1234 struct rlimit rlim;
1235 if (getrlimit(RLIMIT_STACK, &rlim) == 0 &&
1236 (rlim.rlim_cur == RLIM_INFINITY ||
1237 rlim.rlim_cur < stackSizeMinimum)) {
1238 rlim.rlim_cur = stackSizeMinimum;
1239 if (stackSizeMinimum > rlim.rlim_max) {
1240 rlim.rlim_max = stackSizeMinimum;
1242 if (setrlimit(RLIMIT_STACK, &rlim)) {
1243 Logger::Error("failed to set stack limit to %zd\n", stackSizeMinimum);
1248 ShmCounters::initialize(true, Logger::Error);
1249 // Initialize compiler state
1250 compile_file(0, 0, MD5(), 0);
1252 if (!po.lint.empty()) {
1253 if (po.isTempFile) {
1254 tempFile = po.lint;
1257 hphp_process_init();
1258 try {
1259 HPHP::Eval::PhpFile* phpFile = g_vmContext->lookupPhpFile(
1260 makeStaticString(po.lint.c_str()), "", nullptr);
1261 if (phpFile == nullptr) {
1262 throw FileOpenException(po.lint.c_str());
1264 Unit* unit = phpFile->unit();
1265 const StringData* msg;
1266 int line;
1267 if (unit->compileTimeFatal(msg, line)) {
1268 VMParserFrame parserFrame;
1269 parserFrame.filename = po.lint.c_str();
1270 parserFrame.lineNumber = line;
1271 Array bt = g_vmContext->debugBacktrace(false, true,
1272 false, &parserFrame);
1273 throw FatalErrorException(msg->data(), bt);
1275 } catch (FileOpenException &e) {
1276 Logger::Error("%s", e.getMessage().c_str());
1277 return 1;
1278 } catch (const FatalErrorException& e) {
1279 RuntimeOption::CallUserHandlerOnFatals = false;
1280 RuntimeOption::AlwaysLogUnhandledExceptions = true;
1281 g_context->onFatalError(e);
1282 return 1;
1284 Logger::Info("No syntax errors detected in %s", po.lint.c_str());
1285 return 0;
1288 if (!po.parse.empty()) {
1289 Logger::Error("The 'parse' command line option is not supported\n\n");
1290 return 1;
1293 if (argc <= 1 || po.mode == "run" || po.mode == "debug") {
1294 if (po.isTempFile) {
1295 tempFile = po.file;
1298 set_execution_mode("run");
1300 int new_argc;
1301 char **new_argv;
1302 prepare_args(new_argc, new_argv, po.args, po.file.c_str());
1304 if (!po.file.empty()) {
1305 Repo::setCliFile(po.file);
1306 } else if (new_argc > 0) {
1307 Repo::setCliFile(new_argv[0]);
1310 int ret = 0;
1311 hphp_process_init();
1313 string file;
1314 if (new_argc > 0) {
1315 file = new_argv[0];
1318 if (po.mode == "debug") {
1319 StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
1320 RuntimeOption::EnableDebugger = true;
1321 po.debugger_options.fileName = file;
1322 po.debugger_options.user = po.user;
1323 Eval::DebuggerProxyPtr localProxy =
1324 Eval::Debugger::StartClient(po.debugger_options);
1325 if (!localProxy) {
1326 Logger::Error("Failed to start debugger client\n\n");
1327 return 1;
1329 Eval::Debugger::RegisterSandbox(localProxy->getDummyInfo());
1330 std::shared_ptr<std::vector<std::string>> client_args;
1331 bool restart = false;
1332 ret = 0;
1333 while (true) {
1334 try {
1335 execute_command_line_begin(new_argc, new_argv, po.xhprofFlags);
1336 // Set the proxy for this thread to be the localProxy we just
1337 // created. If we're script debugging, this will be the proxy that
1338 // does all of our work. If we're remote debugging, this proxy will
1339 // go unused until we finally stop it when the user quits the
1340 // debugger.
1341 g_context->setSandboxId(localProxy->getDummyInfo().id());
1342 Eval::Debugger::DebuggerSession(po.debugger_options, restart);
1343 restart = false;
1344 execute_command_line_end(po.xhprofFlags, true, file.c_str());
1345 } catch (const Eval::DebuggerRestartException &e) {
1346 execute_command_line_end(0, false, nullptr);
1348 if (!e.m_args->empty()) {
1349 file = e.m_args->at(0);
1350 client_args = e.m_args;
1351 free(new_argv);
1352 prepare_args(new_argc, new_argv, *client_args, nullptr);
1354 // Systemlib.php is not loaded again, so we need this if we
1355 // are to hit any breakpoints in systemlib.
1356 phpSetBreakPoints(localProxy.get());
1357 restart = true;
1358 } catch (const Eval::DebuggerClientExitException &e) {
1359 execute_command_line_end(0, false, nullptr);
1360 break; // end user quitting debugger
1364 } else {
1365 ret = 0;
1366 for (int i = 0; i < po.count; i++) {
1367 execute_command_line_begin(new_argc, new_argv, po.xhprofFlags);
1368 ret = 255;
1369 if (hphp_invoke_simple(file)) {
1370 ret = ExitException::ExitCode;
1372 execute_command_line_end(po.xhprofFlags, true, file.c_str());
1376 free(new_argv);
1377 hphp_process_exit();
1379 return ret;
1382 if (po.mode == "daemon" || po.mode == "server") {
1383 if (!po.user.empty()) RuntimeOption::ServerUser = po.user;
1384 return start_server(RuntimeOption::ServerUser);
1387 if (po.mode == "replay" && !po.args.empty()) {
1388 RuntimeOption::RecordInput = false;
1389 set_execution_mode("server");
1390 HttpServer server; // so we initialize runtime properly
1391 HttpRequestHandler handler(0);
1392 for (int i = 0; i < po.count; i++) {
1393 for (unsigned int j = 0; j < po.args.size(); j++) {
1394 ReplayTransport rt;
1395 rt.replayInput(po.args[j].c_str());
1396 handler.handleRequest(&rt);
1397 printf("%s\n", rt.getResponse().c_str());
1400 return 0;
1403 if (po.mode == "translate" && !po.args.empty()) {
1404 printf("%s", translate_stack(po.args[0].c_str()).c_str());
1405 return 0;
1408 cout << desc << "\n";
1409 return -1;
1412 String canonicalize_path(const String& p, const char* root, int rootLen) {
1413 String path(Util::canonicalize(p.c_str(), p.size()), AttachString);
1414 if (path.charAt(0) == '/') {
1415 const string &sourceRoot = RuntimeOption::SourceRoot;
1416 int len = sourceRoot.size();
1417 if (len && strncmp(path.data(), sourceRoot.c_str(), len) == 0) {
1418 return path.substr(len);
1420 if (root && rootLen && strncmp(path.data(), root, rootLen) == 0) {
1421 return path.substr(rootLen);
1424 return path;
1427 static string systemlib_split(string slib, string* hhas) {
1428 auto pos = slib.find("\n<?hhas\n");
1429 if (pos != string::npos) {
1430 if (hhas) *hhas = slib.substr(pos + 8);
1431 return slib.substr(0, pos);
1433 return slib;
1436 // Retrieve a systemlib (or mini systemlib) from the
1437 // current executable or another ELF object file.
1439 // Additionally, when retrieving the main systemlib
1440 // from the current executable, honor the
1441 // HHVM_SYSTEMLIB environment variable as an override.
1442 string get_systemlib(string* hhas, const string &section /*= "systemlib" */,
1443 const string &filename /*= "" */) {
1444 if (filename.empty() && section == "systemlib") {
1445 if (char *file = getenv("HHVM_SYSTEMLIB")) {
1446 std::ifstream ifs(file);
1447 if (ifs.good()) {
1448 return systemlib_split(std::string(
1449 std::istreambuf_iterator<char>(ifs),
1450 std::istreambuf_iterator<char>()), hhas);
1455 embedded_data desc;
1456 if (!get_embedded_data(section.c_str(), &desc, filename)) return "";
1458 std::ifstream ifs(desc.m_filename);
1459 if (!ifs.good()) return "";
1460 ifs.seekg(desc.m_start, std::ios::beg);
1461 std::unique_ptr<char[]> data(new char[desc.m_len]);
1462 ifs.read(data.get(), desc.m_len);
1463 string ret = systemlib_split(string(data.get(), desc.m_len), hhas);
1464 return ret;
1467 ///////////////////////////////////////////////////////////////////////////////
1468 // C++ ffi
1470 extern "C" void hphp_fatal_error(const char *s) {
1471 throw_fatal(s);
1474 static void on_timeout(int sig, siginfo_t* info, void* context) {
1475 if (sig == SIGVTALRM && info && info->si_code == SI_TIMER) {
1476 auto data = (RequestInjectionData*)info->si_value.sival_ptr;
1477 data->onTimeout();
1481 void hphp_process_init() {
1482 pthread_attr_t attr;
1483 #if defined(_GNU_SOURCE) && defined(__linux__) // Linux+GNU extension
1484 pthread_getattr_np(pthread_self(), &attr);
1485 #else
1486 pthread_attr_init(&attr);
1487 #endif
1488 init_stack_limits(&attr);
1489 pthread_attr_destroy(&attr);
1491 struct sigaction action = {};
1492 action.sa_sigaction = on_timeout;
1493 action.sa_flags = SA_SIGINFO | SA_NODEFER;
1494 sigaction(SIGVTALRM, &action, nullptr);
1496 init_thread_locals();
1498 // Initialize per-process dynamic PHP-visible consts before ClassInfo::Load()
1499 k_PHP_BINARY = makeStaticString(current_executable_path());
1500 k_PHP_BINDIR = makeStaticString(current_executable_directory());
1501 k_PHP_OS = makeStaticString(f_php_uname("s"));
1502 k_PHP_SAPI = makeStaticString(RuntimeOption::ExecutionMode);
1504 ClassInfo::Load();
1505 Process::InitProcessStatics();
1507 // reinitialize pcre table
1508 pcre_reinit();
1510 // the liboniguruma docs say this isnt needed,
1511 // but the implementation of init is not
1512 // thread safe due to bugs
1513 onig_init();
1515 // simple xml also needs one time init
1516 xmlInitParser();
1518 g_vmProcessInit();
1520 PageletServer::Restart();
1521 XboxServer::Restart();
1522 Stream::RegisterCoreWrappers();
1523 Extension::InitModules();
1524 for (InitFiniNode *in = extra_process_init; in; in = in->next) {
1525 in->func();
1527 int64_t save = RuntimeOption::SerializationSizeLimit;
1528 RuntimeOption::SerializationSizeLimit = StringData::MaxSize;
1529 apc_load(apcExtension::LoadThread);
1530 RuntimeOption::SerializationSizeLimit = save;
1532 RDS::requestExit();
1533 // Reset the preloaded g_context
1534 ExecutionContext *context = g_context.getNoCheck();
1535 context->~ExecutionContext();
1536 new (context) ExecutionContext();
1539 static void handle_exception(bool& ret, ExecutionContext* context,
1540 std::string& errorMsg, ContextOfException where,
1541 bool& error, bool richErrorMsg) {
1542 assert(where == ContextOfException::Invoke ||
1543 where == ContextOfException::ReqInit);
1544 try {
1545 handle_exception_helper(ret, context, errorMsg, where, error, richErrorMsg);
1546 } catch (const ExitException &e) {
1547 // Got an ExitException during exception handling, handle
1548 // similarly to the case below but don't call obEndAll().
1549 } catch (...) {
1550 handle_exception_helper(ret, context, errorMsg, ContextOfException::Handler,
1551 error, richErrorMsg);
1552 context->obEndAll();
1556 static void handle_reqinit_exception(bool &ret, ExecutionContext *context,
1557 std::string &errorMsg, bool &error) {
1558 handle_exception(ret, context, errorMsg, ContextOfException::ReqInit, error,
1559 false);
1562 static void handle_invoke_exception(bool &ret, ExecutionContext *context,
1563 std::string &errorMsg, bool &error,
1564 bool richErrorMsg) {
1565 handle_exception(ret, context, errorMsg, ContextOfException::Invoke, error,
1566 richErrorMsg);
1569 static bool hphp_warmup(ExecutionContext *context,
1570 const string &reqInitFunc,
1571 const string &reqInitDoc, bool &error) {
1572 bool ret = true;
1573 error = false;
1574 std::string errorMsg;
1576 ServerStatsHelper ssh("reqinit");
1577 try {
1578 if (!reqInitDoc.empty()) {
1579 include_impl_invoke(reqInitDoc, true);
1581 if (!reqInitFunc.empty()) {
1582 invoke(reqInitFunc.c_str(), Array());
1584 context->backupSession();
1585 } catch (...) {
1586 handle_reqinit_exception(ret, context, errorMsg, error);
1589 return ret;
1592 void hphp_session_init() {
1593 init_thread_locals();
1594 ThreadInfo::s_threadInfo->onSessionInit();
1595 MM().resetStats();
1597 #ifdef ENABLE_SIMPLE_COUNTER
1598 SimpleCounter::Enabled = true;
1599 StackTrace::Enabled = true;
1600 #endif
1602 // Ordering is sensitive; StatCache::requestInit produces work that
1603 // must be done in VMExecutionContext::requestInit.
1604 StatCache::requestInit();
1606 g_vmContext->requestInit();
1609 ExecutionContext *hphp_context_init() {
1610 ExecutionContext *context = g_context.getNoCheck();
1611 context->obStart();
1612 context->obProtect(true);
1613 return context;
1616 bool hphp_invoke_simple(const std::string &filename,
1617 bool warmupOnly /* = false */) {
1618 bool error;
1619 string errorMsg;
1620 return hphp_invoke(g_context.getNoCheck(), filename, false, null_array,
1621 uninit_null(), "", "", error, errorMsg, true, warmupOnly);
1624 bool hphp_invoke(ExecutionContext *context, const std::string &cmd,
1625 bool func, CArrRef funcParams, VRefParam funcRet,
1626 const string &reqInitFunc, const string &reqInitDoc,
1627 bool &error, string &errorMsg,
1628 bool once /* = true */, bool warmupOnly /* = false */,
1629 bool richErrorMsg /* = false */) {
1630 bool isServer = RuntimeOption::ServerExecutionMode();
1631 error = false;
1633 String oldCwd;
1634 if (isServer) {
1635 oldCwd = context->getCwd();
1637 if (!hphp_warmup(context, reqInitFunc, reqInitDoc, error)) {
1638 if (isServer) context->setCwd(oldCwd);
1639 return false;
1642 LitstrTable::get().setReading();
1644 bool ret = true;
1645 if (!warmupOnly) {
1646 try {
1647 ServerStatsHelper ssh("invoke");
1648 if (func) {
1649 funcRet->assignVal(invoke(cmd.c_str(), funcParams));
1650 } else {
1651 if (isServer) hphp_chdir_file(cmd);
1652 include_impl_invoke(cmd.c_str(), once);
1654 } catch (...) {
1655 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
1659 try {
1660 context->onShutdownPreSend();
1661 } catch (...) {
1662 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
1665 if (isServer) context->setCwd(oldCwd);
1666 return ret;
1669 void hphp_context_exit(ExecutionContext *context, bool psp,
1670 bool shutdown /* = true */,
1671 const char *program /* = NULL */) {
1672 if (psp) {
1673 context->onShutdownPostSend();
1675 if (RuntimeOption::EnableDebugger) {
1676 try {
1677 Eval::Debugger::InterruptPSPEnded(program);
1678 } catch (const Eval::DebuggerException &e) {}
1681 // Run shutdown handlers. This may cause user code to run.
1682 static_cast<VMExecutionContext*>(context)->destructObjects();
1683 if (shutdown) {
1684 context->onRequestShutdown();
1687 // Extensions could have shutdown handlers
1688 Extension::RequestShutdownModules();
1690 // Clean up a bunch of request state. No user code after this point.
1691 context->requestExit();
1692 context->obProtect(false);
1693 context->obEndAll();
1696 void hphp_thread_exit() {
1697 finish_thread_locals();
1700 void hphp_session_exit() {
1701 // Server note has to live long enough for the access log to fire.
1702 // RequestLocal is too early.
1703 ServerNote::Reset();
1704 g_context.destroy();
1706 ThreadInfo::s_threadInfo->clearPendingException();
1708 auto& mm = MM();
1709 mm.resetStats();
1712 ServerStatsHelper ssh("rollback");
1713 // sweep functions are allowed to call g_context->, so we need to
1714 // reinitialize g_context here.
1715 g_context.getCheck();
1717 mm.sweep();
1719 // Destroy g_context again because ExecutionContext has
1720 // SmartAllocated data members. These members cannot survive over
1721 // resetAllocator(), so we need to destroy g_context before
1722 // calling resetAllocator().
1723 g_context.destroy();
1725 mm.resetAllocator();
1727 // Do any post-sweep cleanup necessary for global variables
1728 free_global_variables_after_sweep();
1729 g_context.getCheck();
1732 ThreadInfo::s_threadInfo->onSessionExit();
1735 void hphp_process_exit() {
1736 PageletServer::Stop();
1737 XboxServer::Stop();
1738 Eval::Debugger::Stop();
1739 Extension::ShutdownModules();
1740 LightProcess::Close();
1741 for (InitFiniNode *in = extra_process_exit; in; in = in->next) {
1742 in->func();
1746 ///////////////////////////////////////////////////////////////////////////////