static void StartProfiling()

in src/brpc/builtin/hotspots_service.cpp [849:1182]


static void StartProfiling(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());
    butil::IOBufBuilder os;
    bool enabled = false;
    const char* extra_desc = "";
    if (type == PROFILING_CPU) {
        enabled = cpu_profiler_enabled;
    } else if (type == PROFILING_CONTENTION) {
        enabled = true;
    } else if (type == PROFILING_IOBUF) {
        enabled = butil::IsIOBufProfilerEnabled();
        if (!enabled) {
            extra_desc = " (no ENABLE_IOBUF_PROFILER=1 in env or no link tcmalloc )";
        }
    }  else if (type == PROFILING_HEAP) {
        enabled = IsHeapProfilerEnabled();
        if (enabled && !has_TCMALLOC_SAMPLE_PARAMETER()) {
            enabled = false;
            extra_desc = " (no TCMALLOC_SAMPLE_PARAMETER in env)";
        }
    } else if (type == PROFILING_GROWTH) {
        enabled = IsHeapProfilerEnabled();
    }
    const char* const type_str = ProfilingType2String(type);

#if defined(OS_MACOSX)
    if (!has_GOOGLE_PPROF_BINARY_PATH()) {
        enabled = false;
        extra_desc = "(no GOOGLE_PPROF_BINARY_PATH in env)";
    }
