Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / server / http-server.cpp
blobfd20e08ffb409338ac920b5ae1e09735f08fe984
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/server/http-server.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/externals.h"
21 #include "hphp/runtime/base/file-util.h"
22 #include "hphp/runtime/base/http-client.h"
23 #include "hphp/runtime/base/memory-manager.h"
24 #include "hphp/runtime/base/program-functions.h"
25 #include "hphp/runtime/base/runtime-option.h"
26 #include "hphp/runtime/base/init-fini-node.h"
27 #include "hphp/runtime/debugger/debugger.h"
28 #include "hphp/runtime/ext/apc/ext_apc.h"
29 #include "hphp/runtime/server/admin-request-handler.h"
30 #include "hphp/runtime/server/http-request-handler.h"
31 #include "hphp/runtime/server/replay-transport.h"
32 #include "hphp/runtime/server/server-stats.h"
33 #include "hphp/runtime/server/static-content-cache.h"
34 #include "hphp/runtime/server/warmup-request-handler.h"
35 #include "hphp/runtime/server/xbox-server.h"
36 #include "hphp/runtime/vm/vm-regs.h"
38 #include "hphp/util/alloc.h"
39 #include "hphp/util/boot-stats.h"
40 #include "hphp/util/logger.h"
41 #include "hphp/util/process.h"
42 #include "hphp/util/ssl-init.h"
43 #include "hphp/util/stack-trace.h"
44 #include "hphp/util/struct-log.h"
46 #include <folly/Conv.h>
47 #include <folly/Format.h>
48 #include <folly/portability/Unistd.h>
50 #include <sys/types.h>
51 #include <signal.h>
53 namespace HPHP {
55 ///////////////////////////////////////////////////////////////////////////////
56 // statics
58 std::shared_ptr<HttpServer> HttpServer::Server;
59 time_t HttpServer::StartTime;
60 std::atomic<double> HttpServer::LoadFactor{1.0};
61 std::atomic_int HttpServer::QueueDiscount{0};
62 std::atomic_int_fast64_t HttpServer::PrepareToStopTime{0};
63 time_t HttpServer::OldServerStopTime;
64 std::vector<ShutdownStat> HttpServer::ShutdownStats;
65 folly::MicroSpinLock HttpServer::StatsLock;
67 const int kNumProcessors = sysconf(_SC_NPROCESSORS_ONLN);
69 static void on_kill(int sig) {
70 signal(sig, SIG_DFL);
71 // There is a small race condition here with HttpServer::reset in
72 // program-functions.cpp, but it can only happen if we get a signal while
73 // shutting down. The fix is to add a lock to HttpServer::Server but it seems
74 // like overkill.
75 if (HttpServer::Server) {
76 HttpServer::Server->stopOnSignal(sig);
77 } else {
78 raise(sig);
82 ///////////////////////////////////////////////////////////////////////////////
84 HttpServer::HttpServer()
85 : m_stopped(false), m_killed(false), m_stopReason(nullptr),
86 m_watchDog(this, &HttpServer::watchDog) {
87 LoadFactor = RuntimeOption::EvalInitialLoadFactor;
89 // enabling mutex profiling, but it's not turned on
90 LockProfiler::s_pfunc_profile = server_stats_log_mutex;
92 int startingThreadCount = RuntimeOption::ServerThreadCount;
93 uint32_t additionalThreads = 0;
94 if (RuntimeOption::ServerWarmupThrottleRequestCount > 0 &&
95 RuntimeOption::ServerThreadCount > kNumProcessors) {
96 startingThreadCount = kNumProcessors;
97 additionalThreads = RuntimeOption::ServerThreadCount - startingThreadCount;
100 auto serverFactory = ServerFactoryRegistry::getInstance()->getFactory
101 (RuntimeOption::ServerType);
102 const std::string address = RuntimeOption::ServerFileSocket.empty()
103 ? RuntimeOption::ServerIP : RuntimeOption::ServerFileSocket;
104 ServerOptions options(address, RuntimeOption::ServerPort,
105 RuntimeOption::ServerThreadCount, startingThreadCount);
106 options.m_useFileSocket = !RuntimeOption::ServerFileSocket.empty();
107 options.m_serverFD = RuntimeOption::ServerPortFd;
108 options.m_sslFD = RuntimeOption::SSLPortFd;
109 options.m_takeoverFilename = RuntimeOption::TakeoverFilename;
110 options.m_hugeThreads = RuntimeOption::ServerHugeThreadCount;
111 m_pageServer = serverFactory->createServer(options);
112 m_pageServer->addTakeoverListener(this);
113 m_pageServer->addServerEventListener(this);
115 if (additionalThreads) {
116 auto handlerFactory = std::make_shared<WarmupRequestHandlerFactory>(
117 m_pageServer.get(), additionalThreads,
118 RuntimeOption::ServerWarmupThrottleRequestCount,
119 RuntimeOption::RequestTimeoutSeconds);
120 m_pageServer->setRequestHandlerFactory([handlerFactory] {
121 return handlerFactory->createHandler();
123 } else {
124 m_pageServer->setRequestHandlerFactory<HttpRequestHandler>(
125 RuntimeOption::RequestTimeoutSeconds);
128 if (RuntimeOption::EnableSSL) {
129 assertx(SSLInit::IsInited());
130 m_pageServer->enableSSL(RuntimeOption::SSLPort);
133 if (RuntimeOption::EnableSSLWithPlainText) {
134 assertx(SSLInit::IsInited());
135 m_pageServer->enableSSLWithPlainText();
139 ServerOptions admin_options(RuntimeOption::AdminServerIP,
140 RuntimeOption::AdminServerPort,
141 RuntimeOption::AdminThreadCount);
142 admin_options.m_queueToWorkerRatio =
143 RuntimeOption::AdminServerQueueToWorkerRatio;
144 m_adminServer = serverFactory->createServer(admin_options);
145 m_adminServer->setRequestHandlerFactory<AdminRequestHandler>(
146 RuntimeOption::RequestTimeoutSeconds);
148 for (unsigned int i = 0; i < RuntimeOption::SatelliteServerInfos.size();
149 i++) {
150 auto info = RuntimeOption::SatelliteServerInfos[i];
151 auto satellite(SatelliteServer::Create(info));
152 if (satellite) {
153 m_satellites.push_back(std::move(satellite));
157 if (RuntimeOption::XboxServerPort != 0) {
158 auto xboxInfo = std::make_shared<XboxServerInfo>();
159 auto satellite = SatelliteServer::Create(xboxInfo);
160 if (satellite) {
161 m_satellites.push_back(std::move(satellite));
165 StaticContentCache::TheCache.load();
167 m_counterCallback.init(
168 [this](std::map<std::string, int64_t>& counters) {
169 counters["ev_connections"] = m_pageServer->getLibEventConnectionCount();
170 counters["inflight_requests"] = m_pageServer->getActiveWorker();
171 counters["queued_requests"] = m_pageServer->getQueuedJobs();
173 auto const sat_requests = getSatelliteRequestCount();
174 counters["satellite_inflight_requests"] = sat_requests.first;
175 counters["satellite_queued_requests"] = sat_requests.second;
179 signal(SIGTERM, on_kill);
180 signal(SIGUSR1, on_kill);
181 signal(SIGHUP, on_kill);
184 // Synchronously stop satellites
185 void HttpServer::onServerShutdown() {
186 InitFiniNode::ServerFini();
188 Eval::Debugger::Stop();
189 if (RuntimeOption::EnableDebuggerServer) {
190 Logger::Info("debugger server stopped");
193 // When a new instance of HPHP has taken over our page server socket,
194 // stop our admin server and satellites so it can acquire those
195 // ports.
196 if (RuntimeOption::AdminServerPort) {
197 m_adminServer->stop();
199 for (unsigned int i = 0; i < m_satellites.size(); i++) {
200 std::string name = m_satellites[i]->getName();
201 m_satellites[i]->stop();
202 Logger::Info("satellite server %s stopped", name.c_str());
204 XboxServer::Stop();
205 if (RuntimeOption::AdminServerPort) {
206 m_adminServer->waitForEnd();
207 Logger::Info("admin server stopped");
211 void HttpServer::takeoverShutdown() {
212 // We want to synchronously shut down our satellite servers to free up ports,
213 // then asynchronously shut down everything else.
214 onServerShutdown();
215 stop();
218 void HttpServer::serverStopped(HPHP::Server* server) {
219 Logger::Info("Page server stopped");
220 assertx(server == m_pageServer.get());
221 removePid();
223 auto sockFile = RuntimeOption::ServerFileSocket;
224 if (!sockFile.empty()) {
225 unlink(sockFile.c_str());
228 LogShutdownStats();
231 void HttpServer::playShutdownRequest(const std::string& fileName) {
232 if (fileName.empty()) return;
233 Logger::Info("playing request upon shutdown %s", fileName.c_str());
234 try {
235 ReplayTransport rt;
236 rt.replayInput(fileName.c_str());
237 HttpRequestHandler handler(0);
238 handler.run(&rt);
239 if (rt.getResponseCode() == 200) {
240 Logger::Info("successfully finished request: %s", rt.getUrl());
241 } else {
242 Logger::Error("request unsuccessful: %s", rt.getUrl());
244 } catch (...) {
245 Logger::Error("got exception when playing request: %s",
246 fileName.c_str());
250 HttpServer::~HttpServer() {
251 m_counterCallback.deinit();
253 // XXX: why should we have to call stop here? If we haven't already
254 // stopped (and joined all the threads), watchDog could still be
255 // running and leaving this destructor without a wait would be
256 // wrong...
257 stop();
260 static StaticString s_file{"file"}, s_line{"line"};
262 void HttpServer::runOrExitProcess() {
263 if (StaticContentCache::TheFileCache &&
264 StructuredLog::enabled() &&
265 StructuredLog::coinflip(RuntimeOption::EvalStaticContentsLogRate)) {
266 CacheManager::setLogger([](bool existsCheck, const std::string& name) {
267 auto record = StructuredLogEntry{};
268 record.setInt("existsCheck", existsCheck);
269 record.setStr("file", name);
270 bool needsCppStack = true;
271 if (!g_context.isNull()) {
272 VMRegAnchor _;
273 if (vmfp()) {
274 auto const bt =
275 createBacktrace(BacktraceArgs().withArgValues(false));
276 std::vector<std::string> frameStrings;
277 std::vector<folly::StringPiece> frames;
278 for (int i = 0; i < bt.size(); i++) {
279 auto f = tvCastToArrayLike(bt.rvalAt(i).tv());
280 if (f.exists(s_file)) {
281 auto s = tvCastToString(f.rvalAt(s_file).tv()).toCppString();
282 if (f.exists(s_line)) {
283 s += folly::sformat(
284 ":{}",
285 tvCastToInt64(f.rvalAt(s_line).tv())
288 frameStrings.emplace_back(std::move(s));
289 frames.push_back(frameStrings.back());
292 record.setVec("stack", frames);
293 needsCppStack = false;
296 if (needsCppStack) {
297 record.setStackTrace("stack", StackTrace{StackTrace::Force{}});
299 StructuredLog::log("hhvm_file_cache", record);
302 auto startupFailure = [] (const std::string& msg) {
303 Logger::Error(msg);
304 Logger::Error("Shutting down due to failure(s) to bind in "
305 "HttpServer::runAndExitProcess");
306 // Logger flushes itself---we don't need to run any atexit handlers
307 // (historically we've mostly just SEGV'd while trying) ...
308 _Exit(1);
311 if (!RuntimeOption::InstanceId.empty()) {
312 std::string msg = "Starting instance " + RuntimeOption::InstanceId;
313 if (!RuntimeOption::DeploymentId.empty()) {
314 msg += " from deployment " + RuntimeOption::DeploymentId;
316 Logger::Info(msg);
319 m_watchDog.start();
321 if (RuntimeOption::ServerPort) {
322 if (!startServer(true)) {
323 startupFailure("Unable to start page server");
324 not_reached();
326 Logger::Info("page server started");
329 StartTime = time(nullptr);
331 if (RuntimeOption::AdminServerPort) {
332 if (!startServer(false)) {
333 startupFailure("Unable to start admin server");
334 not_reached();
336 Logger::Info("admin server started");
339 for (unsigned int i = 0; i < m_satellites.size(); i++) {
340 std::string name = m_satellites[i]->getName();
341 try {
342 m_satellites[i]->start();
343 Logger::Info("satellite server %s started", name.c_str());
344 } catch (Exception &e) {
345 startupFailure(
346 folly::format("Unable to start satellite server {}: {}",
347 name, e.getMessage()).str()
349 not_reached();
353 if (!Eval::Debugger::StartServer()) {
354 startupFailure("Unable to start debugger server");
355 not_reached();
356 } else if (RuntimeOption::EnableDebuggerServer) {
357 Logger::Info("debugger server started");
360 try {
361 InitFiniNode::ServerInit();
362 } catch (std::exception &e) {
363 startupFailure(
364 folly::sformat("Exception in InitFiniNode::ServerInit(): {}",
365 e.what()));
369 BootStats::mark("servers started");
370 Logger::Info("all servers started");
371 createPid();
372 Lock lock(this);
373 BootStats::done();
374 // continously running until /stop is received on admin server, or
375 // takeover is requested.
376 while (!m_stopped) {
377 wait();
379 if (m_stopReason) {
380 Logger::Warning("Server stopping with reason: %s\n", m_stopReason);
382 // if we were killed, bail out immediately
383 if (m_killed) {
384 Logger::Info("page server killed");
385 return;
389 if (RuntimeOption::ServerPort) {
390 Logger::Info("stopping page server");
391 m_pageServer->stop();
393 onServerShutdown();
395 EvictFileCache();
397 waitForServers();
398 m_watchDog.waitForEnd();
399 playShutdownRequest(RuntimeOption::ServerCleanupRequest);
402 void HttpServer::waitForServers() {
403 if (RuntimeOption::ServerPort) {
404 m_pageServer->waitForEnd();
406 if (RuntimeOption::AdminServerPort) {
407 m_adminServer->waitForEnd();
409 // all other servers invoke waitForEnd on stop
412 static void exit_on_timeout(int sig) {
413 signal(sig, SIG_DFL);
414 #ifdef _WIN32
415 TerminateProcess(GetCurrentProcess(), (UINT)-1);
416 #else
417 kill(getpid(), SIGKILL);
418 #endif
419 // we really shouldn't get here, but who knows.
420 // abort so we catch it as a crash.
421 abort();
424 // Tell OOM killer to kill this process if it has to. This is used during
425 // server shutdown. If we are dying anyway, let's try to protect others.
426 static void oom_sacrifice() {
427 #ifdef __linux__
428 // Use open() instead of fopen() here to avoid additional buffering and
429 // allocation, which could go wrong if memory is really limited.
430 int fd = open("/proc/self/oom_score_adj", O_WRONLY, 0);
431 if (fd >= 0) {
432 write(fd, "800", 3);
433 close(fd);
435 #endif
438 void HttpServer::stop(const char* stopReason) {
439 if (m_stopped) return;
440 // we're shutting down flush http logs
441 Logger::FlushAll();
442 HttpRequestHandler::GetAccessLog().flushAllWriters();
443 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED);
444 oom_sacrifice();
446 if (RuntimeOption::ServerKillOnTimeout) {
447 int totalWait =
448 RuntimeOption::ServerPreShutdownWait +
449 RuntimeOption::ServerShutdownListenWait +
450 RuntimeOption::ServerGracefulShutdownWait;
452 if (totalWait > 0) {
453 // Use a killer thread to _Exit() after totalWait seconds. If
454 // the main thread returns before that, the killer thread will
455 // exit. So don't do join() on this thread. Since we create a
456 // thread here, `HttpServer::stop()` cannot be called in signal
457 // handlers, use `stopOnSignal()` (which uses SIGALRM) in that
458 // case.
459 auto killer = std::thread([totalWait] {
460 #ifdef __linux__
461 sched_param param;
462 param.sched_priority = 5;
463 pthread_setschedparam(pthread_self(), SCHED_RR, &param);
464 // It is OK if we fail to increase thread priority.
465 #endif
466 /* sleep override */
467 std::this_thread::sleep_for(std::chrono::seconds{totalWait});
468 _Exit(1);
470 killer.detach();
474 Lock lock(this);
475 m_stopped = true;
476 m_stopReason = stopReason;
477 notify();
480 void HttpServer::stopOnSignal(int sig) {
481 if (m_stopped) return;
482 // we're shutting down flush http logs
483 Logger::FlushAll();
484 HttpRequestHandler::GetAccessLog().flushAllWriters();
485 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED);
486 oom_sacrifice();
488 // Signal to the main server thread to exit immediately if
489 // we want to die on SIGTERM
490 if (RuntimeOption::ServerKillOnSIGTERM && sig == SIGTERM) {
492 Lock lock(this);
493 m_stopped = true;
494 m_killed = true;
495 m_stopReason = "SIGTERM received";
496 notify();
498 raise(sig);
499 return;
502 if (RuntimeOption::ServerGracefulShutdownWait) {
503 signal(SIGALRM, exit_on_timeout);
504 alarm(RuntimeOption::ServerGracefulShutdownWait);
507 // NOTE: Server->stop does a graceful stop by design.
508 if (m_pageServer) {
509 m_pageServer->stop();
511 if (m_adminServer) {
512 m_adminServer->stop();
515 waitForServers();
518 void HttpServer::EvictFileCache() {
519 // In theory, the kernel should do just as well even if we don't
520 // explicitly advise files out. But we can do it anyway when we
521 // need more free memory, e.g., when a new instance of the server is
522 // about to start.
523 advise_out(RuntimeOption::RepoLocalPath);
524 advise_out(RuntimeOption::FileCache);
525 apc_advise_out();
528 void HttpServer::PrepareToStop() {
529 MarkShutdownStat(ShutdownEvent::SHUTDOWN_PREPARE);
530 PrepareToStopTime.store(time(nullptr), std::memory_order_release);
531 EvictFileCache();
534 void HttpServer::createPid() {
535 if (!RuntimeOption::PidFile.empty()) {
536 FILE * f = fopen(RuntimeOption::PidFile.c_str(), "w");
537 if (f) {
538 auto const pid = getpid();
539 char buf[64];
540 snprintf(buf, sizeof(buf), "%" PRId64, (int64_t)pid);
541 fwrite(buf, strlen(buf), 1, f);
542 fclose(f);
543 } else {
544 Logger::Error("Unable to open pid file %s for write",
545 RuntimeOption::PidFile.c_str());
550 void HttpServer::removePid() {
551 if (!RuntimeOption::PidFile.empty()) {
552 unlink(RuntimeOption::PidFile.c_str());
556 void HttpServer::killPid() {
557 if (!RuntimeOption::PidFile.empty()) {
558 CstrBuffer sb(RuntimeOption::PidFile.c_str());
559 if (sb.size()) {
560 int64_t pid = sb.detach().toInt64();
561 if (pid) {
562 kill((pid_t)pid, SIGKILL);
563 return;
566 Logger::Error("Unable to read pid file %s for any meaningful pid",
567 RuntimeOption::PidFile.c_str());
571 ///////////////////////////////////////////////////////////////////////////////
572 // watch dog thread
574 void HttpServer::watchDog() {
575 int count = 0;
576 bool noneed = false;
577 while (!m_stopped && !noneed) {
578 noneed = true;
580 if (RuntimeOption::DropCacheCycle > 0) {
581 noneed = false;
582 if ((count % RuntimeOption::DropCacheCycle) == 0) { // every hour
583 dropCache();
587 sleep(1);
588 ++count;
590 if (RuntimeOption::MaxRSSPollingCycle > 0) {
591 noneed = false;
592 if ((count % RuntimeOption::MaxRSSPollingCycle) == 0) { // every minute
593 checkMemory();
599 static bool sendAdminCommand(const char* cmd) {
600 if (RuntimeOption::AdminServerPort <= 0) return false;
601 std::string host = RuntimeOption::AdminServerIP;
602 if (host.empty()) host = "localhost";
603 auto passwords = RuntimeOption::AdminPasswords;
604 if (passwords.empty() && !RuntimeOption::AdminPassword.empty()) {
605 passwords.insert(RuntimeOption::AdminPassword);
607 auto passwordIter = passwords.begin();
608 do {
609 std::string url;
610 if (passwordIter != passwords.end()) {
611 url = folly::sformat("http://{}:{}/{}?auth={}", host,
612 RuntimeOption::AdminServerPort,
613 cmd, *passwordIter);
614 ++passwordIter;
615 } else {
616 url = folly::sformat("http://{}:{}/{}", host,
617 RuntimeOption::AdminServerPort, cmd);
619 if (CURL* curl = curl_easy_init()) {
620 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
621 curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
622 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
623 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
624 auto code = curl_easy_perform(curl);
625 curl_easy_cleanup(curl);
626 if (code == CURLE_OK) {
627 Logger::Info("sent %s via admin port", cmd);
628 return true;
631 } while (passwordIter != passwords.end());
632 return false;
635 bool HttpServer::ReduceOldServerLoad() {
636 if (!RuntimeOption::StopOldServer) return false;
637 if (OldServerStopTime > 0) return true;
638 Logger::Info("trying to reduce load of the old HPHP server");
639 return sendAdminCommand("prepare-to-stop");
642 bool HttpServer::StopOldServer() {
643 if (OldServerStopTime > 0) return true;
644 SCOPE_EXIT { OldServerStopTime = time(nullptr); };
645 Logger::Info("trying to shut down old HPHP server by /stop command");
646 return sendAdminCommand("stop");
649 // Return the estimated amount of memory that can be safely taken into
650 // the current process RSS.
651 static inline int64_t availableMemory(const MemInfo& mem, int64_t rss,
652 int factor) {
653 // Estimation of page cache that are readily evictable without
654 // causing big memory pressure. We consider it safe to take
655 // `pageFreeFactor` percent of cached pages (excluding those used by
656 // the current process, estimated to be equal to the current RSS)
657 auto const otherCacheMb = std::max((int64_t)0, mem.cachedMb - rss);
658 auto const availableMb = mem.freeMb + otherCacheMb * factor / 100;
659 return availableMb;
662 bool HttpServer::CanContinue(const MemInfo& mem, int64_t rssMb,
663 int64_t rssNeeded, int cacheFreeFactor) {
664 if (!mem.valid()) return false;
665 if (mem.freeMb < RuntimeOption::ServerCriticalFreeMb) return false;
666 auto const availableMb = availableMemory(mem, rssMb, cacheFreeFactor);
667 auto const result = (rssMb + availableMb >= rssNeeded);
668 if (result) assertx(CanStep(mem, rssMb, rssNeeded, cacheFreeFactor));
669 return result;
672 bool HttpServer::CanStep(const MemInfo& mem, int64_t rssMb,
673 int64_t rssNeeded, int cacheFreeFactor) {
674 if (!mem.valid()) return false;
675 if (mem.freeMb < RuntimeOption::ServerCriticalFreeMb) return false;
676 auto const availableMb = availableMemory(mem, rssMb, cacheFreeFactor);
677 // Estimation of the memory needed till the next check point. Since
678 // the current check point is not the last one, we try to be more
679 // optimistic, by assuming that memory requirement won't grow
680 // drastically between successive check points, and that it won't
681 // grow over our estimate.
682 auto const neededToStep = std::min(rssNeeded / 4, rssNeeded - rssMb);
683 return (availableMb >= neededToStep);
686 void HttpServer::CheckMemAndWait(bool final) {
687 if (!RuntimeOption::StopOldServer) return;
688 if (RuntimeOption::OldServerWait <= 0) return;
690 auto const rssNeeded = RuntimeOption::ServerRSSNeededMb;
691 auto const factor = RuntimeOption::CacheFreeFactor;
692 do {
693 // Don't wait too long
694 if (OldServerStopTime > 0 &&
695 time(nullptr) - OldServerStopTime >= RuntimeOption::OldServerWait) {
696 return;
699 auto const rssMb = Process::GetProcessRSS();
700 MemInfo memInfo;
701 if (!Process::GetMemoryInfo(memInfo)) {
702 Logger::Error("Failed to obtain memory information");
703 HttpServer::StopOldServer();
704 return;
706 Logger::FInfo("Memory pressure check: free/cached/buffers {}/{}/{} "
707 "currentRss {}Mb, required {}Mb.",
708 memInfo.freeMb, memInfo.cachedMb, memInfo.buffersMb,
709 rssMb, rssNeeded);
711 if (!final) {
712 if (CanStep(memInfo, rssMb, rssNeeded, factor)) return;
713 } else {
714 if (CanContinue(memInfo, rssMb, rssNeeded, factor)) return;
717 // OK, we don't have enough memory, let's do something.
718 HttpServer::StopOldServer(); // nop if already called before
719 /* sleep override */ sleep(1);
720 } while (true); // Guaranteed to return in the loop, at least upon timeout.
721 not_reached();
724 void HttpServer::MarkShutdownStat(ShutdownEvent event) {
725 if (!RuntimeOption::EvalLogServerRestartStats) return;
726 std::lock_guard<folly::MicroSpinLock> lock(StatsLock);
727 MemInfo mem;
728 Process::GetMemoryInfo(mem);
729 auto const rss = Process::GetProcessRSS();
730 auto const requests = requestCount();
731 if (event == ShutdownEvent::SHUTDOWN_PREPARE) {
732 ShutdownStats.clear();
733 ShutdownStats.reserve(ShutdownEvent::kNumEvents);
735 #ifndef NDEBUG
736 if (!ShutdownStats.empty()) {
737 assertx(ShutdownStats.back().event <= event);
739 #endif
740 ShutdownStats.push_back({event, time(nullptr), mem, rss, requests});
743 void HttpServer::LogShutdownStats() {
744 if (!RuntimeOption::EvalLogServerRestartStats) return;
745 StructuredLogEntry entry;
746 std::lock_guard<folly::MicroSpinLock> lock(StatsLock);
747 if (ShutdownStats.empty()) return;
748 for (size_t i = 0; i < ShutdownStats.size(); ++i) {
749 const auto& stat = ShutdownStats[i];
750 auto const eventName = stat.eventName();
751 entry.setInt(folly::sformat("{}.rss", eventName), stat.rss);
752 entry.setInt(folly::sformat("{}.free", eventName),
753 stat.memUsage.freeMb);
754 entry.setInt(folly::sformat("{}.cached", eventName),
755 stat.memUsage.cachedMb);
756 entry.setInt(folly::sformat("{}.buffers", eventName),
757 stat.memUsage.buffersMb);
758 // Log the difference since last event, if available
759 if (i > 0) {
760 const auto& last = ShutdownStats[i - 1];
761 auto const lastEvent = last.eventName();
762 entry.setInt(folly::sformat("{}.duration", lastEvent),
763 stat.time - last.time);
764 entry.setInt(folly::sformat("{}.requests", lastEvent),
765 stat.requestsServed - last.requestsServed);
766 entry.setInt(folly::sformat("{}.rss.delta", lastEvent),
767 stat.rss - last.rss);
770 StructuredLog::log("webserver_shutdown_timing", entry);
771 ShutdownStats.clear();
774 void HttpServer::dropCache() {
775 FILE *f = fopen("/proc/sys/vm/drop_caches", "w");
776 if (f) {
777 // http://www.linuxinsight.com/proc_sys_vm_drop_caches.html
778 const char *FREE_ALL_CACHES = "3\n";
779 fwrite(FREE_ALL_CACHES, 2, 1, f);
780 fclose(f);
784 void HttpServer::checkMemory() {
785 int64_t used = Process::GetProcessRSS() * 1024 * 1024;
786 if (RuntimeOption::MaxRSS > 0 && used > RuntimeOption::MaxRSS) {
787 Logger::Error(
788 "ResourceLimit.MaxRSS %" PRId64 " reached %" PRId64 " used, exiting",
789 RuntimeOption::MaxRSS, used);
790 stop();
794 void HttpServer::getSatelliteStats(
795 std::vector<std::pair<std::string, int>> *stats) {
796 for (const auto& i : m_satellites) {
797 std::pair<std::string, int> active("satellite." + i->getName() + ".load",
798 i->getActiveWorker());
799 std::pair<std::string, int> queued("satellite." + i->getName() + ".queued",
800 i->getQueuedJobs());
801 stats->push_back(active);
802 stats->push_back(queued);
806 std::pair<int, int> HttpServer::getSatelliteRequestCount() const {
807 int inflight = 0;
808 int queued = 0;
809 for (const auto& i : m_satellites) {
810 inflight += i->getActiveWorker();
811 queued += i->getQueuedJobs();
813 return std::make_pair(inflight, queued);
816 ///////////////////////////////////////////////////////////////////////////////
817 // page server
819 bool HttpServer::startServer(bool pageServer) {
820 int port = pageServer ?
821 RuntimeOption::ServerPort : RuntimeOption::AdminServerPort;
823 // 1. try something nice
824 for (unsigned int i = 0; i < 60; i++) {
825 try {
826 if (pageServer) {
827 m_pageServer->start();
828 } else {
829 m_adminServer->start();
831 return true;
832 } catch (FailedToListenException &e) {
833 if (RuntimeOption::ServerExitOnBindFail) return false;
835 StopOldServer();
837 if (errno == EACCES) {
838 Logger::Error("%s: Permission denied.", e.what());
839 return false;
842 if (pageServer && !RuntimeOption::ServerFileSocket.empty()) {
843 if (i == 0) {
844 Logger::Info("Unlinking unused socket at %s",
845 RuntimeOption::ServerFileSocket.c_str());
847 struct stat stat_buf;
848 if (stat(RuntimeOption::ServerFileSocket.c_str(), &stat_buf) == 0
849 && S_ISSOCK(stat_buf.st_mode)) {
850 std::string cmd = "bash -c '! fuser ";
851 cmd += RuntimeOption::ServerFileSocket;
852 cmd += "'";
853 if (FileUtil::ssystem(cmd.c_str()) == 0) {
854 unlink(RuntimeOption::ServerFileSocket.c_str());
858 sleep(1);
862 // 2. try something harsh
863 if (RuntimeOption::ServerHarshShutdown) {
864 for (unsigned int i = 0; i < 5; i++) {
865 try {
866 if (pageServer) {
867 m_pageServer->start();
868 } else {
869 m_adminServer->start();
871 return true;
872 } catch (FailedToListenException &e) {
873 if (i == 0) {
874 Logger::Info("shutting down old HPHP server by pid file");
876 killPid();
877 sleep(1);
882 // 3. try something evil
883 if (RuntimeOption::ServerEvilShutdown) {
884 for (unsigned int i = 0; i < 60; i++) {
885 try {
886 if (pageServer) {
887 m_pageServer->start();
888 } else {
889 m_adminServer->start();
891 return true;
892 } catch (FailedToListenException &e) {
893 if (pageServer && !RuntimeOption::ServerFileSocket.empty()) {
894 if (i == 0) {
895 Logger::Info("unlinking socket at %s",
896 RuntimeOption::ServerFileSocket.c_str());
899 struct stat stat_buf;
900 if (stat(RuntimeOption::ServerFileSocket.c_str(), &stat_buf) == 0
901 && S_ISSOCK(stat_buf.st_mode)) {
902 unlink(RuntimeOption::ServerFileSocket.c_str());
904 } else {
905 if (i == 0) {
906 Logger::Info("killing anything listening on port %d", port);
909 std::string cmd = "lsof -t -i :";
910 cmd += folly::to<std::string>(port);
911 cmd += " | xargs kill -9";
912 FileUtil::ssystem(cmd.c_str());
914 sleep(1);
919 return false;
922 ///////////////////////////////////////////////////////////////////////////////