Arquivos
hhvm/hphp/runtime/base/server/admin_request_handler.cpp
T
jdelong d432f19ae1 Allocate unit bytecode and metadata in a read-only memory region
Only in RepoAuthoritative mode, where units can't be
deallocated.  I have a semi-reproducable bug where a unit's m_bc
region is getting corrupted (only occasionally in perflab).
Presumably moving it out of the malloc'd heap will make the bug
corrupt something else, so this probably doesn't really help much, but
it seemed like having a separate region for some cold, read-only,
process-lifetime metadata might make sense.
2013-04-12 11:22:45 -07:00

1068 linhas
35 KiB
C++

/*
+----------------------------------------------------------------------+
| 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 <sstream>
#include <iomanip>
#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 <util/timer.h>
#include "util/repo_schema.h"
#include "runtime/ext/ext_fb.h"
#include "runtime/ext/ext_apc.h"
#ifdef GOOGLE_CPU_PROFILER
#include <google/profiler.h>
#endif
#ifdef GOOGLE_HEAP_PROFILER
#include <google/heap-profiler.h>
#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, <timestamp>, or <-n> second ago\n"
" to optional, <timestamp>, or <-n> second ago\n"
"/stats.xml: show server stats in XML\n"
" from optional, <timestamp>, or <-n> second ago\n"
" to optional, <timestamp>, or <-n> second ago\n"
" agg optional, aggragation: *, url, code\n"
" keys optional, <key>,<key/hit>,<key/sec>,<: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 <keysample>\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<std::string>::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 << "<tcmalloc-stats>" << endl;
stats << " <user_allocated>" << user_allocated << "</user_allocated>"
<< endl;
stats << " <heap_size>" << heap_size << "</heap_size>" << endl;
stats << " <slack_bytes>" << slack_bytes << "</slack_bytes>" << endl;
stats << " <pageheap_free>" << pageheap_free
<< "</pageheap_free>" << endl;
stats << " <pageheap_unmapped>" << pageheap_unmapped
<< "</pageheap_unmapped>" << endl;
stats << " <thread_cache_max>" << tc_max
<< "</thread_cache_max>" << endl;
stats << " <thread_cache_allocated>" << tc_allocated
<< "</thread_cache_allocated>" << endl;
stats << "</tcmalloc-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 << "<jemalloc-stats>" << endl;
stats << " <allocated>" << allocated << "</allocated>" << endl;
stats << " <active>" << active << "</active>" << endl;
stats << " <mapped>" << mapped << "</mapped>" << endl;
stats << "</jemalloc-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<string>(count));
return true;
}
if (cmd == "check-ev") {
int count =
HttpServer::Server->getPageServer()->getLibEventConnectionCount();
transport->sendString(lexical_cast<string>(count));
return true;
}
if (cmd == "check-queued") {
int count = HttpServer::Server->getPageServer()->getQueuedJobs();
transport->sendString(lexical_cast<string>(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<string>(count));
return true;
}
if (cmd == "check-pl-queued") {
int count = PageletServer::GetQueuedJobs();
transport->sendString(lexical_cast<string>(count));
return true;
}
if (cmd == "check-mem") {
return toggle_switch(transport, RuntimeOption::CheckMemory);
}
if (cmd == "check-sql") {
string stats = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
stats += "<SQL>\n";
stats += MySqlStats::ReportStats();
stats += "</SQL>\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<ThreadInfo::Executing, int> counts;
ThreadInfo::GetExecutionSamples(counts);
string res = "[ ";
for (std::map<ThreadInfo::Executing, int>::const_iterator iter =
counts.begin(); iter != counts.end(); ++iter) {
res += lexical_cast<string>(iter->first) + ", " +
lexical_cast<string>(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<string> 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<string>(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<int, PCInfo> 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;
}
///////////////////////////////////////////////////////////////////////////////
}