#endif
    
    if (!use_html) {
        if (!enabled) {
            os << "Error: " << type_str << " profiler is not enabled."
               << extra_desc << "\n"
                "Read the docs: docs/cn/{cpu_profiler.md,heap_profiler.md}\n";
            os.move_to(cntl->response_attachment());
            cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
            return;
        }
        // Console can only use non-responsive version, namely the curl
        // blocks until profiling is done.
        return DoProfiling(type, cntl, done_guard.release());
    }

    const int seconds = ReadSeconds(cntl);
    const std::string* view = cntl->http_request().uri().GetQuery("view");
    const bool show_ccount = cntl->http_request().uri().GetQuery("ccount");
    const std::string* base_name = cntl->http_request().uri().GetQuery("base");
    const std::string* display_type_query = cntl->http_request().uri().GetQuery("display_type");
    DisplayType display_type = DisplayType::kDot;
    if (display_type_query) {
        display_type = StringToDisplayType(*display_type_query);
        if (display_type == DisplayType::kUnknown) {
            return cntl->SetFailed(EINVAL, "Invalid display_type=%s", display_type_query->c_str());
        }
#if defined(OS_LINUX)
        const char* flamegraph_tool = getenv("FLAMEGRAPH_PL_PATH");
        if (display_type == DisplayType::kFlameGraph && !flamegraph_tool) {
            return cntl->SetFailed(EINVAL, "Failed to find environment variable "
                "FLAMEGRAPH_PL_PATH, please read cpu_profiler doc"
                "(https://github.com/apache/brpc/blob/master/docs/cn/cpu_profiler.md)");
        }
#endif
    }

    ProfilingClient profiling_client;
    size_t nwaiters = 0;
    ProfilingEnvironment & env = g_env[type];
    if (view == NULL) {
        BAIDU_SCOPED_LOCK(env.mutex);
        if (env.client) {
            profiling_client = *env.client;
            nwaiters = (env.waiters ? env.waiters->size() : 0);
        }
    }
    
    cntl->http_response().set_content_type("text/html");
    os << "<!DOCTYPE html><html><head>\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"
        "<script type=\"text/javascript\">\n"
        "function generateURL() {\n"
        "  var past_prof = document.getElementById('view_prof').value;\n"
          "  var base_prof_el = document.getElementById('base_prof');\n"
          "  var base_prof = base_prof_el != null ? base_prof_el.value : '';\n"
        "  var display_type = document.getElementById('display_type').value;\n";
    if (type == PROFILING_CONTENTION) {
        os << "  var show_ccount = document.getElementById('ccount_cb').checked;\n";
    }
    os << "  var targetURL = '/hotspots/" << type_str << "';\n"
        "  targetURL += '?display_type=' + display_type;\n"
        "  if (past_prof != '') {\n"
        "    targetURL += '&view=' + past_prof;\n"
        "  }\n"
        "  if (base_prof != '') {\n"
        "    targetURL += '&base=' + base_prof;\n"
        "  }\n";
    if (type == PROFILING_CONTENTION) {
        os <<
        "  if (show_ccount) {\n"
        "    targetURL += '&ccount';\n"
        "  }\n";
    }
    os << "  return targetURL;\n"
        "}\n"
        "$(function() {\n"
        "  function onDataReceived(data) {\n";
    if (view == NULL) {
        os <<
        "    var selEnd = data.indexOf('[addToProfEnd]');\n"
        "    if (selEnd != -1) {\n"
        "      var sel = document.getElementById('view_prof');\n"
        "      var option = document.createElement('option');\n"
        "      option.value = data.substring(0, selEnd);\n"
        "      option.text = option.value;\n"
        "      var slash_index = option.value.lastIndexOf('/');\n"
        "      if (slash_index != -1) {\n"
        "        option.text = option.value.substring(slash_index + 1);\n"
        "      }\n"
        "      var option1 = sel.options[1];\n"
        "      if (option1 == null || option1.text != option.text) {\n"
        "        sel.add(option, 1);\n"
        "      } else if (option1 != null) {\n"
        "        console.log('merged ' + option.text);\n"
        "      }\n"
        "      sel.selectedIndex = 1;\n"
        "      window.history.pushState('', '', generateURL());\n"
        "      data = data.substring(selEnd + '[addToProfEnd]'.length);\n"
        "   }\n";
    }
    os <<
        "    var index = data.indexOf('digraph ');\n"
        "    if (index == -1) {\n"
        "      var selEnd = data.indexOf('[addToProfEnd]');\n"
        "      if (selEnd != -1) {\n"
        "        data = data.substring(selEnd + '[addToProfEnd]'.length);\n"
        "      }\n"
        "      $(\"#profiling-result\").html('<pre>' + data + '</pre>');\n"
        "      if (data.indexOf('FlameGraph') != -1) { init(); }"
        "    } else {\n"
        "      $(\"#profiling-result\").html('Plotting ...');\n"
        "      var svg = Viz(data.substring(index), \"svg\");\n"
        "      $(\"#profiling-result\").html(svg);\n"
        "    }\n"
        "  }\n"
        "  function onErrorReceived(xhr, ajaxOptions, thrownError) {\n"
        "    $(\"#profiling-result\").html(xhr.responseText);\n"
        "  }\n"
        "  $.ajax({\n"
        "    url: \"/hotspots/" << type_str << "_non_responsive?console=1";
    if (type == PROFILING_CPU || type == PROFILING_CONTENTION) {
        os << "&seconds=" << seconds;
    }
    if (profiling_client.id != 0) {
        os << "&profiling_id=" << profiling_client.id;
    }
    os << "&display_type=" << DisplayTypeToString(display_type);
    if (show_ccount) {
        os << "&ccount";
    }
    if (view) {
        os << "&view=" << *view;
    }
    if (base_name) {
        os << "&base=" << *base_name;
    }
    os << "\",\n"
        "    type: \"GET\",\n"
        "    dataType: \"html\",\n"
        "    success: onDataReceived,\n"
        "    error: onErrorReceived\n"
        "  });\n"
        "});\n"
        "function onSelectProf() {\n"
        "  window.location.href = generateURL();\n"
        "}\n"
        "function onChangedCB(cb) {\n"
        "  onSelectProf();\n"
        "}\n"
        "</script>\n"
        "</head>\n"
        "<body>\n";
    cntl->server()->PrintTabsBody(os, type_str);

    TRACEPRINTF("Begin to enumerate profiles");
    std::vector<std::string> past_profs;
    butil::FilePath prof_dir(FLAGS_rpc_profiling_dir);
    prof_dir = prof_dir.Append(GetProgramChecksum());
    std::string file_pattern;
    file_pattern.reserve(15);
    file_pattern.append("*.");
    file_pattern.append(type_str);
    butil::FileEnumerator prof_enum(prof_dir, false/*non recursive*/,
                                   butil::FileEnumerator::FILES,
                                   file_pattern);
    std::string file_path;
    for (butil::FilePath name = prof_enum.Next(); !name.empty();
         name = prof_enum.Next()) {
        // NOTE: name already includes dir.
        if (past_profs.empty()) {
            past_profs.reserve(16);
        }
        past_profs.push_back(name.value());
    }
    if (!past_profs.empty()) {
        TRACEPRINTF("Sort %lu profiles in decending order", past_profs.size());
        std::sort(past_profs.begin(), past_profs.end(), std::greater<std::string>());
        int max_profiles = FLAGS_max_profiles_kept/*may be reloaded*/;
        if (max_profiles < 0) {
            max_profiles = 0;
        }
        if (past_profs.size() > (size_t)max_profiles) {
            TRACEPRINTF("Remove %lu profiles",
                        past_profs.size() - (size_t)max_profiles);
            for (size_t i = max_profiles; i < past_profs.size(); ++i) {
                CHECK(butil::DeleteFile(butil::FilePath(past_profs[i]), false));
                std::string cache_path;
                cache_path.reserve(past_profs[i].size() + 7);
                cache_path += past_profs[i];
                cache_path += ".cache";
                CHECK(butil::DeleteFile(butil::FilePath(cache_path), true));
            }
            past_profs.resize(max_profiles);
        }
    }
    TRACEPRINTF("End enumeration");

    os << "<pre style='display:inline'>View: </pre>"
        "<select id='view_prof' onchange='onSelectProf()'>";
    os << "<option value=''>&lt;new profile&gt;</option>";
    for (size_t i = 0; i < past_profs.size(); ++i) {
        os << "<option value='" << past_profs[i] << "' ";
        if (view != NULL && past_profs[i] == *view) {
            os << "selected";
        }
        os << '>' << GetBaseName(&past_profs[i]);
    }
    os << "</select>";
    os << "<div><pre style='display:inline'>Display: </pre>"
        "<select id='display_type' onchange='onSelectProf()'>"
        "<option value=dot" << (display_type == DisplayType::kDot ? " selected" : "") << ">dot</option>"
