Optimize GC trigger check
[hiphop-php.git] / hphp / runtime / base / program-functions.cpp
blob053a6c28e0d0f288d278cb42e4177d614de541fc
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/program-functions.h"
18 #include "hphp/runtime/base/array-init.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/builtin-functions.h"
21 #include "hphp/runtime/base/code-coverage.h"
22 #include "hphp/runtime/base/config.h"
23 #include "hphp/runtime/base/execution-context.h"
24 #include "hphp/runtime/base/extended-logger.h"
25 #include "hphp/runtime/base/externals.h"
26 #include "hphp/runtime/base/file-util.h"
27 #include "hphp/runtime/base/hhprof.h"
28 #include "hphp/runtime/base/ini-setting.h"
29 #include "hphp/runtime/base/member-reflection.h"
30 #include "hphp/runtime/base/memory-manager.h"
31 #include "hphp/runtime/base/perf-mem-event.h"
32 #include "hphp/runtime/base/php-globals.h"
33 #include "hphp/runtime/base/plain-file.h"
34 #include "hphp/runtime/base/runtime-error.h"
35 #include "hphp/runtime/base/runtime-option.h"
36 #include "hphp/runtime/base/stat-cache.h"
37 #include "hphp/runtime/base/stream-wrapper-registry.h"
38 #include "hphp/runtime/base/surprise-flags.h"
39 #include "hphp/runtime/base/init-fini-node.h"
40 #include "hphp/runtime/base/unit-cache.h"
41 #include "hphp/runtime/base/thread-safe-setlocale.h"
42 #include "hphp/runtime/base/variable-serializer.h"
43 #include "hphp/runtime/base/zend-math.h"
44 #include "hphp/runtime/base/zend-strtod.h"
45 #include "hphp/runtime/debugger/debugger.h"
46 #include "hphp/runtime/debugger/debugger_client.h"
47 #include "hphp/runtime/debugger/debugger_hook_handler.h"
48 #include "hphp/runtime/ext/apc/ext_apc.h"
49 #include "hphp/runtime/ext/xhprof/ext_xhprof.h"
50 #include "hphp/runtime/ext/extension-registry.h"
51 #include "hphp/runtime/ext/json/ext_json.h"
52 #include "hphp/runtime/ext/std/ext_std_file.h"
53 #include "hphp/runtime/ext/std/ext_std_function.h"
54 #include "hphp/runtime/ext/std/ext_std_variable.h"
55 #include "hphp/runtime/ext/xdebug/status.h"
56 #include "hphp/runtime/ext/xenon/ext_xenon.h"
57 #include "hphp/runtime/server/admin-request-handler.h"
58 #include "hphp/runtime/server/cli-server.h"
59 #include "hphp/runtime/server/http-request-handler.h"
60 #include "hphp/runtime/server/log-writer.h"
61 #include "hphp/runtime/server/rpc-request-handler.h"
62 #include "hphp/runtime/server/http-server.h"
63 #include "hphp/runtime/server/pagelet-server.h"
64 #include "hphp/runtime/server/replay-transport.h"
65 #include "hphp/runtime/server/server-note.h"
66 #include "hphp/runtime/server/server-stats.h"
67 #include "hphp/runtime/server/xbox-server.h"
68 #include "hphp/runtime/vm/debug/debug.h"
69 #include "hphp/runtime/vm/jit/code-cache.h"
70 #include "hphp/runtime/vm/jit/tc.h"
71 #include "hphp/runtime/vm/jit/mcgen.h"
72 #include "hphp/runtime/vm/jit/translator.h"
73 #include "hphp/runtime/vm/extern-compiler.h"
74 #include "hphp/runtime/vm/repo.h"
75 #include "hphp/runtime/vm/runtime.h"
76 #include "hphp/runtime/vm/treadmill.h"
78 #include "hphp/util/abi-cxx.h"
79 #include "hphp/util/arch.h"
80 #include "hphp/util/boot-stats.h"
81 #include "hphp/util/build-info.h"
82 #include "hphp/util/compatibility.h"
83 #include "hphp/util/capability.h"
84 #include "hphp/util/embedded-data.h"
85 #include "hphp/util/hardware-counter.h"
86 #include "hphp/util/hphp-config.h"
87 #include "hphp/util/kernel-version.h"
88 #ifndef _MSC_VER
89 #include "hphp/util/light-process.h"
90 #endif
91 #include "hphp/util/perf-event.h"
92 #include "hphp/util/process-exec.h"
93 #include "hphp/util/process.h"
94 #include "hphp/util/service-data.h"
95 #include "hphp/util/shm-counter.h"
96 #include "hphp/util/stack-trace.h"
97 #include "hphp/util/timer.h"
98 #include "hphp/util/type-scan.h"
100 #include "hphp/zend/zend-string.h"
102 #include <folly/CPortability.h>
103 #include <folly/Portability.h>
104 #include <folly/Random.h>
105 #include <folly/Range.h>
106 #include <folly/Singleton.h>
107 #include <folly/portability/Fcntl.h>
108 #include <folly/portability/Libgen.h>
109 #include <folly/portability/Stdlib.h>
110 #include <folly/portability/Unistd.h>
112 #include <boost/algorithm/string/replace.hpp>
113 #include <boost/program_options/options_description.hpp>
114 #include <boost/program_options/positional_options.hpp>
115 #include <boost/program_options/variables_map.hpp>
116 #include <boost/filesystem.hpp>
118 #include <oniguruma.h>
119 #include <signal.h>
120 #include <libxml/parser.h>
122 #include <chrono>
123 #include <exception>
124 #include <fstream>
125 #include <iterator>
126 #include <map>
127 #include <memory>
128 #include <string>
129 #include <vector>
131 #ifdef _MSC_VER
132 #include <windows.h>
133 #include <winuser.h>
134 #endif
136 using namespace boost::program_options;
137 using std::cout;
139 constexpr auto MAX_INPUT_NESTING_LEVEL = 64;
141 namespace HPHP {
143 ///////////////////////////////////////////////////////////////////////////////
144 // Forward declarations.
146 void initialize_repo();
149 * XXX: VM process initialization is handled through a function
150 * pointer so libhphp_runtime.a can be linked into programs that don't
151 * actually initialize the VM.
153 void (*g_vmProcessInit)();
155 void timezone_init();
157 void pcre_init();
158 void pcre_reinit();
160 ///////////////////////////////////////////////////////////////////////////////
161 // helpers
163 struct ProgramOptions {
164 std::string mode;
165 std::vector<std::string> config;
166 std::vector<std::string> confStrings;
167 std::vector<std::string> iniStrings;
168 int port;
169 int portfd;
170 int sslportfd;
171 int admin_port;
172 std::string user;
173 std::string file;
174 std::string lint;
175 bool isTempFile;
176 int count;
177 bool noSafeAccessCheck;
178 std::vector<std::string> args;
179 std::string buildId;
180 std::string instanceId;
181 int xhprofFlags;
182 std::string show;
183 std::string parse;
184 int vsDebugPort;
185 bool vsDebugNoWait;
186 Eval::DebuggerClientOptions debugger_options;
189 struct StartTime {
190 StartTime() : startTime(time(nullptr)) {}
191 time_t startTime;
194 static StartTime s_startTime;
195 static std::string tempFile;
196 std::vector<std::string> s_config_files;
197 std::vector<std::string> s_ini_strings;
199 time_t start_time() {
200 return s_startTime.startTime;
203 const StaticString
204 s_HPHP("HPHP"),
205 s_HHVM("HHVM"),
206 s_HHVM_JIT("HHVM_JIT"),
207 s_HHVM_ARCH("HHVM_ARCH"),
208 s_REQUEST_START_TIME("REQUEST_START_TIME"),
209 s_REQUEST_TIME("REQUEST_TIME"),
210 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
211 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
212 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
213 s_SCRIPT_NAME("SCRIPT_NAME"),
214 s_PHP_SELF("PHP_SELF"),
215 s_argc("argc"),
216 s_argv("argv"),
217 s_PWD("PWD"),
218 s_HOSTNAME("HOSTNAME"),
219 s__SERVER("_SERVER"),
220 s__ENV("_ENV");
222 static __thread bool s_sessionInitialized{false};
224 static void process_cmd_arguments(int argc, char **argv) {
225 php_global_set(s_argc, Variant(argc));
226 Array argvArray(staticEmptyArray());
227 for (int i = 0; i < argc; i++) {
228 argvArray.append(String(argv[i]));
230 php_global_set(s_argv, argvArray);
233 static void process_env_variables(Array& variables, char** envp,
234 std::map<std::string, std::string>& envVariables) {
235 for (auto& kv : envVariables) {
236 variables.set(String(kv.first), String(kv.second));
238 for (char **env = envp; env && *env; env++) {
239 char *p = strchr(*env, '=');
240 if (p) {
241 String name(*env, p - *env, CopyString);
242 register_variable(variables, (char*)name.data(),
243 String(p + 1, CopyString));
248 void process_env_variables(Array& variables) {
249 process_env_variables(variables, environ, RuntimeOption::EnvVariables);
252 // Handle adding a variable to an array, supporting keys that look
253 // like array expressions (like 'FOO[][key1][k2]').
254 void register_variable(Array& variables, char *name, const Variant& value,
255 bool overwrite /* = true */) {
256 // ignore leading spaces in the variable name
257 char *var = name;
258 while (*var && *var == ' ') {
259 var++;
262 // ensure that we don't have spaces or dots in the variable name
263 // (not binary safe)
264 bool is_array = false;
265 char *ip = nullptr; // index pointer
266 char *p = var;
267 for (; *p; p++) {
268 if (*p == ' ' || *p == '.') {
269 *p = '_';
270 } else if (*p == '[') {
271 is_array = true;
272 ip = p;
273 *p = 0;
274 break;
277 int var_len = p - var;
278 if (var_len == 0) {
279 // empty variable name, or variable name with a space in it
280 return;
283 // GPC elements holds Variants that are acting as smart pointers to
284 // RefDatas that we've created in the process of a multi-dim key.
285 std::vector<Variant> gpc_elements;
286 if (is_array) gpc_elements.reserve(MAX_INPUT_NESTING_LEVEL);
288 // The array pointer we're currently adding to. If we're doing a
289 // multi-dimensional set, this will point at the m_data.parr inside
290 // of a RefData sometimes (via toArrRef on the variants in
291 // gpc_elements).
292 Array* symtable = &variables;
294 char* index = var;
295 int index_len = var_len;
297 if (is_array) {
298 int nest_level = 0;
299 while (true) {
300 if (++nest_level > MAX_INPUT_NESTING_LEVEL) {
301 Logger::Warning("Input variable nesting level exceeded");
302 return;
305 ip++;
306 char *index_s = ip;
307 int new_idx_len = 0;
308 if (isspace(*ip)) {
309 ip++;
311 if (*ip == ']') {
312 index_s = nullptr;
313 } else {
314 ip = strchr(ip, ']');
315 if (!ip) {
316 // PHP variables cannot contain '[' in their names,
317 // so we replace the character with a '_'
318 *(index_s - 1) = '_';
320 index_len = 0;
321 if (index) {
322 index_len = strlen(index);
324 goto plain_var;
326 *ip = 0;
327 new_idx_len = strlen(index_s);
330 if (!index) {
331 auto lval = symtable->lvalAt();
332 lval.type() = KindOfPersistentArray;
333 lval.val().parr = staticEmptyArray();
334 gpc_elements.push_back(uninit_null());
335 gpc_elements.back().assignRef(tvAsVariant(lval.tv_ptr()));
336 } else {
337 String key(index, index_len, CopyString);
338 auto const v = symtable->rvalAt(key).unboxed();
339 if (isNullType(v.type()) || !isArrayLikeType(v.type())) {
340 symtable->set(key, Array::Create());
342 gpc_elements.push_back(uninit_null());
343 gpc_elements.back().assignRef(
344 tvAsVariant(symtable->lvalAt(key).tv_ptr())
347 symtable = &gpc_elements.back().toArrRef();
348 /* ip pointed to the '[' character, now obtain the key */
349 index = index_s;
350 index_len = new_idx_len;
352 ip++;
353 if (*ip == '[') {
354 is_array = true;
355 *ip = 0;
356 } else {
357 goto plain_var;
360 } else {
361 plain_var:
362 if (!index) {
363 symtable->append(value);
364 } else {
365 String key(index, index_len, CopyString);
366 if (overwrite || !symtable->exists(key)) {
367 symtable->set(key, value);
373 enum class ContextOfException {
374 ReqInit = 1,
375 Invoke,
376 Handler,
379 static void handle_exception_append_bt(std::string& errorMsg,
380 const ExtendedException& e) {
381 Array bt = e.getBacktrace();
382 if (!bt.empty()) {
383 errorMsg += ExtendedLogger::StringOfStackTrace(bt);
387 void bump_counter_and_rethrow(bool isPsp) {
388 try {
389 throw;
390 } catch (const RequestTimeoutException& e) {
391 if (isPsp) {
392 static auto requestTimeoutPSPCounter = ServiceData::createTimeSeries(
393 "requests_timed_out_psp", {ServiceData::StatsType::COUNT});
394 requestTimeoutPSPCounter->addValue(1);
395 ServerStats::Log("request.timed_out.psp", 1);
396 } else {
397 static auto requestTimeoutCounter = ServiceData::createTimeSeries(
398 "requests_timed_out_non_psp", {ServiceData::StatsType::COUNT});
399 requestTimeoutCounter->addValue(1);
400 ServerStats::Log("request.timed_out.non_psp", 1);
402 throw;
403 } catch (const RequestCPUTimeoutException& e) {
404 if (isPsp) {
405 static auto requestCPUTimeoutPSPCounter = ServiceData::createTimeSeries(
406 "requests_cpu_timed_out_psp", {ServiceData::StatsType::COUNT});
407 requestCPUTimeoutPSPCounter->addValue(1);
408 ServerStats::Log("request.cpu_timed_out.psp", 1);
409 } else {
410 static auto requestCPUTimeoutCounter = ServiceData::createTimeSeries(
411 "requests_cpu_timed_out_non_psp", {ServiceData::StatsType::COUNT});
412 requestCPUTimeoutCounter->addValue(1);
413 ServerStats::Log("request.cpu_timed_out.non_psp", 1);
415 throw;
416 } catch (const RequestMemoryExceededException& e) {
417 if (isPsp) {
418 static auto requestMemoryExceededPSPCounter =
419 ServiceData::createTimeSeries(
420 "requests_memory_exceeded_psp", {ServiceData::StatsType::COUNT});
421 requestMemoryExceededPSPCounter->addValue(1);
422 ServerStats::Log("request.memory_exceeded.psp", 1);
423 } else {
424 static auto requestMemoryExceededCounter = ServiceData::createTimeSeries(
425 "requests_memory_exceeded_non_psp", {ServiceData::StatsType::COUNT});
426 requestMemoryExceededCounter->addValue(1);
427 ServerStats::Log("request.memory_exceeded.non_psp", 1);
430 #ifdef USE_JEMALLOC
431 // Capture a pprof (C++) dump when we OOM a request
432 // TODO: (t3753133) Should dump a PHP-instrumented pprof dump here as well
433 jemalloc_pprof_dump("", false);
434 #endif
436 throw;
440 static void handle_exception_helper(bool& ret,
441 ExecutionContext* context,
442 std::string& errorMsg,
443 ContextOfException where,
444 bool& error,
445 bool richErrorMsg) {
446 // Clear oom/timeout while handling exception and restore them afterwards.
447 auto& flags = stackLimitAndSurprise();
448 auto const origFlags = flags.fetch_and(~ResourceFlags) & ResourceFlags;
450 SCOPE_EXIT {
451 flags.fetch_or(origFlags);
454 try {
455 bump_counter_and_rethrow(false /* isPsp */);
456 } catch (const Eval::DebuggerException &e) {
457 throw;
458 } catch (const ExitException &e) {
459 if (where == ContextOfException::ReqInit) {
460 ret = false;
461 } else if (where != ContextOfException::Handler &&
462 !context->getExitCallback().isNull() &&
463 is_callable(context->getExitCallback())) {
464 Array stack = e.getBacktrace();
465 Array argv = make_packed_array(tl_exit_code, stack);
466 vm_call_user_func(context->getExitCallback(), argv);
468 } catch (const XDebugExitExn&) {
469 // Do nothing, this is normal behavior.
470 } catch (const PhpFileDoesNotExistException &e) {
471 ret = false;
472 if (where != ContextOfException::Handler) {
473 raise_notice("%s", e.getMessage().c_str());
474 } else {
475 Logger::Error("%s", e.getMessage().c_str());
477 if (richErrorMsg) {
478 handle_exception_append_bt(errorMsg, e);
480 } catch (const Exception &e) {
481 bool oldRet = ret;
482 bool origError = error;
483 std::string origErrorMsg = errorMsg;
484 ret = false;
485 error = true;
486 errorMsg = "";
487 if (where == ContextOfException::Handler) {
488 errorMsg = "Exception handler threw an exception: ";
490 errorMsg += e.what();
491 if (where == ContextOfException::Invoke) {
492 bool handlerRet = context->onFatalError(e);
493 if (handlerRet) {
494 ret = oldRet;
495 error = origError;
496 errorMsg = origErrorMsg;
498 } else {
499 Logger::Error("%s", errorMsg.c_str());
501 if (richErrorMsg) {
502 const ExtendedException *ee = dynamic_cast<const ExtendedException *>(&e);
503 if (ee) {
504 handle_exception_append_bt(errorMsg, *ee);
507 } catch (const Object &e) {
508 bool oldRet = ret;
509 bool origError = error;
510 auto const origErrorMsg = errorMsg;
511 ret = false;
512 error = true;
513 errorMsg = "";
514 if (where == ContextOfException::Handler) {
515 errorMsg = "Exception handler threw an object exception: ";
517 try {
518 errorMsg += e.toString().data();
519 } catch (...) {
520 errorMsg += "(unable to call toString())";
522 if (where == ContextOfException::Invoke) {
523 bool handlerRet = context->onUnhandledException(e);
524 if (handlerRet) {
525 ret = oldRet;
526 error = origError;
527 errorMsg = origErrorMsg;
529 } else {
530 Logger::Error("%s", errorMsg.c_str());
532 } catch (...) {
533 ret = false;
534 error = true;
535 errorMsg = "(unknown exception was thrown)";
536 Logger::Error("%s", errorMsg.c_str());
540 static bool hphp_chdir_file(const std::string& filename) {
541 bool ret = false;
542 String s = File::TranslatePath(filename);
543 char *buf = strndup(s.data(), s.size());
544 char *dir = dirname(buf);
545 assert(dir);
546 if (dir) {
547 if (File::IsVirtualDirectory(dir)) {
548 g_context->setCwd(String(dir, CopyString));
549 ret = true;
550 } else {
551 struct stat sb;
552 stat(dir, &sb);
553 if ((sb.st_mode & S_IFMT) == S_IFDIR) {
554 ret = true;
555 if (*dir != '.') {
556 g_context->setCwd(String(dir, CopyString));
561 free(buf);
562 return ret;
565 static void handle_resource_exceeded_exception() {
566 try {
567 throw;
568 } catch (RequestTimeoutException&) {
569 setSurpriseFlag(TimedOutFlag);
570 } catch (RequestCPUTimeoutException&) {
571 setSurpriseFlag(CPUTimedOutFlag);
572 } catch (RequestMemoryExceededException&) {
573 setSurpriseFlag(MemExceededFlag);
574 } catch (...) {}
577 void handle_destructor_exception(const char* situation) {
578 std::string errorMsg;
580 try {
581 throw;
582 } catch (ExitException &e) {
583 // ExitException is fine, no need to show a warning.
584 TI().setPendingException(e.clone());
585 return;
586 } catch (Object &e) {
587 // For user exceptions, invoke the user exception handler
588 errorMsg = situation;
589 errorMsg += " threw an object exception: ";
590 try {
591 errorMsg += e.toString().data();
592 } catch (...) {
593 handle_resource_exceeded_exception();
594 errorMsg += "(unable to call toString())";
596 } catch (Exception &e) {
597 TI().setPendingException(e.clone());
598 errorMsg = situation;
599 errorMsg += " raised a fatal error: ";
600 errorMsg += e.what();
601 } catch (...) {
602 errorMsg = situation;
603 errorMsg += " threw an unknown exception";
605 // For fatal errors and unknown exceptions, we raise a warning.
606 // If there is a user error handler it will be invoked, otherwise
607 // the default error handler will be invoked.
608 try {
609 raise_warning_unsampled("%s", errorMsg.c_str());
610 } catch (...) {
611 handle_resource_exceeded_exception();
613 // The user error handler fataled or threw an exception,
614 // print out the error message directly to the log
615 Logger::Warning("%s", errorMsg.c_str());
619 void init_command_line_session(int argc, char** argv) {
620 StackTraceNoHeap::AddExtraLogging("ThreadType", "CLI");
621 std::string args;
622 for (int i = 0; i < argc; i++) {
623 if (i) args += " ";
624 args += argv[i];
626 StackTraceNoHeap::AddExtraLogging("Arguments", args.c_str());
628 hphp_session_init();
629 auto const context = g_context.getNoCheck();
630 context->obSetImplicitFlush(true);
633 void
634 init_command_line_globals(int argc, char** argv, char** envp,
635 int xhprof,
636 std::map<std::string, std::string>& serverVariables,
637 std::map<std::string, std::string>& envVariables) {
638 auto& variablesOrder = RID().getVariablesOrder();
640 if (variablesOrder.find('e') != std::string::npos ||
641 variablesOrder.find('E') != std::string::npos) {
642 Array envArr(Array::Create());
643 process_env_variables(envArr, envp, envVariables);
644 envArr.set(s_HPHP, 1);
645 envArr.set(s_HHVM, 1);
646 if (RuntimeOption::EvalJit) {
647 envArr.set(s_HHVM_JIT, 1);
649 switch (arch()) {
650 case Arch::X64:
651 envArr.set(s_HHVM_ARCH, "x64");
652 break;
653 case Arch::ARM:
654 envArr.set(s_HHVM_ARCH, "arm");
655 break;
656 case Arch::PPC64:
657 envArr.set(s_HHVM_ARCH, "ppc64");
658 break;
660 php_global_set(s__ENV, envArr);
663 process_cmd_arguments(argc, argv);
665 if (variablesOrder.find('s') != std::string::npos ||
666 variablesOrder.find('S') != std::string::npos) {
667 Array serverArr(Array::Create());
668 process_env_variables(serverArr, envp, envVariables);
669 time_t now;
670 struct timeval tp = {0};
671 double now_double;
672 if (!gettimeofday(&tp, nullptr)) {
673 now_double = (double)(tp.tv_sec + tp.tv_usec / 1000000.00);
674 now = tp.tv_sec;
675 } else {
676 now = time(nullptr);
677 now_double = (double)now;
679 String file = empty_string();
680 if (argc > 0) {
681 file = String::attach(StringData::Make(argv[0], CopyString));
683 serverArr.set(s_REQUEST_START_TIME, now);
684 serverArr.set(s_REQUEST_TIME, now);
685 serverArr.set(s_REQUEST_TIME_FLOAT, now_double);
686 serverArr.set(s_DOCUMENT_ROOT, empty_string_variant_ref);
687 serverArr.set(s_SCRIPT_FILENAME, file);
688 serverArr.set(s_SCRIPT_NAME, file);
689 serverArr.set(s_PHP_SELF, file);
690 serverArr.set(s_argv, php_global(s_argv));
691 serverArr.set(s_argc, php_global(s_argc));
692 serverArr.set(s_PWD, g_context->getCwd());
693 char hostname[1024];
694 if (RuntimeOption::ServerExecutionMode() &&
695 !is_cli_mode() &&
696 !gethostname(hostname, sizeof(hostname))) {
697 // gethostname may not null-terminate
698 hostname[sizeof(hostname) - 1] = '\0';
699 serverArr.set(s_HOSTNAME, String(hostname, CopyString));
702 for (auto& kv : serverVariables) {
703 serverArr.set(String(kv.first.c_str()), String(kv.second.c_str()));
706 php_global_set(s__SERVER, serverArr);
709 if (xhprof) {
710 HHVM_FN(xhprof_enable)(xhprof, uninit_null().toArray());
713 if (RuntimeOption::RequestTimeoutSeconds) {
714 RID().setTimeout(RuntimeOption::RequestTimeoutSeconds);
717 if (RuntimeOption::XenonForceAlwaysOn) {
718 Xenon::getInstance().surpriseAll();
721 InitFiniNode::GlobalsInit();
722 // Initialize the debugger
723 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestInitHook());
726 void execute_command_line_begin(int argc, char **argv, int xhprof) {
727 init_command_line_session(argc, argv);
728 init_command_line_globals(argc, argv, environ, xhprof,
729 RuntimeOption::ServerVariables,
730 RuntimeOption::EnvVariables);
733 void execute_command_line_end(int xhprof, bool coverage, const char *program) {
734 if (RuntimeOption::EvalDumpTC ||
735 RuntimeOption::EvalDumpIR ||
736 RuntimeOption::EvalDumpRegion) {
737 jit::mcgen::joinWorkerThreads();
738 jit::tc::dump();
740 if (xhprof) {
741 Variant profileData = HHVM_FN(xhprof_disable)();
742 if (!profileData.isNull()) {
743 HHVM_FN(var_dump)(Variant::attach(
744 HHVM_FN(json_encode)(HHVM_FN(xhprof_disable)())
748 g_context->onShutdownPostSend(); // runs more php
749 Eval::Debugger::InterruptPSPEnded(program);
750 hphp_context_exit();
751 hphp_session_exit();
752 auto& ti = TI();
753 if (coverage && ti.m_reqInjectionData.getCoverage() &&
754 !RuntimeOption::CodeCoverageOutputFile.empty()) {
755 ti.m_coverage->Report(RuntimeOption::CodeCoverageOutputFile);
759 #if defined(__APPLE__) || defined(_MSC_VER)
760 const void* __hot_start = nullptr;
761 const void* __hot_end = nullptr;
762 #define AT_END_OF_TEXT
763 #else
764 #define AT_END_OF_TEXT __attribute__((__section__(".stub")))
765 #endif
767 #define ALIGN_HUGE_PAGE __attribute__((__aligned__(2 * 1024 * 1024)))
769 static void
770 NEVER_INLINE AT_END_OF_TEXT ALIGN_HUGE_PAGE __attribute__((__optimize__("2")))
771 hugifyText(char* from, char* to) {
772 #if !FOLLY_SANITIZE && defined MADV_HUGEPAGE
773 if (from > to || (to - from) < sizeof(uint64_t)) {
774 // This shouldn't happen if HHVM is behaving correctly (I think),
775 // but if it does then there is nothing to do and we should bail
776 // out early because the call to wordcpy() below can't handle
777 // zero size or negative sizes.
778 return;
780 size_t sz = to - from;
781 void* mem = malloc(sz);
782 memcpy(mem, from, sz);
784 // This maps out a portion of our executable
785 // We need to be very careful about what we do
786 // until we replace the original code
787 mmap(from, sz,
788 PROT_READ | PROT_WRITE | PROT_EXEC,
789 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
790 -1, 0);
791 // This is in glibc, which isn't a problem, except for
792 // the trampoline code in .plt, which we dealt with
793 // in the linker script
794 madvise(from, sz, MADV_HUGEPAGE);
795 // Don't use memcpy because its probably one of the
796 // functions thats been mapped out.
797 // Needs the attribute((optimize("2")) to prevent
798 // g++ from turning this back into memcpy(!)
799 wordcpy((uint64_t*)from, (uint64_t*)mem, sz / sizeof(uint64_t));
800 mprotect(from, sz, PROT_READ | PROT_EXEC);
801 free(mem);
802 mlock(from, to - from);
803 Debug::DebugInfo::setPidMapOverlay(from, to);
804 std::stringstream ss;
805 ss << "Mapped text section onto huge pages from " <<
806 std::hex << (uint64_t*)from << " to " << (uint64_t*)to;
807 Logger::Info(ss.str());
808 #endif
811 static void pagein_self(void) {
812 unsigned long begin, end, inode, pgoff;
813 char mapname[PATH_MAX];
814 char perm[5];
815 char dev[6];
816 char *buf;
817 int bufsz;
818 int r;
819 FILE *fp;
821 // pad due to the spaces between the inode number and the mapname
822 bufsz = sizeof(unsigned long) * 4 + sizeof(mapname) + sizeof(char) * 11 + 100;
823 buf = (char *)malloc(bufsz);
824 if (buf == nullptr)
825 return;
827 BootStats::Block timer("mapping self");
828 fp = fopen("/proc/self/maps", "r");
829 if (fp != nullptr) {
830 while (!feof(fp)) {
831 if (fgets(buf, bufsz, fp) == 0)
832 break;
833 r = sscanf(buf, "%lx-%lx %4s %lx %5s %ld %s",
834 &begin, &end, perm, &pgoff, dev, &inode, mapname);
836 // page in read-only segments that correspond to a file on disk
837 if (r != 7 ||
838 perm[0] != 'r' ||
839 perm[1] != '-' ||
840 access(mapname, F_OK) != 0) {
841 continue;
844 auto beginPtr = (char*)begin;
845 auto endPtr = (char*)end;
846 auto hotStart = (char*)__hot_start;
847 auto hotEnd = (char*)__hot_end;
848 const size_t hugePageBytes = 2L * 1024 * 1024;
850 if (mlock(beginPtr, end - begin) == 0) {
851 if (RuntimeOption::EvalMaxHotTextHugePages > 0 &&
852 __hot_start &&
853 __hot_end &&
854 hugePagesSupported() &&
855 beginPtr <= hotStart &&
856 hotEnd <= endPtr) {
858 char* from = hotStart - ((intptr_t)hotStart & (hugePageBytes - 1));
859 char* to = hotEnd + (hugePageBytes - 1);
860 to -= (intptr_t)to & (hugePageBytes - 1);
861 const size_t maxHugeHotTextBytes =
862 RuntimeOption::EvalMaxHotTextHugePages * hugePageBytes;
863 if (to - from > maxHugeHotTextBytes) {
864 to = from + maxHugeHotTextBytes;
866 if (to <= (void*)hugifyText) {
867 hugifyText(from, to);
870 if (!RuntimeOption::LockCodeMemory) {
871 munlock(beginPtr, end - begin);
875 fclose(fp);
877 free(buf);
880 /* Sets RuntimeOption::ExecutionMode according to commandline options prior to
881 * config load. Returns false upon unrecognized mode.
883 static bool set_execution_mode(folly::StringPiece mode) {
884 if (mode == "daemon" || mode == "server" || mode == "replay") {
885 RuntimeOption::ServerMode = true;
886 Logger::Escape = true;
887 return true;
888 } else if (mode == "run" || mode == "debug" || mode == "translate" ||
889 mode == "dumphhas" || mode == "verify" || mode == "vsdebug") {
890 // We don't run PHP in "translate" mode, so just treat it like cli mode.
891 RuntimeOption::ServerMode = false;
892 Logger::Escape = false;
893 return true;
895 // Invalid mode.
896 return false;
899 /* Reads a file into the OS page cache, with rate limiting. */
900 static bool readahead_rate(const char* path, int64_t mbPerSec) {
901 int ret = open(path, O_RDONLY);
902 if (ret < 0) return false;
903 const int fd = ret;
904 SCOPE_EXIT { close(fd); };
906 constexpr size_t kReadaheadBytes = 1 << 20;
907 std::unique_ptr<char[]> buf(new char[kReadaheadBytes]);
908 int64_t total = 0;
909 auto startTime = std::chrono::steady_clock::now();
910 do {
911 ret = read(fd, buf.get(), kReadaheadBytes);
912 if (ret > 0) {
913 total += ret;
914 // Unit math: bytes / (MB / seconds) = microseconds
915 auto endTime = startTime + std::chrono::microseconds(total / mbPerSec);
916 auto sleepT = endTime - std::chrono::steady_clock::now();
917 // Don't sleep too frequently.
918 if (sleepT >= std::chrono::seconds(1)) {
919 Logger::Info(folly::sformat(
920 "readahead sleeping {}ms after total {}b",
921 std::chrono::duration_cast<std::chrono::milliseconds>(sleepT).count(),
922 total));
923 /* sleep override */ std::this_thread::sleep_for(sleepT);
926 } while (ret > 0);
927 return ret == 0;
930 static int start_server(const std::string &username, int xhprof) {
931 BootStats::start();
932 HttpServer::CheckMemAndWait();
933 InitFiniNode::ServerPreInit();
935 if (!RuntimeOption::EvalUnixServerPath.empty()) {
936 init_cli_server(RuntimeOption::EvalUnixServerPath.c_str());
939 // Before we start the webserver, make sure the entire
940 // binary is paged into memory.
941 pagein_self();
942 BootStats::mark("pagein_self");
944 set_execution_mode("server");
946 #if !defined(SKIP_USER_CHANGE)
947 if (!username.empty()) {
948 if (Logger::UseCronolog) {
949 for (const auto& el : RuntimeOption::ErrorLogs) {
950 Cronolog::changeOwner(username, el.second.symLink);
953 if (!Capability::ChangeUnixUser(username, RuntimeOption::AllowRunAsRoot)) {
954 _exit(1);
956 LightProcess::ChangeUser(username);
957 compilers_set_user(username);
958 } else if (getuid() == 0 && !RuntimeOption::AllowRunAsRoot) {
959 Logger::Error("hhvm not allowed to run as root unless "
960 "-vServer.AllowRunAsRoot=1 is used.");
961 _exit(1);
963 Capability::SetDumpable();
964 // Include hugetlb pages in core dumps.
965 Process::SetCoreDumpHugePages();
966 #endif
968 hphp_process_init();
969 SCOPE_EXIT {
970 hphp_process_exit();
971 try { Logger::Info("all servers stopped"); } catch(...) {}
974 HttpRequestHandler::GetAccessLog().init
975 (RuntimeOption::AccessLogDefaultFormat, RuntimeOption::AccessLogs,
976 username);
977 AdminRequestHandler::GetAccessLog().init
978 (RuntimeOption::AdminLogFormat, RuntimeOption::AdminLogSymLink,
979 RuntimeOption::AdminLogFile,
980 username);
981 RPCRequestHandler::GetAccessLog().init
982 (RuntimeOption::AccessLogDefaultFormat, RuntimeOption::RPCLogs,
983 username);
984 SCOPE_EXIT { HttpRequestHandler::GetAccessLog().flushAllWriters(); };
985 SCOPE_EXIT { AdminRequestHandler::GetAccessLog().flushAllWriters(); };
986 SCOPE_EXIT { RPCRequestHandler::GetAccessLog().flushAllWriters(); };
987 SCOPE_EXIT { Logger::FlushAll(); };
989 if (RuntimeOption::ServerInternalWarmupThreads > 0) {
990 HttpServer::CheckMemAndWait();
991 InitFiniNode::WarmupConcurrentStart(
992 RuntimeOption::ServerInternalWarmupThreads);
995 HttpServer::CheckMemAndWait();
996 // Create the HttpServer before any warmup requests to properly
997 // initialize the process
998 HttpServer::Server = std::make_shared<HttpServer>();
1000 if (xhprof) {
1001 HHVM_FN(xhprof_enable)(xhprof, uninit_null().toArray());
1004 std::unique_ptr<std::thread> readaheadThread;
1006 if (RuntimeOption::RepoLocalReadaheadRate > 0 &&
1007 !RuntimeOption::RepoLocalPath.empty()) {
1008 HttpServer::CheckMemAndWait();
1009 readaheadThread = std::make_unique<std::thread>([&] {
1010 BootStats::Block timer("Readahead Repo");
1011 auto path = RuntimeOption::RepoLocalPath.c_str();
1012 Logger::Info("readahead %s", path);
1013 #ifdef __linux__
1014 // glibc doesn't have a wrapper for ioprio_set(), so we need to use
1015 // syscall(). The constants here are consistent with the kernel source.
1016 // See http://lxr.free-electrons.com/source/include/linux/ioprio.h
1017 auto constexpr IOPRIO_CLASS_SHIFT = 13;
1018 enum {
1019 IOPRIO_CLASS_NONE,
1020 IOPRIO_CLASS_RT,
1021 IOPRIO_CLASS_BE,
1022 IOPRIO_CLASS_IDLE,
1024 // Set to lowest IO priority.
1025 constexpr int ioprio = (IOPRIO_CLASS_IDLE << IOPRIO_CLASS_SHIFT);
1027 // ioprio_set() is available starting kernel 2.6.13
1028 KernelVersion version;
1029 if (version.m_major > 2 ||
1030 (version.m_major == 2 &&
1031 (version.m_minor > 6 ||
1032 (version.m_minor == 6 && version.m_release >= 13)))) {
1033 syscall(SYS_ioprio_set,
1034 1 /* IOPRIO_WHO_PROCESS, in fact, it is this thread */,
1035 0 /* current thread */,
1036 ioprio);
1038 #endif
1039 const auto mbPerSec = RuntimeOption::RepoLocalReadaheadRate;
1040 if (!readahead_rate(path, mbPerSec)) {
1041 Logger::Error("readahead failed: %s", strerror(errno));
1044 if (!RuntimeOption::RepoLocalReadaheadConcurrent) {
1045 // TODO(10152762): Run this concurrently with non-disk warmup.
1046 readaheadThread->join();
1047 readaheadThread.reset();
1051 if (RuntimeOption::ServerInternalWarmupThreads > 0) {
1052 BootStats::Block timer("concurrentWaitForEnd");
1053 InitFiniNode::WarmupConcurrentWaitForEnd();
1056 if (RuntimeOption::RepoPreload) {
1057 HttpServer::CheckMemAndWait();
1058 BootStats::Block timer("Preloading Repo");
1059 profileWarmupStart();
1060 preloadRepo();
1061 profileWarmupEnd();
1064 // If we have any warmup requests, replay them before listening for
1065 // real connections
1067 Logger::Info("Warming up");
1068 if (!RuntimeOption::EvalJitProfileWarmupRequests) profileWarmupStart();
1069 SCOPE_EXIT { profileWarmupEnd(); };
1070 std::map<std::string, int> seen;
1071 for (auto& file : RuntimeOption::ServerWarmupRequests) {
1072 HttpServer::CheckMemAndWait();
1073 // Take only the last part
1074 folly::StringPiece f(file);
1075 auto pos = f.rfind('/');
1076 std::string str(pos == f.npos ? file : f.subpiece(pos + 1).str());
1077 auto count = seen[str];
1078 BootStats::Block timer(folly::sformat("warmup:{}:{}", str, count++));
1079 seen[str] = count;
1081 HttpRequestHandler handler(0);
1082 ReplayTransport rt;
1083 timespec start;
1084 Timer::GetMonotonicTime(start);
1085 std::string error;
1086 Logger::Info("Replaying warmup request %s", file.c_str());
1088 try {
1089 rt.onRequestStart(start);
1090 rt.replayInput(Hdf(file));
1091 handler.run(&rt);
1093 timespec stop;
1094 Timer::GetMonotonicTime(stop);
1095 Logger::Info("Finished successfully in %ld seconds",
1096 stop.tv_sec - start.tv_sec);
1097 } catch (std::exception& e) {
1098 error = e.what();
1101 if (error.size()) {
1102 Logger::Info("Got exception during warmup: %s", error.c_str());
1106 BootStats::mark("warmup");
1108 if (RuntimeOption::StopOldServer) HttpServer::StopOldServer();
1110 if (RuntimeOption::EvalEnableNuma && !getenv("HHVM_DISABLE_NUMA")) {
1111 #ifdef USE_JEMALLOC
1112 unsigned narenas;
1113 size_t mib[3];
1114 size_t miblen = 3;
1115 if (mallctlWrite<uint64_t>("epoch", 1, true) == 0 &&
1116 mallctlRead("arenas.narenas", &narenas, true) == 0 &&
1117 mallctlnametomib("arena.0.purge", mib, &miblen) == 0) {
1118 mib[1] = size_t(narenas);
1119 mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0);
1121 #endif
1122 enable_numa(RuntimeOption::EvalEnableNumaLocal);
1123 BootStats::mark("enable_numa");
1126 #ifdef FACEBOOK
1127 // we're serving real traffic now, start batching log output
1128 if (RuntimeOption::UseThriftLogger) {
1129 Logger::Info("Turning logger batching on, batch size %ld",
1130 RuntimeOption::LoggerBatchSize);
1131 Logger::SetBatchSize(RuntimeOption::LoggerBatchSize);
1132 Logger::SetFlushTimeout(
1133 std::chrono::milliseconds(RuntimeOption::LoggerFlushTimeout));
1135 #endif
1137 HttpServer::CheckMemAndWait(true); // Final wait
1138 if (readaheadThread.get()) {
1139 readaheadThread->join();
1140 readaheadThread.reset();
1143 if (!RuntimeOption::EvalUnixServerPath.empty()) {
1144 start_cli_server();
1147 SlabManager::init(); // allocate hugetlb pages for pooled slabs
1149 HttpServer::Server->runOrExitProcess();
1150 HttpServer::Server.reset();
1152 return 0;
1155 static void logSettings() {
1156 if (RuntimeOption::ServerLogSettingsOnStartup) {
1157 Logger::Info("Settings: %s\n", IniSetting::GetAllAsJSON().c_str());
1161 static InitFiniNode s_logSettings(logSettings, InitFiniNode::When::ServerInit);
1163 std::string translate_stack(const char *hexencoded, bool with_frame_numbers) {
1164 if (!hexencoded || !*hexencoded) {
1165 return "";
1168 StackTrace st(hexencoded);
1169 std::vector<std::shared_ptr<StackFrameExtra>> frames;
1170 st.get(frames);
1172 std::ostringstream out;
1173 for (size_t i = 0; i < frames.size(); i++) {
1174 auto f = frames[i];
1175 if (with_frame_numbers) {
1176 out << "# " << (i < 10 ? " " : "") << i << ' ';
1178 out << f->toString();
1179 out << '\n';
1181 return out.str();
1184 ///////////////////////////////////////////////////////////////////////////////
1186 static void prepare_args(int &argc,
1187 char **&argv,
1188 const std::vector<std::string> &args,
1189 const char *file) {
1190 argv = (char **)malloc((args.size() + 2) * sizeof(char*));
1191 argc = 0;
1192 if (file && *file) {
1193 argv[argc++] = (char*)file;
1195 for (int i = 0; i < (int)args.size(); i++) {
1196 argv[argc++] = (char*)args[i].c_str();
1198 argv[argc] = nullptr;
1201 static int execute_program_impl(int argc, char **argv);
1202 int execute_program(int argc, char **argv) {
1203 int ret_code = -1;
1204 try {
1205 try {
1206 initialize_repo();
1207 ret_code = execute_program_impl(argc, argv);
1208 } catch (const Exception &e) {
1209 Logger::Error("Uncaught exception: %s", e.what());
1210 throw;
1211 } catch (const std::exception &e) {
1212 Logger::Error("Uncaught exception: %s", e.what());
1213 throw;
1214 } catch (...) {
1215 Logger::Error("Uncaught exception: (unknown)");
1216 throw;
1218 if (tempFile.length() && boost::filesystem::exists(tempFile)) {
1219 boost::filesystem::remove(tempFile);
1221 } catch (...) {
1222 if (HttpServer::Server ||
1223 folly::SingletonVault::singleton()->livingSingletonCount()) {
1224 // an exception was thrown that prevented proper shutdown. Its not
1225 // safe to destroy the globals, or run atexit handlers.
1226 // abort() so it shows up as a crash, and we can diagnose/fix the
1227 // exception
1228 abort();
1232 return ret_code;
1235 static bool open_server_log_files() {
1236 bool openedLog = false;
1237 for (const auto& el : RuntimeOption::ErrorLogs) {
1238 bool ok = true;
1239 const auto& name = el.first;
1240 const auto& errlog = el.second;
1241 if (!errlog.logFile.empty()) {
1242 if (errlog.isPipeOutput()) {
1243 auto output = popen(errlog.logFile.substr(1).c_str(), "w");
1244 ok = (output != nullptr);
1245 Logger::SetOutput(name, output, true);
1246 } else if (Logger::UseCronolog && errlog.hasTemplate()) {
1247 auto cronoLog = Logger::CronoOutput(name);
1248 always_assert(cronoLog);
1249 cronoLog->m_template = errlog.logFile;
1250 cronoLog->setPeriodicity();
1251 if (errlog.periodMultiplier) {
1252 cronoLog->m_periodMultiple = errlog.periodMultiplier;
1254 cronoLog->m_linkName = errlog.symLink;
1255 } else {
1256 auto output = fopen(errlog.logFile.c_str(), "a");
1257 ok = (output != nullptr);
1258 Logger::SetOutput(name, output, false);
1260 if (!ok) Logger::Error("Can't open log file: %s", errlog.logFile.c_str());
1261 openedLog |= ok;
1264 return openedLog;
1267 static int compute_hhvm_argc(const options_description& desc,
1268 int argc, char** argv) {
1269 enum ArgCode {
1270 NO_ARG = 0,
1271 ARG_REQUIRED = 1,
1272 ARG_OPTIONAL = 2
1274 const auto& vec = desc.options();
1275 std::map<std::string,ArgCode> long_options;
1276 std::map<std::string,ArgCode> short_options;
1277 // Build lookup maps for the short options and the long options
1278 for (unsigned i = 0; i < vec.size(); ++i) {
1279 auto opt = vec[i];
1280 auto long_name = opt->long_name();
1281 ArgCode code = NO_ARG;
1282 if (opt->semantic()->max_tokens() == 1) {
1283 if (opt->semantic()->min_tokens() == 1) {
1284 code = ARG_REQUIRED;
1285 } else {
1286 code = ARG_OPTIONAL;
1289 long_options[long_name] = code;
1290 auto format_name = opt->format_name();
1291 if (format_name.size() >= 2 && format_name[0] == '-' &&
1292 format_name[1] != '-') {
1293 auto short_name = format_name.substr(1,1);
1294 short_options[short_name] = code;
1297 // Loop over the args
1298 int pos = 1;
1299 while (pos < argc) {
1300 const char* str = argv[pos];
1301 int len = strlen(str);
1302 if (len == 2 && memcmp(str, "--", 2) == 0) {
1303 // We found "--". All args after this are intended for the
1304 // PHP application
1305 ++pos;
1306 break;
1308 if (len >= 3 && str[0] == '-' && str[1] == '-') {
1309 // Handle long options
1310 ++pos;
1311 std::string s(str+2);
1312 auto it = long_options.find(s);
1313 if (it != long_options.end() && it->second != NO_ARG && pos < argc &&
1314 (it->second == ARG_REQUIRED || argv[pos][0] != '-')) {
1315 ++pos;
1317 } else if (len >= 2 && str[0] == '-') {
1318 // Handle short options
1319 ++pos;
1320 std::string s;
1321 s.append(1, str[1]);
1322 auto it = short_options.find(s);
1323 if (it != short_options.end() && it->second != 0 && len == 2 &&
1324 pos < argc && (it->second == ARG_REQUIRED || argv[pos][0] != '-')) {
1325 ++pos;
1327 } else {
1328 // We've found a non-option argument. This arg and all args
1329 // that follow are intended for the PHP application
1330 break;
1333 return pos;
1337 * alloc.h defines a minimum C++ stack size but that only applies to threads we
1338 * manually create. When the main thread will be executing PHP rather than just
1339 * managing a server, make sure its stack is big enough.
1341 static void set_stack_size() {
1342 struct rlimit rlim;
1343 if (getrlimit(RLIMIT_STACK, &rlim) != 0) return;
1345 if (rlim.rlim_cur < kStackSizeMinimum || rlim.rlim_cur == RLIM_INFINITY) {
1346 #ifdef _WIN32
1347 Logger::Error("stack limit too small, use peflags -x to increase %zd\n",
1348 kStackSizeMinimum);
1349 #else
1350 rlim.rlim_cur = kStackSizeMinimum;
1351 if (setrlimit(RLIMIT_STACK, &rlim)) {
1352 Logger::Error("failed to set stack limit to %zd\n", kStackSizeMinimum);
1354 #endif
1358 #if defined(BOOST_VERSION) && BOOST_VERSION <= 105400
1359 std::string get_right_option_name(const basic_parsed_options<char>& opts,
1360 std::string& wrong_name) {
1361 // Remove any - from the wrong name for better comparing
1362 // since it will probably come prepended with --
1363 wrong_name.erase(
1364 std::remove(wrong_name.begin(), wrong_name.end(), '-'), wrong_name.end());
1365 for (basic_option<char> opt : opts.options) {
1366 std::string s_opt = opt.string_key;
1367 // We are only dealing with options that have a - in them.
1368 if (s_opt.find("-") != std::string::npos) {
1369 if (s_opt.find(wrong_name) != std::string::npos) {
1370 return s_opt;
1374 return "";
1376 #endif
1378 static int execute_program_impl(int argc, char** argv) {
1379 std::string usage = "Usage:\n\n ";
1380 usage += argv[0];
1381 usage += " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
1383 ProgramOptions po;
1384 options_description desc(usage.c_str());
1385 desc.add_options()
1386 ("help", "display this message")
1387 ("version", "display version number")
1388 ("modules", "display modules")
1389 ("info", "PHP information")
1390 ("php", "emulate the standard php command line")
1391 ("compiler-id", "display the git hash for the compiler")
1392 ("repo-schema", "display the repository schema id")
1393 ("mode,m", value<std::string>(&po.mode)->default_value("run"),
1394 "run | debug (d) | vsdebug | server (s) | daemon | replay | "
1395 "translate (t) | verify")
1396 ("interactive,a", "Shortcut for --mode debug") // -a is from PHP5
1397 ("config,c", value<std::vector<std::string>>(&po.config)->composing(),
1398 "load specified config file")
1399 ("config-value,v",
1400 value<std::vector<std::string>>(&po.confStrings)->composing(),
1401 "individual configuration string in a format of name=value, where "
1402 "name can be any valid configuration for a config file")
1403 ("define,d", value<std::vector<std::string>>(&po.iniStrings)->composing(),
1404 "define an ini setting in the same format ( foo[=bar] ) as provided in a "
1405 ".ini file")
1406 ("no-config", "don't use the default php.ini")
1407 ("port,p", value<int>(&po.port)->default_value(-1),
1408 "start an HTTP server at specified port")
1409 ("port-fd", value<int>(&po.portfd)->default_value(-1),
1410 "use specified fd instead of creating a socket")
1411 ("ssl-port-fd", value<int>(&po.sslportfd)->default_value(-1),
1412 "use specified fd for SSL instead of creating a socket")
1413 ("admin-port", value<int>(&po.admin_port)->default_value(-1),
1414 "start admin listener at specified port")
1415 ("debug-config", value<std::string>(&po.debugger_options.configFName),
1416 "load specified debugger config file")
1417 ("debug-host,h",
1418 value<std::string>(&po.debugger_options.host)->implicit_value("localhost"),
1419 "connect to debugger server at specified address")
1420 ("debug-port", value<int>(&po.debugger_options.port)->default_value(-1),
1421 "connect to debugger server at specified port")
1422 ("debug-extension", value<std::string>(&po.debugger_options.extension),
1423 "PHP file that extends command 'arg'")
1424 ("debug-cmd", value<std::vector<std::string>>(
1425 &po.debugger_options.cmds)->composing(),
1426 "executes this debugger command and returns its output in stdout")
1427 ("debug-sandbox",
1428 value<std::string>(&po.debugger_options.sandbox)->default_value("default"),
1429 "initial sandbox to attach to when debugger is started")
1430 ("user,u", value<std::string>(&po.user),
1431 "run server under this user account")
1432 ("file,f", value<std::string>(&po.file),
1433 "execute specified file")
1434 ("lint,l", value<std::string>(&po.lint),
1435 "lint specified file")
1436 ("show,w", value<std::string>(&po.show),
1437 "output specified file and do nothing else")
1438 ("temp-file",
1439 "file specified is temporary and removed after execution")
1440 ("count", value<int>(&po.count)->default_value(1),
1441 "how many times to repeat execution")
1442 ("no-safe-access-check",
1443 value<bool>(&po.noSafeAccessCheck)->default_value(false),
1444 "whether to ignore safe file access check")
1445 ("arg", value<std::vector<std::string>>(&po.args)->composing(),
1446 "arguments")
1447 ("extra-header", value<std::string>(&Logger::ExtraHeader),
1448 "extra-header to add to log lines")
1449 ("build-id", value<std::string>(&po.buildId),
1450 "unique identifier of compiled server code")
1451 ("instance-id", value<std::string>(&po.instanceId),
1452 "unique identifier of server instance")
1453 ("xhprof-flags", value<int>(&po.xhprofFlags)->default_value(0),
1454 "Set XHProf flags")
1455 ("vsDebugPort", value<int>(&po.vsDebugPort)->default_value(-1),
1456 "Debugger port to listen on for the VS Code debugger extension")
1457 ("vsDebugNoWait", value<bool>(&po.vsDebugNoWait)->default_value(false),
1458 "Indicates the debugger should not block script startup waiting for "
1459 "a debugger client to attach. Only applies if vsDebugPort is specified.")
1462 positional_options_description p;
1463 p.add("arg", -1);
1464 variables_map vm;
1466 // Before invoking the boost command line parser, we do a manual pass
1467 // to find the first occurrence of either "--" or a non-option argument
1468 // in order to determine which arguments should be consumed by HHVM and
1469 // which arguments should be passed along to the PHP application. This
1470 // is necessary so that the boost command line parser doesn't choke on
1471 // args intended for the PHP application.
1472 int hhvm_argc = compute_hhvm_argc(desc, argc, argv);
1473 // Need to have a parent try for opts so I can use opts in the catch of
1474 // one of the sub-tries below.
1475 try {
1476 // Invoke the boost command line parser to parse the args for HHVM.
1477 auto opts = command_line_parser(hhvm_argc, argv)
1478 .options(desc)
1479 .positional(p)
1480 // If these style options are changed, compute_hhvm_argc() will
1481 // need to be updated appropriately
1482 .style(command_line_style::default_style &
1483 ~command_line_style::allow_guessing &
1484 ~command_line_style::allow_sticky &
1485 ~command_line_style::long_allow_adjacent)
1486 .run();
1487 try {
1488 // Manually append the args for the PHP application.
1489 int pos = 0;
1490 for (unsigned m = 0; m < opts.options.size(); ++m) {
1491 const auto& bo = opts.options[m];
1492 if (bo.string_key == "arg") {
1493 ++pos;
1496 for (unsigned m = hhvm_argc; m < argc; ++m) {
1497 std::string str = argv[m];
1498 basic_option<char> bo;
1499 bo.string_key = "arg";
1500 bo.position_key = pos++;
1501 bo.value.push_back(str);
1502 bo.original_tokens.push_back(str);
1503 bo.unregistered = false;
1504 bo.case_insensitive = false;
1505 opts.options.push_back(bo);
1507 // Process the options
1508 store(opts, vm);
1509 notify(vm);
1510 if (vm.count("interactive") /* or -a */) po.mode = "debug";
1511 else if (po.mode.empty()) po.mode = "run";
1512 else if (po.mode == "d") po.mode = "debug";
1513 else if (po.mode == "s") po.mode = "server";
1514 else if (po.mode == "t") po.mode = "translate";
1516 if (!set_execution_mode(po.mode)) {
1517 Logger::Error("Error in command line: invalid mode: %s",
1518 po.mode.c_str());
1519 cout << desc << "\n";
1520 return -1;
1522 if (po.config.empty() && !vm.count("no-config")
1523 && ::getenv("HHVM_NO_DEFAULT_CONFIGS") == nullptr) {
1524 auto file_callback = [&po] (const char *filename) {
1525 Logger::Verbose("Using default config file: %s", filename);
1526 po.config.push_back(filename);
1528 add_default_config_files_globbed(DEFAULT_CONFIG_DIR "/php*.ini",
1529 file_callback);
1530 add_default_config_files_globbed(DEFAULT_CONFIG_DIR "/config*.hdf",
1531 file_callback);
1533 const auto env_config = ::getenv("HHVM_CONFIG_FILE");
1534 if (env_config != nullptr) {
1535 add_default_config_files_globbed(
1536 env_config,
1537 [&po](const char* filename) {
1538 Logger::Verbose("Using config file from environment: %s", filename);
1539 po.config.push_back(filename);
1543 // When we upgrade boost, we can remove this and also get rid of the parent
1544 // try statement and move opts back into the original try block
1545 #if defined(BOOST_VERSION) && BOOST_VERSION >= 105000 && BOOST_VERSION <= 105400
1546 } catch (const error_with_option_name &e) {
1547 std::string wrong_name = e.get_option_name();
1548 std::string right_name = get_right_option_name(opts, wrong_name);
1549 std::string message = e.what();
1550 if (right_name != "") {
1551 boost::replace_all(message, wrong_name, right_name);
1553 Logger::Error("Error in command line: %s", message.c_str());
1554 cout << desc << "\n";
1555 return -1;
1556 #endif
1557 } catch (const error &e) {
1558 Logger::Error("Error in command line: %s", e.what());
1559 cout << desc << "\n";
1560 return -1;
1561 } catch (...) {
1562 Logger::Error("Error in command line.");
1563 cout << desc << "\n";
1564 return -1;
1566 } catch (const error &e) {
1567 Logger::Error("Error in command line: %s", e.what());
1568 cout << desc << "\n";
1569 return -1;
1570 } catch (...) {
1571 Logger::Error("Error in command line parsing.");
1572 cout << desc << "\n";
1573 return -1;
1575 // reuse -h for help command if possible
1576 if (vm.count("help") || (vm.count("debug-host") && po.mode != "debug")) {
1577 cout << desc << "\n";
1578 return 0;
1580 if (vm.count("version")) {
1581 cout << "HipHop VM";
1582 cout << " " << HHVM_VERSION;
1583 cout << " (" << (debug ? "dbg" : "rel") << ")\n";
1584 cout << "Compiler: " << compilerId() << "\n";
1585 cout << "Repo schema: " << repoSchemaId() << "\n";
1586 return 0;
1588 if (vm.count("modules")) {
1589 Array exts = ExtensionRegistry::getLoaded();
1590 cout << "[PHP Modules]" << "\n";
1591 for (ArrayIter iter(exts); iter; ++iter) {
1592 cout << iter.second().toString().toCppString() << "\n";
1594 return 0;
1596 if (vm.count("compiler-id")) {
1597 cout << compilerId() << "\n";
1598 return 0;
1601 if (vm.count("repo-schema")) {
1602 cout << repoSchemaId() << "\n";
1603 return 0;
1606 if (!po.show.empty()) {
1607 auto f = req::make<PlainFile>();
1608 f->open(po.show, "r");
1609 if (!f->valid()) {
1610 Logger::Error("Unable to open file %s", po.show.c_str());
1611 return 1;
1613 f->print();
1614 f->close();
1615 return 0;
1618 po.isTempFile = vm.count("temp-file");
1620 // forget the source for systemlib.php unless we are debugging
1621 if (po.mode != "debug" && po.mode != "vsdebug") SystemLib::s_source = "";
1622 if (po.mode == "vsdebug") {
1623 RuntimeOption::EnableVSDebugger = true;
1624 RuntimeOption::VSDebuggerListenPort = po.vsDebugPort;
1625 RuntimeOption::VSDebuggerNoWait = po.vsDebugNoWait;
1628 // we need to initialize pcre cache table very early
1629 pcre_init();
1631 tl_heap.getCheck();
1632 if (RuntimeOption::ServerExecutionMode()) {
1633 // Create the hardware counter before reading options,
1634 // so that the main thread never has inherit set in server
1635 // mode
1636 HardwareCounter::s_counter.getCheck();
1638 std::vector<std::string> messages;
1639 // We want the ini map to be freed after processing and loading the options
1640 // So put this in its own block
1642 IniSettingMap ini = IniSettingMap();
1643 Hdf config;
1644 s_config_files = po.config;
1645 // Start with .hdf and .ini files
1646 for (auto& filename : s_config_files) {
1647 if (boost::filesystem::exists(filename)) {
1648 Config::ParseConfigFile(filename, ini, config);
1649 } else {
1650 Logger::Warning(
1651 "The configuration file %s does not exist",
1652 filename.c_str()
1656 // Now, take care of CLI options and then officially load and bind things
1657 s_ini_strings = po.iniStrings;
1658 RuntimeOption::Load(ini, config, po.iniStrings, po.confStrings, &messages);
1659 std::vector<std::string> badnodes;
1660 config.lint(badnodes);
1661 for (const auto& badnode : badnodes) {
1662 const auto msg = "Possible bad config node: " + badnode;
1663 fprintf(stderr, "%s\n", msg.c_str());
1664 messages.push_back(msg);
1668 std::vector<int> inherited_fds;
1669 RuntimeOption::BuildId = po.buildId;
1670 RuntimeOption::InstanceId = po.instanceId;
1672 // Do this as early as possible to avoid creating temp files and spawing
1673 // light processes. Correct compilation still requires loading all of the
1674 // ini/hdf/cli options.
1675 if (po.mode == "dumphhas" || po.mode == "verify") {
1676 if (po.file.empty() && po.args.empty()) {
1677 std::cerr << "Nothing to do. Pass a php file to compile.\n";
1678 return 1;
1681 auto const file = [] (std::string file) -> std::string {
1682 if (!FileUtil::isAbsolutePath(file)) {
1683 return SourceRootInfo::GetCurrentSourceRoot() + std::move(file);
1685 return file;
1686 }(po.file.empty() ? po.args[0] : po.file);
1688 RuntimeOption::RepoCommit = false; // avoid initializing a repo
1690 std::fstream fs(file, std::ios::in);
1691 if (!fs) {
1692 std::cerr << "Unable to open \"" << file << "\"\n";
1693 return 1;
1695 std::stringstream contents;
1696 contents << fs.rdbuf();
1698 auto const str = contents.str();
1699 auto const md5 = MD5{mangleUnitMd5(string_md5(str))};
1701 compilers_start();
1702 hphp_thread_init();
1703 g_context.getCheck();
1704 SCOPE_EXIT { hphp_thread_exit(); };
1706 // Initialize compiler state
1707 compile_file(0, 0, MD5(), 0);
1709 if (po.mode == "dumphhas") RuntimeOption::EvalDumpHhas = true;
1710 else RuntimeOption::EvalVerifyOnly = true;
1711 SystemLib::s_inited = true;
1713 // Ensure write to SystemLib::s_inited is visible by other threads.
1714 std::atomic_thread_fence(std::memory_order_release);
1716 auto compiled = compile_file(str.c_str(), str.size(), md5, file.c_str(),
1717 nullptr);
1719 if (po.mode == "verify") {
1720 return 0;
1723 // This will dump the hhas for file as EvalDumpHhas was set
1724 if (!compiled) {
1725 std::cerr << "Unable to compile \"" << file << "\"\n";
1726 return 1;
1729 return 0;
1732 if (po.port != -1) {
1733 RuntimeOption::ServerPort = po.port;
1735 if (po.portfd != -1) {
1736 RuntimeOption::ServerPortFd = po.portfd;
1737 inherited_fds.push_back(po.portfd);
1739 if (po.sslportfd != -1) {
1740 RuntimeOption::SSLPortFd = po.sslportfd;
1741 inherited_fds.push_back(po.sslportfd);
1743 if (po.admin_port != -1) {
1744 RuntimeOption::AdminServerPort = po.admin_port;
1746 if (po.noSafeAccessCheck) {
1747 RuntimeOption::SafeFileAccess = false;
1749 IniSetting::s_system_settings_are_set = true;
1750 tl_heap->resetRuntimeOptions();
1752 auto opened_logs = open_server_log_files();
1753 if (po.mode == "daemon") {
1754 if (!opened_logs) {
1755 Logger::Error("Log file not specified under daemon mode.\n\n");
1757 proc::daemonize();
1760 if (RuntimeOption::ServerExecutionMode()) {
1761 for (auto const& m : messages) {
1762 Logger::Info(m);
1766 #ifndef _MSC_VER
1767 // Defer the initialization of light processes until the log file handle is
1768 // created, so that light processes can log to the right place. If we ever
1769 // lose a light process, stop the server instead of proceeding in an
1770 // uncertain state. Don't start them in DumpHhas mode because
1771 // it _Exit()s after loading the first non-systemlib unit.
1772 if (!RuntimeOption::EvalDumpHhas) {
1773 LightProcess::SetLostChildHandler([](pid_t /*child*/) {
1774 if (!HttpServer::Server) return;
1775 if (!HttpServer::Server->isStopped()) {
1776 HttpServer::Server->stopOnSignal(SIGCHLD);
1779 LightProcess::Initialize(RuntimeOption::LightProcessFilePrefix,
1780 RuntimeOption::LightProcessCount,
1781 RuntimeOption::EvalRecordSubprocessTimes,
1782 inherited_fds);
1784 #endif
1785 #ifdef USE_JEMALLOC_EXTENT_HOOKS
1786 // Set up extent hook so that we can place jemalloc metadata on 1G pages.
1787 // This needs to be done after initializing LightProcess (which forks),
1788 // because the child process does malloc which won't work with jemalloc
1789 // metadata on 1G huge pages.
1790 setup_jemalloc_metadata_extent_hook(
1791 RuntimeOption::EvalEnableArenaMetadata1GPage,
1792 RuntimeOption::EvalEnableNumaArenaMetadata1GPage,
1793 RuntimeOption::EvalArenaMetadataReservedSize
1795 #endif
1796 // We want to do this as early as possible because any allocations before-hand
1797 // will get a generic unknown type type-index.
1798 try {
1799 type_scan::init();
1800 } catch (const type_scan::InitException& exn) {
1801 Logger::Error("Unable to initialize GC type-scanners: %s", exn.what());
1802 exit(HPHP_EXIT_FAILURE);
1804 ThreadLocalManager::GetManager().initTypeIndices();
1806 // It's okay if this fails.
1807 init_member_reflection();
1809 if (!ShmCounters::initialize(true, Logger::Error)) {
1810 exit(HPHP_EXIT_FAILURE);
1812 // Initialize compiler state
1813 compile_file(0, 0, MD5(), 0);
1815 if (!po.lint.empty()) {
1816 Logger::LogHeader = false;
1817 Logger::LogLevel = Logger::LogInfo;
1818 Logger::UseCronolog = false;
1819 Logger::UseLogFile = true;
1820 // we're linting, reset whatever logger settings and write once to stdout
1821 Logger::ClearThreadLog();
1822 for (auto& el : RuntimeOption::ErrorLogs) {
1823 const auto& name = el.first;
1824 Logger::SetTheLogger(name, nullptr);
1826 Logger::SetTheLogger(Logger::DEFAULT, new Logger());
1828 if (po.isTempFile) {
1829 tempFile = po.lint;
1832 hphp_process_init();
1833 SCOPE_EXIT { hphp_process_exit(); };
1835 try {
1836 auto const unit = lookupUnit(
1837 makeStaticString(po.lint.c_str()), "", nullptr);
1838 if (unit == nullptr) {
1839 throw FileOpenException(po.lint);
1841 const StringData* msg;
1842 int line;
1843 if (unit->compileTimeFatal(msg, line)) {
1844 VMParserFrame parserFrame;
1845 parserFrame.filename = po.lint.c_str();
1846 parserFrame.lineNumber = line;
1847 Array bt = createBacktrace(BacktraceArgs()
1848 .withSelf()
1849 .setParserFrame(&parserFrame));
1850 raise_fatal_error(msg->data(), bt);
1852 } catch (FileOpenException &e) {
1853 Logger::Error("%s", e.getMessage().c_str());
1854 return 1;
1855 } catch (const FatalErrorException& e) {
1856 RuntimeOption::CallUserHandlerOnFatals = false;
1857 RuntimeOption::AlwaysLogUnhandledExceptions = false;
1858 g_context->onFatalError(e);
1859 return 1;
1861 Logger::Info("No syntax errors detected in %s", po.lint.c_str());
1862 return 0;
1865 if (argc <= 1 || po.mode == "run" || po.mode == "debug" ||
1866 po.mode == "vsdebug") {
1867 set_stack_size();
1869 if (po.isTempFile) {
1870 tempFile = po.file;
1873 set_execution_mode("run");
1874 /* recreate the hardware counters for the main thread now that we know
1875 * whether to include subprocess times */
1876 HardwareCounter::s_counter.destroy();
1877 HardwareCounter::s_counter.getCheck();
1879 int new_argc;
1880 char **new_argv;
1881 prepare_args(new_argc, new_argv, po.args, po.file.c_str());
1883 std::string const cliFile = !po.file.empty() ? po.file :
1884 new_argv[0] ? new_argv[0] : "";
1885 if (po.mode != "debug" && cliFile.empty()) {
1886 std::cerr << "Nothing to do. Either pass a .php file to run, or "
1887 "use -m server\n";
1888 return 1;
1890 Repo::setCliFile(cliFile);
1892 int ret = 0;
1893 hphp_process_init();
1894 SCOPE_EXIT { hphp_process_exit(); };
1896 if (RuntimeOption::EvalUseRemoteUnixServer != "no" &&
1897 !RuntimeOption::EvalUnixServerPath.empty() &&
1898 (!po.file.empty() || !po.args.empty())) {
1899 std::vector<std::string> args;
1900 if (!po.file.empty()) {
1901 args.emplace_back(po.file);
1903 args.insert(args.end(), po.args.begin(), po.args.end());
1904 run_command_on_cli_server(
1905 RuntimeOption::EvalUnixServerPath.c_str(), args
1907 if (RuntimeOption::EvalUseRemoteUnixServer == "only") {
1908 Logger::Error("Failed to connect to unix server.");
1909 exit(255);
1913 std::string file;
1914 if (new_argc > 0) {
1915 file = new_argv[0];
1918 if (po.mode == "debug") {
1919 StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
1920 RuntimeOption::EnableHphpdDebugger = true;
1921 po.debugger_options.fileName = file;
1922 po.debugger_options.user = po.user;
1923 Eval::DebuggerProxyPtr localProxy =
1924 Eval::Debugger::StartClient(po.debugger_options);
1925 if (!localProxy) {
1926 Logger::Error("Failed to start debugger client\n\n");
1927 return 1;
1929 Eval::Debugger::RegisterSandbox(localProxy->getDummyInfo());
1930 std::shared_ptr<std::vector<std::string>> client_args;
1931 bool restart = false;
1932 ret = 0;
1933 while (true) {
1934 try {
1935 assert(po.debugger_options.fileName == file);
1936 execute_command_line_begin(new_argc, new_argv, po.xhprofFlags);
1937 // Set the proxy for this thread to be the localProxy we just
1938 // created. If we're script debugging, this will be the proxy that
1939 // does all of our work. If we're remote debugging, this proxy will
1940 // go unused until we finally stop it when the user quits the
1941 // debugger.
1942 g_context->setSandboxId(localProxy->getDummyInfo().id());
1943 if (restart) {
1944 // Systemlib.php is not loaded again, so we need this if we
1945 // are to hit any breakpoints in systemlib.
1946 proxySetBreakPoints(localProxy.get());
1948 Eval::Debugger::DebuggerSession(po.debugger_options, restart);
1949 restart = false;
1950 execute_command_line_end(po.xhprofFlags, true, file.c_str());
1951 } catch (const Eval::DebuggerRestartException &e) {
1952 execute_command_line_end(0, false, nullptr);
1954 if (!e.m_args->empty()) {
1955 file = e.m_args->at(0);
1956 po.debugger_options.fileName = file;
1957 client_args = e.m_args;
1958 free(new_argv);
1959 prepare_args(new_argc, new_argv, *client_args, nullptr);
1961 restart = true;
1962 } catch (const Eval::DebuggerClientExitException &e) {
1963 execute_command_line_end(0, false, nullptr);
1964 break; // end user quitting debugger
1968 } else {
1969 ret = 0;
1970 for (int i = 0; i < po.count; i++) {
1971 execute_command_line_begin(new_argc, new_argv, po.xhprofFlags);
1972 ret = 255;
1973 if (hphp_invoke_simple(file, false /* warmup only */)) {
1974 ret = tl_exit_code;
1976 execute_command_line_end(po.xhprofFlags, true, file.c_str());
1980 free(new_argv);
1982 return ret;
1985 if (po.mode == "daemon" || po.mode == "server") {
1986 if (!po.user.empty()) RuntimeOption::ServerUser = po.user;
1987 return start_server(RuntimeOption::ServerUser, po.xhprofFlags);
1990 if (po.mode == "replay" && !po.args.empty()) {
1991 RuntimeOption::RecordInput = false;
1992 set_execution_mode("server");
1993 HttpServer server; // so we initialize runtime properly
1994 HttpRequestHandler handler(0);
1995 for (int i = 0; i < po.count; i++) {
1996 for (unsigned int j = 0; j < po.args.size(); j++) {
1997 ReplayTransport rt;
1998 rt.replayInput(po.args[j].c_str());
1999 handler.run(&rt);
2000 printf("%s\n", rt.getResponse().c_str());
2003 return 0;
2006 if (po.mode == "translate" && !po.args.empty()) {
2007 printf("%s", translate_stack(po.args[0].c_str()).c_str());
2008 return 0;
2011 cout << desc << "\n";
2012 return -1;
2015 String canonicalize_path(const String& p, const char* root, int rootLen) {
2016 String path = FileUtil::canonicalize(p);
2017 if (path.charAt(0) == '/') {
2018 auto const& sourceRoot = RuntimeOption::SourceRoot;
2019 int len = sourceRoot.size();
2020 if (len && strncmp(path.data(), sourceRoot.c_str(), len) == 0) {
2021 return path.substr(len);
2023 if (root && rootLen && strncmp(path.data(), root, rootLen) == 0) {
2024 return path.substr(rootLen);
2027 return path;
2030 static std::string systemlib_split(const std::string& slib, std::string* hhas) {
2031 auto pos = slib.find("\n<?hhas\n");
2032 if (pos != std::string::npos) {
2033 if (hhas) *hhas = slib.substr(pos + 8);
2034 return slib.substr(0, pos);
2036 return slib;
2039 // Retrieve a systemlib (or mini systemlib) from the
2040 // current executable or another ELF object file.
2042 // Additionally, when retrieving the main systemlib
2043 // from the current executable, honor the
2044 // HHVM_SYSTEMLIB environment variable as an override.
2045 std::string get_systemlib(std::string* hhas,
2046 const std::string &section /*= "systemlib" */,
2047 const std::string &filename /*= "" */) {
2048 if (filename.empty() && section == "systemlib") {
2049 if (auto const file = getenv("HHVM_SYSTEMLIB")) {
2050 std::ifstream ifs(file);
2051 if (ifs.good()) {
2052 return systemlib_split(std::string(
2053 std::istreambuf_iterator<char>(ifs),
2054 std::istreambuf_iterator<char>()), hhas);
2059 embedded_data desc;
2060 if (!get_embedded_data(section.c_str(), &desc, filename)) return "";
2062 auto const data = read_embedded_data(desc);
2063 return systemlib_split(data, hhas);
2066 ///////////////////////////////////////////////////////////////////////////////
2067 // C++ ffi
2069 #ifndef _MSC_VER
2070 static void on_timeout(int sig, siginfo_t* info, void* /*context*/) {
2071 if (sig == SIGVTALRM && info && info->si_code == SI_TIMER) {
2072 auto data = (RequestTimer*)info->si_value.sival_ptr;
2073 if (data) {
2074 data->onTimeout();
2075 } else {
2076 Xenon::getInstance().onTimer();
2080 #endif
2083 * Update constants to their real values and sync some runtime options
2085 static void update_constants_and_options() {
2086 assert(ExtensionRegistry::modulesInitialised());
2087 // If extension constants were used in the ini files (e.g., E_ALL) they
2088 // would have come out as 0 in the previous pass until we load and
2089 // initialize our extensions, which we do in RuntimeOption::Load() via
2090 // ExtensionRegistry::ModuleLoad() and in ExtensionRegistry::ModuleInit()
2091 // in hphp_process_init(). We will re-import and set only the constants that
2092 // have been now bound to their proper value.
2093 IniSettingMap ini = IniSettingMap();
2094 for (auto& filename: s_config_files) {
2095 SuppressHackArrCompatNotices shacn;
2096 Config::ParseIniFile(filename, ini, true);
2098 // Reset the INI settings from the CLI.
2099 for (auto& iniStr: s_ini_strings) {
2100 SuppressHackArrCompatNotices shacn;
2101 Config::ParseIniString(iniStr, ini, true);
2104 // Reset, possibly, some request dependent runtime options based on certain
2105 // setting values. Do this here so we ensure the constants have been loaded
2106 // correctly (e.g., error_reporting E_ALL, etc.)
2107 Variant sys;
2108 if (IniSetting::GetSystem("error_reporting", sys)) {
2109 RuntimeOption::RuntimeErrorReportingLevel = sys.toInt64();
2110 RID().setErrorReportingLevel(RuntimeOption::RuntimeErrorReportingLevel);
2112 if (IniSetting::GetSystem("memory_limit", sys)) {
2113 RID().setMemoryLimit(sys.toString().toCppString());
2114 RuntimeOption::RequestMemoryMaxBytes = RID().getMemoryLimitNumeric();
2118 void hphp_thread_init() {
2119 #ifdef USE_JEMALLOC_EXTENT_HOOKS
2120 thread_huge_tcache_create();
2121 #endif
2122 ServerStats::GetLogger();
2123 zend_get_bigint_data();
2124 zend_rand_init();
2125 get_server_note();
2126 tl_heap.getCheck()->init();
2128 assert(ThreadInfo::s_threadInfo.isNull());
2129 ThreadInfo::s_threadInfo.getCheck()->init();
2131 HardwareCounter::s_counter.getCheck();
2132 ExtensionRegistry::threadInit();
2133 InitFiniNode::ThreadInit();
2135 // Ensure that there's no request-allocated memory. This call must happen at
2136 // least once after RDS has been initialized by ThreadInfo::init(), to ensure
2137 // MemoryManager::resetGC() sets a proper trigger threshold.
2138 hphp_memory_cleanup();
2141 void hphp_thread_exit() {
2142 InitFiniNode::ThreadFini();
2143 ExtensionRegistry::threadShutdown();
2144 if (!g_context.isNull()) g_context.destroy();
2145 #ifdef USE_JEMALLOC_EXTENT_HOOKS
2146 thread_huge_tcache_destroy();
2147 #endif
2150 void hphp_process_init() {
2151 pthread_attr_t attr;
2152 // Linux+GNU extension
2153 #if defined(_GNU_SOURCE) && defined(__linux__)
2154 if (pthread_getattr_np(pthread_self(), &attr) != 0 ) {
2155 Logger::Error("pthread_getattr_np failed before checking stack limits");
2156 _exit(1);
2158 #else
2159 if (pthread_attr_init(&attr) != 0 ) {
2160 Logger::Error("pthread_attr_init failed before checking stack limits");
2161 _exit(1);
2163 #endif
2164 init_stack_limits(&attr);
2165 if (pthread_attr_destroy(&attr) != 0 ) {
2166 Logger::Error("pthread_attr_destroy failed after checking stack limits");
2167 _exit(1);
2169 BootStats::mark("pthread_init");
2171 Process::InitProcessStatics();
2172 BootStats::mark("Process::InitProcessStatics");
2174 HHProf::Init();
2175 tl_miter_table.getCheck();
2177 // initialize the tzinfo cache.
2178 timezone_init();
2179 BootStats::mark("timezone_init");
2181 // start any external compilers
2182 compilers_start();
2183 BootStats::mark("compilers_start");
2185 hphp_thread_init();
2187 #ifndef _MSC_VER
2188 struct sigaction action = {};
2189 action.sa_sigaction = on_timeout;
2190 action.sa_flags = SA_SIGINFO | SA_NODEFER;
2191 sigaction(SIGVTALRM, &action, nullptr);
2192 #endif
2193 // start takes milliseconds, Period is a double in seconds
2194 Xenon::getInstance().start(1000 * RuntimeOption::XenonPeriodSeconds);
2195 BootStats::mark("xenon");
2197 // reinitialize pcre table
2198 pcre_reinit();
2199 BootStats::mark("pcre_reinit");
2201 // the liboniguruma docs say this isnt needed,
2202 // but the implementation of init is not
2203 // thread safe due to bugs
2204 onig_init();
2205 BootStats::mark("onig_init");
2207 // simple xml also needs one time init
2208 xmlInitParser();
2209 BootStats::mark("xmlInitParser");
2211 g_context.getCheck();
2212 // Some event handlers are registered during the startup process.
2213 g_context->acceptRequestEventHandlers(true);
2214 InitFiniNode::ProcessPreInit();
2215 // TODO(9795696): Race in thread map may trigger spurious logging at
2216 // thread exit, so for now, only spawn threads if we're a server.
2217 const uint32_t maxWorkers = RuntimeOption::ServerExecutionMode() ? 3 : 0;
2218 folly::SingletonVault::singleton()->registrationComplete();
2219 InitFiniNode::ProcessInitConcurrentStart(maxWorkers);
2220 SCOPE_EXIT {
2221 InitFiniNode::ProcessInitConcurrentWaitForEnd();
2222 BootStats::mark("extra_process_init_concurrent_wait");
2224 g_vmProcessInit();
2225 BootStats::mark("g_vmProcessInit");
2227 PageletServer::Restart();
2228 BootStats::mark("PageletServer::Restart");
2229 XboxServer::Restart();
2230 BootStats::mark("XboxServer::Restart");
2231 Stream::RegisterCoreWrappers();
2232 BootStats::mark("Stream::RegisterCoreWrappers");
2233 ExtensionRegistry::moduleInit();
2234 BootStats::mark("ExtensionRegistry::moduleInit");
2236 // Now that constants have been bound we can update options using constants
2237 // in ini files (e.g., E_ALL) and sync some other options
2238 update_constants_and_options();
2240 InitFiniNode::ProcessInit();
2241 BootStats::mark("extra_process_init");
2243 UnlimitSerializationScope unlimit;
2244 // TODO(9755792): Add real execution mode for snapshot generation.
2245 if (apcExtension::PrimeLibraryUpgradeDest != "") {
2246 Timer timer(Timer::WallTime, "optimizeApcPrime");
2247 apc_load(apcExtension::LoadThread);
2248 } else {
2249 apc_load(apcExtension::LoadThread);
2251 BootStats::mark("apc_load");
2254 rds::requestExit();
2255 BootStats::mark("rds::requestExit");
2256 // Reset the preloaded g_context
2257 ExecutionContext *context = g_context.getNoCheck();
2258 context->onRequestShutdown(); // TODO T20898959 kill early REH usage.
2259 context->~ExecutionContext();
2260 new (context) ExecutionContext();
2261 BootStats::mark("ExecutionContext");
2263 // TODO(9755792): Add real execution mode for snapshot generation.
2264 if (apcExtension::PrimeLibraryUpgradeDest != "") {
2265 Logger::Info("APC PrimeLibrary upgrade mode completed; exiting.");
2266 hphp_process_exit();
2267 exit(0);
2271 static void handle_exception(bool& ret, ExecutionContext* context,
2272 std::string& errorMsg, ContextOfException where,
2273 bool& error, bool richErrorMsg) {
2274 assert(where == ContextOfException::Invoke ||
2275 where == ContextOfException::ReqInit);
2276 try {
2277 handle_exception_helper(ret, context, errorMsg, where, error, richErrorMsg);
2278 } catch (const ExitException &e) {
2279 // Got an ExitException during exception handling, handle
2280 // similarly to the case below but don't call obEndAll().
2281 } catch (...) {
2282 handle_exception_helper(ret, context, errorMsg, ContextOfException::Handler,
2283 error, richErrorMsg);
2284 context->obEndAll();
2288 static void handle_reqinit_exception(bool &ret, ExecutionContext *context,
2289 std::string &errorMsg, bool &error) {
2290 handle_exception(ret, context, errorMsg, ContextOfException::ReqInit, error,
2291 false);
2294 static void handle_invoke_exception(bool &ret, ExecutionContext *context,
2295 std::string &errorMsg, bool &error,
2296 bool richErrorMsg) {
2297 handle_exception(ret, context, errorMsg, ContextOfException::Invoke, error,
2298 richErrorMsg);
2301 static bool hphp_warmup(ExecutionContext *context,
2302 const std::string &reqInitFunc,
2303 const std::string &reqInitDoc, bool &error) {
2304 bool ret = true;
2305 error = false;
2306 std::string errorMsg;
2308 ServerStatsHelper ssh("reqinit");
2309 try {
2310 if (!reqInitDoc.empty()) {
2311 include_impl_invoke(reqInitDoc, true);
2313 if (!reqInitFunc.empty()) {
2314 invoke(reqInitFunc.c_str(), Array());
2316 context->backupSession();
2317 } catch (...) {
2318 handle_reqinit_exception(ret, context, errorMsg, error);
2321 return ret;
2324 void hphp_session_init() {
2325 assert(!s_sessionInitialized);
2326 g_context.getCheck();
2327 AsioSession::Init();
2328 Socket::clearLastError();
2329 TI().onSessionInit();
2330 tl_heap->resetExternalStats();
2332 g_thread_safe_locale_handler->reset();
2333 Treadmill::startRequest();
2335 #ifdef ENABLE_SIMPLE_COUNTER
2336 SimpleCounter::Enabled = true;
2337 StackTrace::Enabled = true;
2338 #endif
2340 // Ordering is sensitive; StatCache::requestInit produces work that
2341 // must be done in ExecutionContext::requestInit.
2342 StatCache::requestInit();
2344 // Allow request event handlers to be created now that a new request has
2345 // started.
2346 g_context->acceptRequestEventHandlers(true);
2348 g_context->requestInit();
2349 s_sessionInitialized = true;
2350 ExtensionRegistry::requestInit();
2352 // Sample function calls for this request
2353 if (RID().logFunctionCalls()) {
2354 EventHook::Enable();
2357 auto const pme_freq = RuntimeOption::EvalPerfMemEventRequestFreq;
2358 if (pme_freq > 0 && folly::Random::rand32(pme_freq) == 0) {
2359 // Enable memory access sampling for this request.
2360 perf_event_enable(
2361 RuntimeOption::EvalPerfMemEventSampleFreq,
2362 [] (PerfEvent) { setSurpriseFlag(PendingPerfEventFlag); }
2367 bool hphp_invoke_simple(const std::string& filename, bool warmupOnly) {
2368 bool error;
2369 std::string errorMsg;
2370 return hphp_invoke(g_context.getNoCheck(), filename, false, null_array,
2371 uninit_null(), "", "", error, errorMsg,
2372 true /* once */,
2373 warmupOnly,
2374 false /* richErrorMsg */);
2377 const StaticString s_slash("/");
2379 static void run_prelude(std::string prelude, String cmd,
2380 const char* currentDir) {
2381 prelude = "/" + prelude;
2382 struct stat s;
2383 auto cwd = resolveVmInclude(cmd.get(), currentDir, &s);
2384 if (cwd.isNull()) return;
2385 auto const w = Stream::getWrapperFromURI(cwd, nullptr, false);
2386 do {
2387 cwd = f_dirname(cwd);
2388 auto const f = String::attach(
2389 StringData::Make(cwd.data(), prelude.data())
2391 if (w->access(f, R_OK) == 0) {
2392 require(f.data(), false, currentDir, true);
2393 break;
2395 } while (!cwd.empty() && !cwd.equal(s_slash));
2398 bool hphp_invoke(ExecutionContext *context, const std::string &cmd,
2399 bool func, const Array& funcParams, VRefParam funcRet,
2400 const std::string &reqInitFunc, const std::string &reqInitDoc,
2401 bool &error, std::string &errorMsg,
2402 bool once, bool warmupOnly,
2403 bool richErrorMsg) {
2404 bool isServer =
2405 RuntimeOption::ServerExecutionMode() && !is_cli_mode();
2406 error = false;
2408 // Make sure we have the right current working directory within the repo
2409 // based on what server.source_root was set to (current process directory
2410 // being the default)
2411 if (RuntimeOption::RepoAuthoritative) {
2412 context->setCwd(RuntimeOption::SourceRoot);
2415 String oldCwd;
2416 if (isServer) {
2417 oldCwd = context->getCwd();
2419 if (!hphp_warmup(context, reqInitFunc, reqInitDoc, error)) {
2420 if (isServer) context->setCwd(oldCwd);
2421 return false;
2424 tl_heap->resetCouldOOM(isStandardRequest());
2425 RID().resetTimer();
2427 bool ret = true;
2428 if (!warmupOnly) {
2429 try {
2430 ServerStatsHelper ssh("invoke");
2431 if (!RuntimeOption::AutoPrependFile.empty() &&
2432 RuntimeOption::AutoPrependFile != "none") {
2433 require(RuntimeOption::AutoPrependFile, false,
2434 context->getCwd().data(), true);
2436 auto const& prelude = RuntimeOption::EvalPreludePath;
2437 if (!prelude.empty()) {
2438 run_prelude(prelude, String(cmd, CopyString), context->getCwd().data());
2440 if (func) {
2441 funcRet.assignIfRef(invoke(cmd.c_str(), funcParams));
2442 } else {
2443 if (isServer) hphp_chdir_file(cmd);
2444 include_impl_invoke(cmd.c_str(), once);
2446 if (!RuntimeOption::AutoAppendFile.empty() &&
2447 RuntimeOption::AutoAppendFile != "none") {
2448 require(RuntimeOption::AutoAppendFile, false,
2449 context->getCwd().data(), true);
2451 } catch (...) {
2452 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
2456 try {
2457 context->onShutdownPreSend();
2458 } catch (...) {
2459 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
2462 if (isServer) context->setCwd(oldCwd);
2463 return ret;
2466 void hphp_context_shutdown() {
2467 // Run shutdown handlers. This may cause user code to run.
2468 g_thread_safe_locale_handler->reset();
2470 auto const context = g_context.getNoCheck();
2471 context->destructObjects();
2472 context->onRequestShutdown();
2474 try {
2475 // Shutdown the debugger. This can throw, but we don't care about what the
2476 // error is.
2477 DEBUGGER_ATTACHED_ONLY(phpDebuggerRequestShutdownHook());
2478 } catch (...) {
2479 // Gotta catch 'em all!
2482 // Extensions could have shutdown handlers
2483 ExtensionRegistry::requestShutdown();
2484 InitFiniNode::RequestFini();
2486 // Extension shutdown could have re-initialized some
2487 // request locals
2488 context->onRequestShutdown();
2490 // This causes request event handler registration to fail until the next
2491 // request starts.
2492 context->acceptRequestEventHandlers(false);
2495 void hphp_context_exit(bool shutdown /* = true */) {
2496 if (shutdown) {
2497 hphp_context_shutdown();
2500 // Clean up a bunch of request state. No user code after this point.
2501 MemoryManager::setExiting();
2502 auto const context = g_context.getNoCheck();
2503 context->requestExit();
2504 context->obProtect(false);
2505 context->obEndAll();
2508 void hphp_memory_cleanup() {
2509 auto& mm = *tl_heap;
2510 // sweep functions are allowed to access g_context,
2511 // so we can't destroy it yet
2512 mm.sweep();
2514 // We should never have any registered RequestEventHandlers. If we do
2515 // something after onRequestShutdown registered a RequestEventHandler.
2516 // Its now too late to run the requestShutdown functions, but if we carry
2517 // on, requestInit and requestShutdown will never be called again.
2518 // I considered just clearing the inited flags; which works for some
2519 // RequestEventHandlers - but its a disaster for others. So just fail hard
2520 // here.
2521 always_assert(g_context.isNull() || !g_context->hasRequestEventHandlers());
2523 // g_context is request allocated, and has some members that need
2524 // cleanup, so destroy it before its too late
2525 g_context.destroy();
2527 weakref_cleanup();
2528 mm.resetAllocator();
2529 mm.resetCouldOOM();
2532 void hphp_session_exit(const Transport* transport) {
2533 assert(s_sessionInitialized);
2534 // Server note and INI have to live long enough for the access log to fire.
2535 // RequestLocal is too early.
2536 ServerNote::Reset();
2537 IniSetting::ResetSavedDefaults();
2538 // In JitPGO mode, check if it's time to schedule the retranslation of all
2539 // profiled functions and, if so, schedule it.
2540 jit::mcgen::checkRetranslateAll();
2541 jit::tc::requestExit();
2542 // Similarly, apc strings could be in the ServerNote array, and
2543 // it's possible they are scheduled to be destroyed after this request
2544 // finishes.
2545 Treadmill::finishRequest();
2547 TI().onSessionExit();
2549 if (transport) {
2550 std::unique_ptr<StructuredLogEntry> entry;
2551 if (RuntimeOption::EvalProfileHWStructLog) {
2552 entry = std::make_unique<StructuredLogEntry>();
2553 entry->setInt("response_code", transport->getResponseCode());
2555 HardwareCounter::UpdateServiceData(transport->getCpuTime(),
2556 transport->getWallTime(),
2557 entry.get(),
2558 true /*psp*/);
2559 if (entry) StructuredLog::log("hhvm_request_perf", *entry);
2562 // We might have events from after the final surprise flag check of the
2563 // request, so consume them here.
2564 perf_event_consume(record_perf_mem_event);
2565 perf_event_disable();
2568 ServerStatsHelper ssh("rollback");
2570 hphp_memory_cleanup();
2573 assert(tl_heap->empty());
2575 s_sessionInitialized = false;
2576 s_extra_request_nanoseconds = 0;
2579 void hphp_process_exit() noexcept {
2580 // We want to do clean up on a best-effort basis: don't skip later steps if
2581 // an earlier step fails, and don't propagate exceptions ouf of this function
2582 #define LOG_AND_IGNORE(voidexpr) try { voidexpr; } catch (...) { \
2583 Logger::Error("got exception in cleanup step: " #voidexpr); }
2584 LOG_AND_IGNORE(teardown_cli_server())
2585 LOG_AND_IGNORE(Xenon::getInstance().stop())
2586 LOG_AND_IGNORE(jit::mcgen::joinWorkerThreads())
2587 LOG_AND_IGNORE(jit::tc::processExit())
2588 LOG_AND_IGNORE(PageletServer::Stop())
2589 LOG_AND_IGNORE(XboxServer::Stop())
2590 // Debugger::Stop() needs an execution context
2591 LOG_AND_IGNORE(g_context.getCheck())
2592 LOG_AND_IGNORE(Eval::Debugger::Stop())
2593 LOG_AND_IGNORE(g_context.destroy())
2594 LOG_AND_IGNORE(ExtensionRegistry::moduleShutdown())
2595 LOG_AND_IGNORE(compilers_shutdown())
2596 #ifndef _MSC_VER
2597 LOG_AND_IGNORE(LightProcess::Close())
2598 #endif
2599 LOG_AND_IGNORE(InitFiniNode::ProcessFini())
2600 LOG_AND_IGNORE(folly::SingletonVault::singleton()->destroyInstances())
2601 LOG_AND_IGNORE(embedded_data_cleanup())
2602 #undef LOG_AND_IGNORE
2605 bool is_hphp_session_initialized() {
2606 return s_sessionInitialized;
2609 static struct SetThreadInitFini {
2610 template<class ThreadT> static typename std::enable_if<
2611 std::is_integral<ThreadT>::value || std::is_pointer<ThreadT>::value>::type
2612 recordThreadAddr(ThreadT threadId, char* stackAddr, size_t stackSize) {
2613 // In the current glibc implementation, pthread_t is a 64-bit unsigned
2614 // integer, whose value equals the address of the thread control block
2615 // (TCB). In x64_64, this is right above the TLS block. In addition,
2616 // TLS and TCB sits at the high end of the stack, i.e.,
2618 // stackAddr + stackSize ----> +---------------+
2619 // | TCB |
2620 // threadId ----> +---------------+
2621 // | TLS |
2622 // +---------------+
2623 // | Stack |
2624 // . .
2625 // . .
2626 // stackAddr ----> +---------------+
2627 auto const tcbBase = reinterpret_cast<char*>(threadId);
2628 auto stackEnd = stackAddr + stackSize;
2629 if (tcbBase > stackAddr && tcbBase < stackEnd) { // the expected layout
2630 // TCB
2631 Debug::DebugInfo::recordDataMap(
2632 tcbBase, stackEnd,
2633 folly::sformat("Thread-{}", static_cast<void*>(tcbBase)));
2634 // TLS
2635 auto const tlsRange = getCppTdata();
2636 auto const tlsSize = (tlsRange.second + 15) / 16 * 16;
2637 stackEnd = tcbBase - tlsSize;
2638 if (tlsSize) {
2639 Debug::DebugInfo::recordDataMap(
2640 stackEnd, tcbBase,
2641 folly::sformat("TLS-{}", static_cast<void*>(tcbBase)));
2644 Debug::DebugInfo::recordDataMap(
2645 stackAddr, stackEnd,
2646 folly::sformat("Stack-{}", static_cast<void*>(tcbBase)));
2648 template <class ThreadT>
2649 static typename std::enable_if<!std::is_integral<ThreadT>::value &&
2650 !std::is_pointer<ThreadT>::value>::type
2651 recordThreadAddr(ThreadT /*threadId*/, char* stackAddr, size_t stackSize) {
2652 // pthread_t is not an integer or pointer to TCB in this pthread
2653 // implementation. But we can still figure out where TLS is.
2654 auto const tlsRange = getCppTdata();
2655 auto const tlsSize = (tlsRange.second + 15) / 16 * 16;
2656 auto const tlsBaseAddr = reinterpret_cast<char*>(tlsBase());
2657 Debug::DebugInfo::recordDataMap(
2658 tlsBaseAddr, tlsBaseAddr + tlsSize,
2659 folly::sformat("TLS-{}", static_cast<void*>(stackAddr)));
2660 Debug::DebugInfo::recordDataMap(
2661 stackAddr, stackAddr + stackSize,
2662 folly::sformat("Stack-{}", static_cast<void*>(stackAddr)));
2665 SetThreadInitFini() {
2666 AsyncFuncImpl::SetThreadInitFunc(
2667 [] (void*) {
2668 #if defined(_GNU_SOURCE) && defined(__linux__)
2669 if (RuntimeOption::EvalPerfDataMap) {
2670 pthread_t threadId = pthread_self();
2671 pthread_attr_t attr;
2672 pthread_getattr_np(threadId, &attr);
2673 void* stackAddr{nullptr};
2674 size_t stackSize{0};
2675 pthread_attr_getstack(&attr, &stackAddr, &stackSize);
2676 pthread_attr_destroy(&attr);
2677 recordThreadAddr(threadId, static_cast<char*>(stackAddr), stackSize);
2679 #endif
2680 hphp_thread_init();
2682 nullptr);
2683 AsyncFuncImpl::SetThreadFiniFunc([](void*) { hphp_thread_exit(); },
2684 nullptr);
2686 } s_SetThreadInitFini;
2688 ///////////////////////////////////////////////////////////////////////////////