/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include #include #include "runtime/eval/runtime/file_repository.h" #include "runtime/base/server/admin_request_handler.h" #include "runtime/base/server/http_server.h" #include "runtime/base/server/pagelet_server.h" #include "runtime/base/util/http_client.h" #include "runtime/base/server/server_stats.h" #include "runtime/base/runtime_option.h" #include "runtime/base/preg.h" #include "util/process.h" #include "util/logger.h" #include "util/util.h" #include "util/mutex.h" #include "runtime/base/time/datetime.h" #include "runtime/base/memory/memory_manager.h" #include "runtime/base/program_functions.h" #include "runtime/base/shared/shared_store_base.h" #include "runtime/base/memory/leak_detectable.h" #include "runtime/ext/mysql_stats.h" #include "runtime/base/shared/shared_store_stats.h" #include "runtime/vm/repo.h" #include "runtime/vm/translator/translator.h" #include "runtime/vm/translator/translator-deps.h" #include "runtime/vm/translator/translator-x64.h" #include "util/alloc.h" #include #include "util/repo_schema.h" #include "runtime/ext/ext_fb.h" #include "runtime/ext/ext_apc.h" #ifdef GOOGLE_CPU_PROFILER #include #endif #ifdef GOOGLE_HEAP_PROFILER #include #endif using std::endl; namespace HPHP { /////////////////////////////////////////////////////////////////////////////// IMPLEMENT_THREAD_LOCAL(AccessLog::ThreadData, AdminRequestHandler::s_accessLogThreadData); AccessLog AdminRequestHandler::s_accessLog( &(AdminRequestHandler::getAccessLogThreadData)); AdminRequestHandler::AdminRequestHandler() { } // Helper machinery for jemalloc-stats-print command. #ifdef USE_JEMALLOC struct malloc_write { char *s; size_t slen; size_t smax; bool oom; }; void malloc_write_init(malloc_write *mw) { mw->s = nullptr; mw->slen = 0; mw->smax = 0; mw->oom = false; } void malloc_write_fini(malloc_write *mw) { if (mw->s != nullptr) { free(mw->s); malloc_write_init(mw); } } static void malloc_write_cb(void *cbopaque, const char *s) { malloc_write* mw = (malloc_write*)cbopaque; size_t slen = strlen(s); if (mw->oom) { return; } if (mw->slen + slen+1 >= mw->smax) { assert(mw->slen + slen > 0); char* ts = (char*)realloc(mw->s, (mw->slen + slen) << 1); if (ts == nullptr) { mw->oom = true; return; } mw->s = ts; mw->smax = (mw->slen + slen) << 1; } memcpy(&mw->s[mw->slen], s, slen+1); mw->slen += slen; } #endif void AdminRequestHandler::handleRequest(Transport *transport) { GetAccessLog().onNewRequest(); transport->addHeader("Content-Type", "text/plain"); string cmd = transport->getCommand(); do { if (cmd == "" || cmd == "help") { string usage = "/stop: stop the web server\n" "/translate: translate hex encoded stacktrace in 'stack' param\n" " stack required, stack trace to translate\n" " build-id optional, if specified, build ID has to match\n" " bare optional, whether to display frame ordinates\n" "/build-id: returns build id that's passed in from command line" "\n" "/compiler-id: returns the compiler id that built this app\n" "/repo-schema: return the repo schema id used by this app\n" "/check-load: how many threads are actively handling requests\n" "/check-queued: how many http requests are queued waiting to be\n" " handled\n" "/check-health: return json containing basic load/usage stats\n" "/check-ev: how many http requests are active by libevent\n" "/check-pl-load: how many pagelet threads are actively handling\n" " requests\n" "/check-pl-queued: how many pagelet requests are queued waiting to\n" " be handled\n" "/check-mem: report memory quick statistics in log file\n" "/check-sql: report SQL table statistics\n" "/status.xml: show server status in XML\n" "/status.json: show server status in JSON\n" "/status.html: show server status in HTML\n" "/stats-on: main switch: enable server stats\n" "/stats-off: main switch: disable server stats\n" "/stats-clear: clear all server stats\n" "/stats-web: turn on/off server page stats (CPU and gen time)\n" "/stats-mem: turn on/off memory statistics\n" "/stats-apc: turn on/off APC statistics\n" "/stats-apc-key: turn on/off APC key statistics\n" "/stats-mcc: turn on/off memcache statistics\n" "/stats-sql: turn on/off SQL statistics\n" "/stats-mutex: turn on/off mutex statistics\n" " sampling optional, default 1000\n" "/stats.keys: list all available keys\n" " from optional, , or <-n> second ago\n" " to optional, , or <-n> second ago\n" "/stats.xml: show server stats in XML\n" " from optional, , or <-n> second ago\n" " to optional, , or <-n> second ago\n" " agg optional, aggragation: *, url, code\n" " keys optional, ,,,<:regex:>\n" " url optional, only stats of this page or URL\n" " code optional, only stats of pages returning this code\n" "/stats.json: show server stats in JSON\n" " (same as /stats.xml)\n" "/stats.kvp: show server stats in key-value pairs\n" " (same as /stats.xml)\n" "/stats.html: show server stats in HTML\n" " (same as /stats.xml)\n" "/apc-ss: get apc size stats\n" "/apc-ss-flat: get apc size stats in flat format\n" "/apc-ss-keys: get apc size break-down on keys\n" "/apc-ss-dump: dump the size info on each key to /tmp/APC_details\n" " only valid when EnableAPCSizeDetail is true\n" " keysample optional, only dump keys that belongs to the same\n" " group as \n" "/const-ss: get const_map_size\n" "/static-strings: get number of static strings\n" "/dump-apc: dump all current value in APC to /tmp/apc_dump\n" "/dump-const: dump all constant value in constant map to\n" " /tmp/const_map_dump\n" "/dump-file-repo: dump file repository to /tmp/file_repo_dump\n" "/pcre-cache-size: get pcre cache map size\n" #ifdef GOOGLE_CPU_PROFILER "/prof-cpu-on: turn on CPU profiler\n" "/prof-cpu-off: turn off CPU profiler\n" #endif #ifdef GOOGLE_HEAP_PROFILER "/prof-heap-on: turn on heap profiler\n" "/prof-heap-dump: take one snapshot of the heap\n" "/prof-heap-off: turn off heap profiler\n" "/stats-malloc: turn on/off malloc statistics\n" "/leak-on: start leak detection\n" " sampling required, frequency\n" "/leak-off: end leak detection and report leaking\n" " cutoff optional, default 20 seconds, ignore newer allocs\n" #endif #ifdef EXECUTION_PROFILER "/prof-exe: returns sampled execution profile\n" #endif "/vm-tcspace: show space used by translator caches\n" "/vm-dump-tc: dump translation cache to /tmp/tc_dump_a and\n" " /tmp/tc_dump_astub\n" "/vm-preconsts: show information about preconsts\n" "/vm-tcreset: throw away translations and start over\n" ; #ifdef USE_TCMALLOC if (MallocExtensionInstance) { usage.append( "/free-mem: ask tcmalloc to release memory to system\n" "/tcmalloc-stats: get internal tcmalloc stats\n" "/tcmalloc-set-tc: set max mem tcmalloc thread-cache can use\n" ); } #endif #ifdef USE_JEMALLOC if (mallctl) { usage.append( "/jemalloc-stats: get internal jemalloc stats\n" "/jemalloc-stats-print:\n" " get comprehensive jemalloc stats in\n" " human-readable form\n" "/jemalloc-prof-activate:\n" " activate heap profiling\n" "/jemalloc-prof-deactivate:\n" " deactivate heap profiling\n" "/jemalloc-prof-dump:\n" " dump heap profile\n" " file optional, filesystem path\n" ); } #endif transport->sendString(usage); break; } if (!RuntimeOption::AdminPasswords.empty()) { std::set::const_iterator iter = RuntimeOption::AdminPasswords.find(transport->getParam("auth")); if (iter == RuntimeOption::AdminPasswords.end()) { transport->sendString("Unauthorized", 401); break; } } else { if (!RuntimeOption::AdminPassword.empty() && RuntimeOption::AdminPassword != transport->getParam("auth")) { transport->sendString("Unauthorized", 401); break; } } if (cmd == "stop") { transport->sendString("OK\n"); Logger::Info("Got admin port stop request from %s", transport->getRemoteHost()); HttpServer::Server->stop(); break; } if (cmd == "build-id") { transport->sendString(RuntimeOption::BuildId, 200); break; } if (cmd == "compiler-id") { transport->sendString(kCompilerId, 200); break; } if (cmd == "repo-schema") { transport->sendString(kRepoSchemaId, 200); break; } if (cmd == "translate") { string buildId = transport->getParam("build-id"); if (!buildId.empty() && buildId != RuntimeOption::BuildId) { transport->sendString("Build ID doesn't match.", 500); break; } string translated = translate_stack(transport->getParam("stack").c_str(), transport->getParam("bare").empty()); transport->sendString(translated); break; } if (strncmp(cmd.c_str(), "check", 5) == 0 && handleCheckRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "status", 6) == 0 && handleStatusRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "stats", 5) == 0 && handleStatsRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "prof", 4) == 0 && handleProfileRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "leak", 4) == 0 && handleLeakRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "apc-ss", 6) == 0 && handleAPCSizeRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "dump", 4) == 0 && handleDumpCacheRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "const-ss", 8) == 0 && handleConstSizeRequest(cmd, transport)) { break; } if (strcmp(cmd.c_str(), "static-strings") == 0 && handleStaticStringsRequest(cmd, transport)) { break; } if (strncmp(cmd.c_str(), "vm-", 3) == 0 && handleVMRequest(cmd, transport)) { break; } if (cmd == "pcre-cache-size") { std::ostringstream size; size << preg_pcre_cache_size() << endl; transport->sendString(size.str()); break; } #ifdef USE_TCMALLOC if (MallocExtensionInstance) { if (cmd == "free-mem") { MallocExtensionInstance()->ReleaseFreeMemory(); transport->sendString("OK\n"); break; } if (cmd == "tcmalloc-stats") { std::ostringstream stats; size_t user_allocated, heap_size, slack_bytes; size_t pageheap_free, pageheap_unmapped; size_t tc_max, tc_allocated; MallocExtensionInstance()-> GetNumericProperty("generic.current_allocated_bytes", &user_allocated); MallocExtensionInstance()-> GetNumericProperty("generic.heap_size", &heap_size); MallocExtensionInstance()-> GetNumericProperty("tcmalloc.slack_bytes", &slack_bytes); MallocExtensionInstance()-> GetNumericProperty("tcmalloc.pageheap_free_bytes", &pageheap_free); MallocExtensionInstance()-> GetNumericProperty("tcmalloc.pageheap_unmapped_bytes", &pageheap_unmapped); MallocExtensionInstance()-> GetNumericProperty("tcmalloc.max_total_thread_cache_bytes", &tc_max); MallocExtensionInstance()-> GetNumericProperty("tcmalloc.current_total_thread_cache_bytes", &tc_allocated); stats << "" << endl; stats << " " << user_allocated << "" << endl; stats << " " << heap_size << "" << endl; stats << " " << slack_bytes << "" << endl; stats << " " << pageheap_free << "" << endl; stats << " " << pageheap_unmapped << "" << endl; stats << " " << tc_max << "" << endl; stats << " " << tc_allocated << "" << endl; stats << "" << endl; transport->sendString(stats.str()); break; } if (cmd == "tcmalloc-set-tc") { size_t tc_max; MallocExtensionInstance()-> GetNumericProperty("tcmalloc.max_total_thread_cache_bytes", &tc_max); size_t tcache = transport->getInt64Param("s"); bool retval = MallocExtensionInstance()-> SetNumericProperty("tcmalloc.max_total_thread_cache_bytes", tcache); transport->sendString(retval == true ? "OK\n" : "FAILED\n"); break; } } #endif #ifdef USE_JEMALLOC if (mallctl) { if (cmd == "free-mem") { // Purge all dirty unused pages. int err = mallctl("arenas.purge", nullptr, nullptr, nullptr, 0); if (err) { std::ostringstream estr; estr << "Error " << err << " in mallctl(\"arenas.purge\", ...)" << endl; transport->sendString(estr.str()); } else { transport->sendString("OK\n"); } break; } if (cmd == "jemalloc-stats") { // Force jemalloc to update stats cached for use by mallctl(). uint64_t epoch = 1; mallctl("epoch", nullptr, nullptr, &epoch, sizeof(epoch)); size_t allocated = 0; // Initialize in case stats aren't enabled. size_t sz = sizeof(size_t); mallctl("stats.allocated", &allocated, &sz, nullptr, 0); size_t active = 0; mallctl("stats.active", &active, &sz, nullptr, 0); size_t mapped = 0; mallctl("stats.mapped", &mapped, &sz, nullptr, 0); std::ostringstream stats; stats << "" << endl; stats << " " << allocated << "" << endl; stats << " " << active << "" << endl; stats << " " << mapped << "" << endl; stats << "" << endl; transport->sendString(stats.str()); break; } if (cmd == "jemalloc-stats-print") { malloc_write mwo; malloc_write_init(&mwo); malloc_stats_print(malloc_write_cb, (void *)&mwo, ""); if (mwo.oom) { malloc_write_fini(&mwo); transport->sendString("OOM\n"); break; } transport->sendString(mwo.s); malloc_write_fini(&mwo); break; } if (cmd == "jemalloc-prof-activate") { bool active = true; int err = mallctl("prof.active", nullptr, nullptr, &active, sizeof(bool)); if (err) { std::ostringstream estr; estr << "Error " << err << " in mallctl(\"prof.active\", ...)" << endl; transport->sendString(estr.str()); } else { transport->sendString("OK\n"); } break; } if (cmd == "jemalloc-prof-deactivate") { bool active = false; int err = mallctl("prof.active", nullptr, nullptr, &active, sizeof(bool)); if (err) { std::ostringstream estr; estr << "Error " << err << " in mallctl(\"prof.active\", ...)" << endl; transport->sendString(estr.str()); } else { transport->sendString("OK\n"); } break; } if (cmd == "jemalloc-prof-dump") { string f = transport->getParam("file"); if (f != "") { const char *s = f.c_str(); int err = mallctl("prof.dump", nullptr, nullptr, (void *)&s, sizeof(char *)); if (err) { std::ostringstream estr; estr << "Error " << err << " in mallctl(\"prof.dump\", ..., \"" << f << "\", ...)" << endl; transport->sendString(estr.str()); break; } } else { int err = mallctl("prof.dump", nullptr, nullptr, nullptr, 0); if (err) { std::ostringstream estr; estr << "Error " << err << " in mallctl(\"prof.dump\", ...)" << endl; transport->sendString(estr.str()); break; } } transport->sendString("OK\n"); break; } } #endif transport->sendString("Unknown command: " + cmd + "\n", 404); } while (0); GetAccessLog().log(transport, nullptr); } /////////////////////////////////////////////////////////////////////////////// // stats commands static bool toggle_switch(Transport *transport, bool &setting) { setting = !setting; transport->sendString(setting ? "On\n" : "Off\n"); return true; } static bool send_report(Transport *transport, ServerStats::Format format, const char *mime) { int64_t from = transport->getInt64Param ("from"); int64_t to = transport->getInt64Param ("to"); string agg = transport->getParam ("agg"); string keys = transport->getParam ("keys"); string url = transport->getParam ("url"); int code = transport->getIntParam ("code"); string prefix = transport->getParam ("prefix"); string out; ServerStats::Report(out, format, from, to, agg, keys, url, code, prefix); transport->addHeader("Content-Type", mime); transport->sendString(out); return true; } static bool send_status(Transport *transport, ServerStats::Format format, const char *mime) { string out; ServerStats::ReportStatus(out, format); transport->addHeader("Content-Type", mime); transport->sendString(out); return true; } namespace VM { extern size_t hhbc_arena_capacity(); } bool AdminRequestHandler::handleCheckRequest(const std::string &cmd, Transport *transport) { if (cmd == "check-load") { int count = HttpServer::Server->getPageServer()->getActiveWorker(); transport->sendString(lexical_cast(count)); return true; } if (cmd == "check-ev") { int count = HttpServer::Server->getPageServer()->getLibEventConnectionCount(); transport->sendString(lexical_cast(count)); return true; } if (cmd == "check-queued") { int count = HttpServer::Server->getPageServer()->getQueuedJobs(); transport->sendString(lexical_cast(count)); return true; } if (cmd == "check-health") { std::stringstream out; bool first = true; out << "{" << endl; auto appendStat = [&](const char* name, int64_t value) { out << (!first ? "," : "") << " \"" << name << "\":" << value << endl; first = false; }; ServerPtr server = HttpServer::Server->getPageServer(); appendStat("load", server->getActiveWorker()); appendStat("queued", server->getQueuedJobs()); VM::Transl::Translator* tx = VM::Transl::Translator::Get(); appendStat("hhbc-roarena-capac", VM::hhbc_arena_capacity()); appendStat("tc-size", tx->getCodeSize()); appendStat("tc-stubsize", tx->getStubSize()); appendStat("targetcache", tx->getTargetCacheSize()); appendStat("units", Eval::FileRepository::getLoadedFiles()); out << "}" << endl; transport->sendString(out.str()); return true; } if (cmd == "check-pl-load") { int count = PageletServer::GetActiveWorker(); transport->sendString(lexical_cast(count)); return true; } if (cmd == "check-pl-queued") { int count = PageletServer::GetQueuedJobs(); transport->sendString(lexical_cast(count)); return true; } if (cmd == "check-mem") { return toggle_switch(transport, RuntimeOption::CheckMemory); } if (cmd == "check-sql") { string stats = "\n"; stats += "\n"; stats += MySqlStats::ReportStats(); stats += "\n"; transport->sendString(stats); return true; } return false; } bool AdminRequestHandler::handleStatusRequest(const std::string &cmd, Transport *transport) { if (cmd == "status.xml") { return send_status(transport, ServerStats::XML, "application/xml"); } if (cmd == "status.json") { return send_status(transport, ServerStats::JSON, "application/json"); } if (cmd == "status.html" || cmd == "status.htm") { return send_status(transport, ServerStats::HTML, "text/html"); } return false; } bool AdminRequestHandler::handleStatsRequest(const std::string &cmd, Transport *transport) { if (cmd == "stats-on") { RuntimeOption::EnableStats = true; transport->sendString("OK\n"); return true; } if (cmd == "stats-off") { RuntimeOption::EnableStats = false; transport->sendString("OK\n"); return true; } if (cmd == "stats-clear") { ServerStats::Clear(); transport->sendString("OK\n"); return true; } if (cmd == "stats-web") { return toggle_switch(transport, RuntimeOption::EnableWebStats); } if (cmd == "stats-mem") { toggle_switch(transport, RuntimeOption::EnableMemoryStats); return true; } if (cmd == "stats-malloc") { toggle_switch(transport, RuntimeOption::EnableMallocStats); LeakDetectable::EnableMallocStats(RuntimeOption::EnableMallocStats); return true; } if (cmd == "stats-apc") { return toggle_switch(transport, RuntimeOption::EnableAPCStats); } if (cmd == "stats-apc-key") { return toggle_switch(transport, RuntimeOption::EnableAPCKeyStats); } if (cmd == "stats-mcc") { return toggle_switch(transport, RuntimeOption::EnableMemcacheStats); } if (cmd == "stats-sql") { return toggle_switch(transport, RuntimeOption::EnableSQLStats); } if (cmd == "stats-mutex") { int sampling = transport->getIntParam("sampling"); if (sampling > 0) { LockProfiler::s_profile_sampling = sampling; } return toggle_switch(transport, LockProfiler::s_profile); } if (cmd == "stats.keys") { int64_t from = transport->getInt64Param("from"); int64_t to = transport->getInt64Param("to"); string out; ServerStats::GetKeys(out, from, to); transport->sendString(out); return true; } if (cmd == "stats.xml") { return send_report(transport, ServerStats::XML, "application/xml"); } if (cmd == "stats.json") { return send_report(transport, ServerStats::JSON, "application/json"); } if (cmd == "stats.kvp") { return send_report(transport, ServerStats::KVP, "text/plain"); } if (cmd == "stats.html" || cmd == "stats.htm") { return send_report(transport, ServerStats::HTML, "text/html"); } if (cmd == "stats.xsl") { string xsl; if (!RuntimeOption::StatsXSLProxy.empty()) { StringBuffer response; if (HttpClient().get(RuntimeOption::StatsXSLProxy.c_str(), response) == 200) { xsl = response.data(); if (!xsl.empty()) { transport->addHeader("Content-Type", "application/xml"); transport->sendString(xsl); return true; } } } transport->sendString("Not Found\n", 404); return true; } return false; } /////////////////////////////////////////////////////////////////////////////// // profile commands bool AdminRequestHandler::handleProfileRequest(const std::string &cmd, Transport *transport) { if (cmd == "prof-exe") { std::map counts; ThreadInfo::GetExecutionSamples(counts); string res = "[ "; for (std::map::const_iterator iter = counts.begin(); iter != counts.end(); ++iter) { res += lexical_cast(iter->first) + ", " + lexical_cast(iter->second) + ", "; } res += "-1 ]"; transport->sendString(res); return true; } #ifdef GOOGLE_CPU_PROFILER if (handleCPUProfilerRequest(cmd, transport)) { return true; } #endif #ifdef GOOGLE_HEAP_PROFILER if (handleHeapProfilerRequest(cmd, transport)) { return true; } #endif return false; } #ifdef GOOGLE_CPU_PROFILER bool AdminRequestHandler::handleCPUProfilerRequest(const std::string &cmd, Transport *transport) { string file = RuntimeOption::ProfilerOutputDir + "/" + Process::HostName + "/hphp.prof"; if (cmd == "prof-cpu-on") { if (Util::mkdir(file)) { ProfilerStart(file.c_str()); transport->sendString("OK\n"); } else { transport->sendString("Unable to mkdir for profile data.\n"); } return true; } if (cmd == "prof-cpu-off") { ProfilerStop(); ProfilerFlush(); transport->sendString("OK\n"); return true; } return false; } #endif #ifdef GOOGLE_HEAP_PROFILER bool AdminRequestHandler::handleHeapProfilerRequest(const std::string &cmd, Transport *transport) { string root = RuntimeOption::ProfilerOutputDir + "/" + Process::HostName; string file = root + "/hphp.prof"; if (cmd == "prof-heap-on") { if (IsHeapProfilerRunning()) { transport->sendString("HeapProfiler is already running.\n"); } else { if (Util::mkdir(file)) { // clean up leftovers string cmd = "/bin/rm -f "; cmd += file + ".*.heap"; Util::ssystem(cmd.c_str()); HeapProfilerStart(file.c_str()); HeapProfilerDump("start"); transport->sendString("OK\n"); } else { transport->sendString("Unable to mkdir for profile data.\n"); } } return true; } if (cmd == "prof-heap-dump") { if (!IsHeapProfilerRunning()) { transport->sendString("HeapProfiler is not running.\n"); } else { HeapProfilerDump("end"); transport->sendString("OK\n"); } return true; } if (cmd == "prof-heap-off") { if (!IsHeapProfilerRunning()) { transport->sendString("HeapProfiler is not running.\n"); } else { HeapProfilerDump("end"); HeapProfilerStop(); transport->sendString("OK\n"); const char *argv[] = {"", root.c_str(), "-name", "*.heap", nullptr}; string files; Process::Exec("find", argv, nullptr, files); vector out; Util::split('\n', files.c_str(), out, true); if (out.size() > 1) { string base = "--base="; base += out[0]; } else { Logger::Error("Unable to find heap profiler output"); } } return true; } return false; } #endif /////////////////////////////////////////////////////////////////////////////// // leak detection bool AdminRequestHandler::handleLeakRequest(const std::string &cmd, Transport *transport) { if (cmd == "leak-on") { LeakDetectable::BeginLeakChecking(); // class-based detection starts LeakDetectable::MallocSampling = transport->getIntParam("sampling"); LeakDetectable::BeginMallocSampling(); // malloc-based detection starts transport->sendString("OK\n"); return true; } if (cmd == "leak-off") { std::string dumps; // class-based detection ends int count = LeakDetectable::EndLeakChecking(dumps, 1); dumps += "\n"; dumps += boost::lexical_cast(count) + " leaked LeakDetectables\n"; // malloc-based detection int cutoff = transport->getIntParam("cutoff"); LeakDetectable::EndMallocSampling(dumps, cutoff ? cutoff : 20); LeakDetectable::MallocSampling = 0; transport->sendString(dumps); return true; } return false; } /////////////////////////////////////////////////////////////////////////////// // APC size profiling bool AdminRequestHandler::handleAPCSizeRequest (const std::string &cmd, Transport *transport) { if (!RuntimeOption::EnableAPCSizeStats && (cmd == "apc-ss" || cmd == "apc-ss-keys" || cmd == "apc-ss-dump" || cmd == "apc-ss-flat")) { transport->sendString("Not Enabled\n"); return true; } if (cmd == "apc-ss") { std::string result = SharedStoreStats::report_basic(); transport->sendString(result); return true; } if (cmd == "apc-ss-flat") { std::string result = SharedStoreStats::report_basic_flat(); transport->sendString(result); return true; } if (cmd == "apc-ss-keys") { if (!RuntimeOption::EnableAPCSizeGroup) { transport->sendString("Not Enabled\n"); return true; } std::string result = SharedStoreStats::report_keys(); transport->sendString(result); return true; } if (cmd == "apc-ss-dump") { if (!RuntimeOption::EnableAPCSizeDetail) { transport->sendString("Not Enabled\n"); return true; } string key_sample = transport->getParam("keysample"); if (SharedStoreStats::snapshot("/tmp/APC_details", key_sample)) { transport->sendString("Done\n"); } else { transport->sendString("Failed\n"); } return true; } return false; } bool AdminRequestHandler::handleConstSizeRequest (const std::string &cmd, Transport *transport) { if (!RuntimeOption::EnableConstLoad && cmd == "const-ss") { transport->sendString("Not Enabled\n"); return true; } if (cmd == "const-ss") { std::ostringstream result; size_t size = get_const_map_size(); result << "{ \"hphp.const_map.size\":" << size << "}\n"; transport->sendString(result.str()); return true; } return false; } bool AdminRequestHandler::handleStaticStringsRequest(const std::string& cmd, Transport* transport) { std::ostringstream result; result << StringData::GetStaticStringCount(); transport->sendString(result.str()); return true; } namespace { struct PCInfo { PCInfo() : count(0), unique(0) {} int count; int unique; }; typedef std::map InfoMap; } bool AdminRequestHandler::handleVMRequest(const std::string &cmd, Transport *transport) { if (cmd == "vm-tcspace") { transport->sendString(VM::Transl::Translator::Get()->getUsage()); return true; } if (cmd == "vm-preconsts") { InfoMap counts; using namespace HPHP::VM::Transl; for (PreConstDepMap::iterator i = gPreConsts.begin(); i != gPreConsts.end(); ++i) { PreConstDep& dep = i->second; PCInfo& info = counts[dep.preConsts.size()]; info.count++; if (preConstVecHasUnique(dep.preConsts)) { info.unique++; } } using std::setw; using std::right; using std::setfill; std::stringstream out; const int width = 10; out << setfill(' ') << right; out << setw(width) << "size" << setw(width) << "count" << setw(width) << "unique" << endl; for (InfoMap::iterator i = counts.begin(); i != counts.end(); ++i) { out << setw(width) << i->first << setw(width) << i->second.count << setw(width) << i->second.unique << endl; } transport->sendString(out.str()); return true; } if (cmd == "vm-dump-tc") { if (HPHP::VM::Transl::tc_dump()) { transport->sendString("Done"); } else { transport->sendString("Error dumping the translation cache"); } return true; } if (cmd == "vm-tcreset") { int64_t start = Timer::GetCurrentTimeMicros(); if (HPHP::VM::Transl::tx64->replace()) { string msg; Util::string_printf(msg, "Done %ld ms", (Timer::GetCurrentTimeMicros() - start) / 1000); transport->sendString(msg); } else { transport->sendString("Failed"); } return true; } return false; } /////////////////////////////////////////////////////////////////////////////// // Dump cache content extern bool const_dump(const char *filename); bool (*file_dump)(const char *filename) = nullptr; bool AdminRequestHandler::handleDumpCacheRequest(const std::string &cmd, Transport *transport) { if (cmd == "dump-const") { if (!RuntimeOption::EnableApc || !RuntimeOption::EnableConstLoad || RuntimeOption::ApcPrimeLibrary.empty()) { transport->sendString("No Constant Cache\n"); return true; } const_dump("/tmp/const_map_dump"); transport->sendString("Done"); return true; } if (cmd == "dump-apc") { if (!RuntimeOption::EnableApc) { transport->sendString("No APC\n"); return true; } string keyOnlyParam = transport->getParam("keyonly"); bool keyOnly = false; if (keyOnlyParam == "true" || keyOnlyParam == "1") { keyOnly = true; } int waitSeconds = transport->getIntParam("waitseconds"); if (!waitSeconds) { waitSeconds = RuntimeOption::RequestTimeoutSeconds > 0 ? RuntimeOption::RequestTimeoutSeconds : 10; } apc_dump("/tmp/apc_dump", keyOnly, waitSeconds); transport->sendString("Done"); return true; } if (cmd == "dump-file-repo") { if (file_dump) { (*file_dump)("/tmp/file_repo_dump"); } transport->sendString("Done"); return true; } return false; } /////////////////////////////////////////////////////////////////////////////// }