void AdminRequestHandler::handleRequest()

in hphp/runtime/server/admin-request-handler.cpp [286:1003]


void AdminRequestHandler::handleRequest(Transport *transport) {
  transport->addHeader("Content-Type", "text/plain");
  std::string cmd = transport->getCommand();

  do {
    if (cmd == "" || cmd == "help") {
      string usage =
        "/stop:            stop the web server\n"
        "    instance-id   optional, if specified, instance ID has to match\n"
        "/oom-kill:        abort all requests whose memory usage exceed\n"
        "                  Server.RequestMemoryOOMKillBytes\n"
        "/free-mem:        ask allocator to release unused memory to system\n"
        "/prepare-to-stop: ask the server to prepare for stopping\n"
        "/flush-profile:   flush profiling counters (in -fprofile-gen builds)\n"
        "/flush-logs:      trigger batching log-writers to flush all content\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"
        "/instance-id:     instance id that's passed in from command line\n"
        "/compiler-id:     returns the compiler id that built this app\n"
        "/config-id:       returns the config id passed in from command line\n"
        "/repo-schema:     return the repo schema id used by this app\n"
        "/ini-get-all:     dump all settings as JSON\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-sql:       report SQL table statistics\n"
        "/check-sat        how many satellite threads are actively handling\n"
        "                  requests and queued waiting to be handled\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"

        "/rqtrace-stats:   show aggregate request trace stats in JSON\n"

        "/memory.xml:      show memory status in XML\n"
        "/memory.json:     show memory status in JSON\n"
        "/memory.html:     show memory status in HTML\n"

        "/statcache-clear: clear the stat cache entries\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-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.kvp:       show server stats in key-value pairs\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"

        "/xenon-snap:      generate a Xenon snapshot, which is logged later\n"
        "/hugepage:        show stats about hugepage usage\n"
        "/jit-des-info:    show information about deserialized profile data\n"

        "/static-strings:  get number of static strings\n"
        "/static-strings-rds: ... that correspond to defined constants\n"
        "/dump-static-strings: dump static strings to /tmp/static_strings\n"
        "/random-static-strings: return randomly selected static strings\n"
        "    count         number of strings to return, default 1\n"
        "/dump-apc:        dump all current value in APC to /tmp/apc_dump\n"
        "/dump-apc-prefix: dump a key prefix contents from APC to\n"
        "                  /tmp/apc_dump_prefix\n"
        "    prefix        required, the prefix to dump\n"
        "    count         optional, the number of keys to dump, default 1\n"
        "/dump-apc-info:   show basic APC stats\n"
        "/dump-apc-meta:   dump meta information for all objects in APC to\n"
        "                  /tmp/apc_dump_meta\n"
        "/random-apc:      dump the key and size of a random APC entry\n"
        "    count         number of entries to return\n"
        "/treadmill:       dump treadmill information\n"

        "/pcre-cache-size: get pcre cache map size\n"
        "/dump-pcre-cache: dump cached pcre's to /tmp/pcre_cache\n"
        "/dump-array-info: dump array tracer info to /tmp/array_tracer_dump\n"

        "/invalidate-units: remove specified files from the unit cache\n"
        "    path           absolute path of files to invalidate\n"

        "/start-stacktrace-profiler: set enable_stacktrace_profiler to true\n"
        "/relocate:        relocate translations\n"
        "    random        optional, default false, relocate random subset\n"
        "       all        optional, default false, relocate all translations\n"
        "      time        optional, default 20 (seconds)\n"
#ifdef HPHP_TRACE
        "/trace-request:   write trace for next request(s) to "
        + RuntimeOption::getTraceOutputFile() + "\n"
        "    spec          module:level,... spec; see hphp/util/trace.h\n"
        "    count         optional, total requests to trace (default: 1)\n"
        "    url           optional, trace only if URL contains \'url\'\n"
#endif
#ifdef GOOGLE_CPU_PROFILER
        "/prof-cpu-on:     turn on CPU profiler\n"
        "/prof-cpu-off:    turn off CPU profiler\n"
#endif
#ifdef EXECUTION_PROFILER
        "/prof-exe:        returns sampled execution profile\n"
#endif
        "/vm-tcspace:      show space used by translator caches\n"
        "/vm-tcaddr:       show addresses of translation cache sections\n"
        "/vm-dump-tc:      dump translation cache to /tmp/tc_dump_a and\n"
        "                  /tmp/tc_dump_astub\n"
        "/vm-namedentities:show size of the NamedEntityTable\n"
        "/proxy:           set up request proxy\n"
        "    origin        URL to proxy requests to\n"
        "    percentage    percentage of requests to proxy\n"
        "/load-factor:     get or set load factor\n"
        "    set           optional, set new load factor (default 1.0,\n"
        "                  valid range [-1.0, 10.0])\n"
        "/queue-discount:  get/set how much we discount the queue-length \n"
        "    set           optional, set discount value (default 0,\n"
        "                  valid range [0, 10000])\n"
        "/warmup-status:   Describes state of JIT warmup.\n"
        "                  Returns empty string if warmed up.\n"
        ;
#ifdef USE_TCMALLOC
        if (MallocExtensionInstance) {
          usage.append(
              "/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"
              "/jemalloc-prof-request:\n"
              "                  dump thread-local heap profile in\n"
              "                  the next request that runs\n"
              "    file          optional, filesystem path\n"
          );
#ifdef ENABLE_HHPROF
          usage.append(
              "/hhprof/start:    start profiling\n"
              "    requestType   \"all\" or \"next\"*\n"
              "    url           profile next matching url for \"next\"\n"
              "    lgSample      lg sample rate\n"
              "    profileType   \"current\"* or \"cumulative\"\n"
              "/hhprof/status:   configuration and current dump status\n"
              "/hhprof/stop:     stop profiling\n"
              "/pprof/cmdline:   program command line\n"
              "/pprof/heap:      heap dump\n"
              "/pprof/symbol:    symbol lookup\n"
          );
#endif // ENABLE_HHPROF
        }
#endif // USE_JEMALLOC

      AdminCommandExt::iterate([&](AdminCommandExt* ace) {
        usage.append(ace->usage());
        return false;
      });

      transport->sendString(usage);
      break;
    }

    bool needs_password = (cmd != "build-id") && (cmd != "compiler-id") &&
                          (cmd != "instance-id") && (cmd != "flush-logs") &&
                          (cmd != "warmup-status") && (cmd != "config-id")
#if defined(ENABLE_HHPROF) && defined(USE_JEMALLOC)
                          && (mallctl == nullptr || (
                                 (cmd != "hhprof/start")
                              && (cmd != "hhprof/status")
                              && (cmd != "hhprof/stop")
                              && (cmd != "pprof/cmdline")
                              && (cmd != "pprof/heap")
                              && (cmd != "pprof/symbol")))
#endif
                          ;
    // When configured, we allow read-only stats to be read without a password.
    if (needs_password && !RuntimeOption::AdminServerStatsNeedPassword) {
      if ((strncmp(cmd.c_str(), "memory.", 7) == 0) ||
          (strncmp(cmd.c_str(), "stats.", 6) == 0) ||
          (strncmp(cmd.c_str(), "check-", 6) == 0) ||
          (strncmp(cmd.c_str(), "static-strings", 14) == 0) ||
          cmd == "hugepage" || cmd == "pcre-cache-size" ||
          cmd == "vm-tcspace" || cmd == "vm-tcaddr" ||
          cmd == "vm-namedentities" || cmd == "jemalloc-stats") {
        needs_password = false;
      }
    }

    if (needs_password && !RuntimeOption::HashedAdminPasswords.empty()) {
      bool matched = false;
#ifdef HAVE_CRYPTO_PWHASH_STR
      const auto password = transport->getParam("auth");
      for (const std::string& hash : RuntimeOption::HashedAdminPasswords) {
        if (crypto_pwhash_str_verify(hash.data(),
                                     password.data(),
                                     password.size()) == 0) {
          matched = true;
          break;
        }
      }
#endif
      if (!matched) {
        transport->sendString("Unauthorized", 401);
        break;
      }
    } else if (needs_password && !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 (needs_password && !RuntimeOption::AdminPassword.empty() &&
          RuntimeOption::AdminPassword != transport->getParam("auth")) {
        transport->sendString("Unauthorized", 401);
        break;
      }
    }

    if (cmd == "stop") {
      string instanceId = transport->getParam("instance-id");
      if (!instanceId.empty() && instanceId != RuntimeOption::InstanceId) {
        transport->sendString("Instance ID doesn't match.", 500);
        break;
      }

      transport->sendString("OK\n");
      Logger::Info("Got admin port stop request from %s",
                   transport->getRemoteHost());
      HttpServer::Server->stop();
      break;
    }
    if (cmd == "oom-kill") {
      Logger::Info("Invoking OOM killer upon admin port request from %s",
                   transport->getRemoteHost());
      auto const server = HttpServer::Server->getPageServer();
      RequestInfo::InvokeOOMKiller(server->getActiveWorker());
      transport->sendString("OOM killer invoked");
      break;
    }
    if (cmd == "free-mem") {
      const auto before = Process::GetMemUsageMb();
      std::string errStr;
      if (purge_all(&errStr)) {
        const auto after = Process::GetMemUsageMb();
        transport->sendString(
          folly::sformat("Purged {} -> {} MB RSS", before, after).c_str());
      } else {
        transport->sendString(errStr.c_str(), 500);
      }
      break;
    }
    if (cmd == "prepare-to-stop") {
      Logger::Info("Got admin port prepare-to-stop request from %s",
                   transport->getRemoteHost());
      MemInfo info, newInfo;
      Process::GetMemoryInfo(info);
      HttpServer::PrepareToStop();

      // We may consider purge_all() here, too.  But since requests
      // are still coming in, it may not be very useful, and has some
      // performance penalties.

      // TODO: evaluate the effect of sync() and uncomment if
      // desirable.  It is blocking and can take some time, so do it
      // in a separate thread.
      // std::thread t{sync};
      // t.detach();

      transport->sendString("OK\n");
      Process::GetMemoryInfo(newInfo);
      Logger::FInfo("free/cached/buffer {}/{}/{} -> {}/{}/{}",
                    info.freeMb, info.cachedMb, info.buffersMb,
                    newInfo.freeMb, newInfo.cachedMb, newInfo.buffersMb);
      break;
    }
    if (cmd == "flush-profile") {
      HttpServer::ProfileFlush();
      // send the response *after* flushing, so the caller knows the
      // data has been updated.
      transport->sendString("OK\n");
      break;
    }
    if (cmd == "flush-logs") {
      transport->sendString("OK\n");
      Logger::FlushAll();
      HttpRequestHandler::GetAccessLog().flushAllWriters();
      AdminRequestHandler::GetAccessLog().flushAllWriters();
      RPCRequestHandler::GetAccessLog().flushAllWriters();
      break;
    }
    if (cmd == "set-log-level") {
      string result("OK\n");
      string level = transport->getParam("level");
      if (level == "None") {
        Logger::LogLevel = Logger::LogNone;
      } else if (level == "Error") {
        Logger::LogLevel = Logger::LogError;
      } else if (level == "Warning") {
        Logger::LogLevel = Logger::LogWarning;
      } else if (level == "Info") {
        Logger::LogLevel = Logger::LogInfo;
      } else if (level == "Verbose") {
        Logger::LogLevel = Logger::LogVerbose;
      } else {
        result = "Failed to set log level\n";
      }

      transport->sendString(result);
      break;
    }
#ifdef HPHP_TRACE
    if (cmd == "trace-request") {
      Trace::ensureInit(RuntimeOption::getTraceOutputFile());
      // Just discard any existing task.
      delete s_traceTask.exchange(
        new TraceTask{transport->getParam("spec"),
                      transport->getParam("url"),
                      std::max(transport->getInt64Param("count"), 1ll)},
        std::memory_order_acq_rel);
      transport->sendString("OK\n");
      break;
    }
#endif
    if (cmd == "build-id") {
      transport->sendString(RuntimeOption::BuildId, 200);
      break;
    }
    if (cmd == "instance-id") {
      transport->sendString(RuntimeOption::InstanceId, 200);
      break;
    }
    if (cmd == "compiler-id") {
      transport->sendString(compilerId().begin(), 200);
      break;
    }
    if (cmd == "config-id") {
      transport->sendString(std::to_string(RuntimeOption::ConfigId), 200);
      break;
    }
    if (cmd == "repo-schema") {
      transport->sendString(repoSchemaId().begin(), 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(),"memory", 6) == 0 &&
        handleMemoryRequest(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(), "dump-apc", 8) == 0 &&
        handleDumpCacheRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "invalidate-units", 16) == 0 &&
        handleInvalidateUnitRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "xenon-snap", 10) == 0) {
      static int64_t s_lastSampleTime = 0;
      auto const current = TimeStamp::Current();
      if (current > s_lastSampleTime) {
        s_lastSampleTime = current;
        Xenon::getInstance().surpriseAll();
      }
      transport->sendString("a Xenon sample will be collected\n", 200);
      break;
    }
    if (strncmp(cmd.c_str(), "hugepage", 9) == 0) {
#if USE_JEMALLOC_EXTENT_HOOKS
      std::string msg =
        folly::sformat("{} 1G huge pages active\n", num_1g_pages());
      if (auto a = alloc::lowArena()) {
        msg += a->reportStats();
      }
      if (auto a = alloc::highArena()) {
        msg += a->reportStats();
      }
      transport->sendString(msg, 200);
#else
      transport->sendString("", 200);
#endif
      break;
    }
    if (strncmp(cmd.c_str(), "jit-des-info", 13) == 0) {
      if (isJitSerializing() || !jit::ProfData::wasDeserialized()) {
        transport->sendString("", 200);
        break;
      }
      auto msg = folly::sformat("{}:{}",
                                jit::ProfData::buildHost()->slice(),
                                jit::ProfData::buildTime());
      transport->sendString(msg, 200);
    }

    if (strncmp(cmd.c_str(), "static-strings", 14) == 0 &&
        handleStaticStringsRequest(cmd, transport)) {
      break;
    }
    if (strncmp(cmd.c_str(), "dump-static-strings", 19) == 0) {
      if (auto file = dump_file("static_strings")) {
        handleDumpStaticStringsRequest(file->file);
        transport->sendString(folly::sformat("dumped to {}\n", file->path));
      } else {
        transport->sendString("Unable to mkdir or file already exists.\n");
      }
      break;
    }
    if (strncmp(cmd.c_str(), "random-static-strings", 21) == 0) {
      handleRandomStaticStringsRequest(cmd, transport);
      break;
    }
    if (strncmp(cmd.c_str(), "vm-", 3) == 0 &&
        handleVMRequest(cmd, transport)) {
      break;
    }
    if (cmd == "proxy") {
      handleProxyRequest(cmd, transport);
      break;
    }

    if (cmd == "statcache-clear") {
      StatCache::clearCache();
      break;
    }

    if (cmd == "pcre-cache-size") {
      std::ostringstream size;
      size << preg_pcre_cache_size() << endl;
      transport->sendString(size.str());
      break;
    }

    if (cmd == "dump-pcre-cache") {
      if (auto file = dump_file("pcre_cache")) {
        pcre_dump_cache(file->file);
        transport->sendString(folly::sformat("dumped to {}\n", file->path));
      } else {
        transport->sendString("Unable to mkdir or file already exists.\n");
      }
      break;
    }

    if (cmd == "start-stacktrace-profiler") {
      enable_stacktrace_profiler = true;
      transport->sendString("OK\n");
      break;
    }

    if (cmd == "warmup-status") {
      transport->sendString(jit::tc::warmupStatusString());
      break;
    }

    if (strncmp(cmd.c_str(), "random-apc", 10) == 0 &&
        handleRandomApcRequest(cmd, transport)) {
      break;
    }
    if (cmd == "treadmill") {
      transport->sendString(Treadmill::dumpTreadmillInfo());
      break;
    }

    if (cmd == "load-factor") {
      auto const factorStr = transport->getParam("set");
      if (factorStr.empty()) {
        std::ostringstream oss;
        oss << std::fixed << std::setprecision(3)
            << HttpServer::LoadFactor.load();
        transport->sendString(oss.str());
        break;
      }
      double factor = 1.0;
      if (sscanf(factorStr.c_str(), "%lf", &factor) < 1 ||
          factor > 10 || factor < -1) {
        transport->sendString("Invalid load factor spec: " + factorStr, 400);
        break;
      }
      HttpServer::LoadFactor.store(factor, std::memory_order_relaxed);
      transport->sendString(folly::sformat("Load factor updated to {}\n",
                                           factor));
      Logger::Info("Load factor updated to %lf", factor);
      break;
    }
    if (cmd == "queue-discount") {
      auto const discountStr = transport->getParam("set");
      if (discountStr.empty()) {
        transport->sendString(folly::to<string>(
          HttpServer::QueueDiscount.load(std::memory_order_relaxed)));
        break;
      }
      int queue_discount = 0;
      if (sscanf(discountStr.c_str(), "%d", &queue_discount) < 1 ||
          queue_discount > 10000 || queue_discount < 0) {
        transport->sendString("Invalid queue discount spec: " +
          discountStr, 400);
        break;
      }
      HttpServer::QueueDiscount.store(queue_discount,
        std::memory_order_relaxed);
      transport->sendString(folly::sformat("Queue Discount updated to {}\n",
        queue_discount));
      Logger::Info("Queue Discount updated to %d", queue_discount);
      break;
    }
    if (cmd == "ini-get-all") {
      auto out = IniSetting::GetAllAsJSON();
      transport->sendString(out.c_str());
      break;
    }

    if (cmd == "numa-info") {
      std::ostringstream out;
#ifdef HAVE_NUMA
      out << "use_numa: " << use_numa << endl;
      out << "numa_num_nodes: " << numa_num_nodes << endl;
      out << "numa_node_mask: " << numa_node_mask << endl;
      out << "numa_node_set: " << numa_node_set << endl;
#else
      out << "HAVE_NUMA not defined" << endl;
#endif
      transport->sendString(out.str());
      break;
    }

