Dead code in CstrBuffer
[hiphop-php.git] / hphp / runtime / server / http-server.cpp
blob9f793dd19c6dcb366d3f11a6d93883aed05ef541
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/light-process.h"
41 #include "hphp/util/logger.h"
42 #include "hphp/util/process.h"
43 #include "hphp/util/ssl-init.h"
44 #include "hphp/util/stack-trace.h"
45 #include "hphp/util/struct-log.h"
47 #include <folly/Conv.h>
48 #include <folly/Format.h>
49 #include <folly/portability/Unistd.h>
51 #include <signal.h>
53 #ifdef __linux__
54 void DisableFork() __attribute__((__weak__));
55 void EnableForkLogging() __attribute__((__weak__));
56 #endif
58 namespace HPHP {
60 ///////////////////////////////////////////////////////////////////////////////
61 // statics
63 std::shared_ptr<HttpServer> HttpServer::Server;
64 time_t HttpServer::StartTime;
65 std::atomic<double> HttpServer::LoadFactor{1.0};
66 std::atomic_int HttpServer::QueueDiscount{0};
67 std::atomic_int_fast64_t HttpServer::PrepareToStopTime{0};
68 time_t HttpServer::OldServerStopTime;
69 std::vector<ShutdownStat> HttpServer::ShutdownStats;
70 folly::MicroSpinLock HttpServer::StatsLock;
72 const int kNumProcessors = sysconf(_SC_NPROCESSORS_ONLN);
74 static void on_kill(int sig) {
75 signal(sig, SIG_DFL);
76 // There is a small race condition here with HttpServer::reset in
77 // program-functions.cpp, but it can only happen if we get a signal while
78 // shutting down. The fix is to add a lock to HttpServer::Server but it seems
79 // like overkill.
80 if (HttpServer::Server) {
81 HttpServer::Server->stopOnSignal(sig);
82 } else {
83 raise(sig);
87 ///////////////////////////////////////////////////////////////////////////////
89 HttpServer::HttpServer()
90 : m_stopped(false), m_killed(false), m_stopReason(nullptr),
91 m_watchDog(this, &HttpServer::watchDog) {
92 LoadFactor = RuntimeOption::EvalInitialLoadFactor;
94 // enabling mutex profiling, but it's not turned on
95 LockProfiler::s_pfunc_profile = server_stats_log_mutex;
97 int startingThreadCount = RuntimeOption::ServerThreadCount;
98 if (RuntimeOption::ServerWarmupThrottleRequestCount > 0 &&
99 RuntimeOption::ServerThreadCount > kNumProcessors) {
100 startingThreadCount = kNumProcessors;
103 auto serverFactory = ServerFactoryRegistry::getInstance()->getFactory
104 (RuntimeOption::ServerType);
105 const std::string address = RuntimeOption::ServerFileSocket.empty()
106 ? RuntimeOption::ServerIP : RuntimeOption::ServerFileSocket;
107 ServerOptions options(address, RuntimeOption::ServerPort,
108 RuntimeOption::ServerThreadCount, startingThreadCount,
109 RuntimeOption::ServerQueueCount);
110 options.m_useFileSocket = !RuntimeOption::ServerFileSocket.empty();
111 options.m_serverFD = RuntimeOption::ServerPortFd;
112 options.m_sslFD = RuntimeOption::SSLPortFd;
113 options.m_takeoverFilename = RuntimeOption::TakeoverFilename;
114 options.m_hugeThreads = RuntimeOption::ServerHugeThreadCount;
115 m_pageServer = serverFactory->createServer(options);
116 m_pageServer->addTakeoverListener(this);
117 m_pageServer->addServerEventListener(this);
119 if (startingThreadCount != RuntimeOption::ServerThreadCount) {
120 auto handlerFactory = std::make_shared<WarmupRequestHandlerFactory>(
121 m_pageServer.get(),
122 RuntimeOption::ServerWarmupThrottleRequestCount,
123 RuntimeOption::RequestTimeoutSeconds);
124 m_pageServer->setRequestHandlerFactory([handlerFactory] {
125 return handlerFactory->createHandler();
127 } else {
128 m_pageServer->setRequestHandlerFactory<HttpRequestHandler>(
129 RuntimeOption::RequestTimeoutSeconds);
132 if (RuntimeOption::EnableSSL) {
133 assertx(SSLInit::IsInited());
134 m_pageServer->enableSSL(RuntimeOption::SSLPort);
137 if (RuntimeOption::EnableSSLWithPlainText) {
138 assertx(SSLInit::IsInited());
139 m_pageServer->enableSSLWithPlainText();
143 ServerOptions admin_options(RuntimeOption::AdminServerIP,
144 RuntimeOption::AdminServerPort,
145 RuntimeOption::AdminThreadCount);
146 m_adminServer = serverFactory->createServer(admin_options);
147 m_adminServer->setRequestHandlerFactory<AdminRequestHandler>(
148 RuntimeOption::RequestTimeoutSeconds);
150 for (unsigned int i = 0; i < RuntimeOption::SatelliteServerInfos.size();
151 i++) {
152 auto info = RuntimeOption::SatelliteServerInfos[i];
153 auto satellite(SatelliteServer::Create(info));
154 if (satellite) {
155 m_satellites.push_back(std::move(satellite));
159 if (RuntimeOption::XboxServerPort != 0) {
160 auto xboxInfo = std::make_shared<XboxServerInfo>();
161 auto satellite = SatelliteServer::Create(xboxInfo);
162 if (satellite) {
163 m_satellites.push_back(std::move(satellite));
167 StaticContentCache::TheCache.load();
169 m_counterCallback.init(
170 [this](std::map<std::string, int64_t>& counters) {
171 counters["ev_connections"] = m_pageServer->getLibEventConnectionCount();
172 counters["inflight_requests"] = m_pageServer->getActiveWorker();
173 counters["queued_requests"] = m_pageServer->getQueuedJobs();
175 auto const sat_requests = getSatelliteRequestCount();
176 counters["satellite_inflight_requests"] = sat_requests.first;
177 counters["satellite_queued_requests"] = sat_requests.second;
181 signal(SIGTERM, on_kill);
182 signal(SIGUSR1, on_kill);
183 signal(SIGHUP, on_kill);
186 // Synchronously stop satellites
187 void HttpServer::onServerShutdown() {
188 InitFiniNode::ServerFini();
190 Eval::Debugger::Stop();
191 if (RuntimeOption::EnableDebuggerServer) {
192 Logger::Info("debugger server stopped");
195 // When a new instance of HPHP has taken over our page server socket,
196 // stop our admin server and satellites so it can acquire those
197 // ports.
198 if (RuntimeOption::AdminServerPort) {
199 m_adminServer->stop();
201 for (unsigned int i = 0; i < m_satellites.size(); i++) {
202 std::string name = m_satellites[i]->getName();
203 m_satellites[i]->stop();
204 Logger::Info("satellite server %s stopped", name.c_str());
206 XboxServer::Stop();
207 if (RuntimeOption::AdminServerPort) {
208 m_adminServer->waitForEnd();
209 Logger::Info("admin server stopped");
213 void HttpServer::takeoverShutdown() {
214 // We want to synchronously shut down our satellite servers to free up ports,
215 // then asynchronously shut down everything else.
216 onServerShutdown();
217 stop();
220 void HttpServer::serverStopped(HPHP::Server* server) {
221 Logger::Info("Page server stopped");
222 assertx(server == m_pageServer.get());
223 removePid();
225 auto sockFile = RuntimeOption::ServerFileSocket;
226 if (!sockFile.empty()) {
227 unlink(sockFile.c_str());
230 LogShutdownStats();
233 void HttpServer::playShutdownRequest(const std::string& fileName) {
234 if (fileName.empty()) return;
235 Logger::Info("playing request upon shutdown %s", fileName.c_str());
236 try {
237 ReplayTransport rt;
238 rt.replayInput(fileName.c_str());
239 HttpRequestHandler handler(0);
240 handler.run(&rt);
241 if (rt.getResponseCode() == 200) {
242 Logger::Info("successfully finished request: %s", rt.getUrl());
243 } else {
244 Logger::Error("request unsuccessful: %s", rt.getUrl());
246 } catch (...) {
247 Logger::Error("got exception when playing request: %s",
248 fileName.c_str());
252 HttpServer::~HttpServer() {
253 m_counterCallback.deinit();
255 // XXX: why should we have to call stop here? If we haven't already
256 // stopped (and joined all the threads), watchDog could still be
257 // running and leaving this destructor without a wait would be
258 // wrong...
259 stop();
262 static StaticString s_file{"file"}, s_line{"line"};
264 void HttpServer::runOrExitProcess() {
265 if (StaticContentCache::TheFileCache &&
266 StructuredLog::enabled() &&
267 StructuredLog::coinflip(RuntimeOption::EvalStaticContentsLogRate)) {
268 CacheManager::setLogger([](bool existsCheck, const std::string& name) {
269 auto record = StructuredLogEntry{};
270 record.setInt("existsCheck", existsCheck);
271 record.setStr("file", name);
272 bool needsCppStack = true;
273 if (!g_context.isNull()) {
274 VMRegAnchor _;
275 if (vmfp()) {
276 auto const bt =
277 createBacktrace(BacktraceArgs().withArgValues(false));
278 std::vector<std::string> frameStrings;
279 std::vector<folly::StringPiece> frames;
280 for (int i = 0; i < bt.size(); i++) {
281 auto f = tvCastToArrayLike(bt.rvalAt(i).tv());
282 if (f.exists(s_file)) {
283 auto s = tvCastToString(f.rvalAt(s_file).tv()).toCppString();
284 if (f.exists(s_line)) {
285 s += folly::sformat(
286 ":{}",
287 tvCastToInt64(f.rvalAt(s_line).tv())
290 frameStrings.emplace_back(std::move(s));
291 frames.push_back(frameStrings.back());
294 record.setVec("stack", frames);
295 needsCppStack = false;
298 if (needsCppStack) {
299 record.setStackTrace("stack", StackTrace{StackTrace::Force{}});
301 StructuredLog::log("hhvm_file_cache", record);
304 auto startupFailure = [] (const std::string& msg) {
305 Logger::Error(msg);
306 Logger::Error("Shutting down due to failure(s) to bind in "
307 "HttpServer::runAndExitProcess");
308 // Logger flushes itself---we don't need to run any atexit handlers
309 // (historically we've mostly just SEGV'd while trying) ...
310 _Exit(1);
313 if (!RuntimeOption::InstanceId.empty()) {
314 std::string msg = "Starting instance " + RuntimeOption::InstanceId;
315 if (!RuntimeOption::DeploymentId.empty()) {
316 msg += " from deployment " + RuntimeOption::DeploymentId;
318 Logger::Info(msg);
321 m_watchDog.start();
323 if (RuntimeOption::ServerPort) {
324 if (!startServer(true)) {
325 startupFailure("Unable to start page server");
326 not_reached();
328 Logger::Info("page server started");
331 StartTime = time(nullptr);
333 if (RuntimeOption::AdminServerPort) {
334 if (!startServer(false)) {
335 startupFailure("Unable to start admin server");
336 not_reached();
338 Logger::Info("admin server started");
341 for (unsigned int i = 0; i < m_satellites.size(); i++) {
342 std::string name = m_satellites[i]->getName();
343 try {
344 m_satellites[i]->start();
345 Logger::Info("satellite server %s started", name.c_str());
346 } catch (Exception& e) {
347 startupFailure(
348 folly::format("Unable to start satellite server {}: {}",
349 name, e.getMessage()).str()
351 not_reached();
355 if (!Eval::Debugger::StartServer()) {
356 startupFailure("Unable to start debugger server");
357 not_reached();
358 } else if (RuntimeOption::EnableDebuggerServer) {
359 Logger::Info("debugger server started");
362 try {
363 InitFiniNode::ServerInit();
364 } catch (std::exception& e) {
365 startupFailure(
366 folly::sformat("Exception in InitFiniNode::ServerInit(): {}",
367 e.what()));
371 BootStats::mark("servers started");
372 Logger::Info("all servers started");
373 if (!RuntimeOption::ServerForkEnabled) {
374 #ifdef __linux__
375 if (DisableFork) {
376 // We should not fork from the server process. Use light process
377 // instead. This will intercept subsequent fork() calls and make them
378 // fail immediately.
379 DisableFork();
380 } else {
381 // Otherwise, the binary we are building is not HHVM. It is probably
382 // some tests, don't intercept fork().
383 Logger::Warning("ignored runtime option Server.Forking.Enabled=false");
385 #else
386 Logger::Warning("ignored Server.Forking.Enabled=false "
387 "as it only works on Linux");
388 #endif
389 } else {
390 #if FOLLY_HAVE_PTHREAD_ATFORK
391 pthread_atfork(nullptr, nullptr,
392 [] {
393 Process::OOMScoreAdj(1000);
395 #endif
397 if (RuntimeOption::ServerForkLogging) {
398 #ifdef __linux__
399 if (EnableForkLogging) {
400 EnableForkLogging();
401 } else {
402 // the binary we are building is not HHVM. It is probably
403 // some tests, don't intercept fork().
404 Logger::Warning("ignored runtime option Server.Forking.Log=true");
406 #else
407 Logger::Warning("ignored Server.Forking.Log=true "
408 "as it only works on Linux");
409 #endif
411 if (RuntimeOption::EvalServerOOMAdj < 0) {
412 // Avoid HHVM getting killed when a forked process uses too much memory.
413 // A positive adjustment makes it more likely for the server to be killed,
414 // and that's not what we want.
415 Process::OOMScoreAdj(RuntimeOption::EvalServerOOMAdj);
417 createPid();
418 Lock lock(this);
419 BootStats::done();
420 // continously running until /stop is received on admin server, or
421 // takeover is requested.
422 while (!m_stopped) {
423 wait();
425 if (m_stopReason) {
426 Logger::Warning("Server stopping with reason: %s", m_stopReason);
428 // if we were killed, bail out immediately
429 if (m_killed) {
430 Logger::Info("page server killed");
431 return;
435 if (RuntimeOption::ServerPort) {
436 Logger::Info("stopping page server");
437 m_pageServer->stop();
439 onServerShutdown();
441 EvictFileCache();
443 waitForServers();
444 m_watchDog.waitForEnd();
445 playShutdownRequest(RuntimeOption::ServerCleanupRequest);
448 void HttpServer::waitForServers() {
449 if (RuntimeOption::ServerPort) {
450 m_pageServer->waitForEnd();
452 if (RuntimeOption::AdminServerPort) {
453 m_adminServer->waitForEnd();
455 // all other servers invoke waitForEnd on stop
458 static void exit_on_timeout(int sig) {
459 signal(sig, SIG_DFL);
460 #ifdef _WIN32
461 TerminateProcess(GetCurrentProcess(), (UINT)-1);
462 #else
463 kill(getpid(), SIGKILL);
464 #endif
465 // we really shouldn't get here, but who knows.
466 // abort so we catch it as a crash.
467 abort();
470 void HttpServer::stop(const char* stopReason) {
471 if (m_stopping.exchange(true)) return;
472 // we're shutting down flush http logs
473 Logger::FlushAll();
474 HttpRequestHandler::GetAccessLog().flushAllWriters();
475 Process::OOMScoreAdj(1000);
476 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED);
478 if (RuntimeOption::ServerKillOnTimeout) {
479 int totalWait =
480 RuntimeOption::ServerPreShutdownWait +
481 RuntimeOption::ServerShutdownListenWait +
482 RuntimeOption::ServerGracefulShutdownWait;
484 if (totalWait > 0) {
485 // Use a killer thread to _Exit() after totalWait seconds. If
486 // the main thread returns before that, the killer thread will
487 // exit. So don't do join() on this thread. Since we create a
488 // thread here, `HttpServer::stop()` cannot be called in signal
489 // handlers, use `stopOnSignal()` (which uses SIGALRM) in that
490 // case.
491 auto killer = std::thread([totalWait] {
492 #ifdef __linux__
493 sched_param param;
494 param.sched_priority = 5;
495 pthread_setschedparam(pthread_self(), SCHED_RR, &param);
496 // It is OK if we fail to increase thread priority.
497 #endif
498 /* sleep override */
499 std::this_thread::sleep_for(std::chrono::seconds{totalWait});
500 _Exit(1);
502 killer.detach();
506 Lock lock(this);
507 m_stopped = true;
508 m_stopReason = stopReason;
509 notify();
512 void HttpServer::stopOnSignal(int sig) {
513 if (m_stopping.exchange(true)) return;
514 // we're shutting down flush http logs
515 Logger::FlushAll();
516 HttpRequestHandler::GetAccessLog().flushAllWriters();
517 Process::OOMScoreAdj(1000);
518 MarkShutdownStat(ShutdownEvent::SHUTDOWN_INITIATED);
520 // Signal to the main server thread to exit immediately if
521 // we want to die on SIGTERM
522 if (RuntimeOption::ServerKillOnSIGTERM && sig == SIGTERM) {
524 Lock lock(this);
525 m_stopped = true;
526 m_killed = true;
527 m_stopReason = "SIGTERM received";
528 notify();
530 raise(sig);
531 return;
534 if (RuntimeOption::ServerGracefulShutdownWait) {
535 signal(SIGALRM, exit_on_timeout);
536 alarm(RuntimeOption::ServerGracefulShutdownWait);
539 Lock lock(this);
540 m_stopped = true;
541 m_stopReason = "signal received";
542 notify();
545 void HttpServer::EvictFileCache() {
546 // In theory, the kernel should do just as well even if we don't
547 // explicitly advise files out. But we can do it anyway when we
548 // need more free memory, e.g., when a new instance of the server is
549 // about to start.
550 advise_out(RuntimeOption::RepoLocalPath);
551 advise_out(RuntimeOption::FileCache);
552 apc_advise_out();
555 void HttpServer::PrepareToStop() {
556 MarkShutdownStat(ShutdownEvent::SHUTDOWN_PREPARE);
557 PrepareToStopTime.store(time(nullptr), std::memory_order_release);
558 EvictFileCache();
561 void HttpServer::createPid() {
562 if (!RuntimeOption::PidFile.empty()) {
563 FILE * f = fopen(RuntimeOption::PidFile.c_str(), "w");
564 if (f) {
565 auto const pid = getpid();
566 char buf[64];
567 snprintf(buf, sizeof(buf), "%" PRId64, (int64_t)pid);
568 fwrite(buf, strlen(buf), 1, f);
569 fclose(f);
570 } else {
571 Logger::Error("Unable to open pid file %s for write",
572 RuntimeOption::PidFile.c_str());
577 void HttpServer::removePid() {
578 if (!RuntimeOption::PidFile.empty()) {
579 unlink(RuntimeOption::PidFile.c_str());
583 void HttpServer::killPid() {
584 if (!RuntimeOption::PidFile.empty()) {
585 CstrBuffer sb(RuntimeOption::PidFile.c_str());
586 if (sb.size()) {
587 int64_t pid = strtoll(sb.data(), nullptr, 10);
588 if (pid) {
589 kill((pid_t)pid, SIGKILL);
590 return;
593 Logger::Error("Unable to read pid file %s for any meaningful pid",
594 RuntimeOption::PidFile.c_str());
598 ///////////////////////////////////////////////////////////////////////////////
599 // watch dog thread
601 void HttpServer::watchDog() {
602 int count = 0;
603 bool noneed = false;
604 while (!m_stopped && !noneed) {
605 noneed = true;
607 if (RuntimeOption::DropCacheCycle > 0) {
608 noneed = false;
609 if ((count % RuntimeOption::DropCacheCycle) == 0) { // every hour
610 dropCache();
614 sleep(1);
615 ++count;
617 if (RuntimeOption::MaxRSSPollingCycle > 0) {
618 noneed = false;
619 if ((count % RuntimeOption::MaxRSSPollingCycle) == 0) { // every minute
620 checkMemory();
626 static bool sendAdminCommand(const char* cmd) {
627 if (RuntimeOption::AdminServerPort <= 0) return false;
628 std::string host = RuntimeOption::AdminServerIP;
629 if (host.empty()) host = "localhost";
630 auto passwords = RuntimeOption::AdminPasswords;
631 if (passwords.empty() && !RuntimeOption::AdminPassword.empty()) {
632 passwords.insert(RuntimeOption::AdminPassword);
634 auto passwordIter = passwords.begin();
635 do {
636 std::string url;
637 if (passwordIter != passwords.end()) {
638 url = folly::sformat("http://{}:{}/{}?auth={}", host,
639 RuntimeOption::AdminServerPort,
640 cmd, *passwordIter);
641 ++passwordIter;
642 } else {
643 url = folly::sformat("http://{}:{}/{}", host,
644 RuntimeOption::AdminServerPort, cmd);
646 if (CURL* curl = curl_easy_init()) {
647 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
648 curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
649 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
650 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
651 auto code = curl_easy_perform(curl);
652 curl_easy_cleanup(curl);
653 if (code == CURLE_OK) {
654 Logger::Info("sent %s via admin port", cmd);
655 return true;
658 } while (passwordIter != passwords.end());
659 return false;
662 bool HttpServer::ReduceOldServerLoad() {
663 if (!RuntimeOption::StopOldServer) return false;
664 if (OldServerStopTime > 0) return true;
665 Logger::Info("trying to reduce load of the old HPHP server");
666 return sendAdminCommand("prepare-to-stop");
669 bool HttpServer::StopOldServer() {
670 if (OldServerStopTime > 0) return true;
671 SCOPE_EXIT { OldServerStopTime = time(nullptr); };
672 Logger::Info("trying to shut down old HPHP server by /stop command");
673 return sendAdminCommand("stop");
676 // Return the estimated amount of memory that can be safely taken into
677 // the current process RSS.
678 static inline int64_t availableMemory(const MemInfo& mem, int64_t rss,
679 int factor) {
680 // Estimation of page cache that are readily evictable without
681 // causing big memory pressure. We consider it safe to take
682 // `pageFreeFactor` percent of cached pages (excluding those used by
683 // the current process, estimated to be equal to the current RSS)
684 auto const otherCacheMb = std::max((int64_t)0, mem.cachedMb - rss);
685 auto const availableMb = mem.freeMb + otherCacheMb * factor / 100;
686 return availableMb;
689 bool HttpServer::CanContinue(const MemInfo& mem, int64_t rssMb,
690 int64_t rssNeeded, int cacheFreeFactor) {
691 if (!mem.valid()) return false;
692 if (mem.freeMb < RuntimeOption::ServerCriticalFreeMb) return false;
693 auto const availableMb = availableMemory(mem, rssMb, cacheFreeFactor);
694 auto const result = (rssMb + availableMb >= rssNeeded);
695 if (result) assertx(CanStep(mem, rssMb, rssNeeded, cacheFreeFactor));
696 return result;
699 bool HttpServer::CanStep(const MemInfo& mem, int64_t rssMb,
700 int64_t rssNeeded, int cacheFreeFactor) {
701 if (!mem.valid()) return false;
702 if (mem.freeMb < RuntimeOption::ServerCriticalFreeMb) return false;
703 auto const availableMb = availableMemory(mem, rssMb, cacheFreeFactor);
704 // Estimation of the memory needed till the next check point. Since
705 // the current check point is not the last one, we try to be more
706 // optimistic, by assuming that memory requirement won't grow
707 // drastically between successive check points, and that it won't
708 // grow over our estimate.
709 auto const neededToStep = std::min(rssNeeded / 4, rssNeeded - rssMb);
710 return (availableMb >= neededToStep);
713 void HttpServer::CheckMemAndWait(bool final) {
714 if (!RuntimeOption::StopOldServer) return;
715 if (RuntimeOption::OldServerWait <= 0) return;
717 auto const rssNeeded = RuntimeOption::ServerRSSNeededMb;
718 auto const factor = RuntimeOption::CacheFreeFactor;
719 do {
720 // Don't wait too long
721 if (OldServerStopTime > 0 &&
722 time(nullptr) - OldServerStopTime >= RuntimeOption::OldServerWait) {
723 return;
726 auto const rssMb = Process::GetMemUsageMb();
727 MemInfo memInfo;
728 if (!Process::GetMemoryInfo(memInfo)) {
729 Logger::Error("Failed to obtain memory information");
730 HttpServer::StopOldServer();
731 return;
733 Logger::FInfo("Memory pressure check: free/cached/buffers {}/{}/{} "
734 "currentRss {}Mb, required {}Mb.",
735 memInfo.freeMb, memInfo.cachedMb, memInfo.buffersMb,
736 rssMb, rssNeeded);
738 if (!final) {
739 if (CanStep(memInfo, rssMb, rssNeeded, factor)) return;
740 } else {
741 if (CanContinue(memInfo, rssMb, rssNeeded, factor)) return;
744 // OK, we don't have enough memory, let's do something.
745 HttpServer::StopOldServer(); // nop if already called before
746 /* sleep override */ sleep(1);
747 } while (true); // Guaranteed to return in the loop, at least upon timeout.
748 not_reached();
751 void HttpServer::MarkShutdownStat(ShutdownEvent event) {
752 if (!RuntimeOption::EvalLogServerRestartStats) return;
753 std::lock_guard<folly::MicroSpinLock> lock(StatsLock);
754 MemInfo mem;
755 Process::GetMemoryInfo(mem);
756 auto const rss = Process::GetMemUsageMb();
757 auto const requests = requestCount();
758 if (event == ShutdownEvent::SHUTDOWN_PREPARE) {
759 ShutdownStats.clear();
760 ShutdownStats.reserve(ShutdownEvent::kNumEvents);
762 #ifndef NDEBUG
763 if (!ShutdownStats.empty()) {
764 assertx(ShutdownStats.back().event <= event);
766 #endif
767 ShutdownStats.push_back({event, time(nullptr), mem, rss, requests});
770 void HttpServer::LogShutdownStats() {
771 if (!RuntimeOption::EvalLogServerRestartStats) return;
772 StructuredLogEntry entry;
773 std::lock_guard<folly::MicroSpinLock> lock(StatsLock);
774 if (ShutdownStats.empty()) return;
775 for (size_t i = 0; i < ShutdownStats.size(); ++i) {
776 const auto& stat = ShutdownStats[i];
777 auto const eventName = stat.eventName();
778 entry.setInt(folly::sformat("{}.rss", eventName), stat.rss);
779 entry.setInt(folly::sformat("{}.free", eventName),
780 stat.memUsage.freeMb);
781 entry.setInt(folly::sformat("{}.cached", eventName),
782 stat.memUsage.cachedMb);
783 entry.setInt(folly::sformat("{}.buffers", eventName),
784 stat.memUsage.buffersMb);
785 // Log the difference since last event, if available
786 if (i > 0) {
787 const auto& last = ShutdownStats[i - 1];
788 auto const lastEvent = last.eventName();
789 entry.setInt(folly::sformat("{}.duration", lastEvent),
790 stat.time - last.time);
791 entry.setInt(folly::sformat("{}.requests", lastEvent),
792 stat.requestsServed - last.requestsServed);
793 entry.setInt(folly::sformat("{}.rss.delta", lastEvent),
794 stat.rss - last.rss);
797 StructuredLog::log("webserver_shutdown_timing", entry);
798 ShutdownStats.clear();
801 void HttpServer::dropCache() {
802 FILE *f = fopen("/proc/sys/vm/drop_caches", "w");
803 if (f) {
804 // http://www.linuxinsight.com/proc_sys_vm_drop_caches.html
805 const char *FREE_ALL_CACHES = "3\n";
806 fwrite(FREE_ALL_CACHES, 2, 1, f);
807 fclose(f);
811 void HttpServer::checkMemory() {
812 int64_t used = Process::GetMemUsageMb() * 1024 * 1024;
813 if (RuntimeOption::MaxRSS > 0 && used > RuntimeOption::MaxRSS) {
814 Logger::Error(
815 "ResourceLimit.MaxRSS %" PRId64 " reached %" PRId64 " used, exiting",
816 RuntimeOption::MaxRSS, used);
817 stop();
821 void HttpServer::getSatelliteStats(
822 std::vector<std::pair<std::string, int>> *stats) {
823 for (const auto& i : m_satellites) {
824 std::pair<std::string, int> active("satellite." + i->getName() + ".load",
825 i->getActiveWorker());
826 std::pair<std::string, int> queued("satellite." + i->getName() + ".queued",
827 i->getQueuedJobs());
828 stats->push_back(active);
829 stats->push_back(queued);
833 std::pair<int, int> HttpServer::getSatelliteRequestCount() const {
834 int inflight = 0;
835 int queued = 0;
836 for (const auto& i : m_satellites) {
837 inflight += i->getActiveWorker();
838 queued += i->getQueuedJobs();
840 return std::make_pair(inflight, queued);
843 ///////////////////////////////////////////////////////////////////////////////
844 // page server
846 bool HttpServer::startServer(bool pageServer) {
847 int port = pageServer ?
848 RuntimeOption::ServerPort : RuntimeOption::AdminServerPort;
850 // 1. try something nice
851 for (unsigned int i = 0; i < 60; i++) {
852 try {
853 if (pageServer) {
854 m_pageServer->start();
855 } else {
856 m_adminServer->start();
858 return true;
859 } catch (FailedToListenException& e) {
860 if (RuntimeOption::ServerExitOnBindFail) return false;
862 StopOldServer();
864 if (errno == EACCES) {
865 Logger::Error("%s: Permission denied.", e.what());
866 return false;
869 if (pageServer && !RuntimeOption::ServerFileSocket.empty()) {
870 if (i == 0) {
871 Logger::Info("Unlinking unused socket at %s",
872 RuntimeOption::ServerFileSocket.c_str());
874 struct stat stat_buf;
875 if (stat(RuntimeOption::ServerFileSocket.c_str(), &stat_buf) == 0
876 && S_ISSOCK(stat_buf.st_mode)) {
877 std::string cmd = "bash -c '! fuser ";
878 cmd += RuntimeOption::ServerFileSocket;
879 cmd += "'";
880 if (FileUtil::ssystem(cmd.c_str()) == 0) {
881 unlink(RuntimeOption::ServerFileSocket.c_str());
885 sleep(1);
889 // 2. try something harsh
890 if (RuntimeOption::ServerHarshShutdown) {
891 for (unsigned int i = 0; i < 5; i++) {
892 try {
893 if (pageServer) {
894 m_pageServer->start();
895 } else {
896 m_adminServer->start();
898 return true;
899 } catch (FailedToListenException& e) {
900 if (i == 0) {
901 Logger::Info("shutting down old HPHP server by pid file");
903 killPid();
904 sleep(1);
909 // 3. try something evil
910 if (RuntimeOption::ServerEvilShutdown) {
911 for (unsigned int i = 0; i < 60; i++) {
912 try {
913 if (pageServer) {
914 m_pageServer->start();
915 } else {
916 m_adminServer->start();
918 return true;
919 } catch (FailedToListenException& e) {
920 if (pageServer && !RuntimeOption::ServerFileSocket.empty()) {
921 if (i == 0) {
922 Logger::Info("unlinking socket at %s",
923 RuntimeOption::ServerFileSocket.c_str());
926 struct stat stat_buf;
927 if (stat(RuntimeOption::ServerFileSocket.c_str(), &stat_buf) == 0
928 && S_ISSOCK(stat_buf.st_mode)) {
929 unlink(RuntimeOption::ServerFileSocket.c_str());
931 } else {
932 if (i == 0) {
933 Logger::Info("killing anything listening on port %d", port);
936 std::string cmd = "lsof -t -i :";
937 cmd += folly::to<std::string>(port);
938 cmd += " | xargs kill -9";
939 FileUtil::ssystem(cmd.c_str());
941 sleep(1);
946 return false;
949 ///////////////////////////////////////////////////////////////////////////////