in src/brpc/builtin/hotspots_service.cpp [400:607]
static void DisplayResult(Controller* cntl,
google::protobuf::Closure* done,
const char* prof_name,
const butil::IOBuf& result_prefix,
ProfilingType type) {
ClosureGuard done_guard(done);
butil::IOBuf prof_result;
if (cntl->IsCanceled()) {
// If the page is refreshed, older connections are likely to be
// already closed by browser.
return;
}
butil::IOBuf& resp = cntl->response_attachment();
const bool use_html = UseHTML(cntl->http_request());
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 defined(OS_LINUX)
const char* flamegraph_tool = getenv("FLAMEGRAPH_PL_PATH");
#endif
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)
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
}
if (base_name != NULL) {
if (!ValidProfilePath(*base_name)) {
return cntl->SetFailed(EINVAL, "Invalid query `base'");
}
if (!butil::PathExists(butil::FilePath(*base_name))) {
return cntl->SetFailed(
EINVAL, "The profile denoted by `base' does not exist");
}
}
butil::IOBufBuilder os;
os << result_prefix;
char expected_result_name[256];
MakeCacheName(expected_result_name, sizeof(expected_result_name),
prof_name, GetBaseName(base_name),
display_type, show_ccount);
// Try to read cache first.
FILE* fp = fopen(expected_result_name, "r");
if (fp != NULL) {
bool succ = false;
char buffer[1024];
while (1) {
size_t nr = fread(buffer, 1, sizeof(buffer), fp);
if (nr != 0) {
prof_result.append(buffer, nr);
}
if (nr != sizeof(buffer)) {
if (feof(fp)) {
succ = true;
break;
} else if (ferror(fp)) {
LOG(ERROR) << "Encountered error while reading for "
<< expected_result_name;
break;
}
// retry;
}
}
PLOG_IF(ERROR, fclose(fp) != 0) << "Fail to close fp";
if (succ) {
RPC_VLOG << "Hit cache=" << expected_result_name;
os.move_to(resp);
if (use_html) {
resp.append("<pre>");
}
resp.append(prof_result);
if (use_html) {
resp.append("</pre></body></html>");
}
return;
}
}
std::ostringstream cmd_builder;
std::string pprof_tool{GeneratePerlScriptPath(PPROF_FILENAME)};
#if defined(OS_LINUX)
cmd_builder << "perl " << pprof_tool
<< DisplayTypeToPProfArgument(display_type)
<< ((show_ccount || type == PROFILING_IOBUF) ? " --contention " : "");
if (base_name) {
cmd_builder << "--base " << *base_name << ' ';
}
cmd_builder << GetProgramPath() << " " << prof_name;
if (display_type == DisplayType::kFlameGraph) {
// For flamegraph, we don't care about pprof error msg,
// which will cause confusing messages in the final result.
cmd_builder << " 2>/dev/null | perl " << flamegraph_tool << " --width "
<< (FLAGS_max_flame_graph_width > 0 ? FLAGS_max_flame_graph_width : 1200);
}
cmd_builder << " 2>&1 ";
#elif defined(OS_MACOSX)
cmd_builder << s_pprof_binary_path << " "
<< DisplayTypeToPProfArgument(display_type)
<< ((show_ccount || type == PROFILING_IOBUF) ? " --contention " : "");
if (base_name) {
cmd_builder << "-base " << *base_name << ' ';
}
cmd_builder << GetProgramPath() << " " << prof_name << " 2>&1 ";
#endif
const std::string cmd = cmd_builder.str();
for (int ntry = 0; ntry < 2; ++ntry) {
if (!g_written_pprof_perl) {
if (!WriteSmallFile(pprof_tool.c_str(), pprof_perl())) {
os << "Fail to write " << pprof_tool
<< (use_html ? "</body></html>" : "\n");
os.move_to(resp);
cntl->http_response().set_status_code(
HTTP_STATUS_INTERNAL_SERVER_ERROR);
return;
}
g_written_pprof_perl = true;
}
errno = 0; // read_command_output may not set errno, clear it to make sure if
// we see non-zero errno, it's real error.
butil::IOBufBuilder pprof_output;
RPC_VLOG << "Running cmd=" << cmd;
const int rc = butil::read_command_output(pprof_output, cmd.c_str());
if (rc != 0) {
butil::FilePath pprof_path(pprof_tool);
if (!butil::PathExists(pprof_path)) {
// Write the script again.
g_written_pprof_perl = false;
// tell user.
os << pprof_path.value() << " was removed, recreate ...\n\n";
continue;
}
if (rc < 0) {
os << "Fail to execute `" << cmd << "', " << berror()
<< (use_html ? "</body></html>" : "\n");
os.move_to(resp);
cntl->http_response().set_status_code(
HTTP_STATUS_INTERNAL_SERVER_ERROR);
return;
}
// cmd returns non zero, quit normally
}
pprof_output.move_to(prof_result);
// Cache result in file.
char result_name[256];
MakeCacheName(result_name, sizeof(result_name), prof_name,
GetBaseName(base_name), display_type, show_ccount);
// Append the profile name as the visual reminder for what
// current profile is.
butil::IOBuf before_label;
butil::IOBuf tmp;
if (cntl->http_request().uri().GetQuery("view") == NULL) {
tmp.append(prof_name);
tmp.append("[addToProfEnd]");
}
if (prof_result.cut_until(&before_label, ",label=\"") == 0) {
tmp.append(before_label);
tmp.append(",label=\"[");
tmp.append(GetBaseName(prof_name));
if (base_name) {
tmp.append(" - ");
tmp.append(GetBaseName(base_name));
}
tmp.append("]\\l");
tmp.append(prof_result);
tmp.swap(prof_result);
} else {
// Assume it's text. append before result directly.
tmp.append("[");
tmp.append(GetBaseName(prof_name));
if (base_name) {
tmp.append(" - ");
tmp.append(GetBaseName(base_name));
}
tmp.append("]\n");
tmp.append(prof_result);
tmp.swap(prof_result);
}
if (!WriteSmallFile(result_name, prof_result)) {
LOG(ERROR) << "Fail to write " << result_name;
CHECK(butil::DeleteFile(butil::FilePath(result_name), false));
}
break;
}
// NOTE: not send prof_result to os first which does copying.
os.move_to(resp);
if (use_html) {
resp.append("<pre>");
}
resp.append(prof_result);
if (use_html) {
resp.append("</pre></body></html>");
}
}