#ifdef USE_JEMALLOC
    assertx(mallctlnametomib && mallctlbymib);
    if (cmd == "jemalloc-stats") {
      // jemalloc stats update is periodically triggered in the
      // host-health-monitor thread.
      uint32_t error = 0;

      auto call_mallctl = [&](const char* statName) {
        size_t value = 0;
        if (mallctlRead<size_t, true>(statName, &value) != 0) {
          error = 1;
        }
        return value;
      };
      size_t allocated = call_mallctl("stats.allocated");
      size_t active = call_mallctl("stats.active");
      size_t mapped = call_mallctl("stats.mapped");
      auto const low_mapped = alloc::getLowMapped();

      std::ostringstream stats;
      stats << "<jemalloc-stats>" << endl;
      stats << "  <allocated>" << allocated << "</allocated>" << endl;
      stats << "  <active>" << active << "</active>" << endl;
      stats << "  <mapped>" << mapped << "</mapped>" << endl;
      stats << "  <low_mapped>" << low_mapped << "</low_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 == nullptr ? "" : mwo.s);
      malloc_write_fini(&mwo);
      break;
    }
    if (cmd == "jemalloc-prof-activate") {
      if (jemalloc_pprof_enable()) {
        transport->sendString("Error in mallctl(\"prof.active\", true)\n");
      } else {
        transport->sendString("OK\n");
      }
      break;
    }
    if (cmd == "jemalloc-prof-deactivate") {
      if (jemalloc_pprof_disable()) {
        transport->sendString("Error in mallctl(\"prof.active\", false)\n");
      } else {
        transport->sendString("OK\n");
      }
      break;
    }
    if (cmd == "jemalloc-prof-dump") {
      string f = transport->getParam("file");
      if (jemalloc_pprof_dump(f, true)) {
        transport->sendString("Error in mallctl(\"prof.dump\", " + f + ")\n");
      } else {
        transport->sendString("OK\n");
      }
      break;
    }
    if (cmd == "jemalloc-prof-request") {
      auto f = transport->getParam("file");
      bool success = MemoryManager::triggerProfiling(f);

      if (success) {
        transport->sendString("OK\n");
      } else {
        transport->sendString("Request profiling already triggered\n");
      }
      break;
    }
