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);
}
}