2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/program-functions.h"
18 #include "hphp/runtime/base/types.h"
19 #include "hphp/runtime/base/type-conversions.h"
20 #include "hphp/runtime/base/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>
66 #include <oniguruma.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
;
81 extern char **environ
;
83 #define MAX_INPUT_NESTING_LEVEL 64
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 ///////////////////////////////////////////////////////////////////////////////
101 struct ProgramOptions
{
104 std::vector
<std::string
>
115 bool noSafeAccessCheck
;
116 std::vector
<std::string
>
124 Eval::DebuggerClientOptions debugger_options
;
129 StartTime() : startTime(time(nullptr)) {}
132 static StartTime s_startTime
;
133 static string tempFile
;
135 time_t start_time() {
136 return s_startTime
.startTime
;
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"),
154 s_HOSTNAME("HOSTNAME"),
155 s__SERVER("_SERVER"),
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
, '=');
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()) {
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
203 while (*var
&& *var
== ' ') {
207 // ensure that we don't have spaces or dots in the variable name
209 bool is_array
= false;
210 char *ip
= nullptr; // index pointer
213 if (*p
== ' ' || *p
== '.') {
215 } else if (*p
== '[') {
222 int var_len
= p
- var
;
224 // empty variable name, or variable name with a space in it
228 vector
<Variant
> gpc_elements
;
229 gpc_elements
.reserve(MAX_INPUT_NESTING_LEVEL
); // important, so no resize
230 Variant
*symtable
= &variables
;
232 int index_len
= var_len
;
237 if (++nest_level
> MAX_INPUT_NESTING_LEVEL
) {
238 Logger::Warning("Input variable nesting level exceeded");
251 ip
= strchr(ip
, ']');
253 // PHP variables cannot contain '[' in their names,
254 // so we replace the character with a '_'
255 *(index_s
- 1) = '_';
259 index_len
= strlen(index
);
264 new_idx_len
= strlen(index_s
);
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));
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 */
284 index_len
= new_idx_len
;
297 symtable
->append(value
);
299 String
key(index
, index_len
, CopyString
);
300 if (overwrite
|| !symtable
->toArray().exists(key
)) {
301 symtable
->set(key
, value
);
307 enum class ContextOfException
{
313 static void handle_exception_append_bt(std::string
& errorMsg
,
314 const ExtendedException
& e
) {
315 Array bt
= e
.getBackTrace();
317 errorMsg
+= ExtendedLogger::StringOfStackTrace(bt
);
321 static void bump_counter_and_rethrow() {
324 } catch (const RequestTimeoutException
& e
) {
325 static auto requestTimeoutCounter
= ServiceData::createTimeseries(
326 "requests_timed_out", {ServiceData::StatsType::COUNT
});
327 requestTimeoutCounter
->addValue(1);
329 } catch (const RequestMemoryExceededException
& e
) {
330 static auto requestMemoryExceededCounter
= ServiceData::createTimeseries(
331 "requests_memory_exceeded", {ServiceData::StatsType::COUNT
});
332 requestMemoryExceededCounter
->addValue(1);
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);
344 static void handle_exception_helper(bool& ret
,
345 ExecutionContext
* context
,
346 std::string
& errorMsg
,
347 ContextOfException where
,
351 bump_counter_and_rethrow();
352 } catch (const Eval::DebuggerException
&e
) {
354 } catch (const ExitException
&e
) {
355 if (where
== ContextOfException::ReqInit
) {
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
) {
366 if (where
!= ContextOfException::Handler
) {
367 raise_notice("%s", e
.getMessage().c_str());
369 Logger::Error("%s", e
.getMessage().c_str());
372 handle_exception_append_bt(errorMsg
, e
);
374 } catch (const UncatchableException
&e
) {
378 if (RuntimeOption::ServerStackTrace
) {
380 } else if (RuntimeOption::InjectedStackTrace
) {
381 errorMsg
= e
.getMessage();
383 errorMsg
+= ExtendedLogger::StringOfStackTrace(e
.getBackTrace());
385 Logger::Error("%s", errorMsg
.c_str());
387 handle_exception_append_bt(errorMsg
, e
);
389 } catch (const Exception
&e
) {
391 bool origError
= error
;
392 std::string origErrorMsg
= 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
);
405 errorMsg
= origErrorMsg
;
408 Logger::Error("%s", errorMsg
.c_str());
411 const ExtendedException
*ee
= dynamic_cast<const ExtendedException
*>(&e
);
413 handle_exception_append_bt(errorMsg
, *ee
);
416 } catch (const Object
&e
) {
418 bool origError
= error
;
419 std::string origErrorMsg
= errorMsg
;
423 if (where
== ContextOfException::Handler
) {
424 errorMsg
= "Exception handler threw an object exception: ";
427 errorMsg
+= e
.toString().data();
429 errorMsg
+= "(unable to call toString())";
431 if (where
== ContextOfException::Invoke
) {
432 bool handlerRet
= context
->onUnhandledException(e
);
436 errorMsg
= origErrorMsg
;
439 Logger::Error("%s", errorMsg
.c_str());
444 errorMsg
= "(unknown exception was thrown)";
445 Logger::Error("%s", errorMsg
.c_str());
449 static bool hphp_chdir_file(const string filename
) {
451 String s
= File::TranslatePath(filename
);
452 char *buf
= strndup(s
.data(), s
.size());
453 char *dir
= dirname(buf
);
456 if (File::IsVirtualDirectory(dir
)) {
457 g_context
->setCwd(String(dir
, CopyString
));
462 if ((sb
.st_mode
& S_IFMT
) == S_IFDIR
) {
465 g_context
->setCwd(String(dir
, CopyString
));
474 void handle_destructor_exception(const char* situation
) {
479 } catch (ExitException
&e
) {
480 // ExitException is fine, no need to show a warning.
481 ThreadInfo::s_threadInfo
->setPendingException(e
.clone());
483 } catch (Object
&e
) {
484 // For user exceptions, invoke the user exception handler
485 errorMsg
= situation
;
486 errorMsg
+= " threw an object exception: ";
488 errorMsg
+= e
.toString().data();
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();
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.
505 raise_debugging("%s", errorMsg
.c_str());
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");
516 for (int i
= 0; i
< argc
; i
++) {
520 StackTraceNoHeap::AddExtraLogging("Arguments", args
.c_str());
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
);
532 if (RuntimeOption::EvalJit
) {
533 env
.set(s_HHVM_JIT
, 1);
535 switch (JIT::arch()) {
537 env
.set(s_HHVM_ARCH
, "x64");
540 env
.set(s_HHVM_ARCH
, "arm");
544 process_cmd_arguments(argc
, argv
);
546 Variant
& server
= g
->getRef(s__SERVER
);
547 process_env_variables(server
);
549 struct timeval tp
= {0};
551 if (!gettimeofday(&tp
, nullptr)) {
552 now_double
= (double)(tp
.tv_sec
+ tp
.tv_usec
/ 1000000.00);
556 now_double
= (double)now
;
558 String file
= empty_string
;
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());
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()));
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();
604 f_var_dump(HHVM_FN(json_encode
)(f_xhprof_disable()));
606 hphp_context_exit(g_context
.getNoCheck(), true, true, program
);
608 if (coverage
&& ti
->m_reqInjectionData
.getCoverage() &&
609 !RuntimeOption::CodeCoverageOutputFile
.empty()) {
610 ti
->m_coverage
->Report(RuntimeOption::CodeCoverageOutputFile
);
615 const void* __hot_start
= nullptr;
616 const void* __hot_end
= nullptr;
619 void __attribute__((weak
)) __hot_start();
620 void __attribute__((weak
)) __hot_end();
625 # define AT_END_OF_TEXT __attribute__((__section__(".stub")))
627 # define AT_END_OF_TEXT
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
641 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
642 MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_FIXED
,
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
);
655 mlock(from
, to
- from
);
656 Debug::DebugInfo::setPidMapOverlay(from
, to
);
660 static void pagein_self(void) {
661 unsigned long begin
, end
, inode
, pgoff
;
662 char mapname
[PATH_MAX
];
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
);
676 Timer
timer(Timer::WallTime
, "mapping self");
677 fp
= fopen("/proc/self/maps", "r");
680 if (fgets(buf
, bufsz
, fp
) == 0)
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
689 access(mapname
, F_OK
) != 0) {
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
&&
703 hugePagesSupported() &&
704 beginPtr
<= hotStart
&&
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
);
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;
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.
748 set_execution_mode("server");
749 HttpRequestHandler::GetAccessLog().init
750 (RuntimeOption::AccessLogDefaultFormat
, RuntimeOption::AccessLogs
,
752 AdminRequestHandler::GetAccessLog().init
753 (RuntimeOption::AdminLogFormat
, RuntimeOption::AdminLogSymLink
,
754 RuntimeOption::AdminLogFile
,
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();
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
774 for (auto& file
: RuntimeOption::ServerWarmupRequests
) {
775 HttpRequestHandler
handler(0);
778 Timer::GetMonotonicTime(start
);
780 Logger::Info("Replaying warmup request %s", file
.c_str());
783 rt
.onRequestStart(start
);
784 rt
.replayInput(Hdf(file
));
785 handler
.handleRequest(&rt
);
788 Timer::GetMonotonicTime(stop
);
789 Logger::Info("Finished successfully in %ld seconds",
790 stop
.tv_sec
- start
.tv_sec
);
791 } catch (std::exception
& e
) {
796 Logger::Info("Got exception during warmup: %s", error
.c_str());
800 if (RuntimeOption::EvalEnableNuma
) {
802 mallctl("arenas.purge", nullptr, nullptr, nullptr, 0);
804 enable_numa(RuntimeOption::EvalEnableNumaLocal
);
808 HttpServer::Server
->runOrExitProcess();
812 string
translate_stack(const char *hexencoded
, bool with_frame_numbers
) {
813 if (!hexencoded
|| !*hexencoded
) {
817 StackTrace
st(hexencoded
);
818 std::vector
<std::shared_ptr
<StackTrace::Frame
>> frames
;
821 std::ostringstream out
;
822 for (unsigned int i
= 0; i
< frames
.size(); i
++) {
824 if (with_frame_numbers
) {
825 out
<< "# " << (i
< 10 ? " " : "") << i
<< ' ';
827 out
<< f
->toString();
833 ///////////////////////////////////////////////////////////////////////////////
835 static void prepare_args(int &argc
,
837 const std::vector
<std::string
> &args
,
839 argv
= (char **)malloc((args
.size() + 2) * sizeof(char*));
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
) {
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
) {
861 StackTraceNoHeap::AddExtraLogging("Assertion failure", fa
.summary
);
863 } catch (const std::exception
&e
) {
864 Logger::Error("Uncaught exception: %s", e
.what());
866 Logger::Error("Uncaught exception: (unknown)");
868 if (tempFile
.length() && boost::filesystem::exists(tempFile
)) {
869 boost::filesystem::remove(tempFile
);
874 /* -1 - cannot open file
875 * 0 - no need to open file
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
;
888 Logger::Output
= fopen(RuntimeOption::LogFile
.c_str(), "a");
889 if (Logger::Output
) return 1;
892 if (Logger::IsPipeOutput
) {
893 Logger::Output
= popen(RuntimeOption::LogFile
.substr(1).c_str(), "w");
894 if (Logger::Output
) return 2;
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());
906 static void close_server_log_file(int kind
) {
908 fclose(Logger::Output
);
909 } else if (kind
== 2) {
910 pclose(Logger::Output
);
912 always_assert(!Logger::Output
);
916 static int compute_hhvm_argc(const options_description
& desc
,
917 int argc
, char** argv
) {
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
) {
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) {
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
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
957 if (len
>= 3 && str
[0] == '-' && str
[1] == '-') {
958 // Handle long options
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] != '-')) {
966 } else if (len
>= 2 && str
[0] == '-') {
967 // Handle short options
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] != '-')) {
977 // We've found a non-option argument. This arg and all args
978 // that follow are intended for the PHP application
985 static int execute_program_impl(int argc
, char** argv
) {
986 string usage
= "Usage:\n\n ";
988 usage
+= " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
991 options_description
desc(usage
.c_str());
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")
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")
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")
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(),
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),
1057 positional_options_description p
;
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
);
1070 // Invoke the boost command line parser to parse the args for HHVM.
1071 auto opts
= command_line_parser(hhvm_argc
, argv
)
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
)
1081 // Manually append the args for the PHP application.
1083 for (unsigned m
= 0; m
< opts
.options
.size(); ++m
) {
1084 const auto& bo
= opts
.options
[m
];
1085 if (bo
.string_key
== "arg") {
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
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
);
1111 Logger::Error("Error in command line: invalid mode: %s", po
.mode
.c_str());
1112 cout
<< desc
<< "\n";
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";
1127 Logger::Error("Error in command line.");
1128 cout
<< desc
<< "\n";
1131 if (vm
.count("help")) {
1132 cout
<< desc
<< "\n";
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";
1143 if (vm
.count("compiler-id")) {
1144 cout
<< kCompilerId
<< "\n";
1148 if (vm
.count("repo-schema")) {
1149 cout
<< kRepoSchemaId
<< "\n";
1153 if (!po
.show
.empty()) {
1155 f
.open(po
.show
, "r");
1157 Logger::Error("Unable to open file %s", po
.show
.c_str());
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
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
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
,
1233 const size_t stackSizeMinimum
= 8 * 1024 * 1024;
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
) {
1257 hphp_process_init();
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
;
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());
1278 } catch (const FatalErrorException
& e
) {
1279 RuntimeOption::CallUserHandlerOnFatals
= false;
1280 RuntimeOption::AlwaysLogUnhandledExceptions
= true;
1281 g_context
->onFatalError(e
);
1284 Logger::Info("No syntax errors detected in %s", po
.lint
.c_str());
1288 if (!po
.parse
.empty()) {
1289 Logger::Error("The 'parse' command line option is not supported\n\n");
1293 if (argc
<= 1 || po
.mode
== "run" || po
.mode
== "debug") {
1294 if (po
.isTempFile
) {
1298 set_execution_mode("run");
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]);
1311 hphp_process_init();
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
);
1326 Logger::Error("Failed to start debugger client\n\n");
1329 Eval::Debugger::RegisterSandbox(localProxy
->getDummyInfo());
1330 std::shared_ptr
<std::vector
<std::string
>> client_args
;
1331 bool restart
= false;
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
1341 g_context
->setSandboxId(localProxy
->getDummyInfo().id());
1342 Eval::Debugger::DebuggerSession(po
.debugger_options
, restart
);
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
;
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());
1358 } catch (const Eval::DebuggerClientExitException
&e
) {
1359 execute_command_line_end(0, false, nullptr);
1360 break; // end user quitting debugger
1366 for (int i
= 0; i
< po
.count
; i
++) {
1367 execute_command_line_begin(new_argc
, new_argv
, po
.xhprofFlags
);
1369 if (hphp_invoke_simple(file
)) {
1370 ret
= ExitException::ExitCode
;
1372 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1377 hphp_process_exit();
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
++) {
1395 rt
.replayInput(po
.args
[j
].c_str());
1396 handler
.handleRequest(&rt
);
1397 printf("%s\n", rt
.getResponse().c_str());
1403 if (po
.mode
== "translate" && !po
.args
.empty()) {
1404 printf("%s", translate_stack(po
.args
[0].c_str()).c_str());
1408 cout
<< desc
<< "\n";
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
);
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
);
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
§ion
/*= "systemlib" */,
1443 const string
&filename
/*= "" */) {
1444 if (filename
.empty() && section
== "systemlib") {
1445 if (char *file
= getenv("HHVM_SYSTEMLIB")) {
1446 std::ifstream
ifs(file
);
1448 return systemlib_split(std::string(
1449 std::istreambuf_iterator
<char>(ifs
),
1450 std::istreambuf_iterator
<char>()), hhas
);
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
);
1467 ///////////////////////////////////////////////////////////////////////////////
1470 extern "C" void hphp_fatal_error(const char *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
;
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
);
1486 pthread_attr_init(&attr
);
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
);
1505 Process::InitProcessStatics();
1507 // reinitialize pcre table
1510 // the liboniguruma docs say this isnt needed,
1511 // but the implementation of init is not
1512 // thread safe due to bugs
1515 // simple xml also needs one time init
1520 PageletServer::Restart();
1521 XboxServer::Restart();
1522 Stream::RegisterCoreWrappers();
1523 Extension::InitModules();
1524 for (InitFiniNode
*in
= extra_process_init
; in
; in
= in
->next
) {
1527 int64_t save
= RuntimeOption::SerializationSizeLimit
;
1528 RuntimeOption::SerializationSizeLimit
= StringData::MaxSize
;
1529 apc_load(apcExtension::LoadThread
);
1530 RuntimeOption::SerializationSizeLimit
= save
;
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
);
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().
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
,
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
,
1569 static bool hphp_warmup(ExecutionContext
*context
,
1570 const string
&reqInitFunc
,
1571 const string
&reqInitDoc
, bool &error
) {
1574 std::string errorMsg
;
1576 ServerStatsHelper
ssh("reqinit");
1578 if (!reqInitDoc
.empty()) {
1579 include_impl_invoke(reqInitDoc
, true);
1581 if (!reqInitFunc
.empty()) {
1582 invoke(reqInitFunc
.c_str(), Array());
1584 context
->backupSession();
1586 handle_reqinit_exception(ret
, context
, errorMsg
, error
);
1592 void hphp_session_init() {
1593 init_thread_locals();
1594 ThreadInfo::s_threadInfo
->onSessionInit();
1597 #ifdef ENABLE_SIMPLE_COUNTER
1598 SimpleCounter::Enabled
= true;
1599 StackTrace::Enabled
= true;
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();
1612 context
->obProtect(true);
1616 bool hphp_invoke_simple(const std::string
&filename
,
1617 bool warmupOnly
/* = false */) {
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();
1635 oldCwd
= context
->getCwd();
1637 if (!hphp_warmup(context
, reqInitFunc
, reqInitDoc
, error
)) {
1638 if (isServer
) context
->setCwd(oldCwd
);
1642 LitstrTable::get().setReading();
1647 ServerStatsHelper
ssh("invoke");
1649 funcRet
->assignVal(invoke(cmd
.c_str(), funcParams
));
1651 if (isServer
) hphp_chdir_file(cmd
);
1652 include_impl_invoke(cmd
.c_str(), once
);
1655 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
1660 context
->onShutdownPreSend();
1662 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
1665 if (isServer
) context
->setCwd(oldCwd
);
1669 void hphp_context_exit(ExecutionContext
*context
, bool psp
,
1670 bool shutdown
/* = true */,
1671 const char *program
/* = NULL */) {
1673 context
->onShutdownPostSend();
1675 if (RuntimeOption::EnableDebugger
) {
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();
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();
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();
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();
1738 Eval::Debugger::Stop();
1739 Extension::ShutdownModules();
1740 LightProcess::Close();
1741 for (InitFiniNode
*in
= extra_process_exit
; in
; in
= in
->next
) {
1746 ///////////////////////////////////////////////////////////////////////////////