#ifdef ENABLE_HHPROF
    if (cmd == "hhprof/start") {
      HHProf::HandleHHProfStart(transport);
      break;
    }
    if (cmd == "hhprof/status") {
      HHProf::HandleHHProfStatus(transport);
      break;
    }
    if (cmd == "hhprof/stop") {
      HHProf::HandleHHProfStop(transport);
      break;
    }
    if (cmd == "pprof/cmdline") {
      HHProf::HandlePProfCmdline(transport);
      break;
    }
    if (cmd == "pprof/heap") {
      HHProf::HandlePProfHeap(transport);
      break;
    }
    if (cmd == "pprof/symbol") {
      HHProf::HandlePProfSymbol(transport);
      break;
    }
#endif // ENABLE_HHPROF
#endif // USE_JEMALLOC

    if (cmd == "rqtrace-stats") {
      std::stringstream out;
      bool first = true;
      out << "{" << endl;
      auto appendStat =
        [&](folly::StringPiece name, folly::StringPiece kind, int64_t value) {
          out << folly::format(
            "{} \"{}_{}\":{}\n", first ? "" : ",", name, kind, value);
          first = false;
        };
      rqtrace::visit_process_stats(
        [&] (const StringData* name, rqtrace::EventStats stats) {
          appendStat(name->data(), "duration", stats.total_duration);
          appendStat(name->data(), "count", stats.total_count);
        }
      );
      out << "}" << endl;
      transport->sendString(out.str());
      break;
    }

    if (AdminCommandExt::iterate([&](AdminCommandExt* ace) {
          return ace->handleRequest(transport);
       })) {
      break;
    }

    transport->sendString("Unknown command: " + cmd + "\n", 404);
  } while (0);
  transport->onSendEnd();
}