#if defined(OS_LINUX)
        "<option value=flame" << (display_type == DisplayType::kFlameGraph ? " selected" : "") << ">flame</option>"
#endif
        "<option value=text" << (display_type == DisplayType::kText ? " selected" : "") << ">text</option></select>";
    if (type == PROFILING_CONTENTION) {
        os << "&nbsp;&nbsp;&nbsp;<label for='ccount_cb'>"
            "<input id='ccount_cb' type='checkbox'"
           << (show_ccount ? " checked=''" : "") <<
            " onclick='onChangedCB(this);'>count</label>";
    }
    if (type != PROFILING_IOBUF) {
        os << "</div><div><pre style='display:inline'>Diff: </pre>"
              "<select id='base_prof' onchange='onSelectProf()'>"
              "<option value=''>&lt;none&gt;</option>";
        for (size_t i = 0; i<past_profs.size(); ++i) {
            os << "<option value='" << past_profs[i] << "' ";
            if (base_name!=NULL && past_profs[i]==*base_name) {
                os << "selected";
            }
            os << '>' << GetBaseName(&past_profs[i]);
        }
        os << "</select></div>";
    }
    
    if (!enabled && view == NULL) {
        os << "<p><span style='color:red'>Error:</span> "
           << type_str << " profiler is not enabled." << extra_desc << "</p>"
            "<p>To enable all profilers, link tcmalloc and define macros BRPC_ENABLE_CPU_PROFILER"
            "</p><p>Or read docs: <a href='https://github.com/apache/brpc/blob/master/docs/cn/cpu_profiler.md'>cpu_profiler</a>"
            " and <a href='https://github.com/apache/brpc/blob/master/docs/cn/heap_profiler.md'>heap_profiler</a>"
            "</p></body></html>";
        os.move_to(cntl->response_attachment());
        cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
        return;
    }

    if ((type == PROFILING_CPU || type == PROFILING_CONTENTION) && view == NULL) {
        if (seconds < 0) {
            os << "Invalid seconds</body></html>";
            os.move_to(cntl->response_attachment());
            cntl->http_response().set_status_code(HTTP_STATUS_BAD_REQUEST);
            return;
        }
    }

    if (nwaiters >= CONCURRENT_PROFILING_LIMIT) {
        os << "Your profiling request is rejected because of "
            "too many concurrent profiling requests</body></html>";
        os.move_to(cntl->response_attachment());
        cntl->http_response().set_status_code(HTTP_STATUS_SERVICE_UNAVAILABLE);
        return;
    }

    os << "<div id=\"profiling-result\">";
    if (profiling_client.seconds != 0) {
        const int wait_seconds =
            (int)ceil((profiling_client.end_us - butil::cpuwide_time_us())
                      / 1000000.0);
        os << "Your request is merged with the request from "
           << profiling_client.point;
        if (type == PROFILING_CPU || type == PROFILING_CONTENTION) {
            os << ", showing in about " << wait_seconds << " seconds ...";
        }
    } else {
        if ((type == PROFILING_CPU || type == PROFILING_CONTENTION) && view == NULL) {
            os << "Profiling " << ProfilingType2String(type) << " for "
               << seconds << " seconds ...";
        } else {
            os << "Generating " << type_str << " profile ...";
        }
    }
    os << "</div><pre class='logo'><span class='logo_text'>" << logo()
       << "</span></pre></body>\n";
    if (display_type == DisplayType::kDot) {
        // don't need viz.js in text mode.
        os << "<script language=\"javascript\" type=\"text/javascript\""
            " src=\"/js/viz_min\"></script>\n";
    }
    os << "</html>";
    os.move_to(resp);
}