static void DoProfiling()

in src/brpc/builtin/hotspots_service.cpp [609:847]


static void DoProfiling(ProfilingType type,
                        ::google::protobuf::RpcController* cntl_base,
                        ::google::protobuf::Closure* done) {
    ClosureGuard done_guard(done);
    Controller *cntl = static_cast<Controller*>(cntl_base);
    butil::IOBuf& resp = cntl->response_attachment();
    const bool use_html = UseHTML(cntl->http_request());
    cntl->http_response().set_content_type(use_html ? "text/html" : "text/plain");

    butil::IOBufBuilder os;
    if (use_html) {
        os << "<!DOCTYPE html><html><head>\n"
            "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
            "<script language=\"javascript\" type=\"text/javascript\" src=\"/js/jquery_min\"></script>\n"
            << TabsHead()
            << "<style type=\"text/css\">\n"
            ".logo {position: fixed; bottom: 0px; right: 0px; }\n"
            ".logo_text {color: #B0B0B0; }\n"
            "</style>\n"
            "</head>\n"
            "<body>\n";
        cntl->server()->PrintTabsBody(os, ProfilingType2String(type));
    }

    const std::string* view = cntl->http_request().uri().GetQuery("view");
    if (view) {
        if (!ValidProfilePath(*view)) {
            return cntl->SetFailed(EINVAL, "Invalid query `view'");
        }
        if (!butil::PathExists(butil::FilePath(*view))) {
            return cntl->SetFailed(
                EINVAL, "The profile denoted by `view' does not exist");
        }
        DisplayResult(cntl, done_guard.release(), view->c_str(), os.buf(), type);
        return;
    }

    const int seconds = ReadSeconds(cntl);
    if ((type == PROFILING_CPU || type == PROFILING_CONTENTION)) {
        if (seconds < 0) {
            os << "Invalid seconds" << (use_html ? "</body></html>" : "\n");
            os.move_to(cntl->response_attachment());
            cntl->http_response().set_status_code(HTTP_STATUS_BAD_REQUEST);
            return;
        }
    }

    // Log requester
    std::ostringstream client_info;
    client_info << cntl->remote_side();
    if (cntl->auth_context()) {
        client_info << "(auth=" << cntl->auth_context()->user() << ')';
    } else {
        client_info << "(no auth)";
    }
    client_info << " requests for profiling " << ProfilingType2String(type);
    if (type == PROFILING_CPU || type == PROFILING_CONTENTION) {
        LOG(INFO) << client_info.str() << " for " << seconds << " seconds";
    } else {
        LOG(INFO) << client_info.str();
    }
    int64_t prof_id = 0;
    const std::string* prof_id_str =
        cntl->http_request().uri().GetQuery("profiling_id");
    if (prof_id_str != NULL) {
        char* endptr = NULL;
        prof_id = strtoll(prof_id_str->c_str(), &endptr, 10);
        LOG_IF(ERROR, *endptr != '\0') << "Invalid profiling_id=" << prof_id;
    }

    {
        BAIDU_SCOPED_LOCK(g_env[type].mutex);
        if (g_env[type].client) {
            if (NULL == g_env[type].waiters) {
                g_env[type].waiters = new std::vector<ProfilingWaiter>;
            }
            ProfilingWaiter waiter = { cntl, done_guard.release() };
            g_env[type].waiters->push_back(waiter);
            RPC_VLOG << "Queue request from " << cntl->remote_side();
            return;
        }
        if (g_env[type].cached_result != NULL &&
            g_env[type].cached_result->id == prof_id) {
            cntl->http_response().set_status_code(
                g_env[type].cached_result->status_code);
            cntl->response_attachment().append(
                g_env[type].cached_result->result);
            RPC_VLOG << "Hit cached result, id=" << prof_id;
            return;
        }
        CHECK(NULL == g_env[type].client);
        g_env[type].client = new ProfilingClient;
        g_env[type].client->end_us = butil::cpuwide_time_us() + seconds * 1000000L;
        g_env[type].client->seconds = seconds;
        // This id work arounds an issue of chrome (or jquery under chrome) that
        // the ajax call in another tab may be delayed until ajax call in
        // current tab finishes. We assign a increasing-only id to each
        // profiling and save last profiling result along with the assigned id.
        // If the delay happens, the viewr should send the ajax call with an
        // id matching the id in cached result, then the result will be returned
        // directly instead of running another profiling which may take long
        // time.
        if (0 == ++ g_env[type].cur_id) { // skip 0
            ++ g_env[type].cur_id;
        }
        g_env[type].client->id = g_env[type].cur_id;
        g_env[type].client->point = cntl->remote_side();
    }

    RPC_VLOG << "Apply request from " << cntl->remote_side();

    char prof_name[128];
    if (MakeProfName(type, prof_name, sizeof(prof_name)) != 0) {
        os << "Fail to create prof name: " << berror()
           << (use_html ? "</body></html>" : "\n");
        os.move_to(resp);
        cntl->http_response().set_status_code(HTTP_STATUS_INTERNAL_SERVER_ERROR);
        return NotifyWaiters(type, cntl, view);
    }

#if defined(OS_MACOSX)
    if (!has_GOOGLE_PPROF_BINARY_PATH()) {
        os << "no GOOGLE_PPROF_BINARY_PATH in env"
           << (use_html ? "</body></html>" : "\n");
        os.move_to(resp);
        cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
        return NotifyWaiters(type, cntl, view);
    }
#endif
    if (type == PROFILING_CPU) {
        if ((void*)ProfilerStart == NULL || (void*)ProfilerStop == NULL) {
            os << "CPU profiler is not enabled"
               << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
            return NotifyWaiters(type, cntl, view);
        }
        butil::File::Error error;
        const butil::FilePath dir = butil::FilePath(prof_name).DirName();
        if (!butil::CreateDirectoryAndGetError(dir, &error)) {
            os << "Fail to create directory=`" << dir.value() << ", "
               << error << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(
                HTTP_STATUS_INTERNAL_SERVER_ERROR);
            return NotifyWaiters(type, cntl, view);
        }
        if (!ProfilerStart(prof_name)) {
            os << "Another profiler (not via /hotspots/cpu) is running, "
                "try again later" << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(HTTP_STATUS_SERVICE_UNAVAILABLE);
            return NotifyWaiters(type, cntl, view);
        }
        if (bthread_usleep(seconds * 1000000L) != 0) {
            PLOG(WARNING) << "Profiling has been interrupted";
        }
        ProfilerStop();
    } else if (type == PROFILING_CONTENTION) {
        if (!bthread::ContentionProfilerStart(prof_name)) {
            os << "Another profiler (not via /hotspots/contention) is running, "
                "try again later" << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(HTTP_STATUS_SERVICE_UNAVAILABLE);
            return NotifyWaiters(type, cntl, view);
        }
        if (bthread_usleep(seconds * 1000000L) != 0) {
            PLOG(WARNING) << "Profiling has been interrupted";
        }
        bthread::ContentionProfilerStop();
    } else if (type == PROFILING_IOBUF) {
        if (!butil::IsIOBufProfilerEnabled()) {
            os << "IOBuf profiler is not enabled"
               << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
            return NotifyWaiters(type, cntl, view);
        }
        butil::IOBufProfilerFlush(prof_name);
    } else if (type == PROFILING_HEAP) {
        MallocExtension* malloc_ext = MallocExtension::instance();
        if (malloc_ext == NULL || !has_TCMALLOC_SAMPLE_PARAMETER()) {
            os << "Heap profiler is not enabled";
            if (malloc_ext != NULL) {
                os << " (no TCMALLOC_SAMPLE_PARAMETER in env)";
            }
            os << '.' << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
            return NotifyWaiters(type, cntl, view);
        }
        std::string obj;
        malloc_ext->GetHeapSample(&obj);
        if (!WriteSmallFile(prof_name, obj)) {
            os << "Fail to write " << prof_name
               << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(
                HTTP_STATUS_INTERNAL_SERVER_ERROR);
            return NotifyWaiters(type, cntl, view);
        }
    } else if (type == PROFILING_GROWTH) {
        MallocExtension* malloc_ext = MallocExtension::instance();
        if (malloc_ext == NULL) {
            os << "Growth profiler is not enabled."
               << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
            return NotifyWaiters(type, cntl, view);
        }
        std::string obj;
        malloc_ext->GetHeapGrowthStacks(&obj);
        if (!WriteSmallFile(prof_name, obj)) {
            os << "Fail to write " << prof_name
               << (use_html ? "</body></html>" : "\n");
            os.move_to(resp);
            cntl->http_response().set_status_code(
                HTTP_STATUS_INTERNAL_SERVER_ERROR);
            return NotifyWaiters(type, cntl, view);
        }
    } else {
        os << "Unknown ProfilingType=" << type
           << (use_html ? "</body></html>" : "\n");
        os.move_to(resp);
        cntl->http_response().set_status_code(
            HTTP_STATUS_INTERNAL_SERVER_ERROR);
        return NotifyWaiters(type, cntl, view);
    }

    std::vector<ProfilingWaiter> waiters;
    // NOTE: Must be called before DisplayResult which calls done->Run() and
    // deletes cntl.
    ConsumeWaiters(type, cntl, &waiters);
    DisplayResult(cntl, done_guard.release(), prof_name, os.buf(), type);

    for (size_t i = 0; i < waiters.size(); ++i) {
        DisplayResult(waiters[i].cntl, waiters[i].done, prof_name, os.buf(), type);
    }
}