void AdminRequestHandler::handleRequest(Transport *transport) { GetAccessLog().onNewRequest(); 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" #ifdef COMPILER_ID "/compiler-id: returns the compiler id that built this app\n" #endif "/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-mem: report memory quick statistics in log file\n" "/check-apc: report APC quick statistics\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" #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 ; #ifndef NO_TCMALLOC if (MallocExtensionInstance) { usage.append( "/free-mem: ask tcmalloc to release memory to system\n" "/tcmalloc-stats: get internal tcmalloc stats\n" ); } #endif #ifndef NO_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" ); } transport->sendString(usage); break; } #endif if (!RuntimeOption::AdminPassword.empty() && RuntimeOption::AdminPassword != transport->getParam("auth")) { transport->sendString("Unauthorized", 401); break; } if (cmd == "stop") { transport->sendString("OK\n"); HttpServer::Server->stop(); break; } if (cmd == "build-id") { transport->sendString(RuntimeOption::BuildId, 200); break; } #ifdef COMPILER_ID if (cmd == "compiler-id") { transport->sendString(COMPILER_ID, 200); break; } #endif 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; } #ifndef NO_TCMALLOC if (MallocExtensionInstance) { if (cmd == "free-mem") { MallocExtensionInstance()->ReleaseFreeMemory(); transport->sendString("OK\n"); break; } if (cmd == "tcmalloc-stats") { ostringstream stats; size_t user_allocated, heap_size, slack_bytes; MallocExtensionInstance()-> GetNumericProperty("generic.current_allocated_bytes", &user_allocated); MallocExtensionInstance()-> GetNumericProperty("generic.heap_size", &heap_size); MallocExtensionInstance()-> GetNumericProperty("tcmalloc.slack_bytes", &slack_bytes); 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 << "</tcmalloc-stats>" << endl; transport->sendString(stats.str()); break; } } #endif #ifndef NO_JEMALLOC if (mallctl) { if (cmd == "jemalloc-stats") { // Force jemalloc to update stats cached for use by mallctl(). uint64_t epoch = 1; mallctl("epoch", NULL, NULL, &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, NULL, 0); size_t active = 0; mallctl("stats.active", &active, &sz, NULL, 0); size_t mapped = 0; mallctl("stats.mapped", &mapped, &sz, NULL, 0); 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") { char *buf = (char *)malloc(MALLOC_WRITE_CB_BUFLEN); if (buf == NULL) { transport->sendString("OOM\n"); break; } buf[0] = '\0'; malloc_stats_print(malloc_write_cb, (void *)buf, ""); transport->sendString(buf); free(buf); break; } if (cmd == "jemalloc-prof-activate") { bool active = true; int err = mallctl("prof.active", NULL, NULL, &active, sizeof(bool)); if (err) { 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", NULL, NULL, &active, sizeof(bool)); if (err) { 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", NULL, NULL, (void *)&s, sizeof(char *)); if (err) { ostringstream estr; estr << "Error " << err << " in mallctl(\"prof.dump\", ..., \"" << f << "\", ...)" << endl; transport->sendString(estr.str()); break; } } else { int err = mallctl("prof.dump", NULL, NULL, NULL, 0); if (err) { 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); }
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-tcreset: throw away translations and start over\n" "/vm-namedentities:show size of the NamedEntityTable\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); }