hotspots_service.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. // Licensed to the Apache Software Foundation (ASF) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The ASF licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. // Authors: Ge,Jun (gejun@baidu.com)
  18. #include <stdio.h>
  19. #include <gflags/gflags.h>
  20. #include "butil/files/file_enumerator.h"
  21. #include "butil/file_util.h" // butil::FilePath
  22. #include "butil/popen.h" // butil::read_command_output
  23. #include "butil/fd_guard.h" // butil::fd_guard
  24. #include "brpc/log.h"
  25. #include "brpc/controller.h"
  26. #include "brpc/server.h"
  27. #include "brpc/reloadable_flags.h"
  28. #include "brpc/builtin/pprof_perl.h"
  29. #include "brpc/builtin/hotspots_service.h"
  30. #include "brpc/details/tcmalloc_extension.h"
  31. extern "C" {
  32. int __attribute__((weak)) ProfilerStart(const char* fname);
  33. void __attribute__((weak)) ProfilerStop();
  34. }
  35. namespace bthread {
  36. bool ContentionProfilerStart(const char* filename);
  37. void ContentionProfilerStop();
  38. }
  39. namespace brpc {
  40. extern bool cpu_profiler_enabled;
  41. DEFINE_int32(max_profiling_seconds, 300, "upper limit of running time of profilers");
  42. BRPC_VALIDATE_GFLAG(max_profiling_seconds, NonNegativeInteger);
  43. DEFINE_int32(max_profiles_kept, 32,
  44. "max profiles kept for cpu/heap/growth/contention respectively");
  45. BRPC_VALIDATE_GFLAG(max_profiles_kept, PassValidate);
  46. static const char* const PPROF_FILENAME = "pprof.pl";
  47. static int DEFAULT_PROFILING_SECONDS = 10;
  48. static size_t CONCURRENT_PROFILING_LIMIT = 256;
  49. struct ProfilingWaiter {
  50. Controller* cntl;
  51. ::google::protobuf::Closure* done;
  52. };
  53. // Information of the client doing profiling.
  54. struct ProfilingClient {
  55. ProfilingClient() : end_us(0), seconds(0), id(0) {}
  56. int64_t end_us;
  57. int seconds;
  58. int64_t id;
  59. butil::EndPoint point;
  60. };
  61. struct ProfilingResult {
  62. ProfilingResult() : id(0), status_code(HTTP_STATUS_OK) {}
  63. int64_t id;
  64. int status_code;
  65. butil::IOBuf result;
  66. };
  67. static bool g_written_pprof_perl = false;
  68. struct ProfilingEnvironment {
  69. pthread_mutex_t mutex;
  70. int64_t cur_id;
  71. ProfilingClient* client;
  72. std::vector<ProfilingWaiter>* waiters;
  73. ProfilingResult* cached_result;
  74. };
  75. // Different ProfilingType have different env.
  76. static ProfilingEnvironment g_env[4] = {
  77. { PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL, NULL },
  78. { PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL, NULL },
  79. { PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL, NULL },
  80. { PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL, NULL }
  81. };
  82. // The `content' should be small so that it can be written into file in one
  83. // fwrite (at most time).
  84. static bool WriteSmallFile(const char* filepath_in,
  85. const butil::StringPiece& content) {
  86. butil::File::Error error;
  87. butil::FilePath path(filepath_in);
  88. butil::FilePath dir = path.DirName();
  89. if (!butil::CreateDirectoryAndGetError(dir, &error)) {
  90. LOG(ERROR) << "Fail to create directory=`" << dir.value()
  91. << "', " << error;
  92. return false;
  93. }
  94. FILE* fp = fopen(path.value().c_str(), "w");
  95. if (NULL == fp) {
  96. LOG(ERROR) << "Fail to open `" << path.value() << '\'';
  97. return false;
  98. }
  99. bool ret = true;
  100. if (fwrite(content.data(), content.size(), 1UL, fp) != 1UL) {
  101. LOG(ERROR) << "Fail to write into " << path.value();
  102. ret = false;
  103. }
  104. CHECK_EQ(0, fclose(fp));
  105. return ret;
  106. }
  107. static bool WriteSmallFile(const char* filepath_in,
  108. const butil::IOBuf& content) {
  109. butil::File::Error error;
  110. butil::FilePath path(filepath_in);
  111. butil::FilePath dir = path.DirName();
  112. if (!butil::CreateDirectoryAndGetError(dir, &error)) {
  113. LOG(ERROR) << "Fail to create directory=`" << dir.value()
  114. << "', " << error;
  115. return false;
  116. }
  117. FILE* fp = fopen(path.value().c_str(), "w");
  118. if (NULL == fp) {
  119. LOG(ERROR) << "Fail to open `" << path.value() << '\'';
  120. return false;
  121. }
  122. butil::IOBufAsZeroCopyInputStream iter(content);
  123. const void* data = NULL;
  124. int size = 0;
  125. while (iter.Next(&data, &size)) {
  126. if (fwrite(data, size, 1UL, fp) != 1UL) {
  127. LOG(ERROR) << "Fail to write into " << path.value();
  128. fclose(fp);
  129. return false;
  130. }
  131. }
  132. fclose(fp);
  133. return true;
  134. }
  135. static int ReadSeconds(const Controller* cntl) {
  136. int seconds = DEFAULT_PROFILING_SECONDS;
  137. const std::string* param =
  138. cntl->http_request().uri().GetQuery("seconds");
  139. if (param != NULL) {
  140. char* endptr = NULL;
  141. const long sec = strtol(param->c_str(), &endptr, 10);
  142. if (endptr == param->c_str() + param->length()) {
  143. seconds = sec;
  144. } else {
  145. return -1;
  146. }
  147. }
  148. seconds = std::min(seconds, FLAGS_max_profiling_seconds);
  149. return seconds;
  150. }
  151. static const char* GetBaseName(const std::string* full_base_name) {
  152. if (full_base_name == NULL) {
  153. return NULL;
  154. }
  155. size_t offset = full_base_name->find_last_of('/');
  156. if (offset == std::string::npos) {
  157. offset = 0;
  158. } else {
  159. ++offset;
  160. }
  161. return full_base_name->c_str() + offset;
  162. }
  163. static const char* GetBaseName(const char* full_base_name) {
  164. butil::StringPiece s(full_base_name);
  165. size_t offset = s.find_last_of('/');
  166. if (offset == butil::StringPiece::npos) {
  167. offset = 0;
  168. } else {
  169. ++offset;
  170. }
  171. return s.data() + offset;
  172. }
  173. // Test if path of the profile is valid.
  174. // NOTE: this function MUST be applied to all parameters finally passed to
  175. // system related functions (popen/system/exec ...) to avoid potential
  176. // injections from URL and other user inputs.
  177. static bool ValidProfilePath(const butil::StringPiece& path) {
  178. if (!path.starts_with(FLAGS_rpc_profiling_dir)) {
  179. // Must be under the directory.
  180. return false;
  181. }
  182. int consecutive_dot_count = 0;
  183. for (size_t i = 0; i < path.size(); ++i) {
  184. const char c = path[i];
  185. if (c == '.') {
  186. ++consecutive_dot_count;
  187. if (consecutive_dot_count >= 2) {
  188. // Disallow consecutive dots to go to upper level directories.
  189. return false;
  190. } else {
  191. continue;
  192. }
  193. } else {
  194. consecutive_dot_count = 0;
  195. }
  196. if (!isalpha(c) && !isdigit(c) &&
  197. c != '_' && c != '-' && c != '/') {
  198. return false;
  199. }
  200. }
  201. return true;
  202. }
  203. static int MakeCacheName(char* cache_name, size_t len,
  204. const char* prof_name,
  205. const char* base_name,
  206. bool use_text,
  207. bool show_ccount) {
  208. if (base_name) {
  209. return snprintf(cache_name, len, "%s.cache/base_%s%s%s", prof_name,
  210. base_name,
  211. (use_text ? ".text" : ".dot"),
  212. (show_ccount ? ".ccount" : ""));
  213. } else {
  214. return snprintf(cache_name, len, "%s.cache/%s%s", prof_name,
  215. (use_text ? "text" : "dot"),
  216. (show_ccount ? ".ccount" : ""));
  217. }
  218. }
  219. static int MakeProfName(ProfilingType type, char* buf, size_t buf_len) {
  220. int nr = snprintf(buf, buf_len, "%s/%s/", FLAGS_rpc_profiling_dir.c_str(),
  221. GetProgramChecksum());
  222. if (nr < 0) {
  223. return -1;
  224. }
  225. buf += nr;
  226. buf_len -= nr;
  227. time_t rawtime;
  228. time(&rawtime);
  229. struct tm* timeinfo = localtime(&rawtime);
  230. const size_t nw = strftime(buf, buf_len, "%Y%m%d.%H%M%S", timeinfo);
  231. buf += nw;
  232. buf_len -= nw;
  233. // We have checksum in the path, getpid() is not necessary now.
  234. snprintf(buf, buf_len, ".%s", ProfilingType2String(type));
  235. return 0;
  236. }
  237. static void ConsumeWaiters(ProfilingType type, const Controller* cur_cntl,
  238. std::vector<ProfilingWaiter>* waiters) {
  239. waiters->clear();
  240. if ((int)type >= (int)arraysize(g_env)) {
  241. LOG(ERROR) << "Invalid type=" << type;
  242. return;
  243. }
  244. ProfilingEnvironment& env = g_env[type];
  245. if (env.client) {
  246. BAIDU_SCOPED_LOCK(env.mutex);
  247. if (env.client == NULL) {
  248. return;
  249. }
  250. if (env.cached_result == NULL) {
  251. env.cached_result = new ProfilingResult;
  252. }
  253. env.cached_result->id = env.client->id;
  254. env.cached_result->status_code =
  255. cur_cntl->http_response().status_code();
  256. env.cached_result->result = cur_cntl->response_attachment();
  257. delete env.client;
  258. env.client = NULL;
  259. if (env.waiters) {
  260. env.waiters->swap(*waiters);
  261. }
  262. }
  263. }
  264. // This function is always called with g_env[type].mutex UNLOCKED.
  265. static void NotifyWaiters(ProfilingType type, const Controller* cur_cntl,
  266. const std::string* view) {
  267. if (view != NULL) {
  268. return;
  269. }
  270. std::vector<ProfilingWaiter> saved_waiters;
  271. CHECK(g_env[type].client);
  272. ConsumeWaiters(type, cur_cntl, &saved_waiters);
  273. for (size_t i = 0; i < saved_waiters.size(); ++i) {
  274. Controller* cntl = saved_waiters[i].cntl;
  275. ::google::protobuf::Closure* done = saved_waiters[i].done;
  276. cntl->http_response().set_status_code(
  277. cur_cntl->http_response().status_code());
  278. cntl->response_attachment().append(cur_cntl->response_attachment());
  279. done->Run();
  280. }
  281. }
  282. #if defined(OS_MACOSX)
  283. static bool check_GOOGLE_PPROF_BINARY_PATH() {
  284. char* str = getenv("GOOGLE_PPROF_BINARY_PATH");
  285. if (str == NULL) {
  286. return false;
  287. }
  288. butil::fd_guard fd(open(str, O_RDONLY));
  289. if (fd < 0) {
  290. return false;
  291. }
  292. return true;
  293. }
  294. static bool has_GOOGLE_PPROF_BINARY_PATH() {
  295. static bool val = check_GOOGLE_PPROF_BINARY_PATH();
  296. return val;
  297. }
  298. #endif
  299. static void DisplayResult(Controller* cntl,
  300. google::protobuf::Closure* done,
  301. const char* prof_name,
  302. const butil::IOBuf& result_prefix) {
  303. ClosureGuard done_guard(done);
  304. butil::IOBuf prof_result;
  305. if (cntl->IsCanceled()) {
  306. // If the page is refreshed, older connections are likely to be
  307. // already closed by browser.
  308. return;
  309. }
  310. butil::IOBuf& resp = cntl->response_attachment();
  311. const bool use_html = UseHTML(cntl->http_request());
  312. const bool use_text = cntl->http_request().uri().GetQuery("text");
  313. const bool show_ccount = cntl->http_request().uri().GetQuery("ccount");
  314. const std::string* base_name = cntl->http_request().uri().GetQuery("base");
  315. if (base_name != NULL) {
  316. if (!ValidProfilePath(*base_name)) {
  317. return cntl->SetFailed(EINVAL, "Invalid query `base'");
  318. }
  319. if (!butil::PathExists(butil::FilePath(*base_name))) {
  320. return cntl->SetFailed(
  321. EINVAL, "The profile denoted by `base' does not exist");
  322. }
  323. }
  324. butil::IOBufBuilder os;
  325. os << result_prefix;
  326. char expected_result_name[256];
  327. MakeCacheName(expected_result_name, sizeof(expected_result_name),
  328. prof_name, GetBaseName(base_name),
  329. use_text, show_ccount);
  330. // Try to read cache first.
  331. FILE* fp = fopen(expected_result_name, "r");
  332. if (fp != NULL) {
  333. bool succ = false;
  334. char buffer[1024];
  335. while (1) {
  336. size_t nr = fread(buffer, 1, sizeof(buffer), fp);
  337. if (nr != 0) {
  338. prof_result.append(buffer, nr);
  339. }
  340. if (nr != sizeof(buffer)) {
  341. if (feof(fp)) {
  342. succ = true;
  343. break;
  344. } else if (ferror(fp)) {
  345. LOG(ERROR) << "Encountered error while reading for "
  346. << expected_result_name;
  347. break;
  348. }
  349. // retry;
  350. }
  351. }
  352. PLOG_IF(ERROR, fclose(fp) != 0) << "Fail to close fp";
  353. if (succ) {
  354. RPC_VLOG << "Hit cache=" << expected_result_name;
  355. os.move_to(resp);
  356. if (use_html) {
  357. resp.append("<pre>");
  358. }
  359. resp.append(prof_result);
  360. if (use_html) {
  361. resp.append("</pre></body></html>");
  362. }
  363. return;
  364. }
  365. }
  366. std::ostringstream cmd_builder;
  367. std::string pprof_tool;
  368. pprof_tool.reserve(FLAGS_rpc_profiling_dir.size() + 1 + strlen(PPROF_FILENAME));
  369. pprof_tool += FLAGS_rpc_profiling_dir;
  370. pprof_tool.push_back('/');
  371. pprof_tool += PPROF_FILENAME;
  372. #if defined(OS_LINUX)
  373. cmd_builder << "perl " << pprof_tool
  374. << (use_text ? " --text " : " --dot ")
  375. << (show_ccount ? " --contention " : "");
  376. if (base_name) {
  377. cmd_builder << "--base " << *base_name << ' ';
  378. }
  379. cmd_builder << GetProgramName() << " " << prof_name << " 2>&1 ";
  380. #elif defined(OS_MACOSX)
  381. cmd_builder << getenv("GOOGLE_PPROF_BINARY_PATH") << " "
  382. << (use_text ? " -text " : " -dot ")
  383. << (show_ccount ? " -contentions " : "");
  384. if (base_name) {
  385. cmd_builder << "-base " << *base_name << ' ';
  386. }
  387. cmd_builder << prof_name << " 2>&1 ";
  388. #endif
  389. const std::string cmd = cmd_builder.str();
  390. for (int ntry = 0; ntry < 2; ++ntry) {
  391. if (!g_written_pprof_perl) {
  392. if (!WriteSmallFile(pprof_tool.c_str(), pprof_perl())) {
  393. os << "Fail to write " << pprof_tool
  394. << (use_html ? "</body></html>" : "\n");
  395. os.move_to(resp);
  396. cntl->http_response().set_status_code(
  397. HTTP_STATUS_INTERNAL_SERVER_ERROR);
  398. return;
  399. }
  400. g_written_pprof_perl = true;
  401. }
  402. errno = 0; // read_command_output may not set errno, clear it to make sure if
  403. // we see non-zero errno, it's real error.
  404. butil::IOBufBuilder pprof_output;
  405. const int rc = butil::read_command_output(pprof_output, cmd.c_str());
  406. if (rc != 0) {
  407. butil::FilePath path(pprof_tool);
  408. if (!butil::PathExists(path)) {
  409. // Write the script again.
  410. g_written_pprof_perl = false;
  411. // tell user.
  412. os << path.value() << " was removed, recreate ...\n\n";
  413. continue;
  414. }
  415. if (rc < 0) {
  416. os << "Fail to execute `" << cmd << "', " << berror()
  417. << (use_html ? "</body></html>" : "\n");
  418. os.move_to(resp);
  419. cntl->http_response().set_status_code(
  420. HTTP_STATUS_INTERNAL_SERVER_ERROR);
  421. return;
  422. }
  423. // cmd returns non zero, quit normally
  424. }
  425. pprof_output.move_to(prof_result);
  426. // Cache result in file.
  427. char result_name[256];
  428. MakeCacheName(result_name, sizeof(result_name), prof_name,
  429. GetBaseName(base_name), use_text, show_ccount);
  430. // Append the profile name as the visual reminder for what
  431. // current profile is.
  432. butil::IOBuf before_label;
  433. butil::IOBuf tmp;
  434. if (cntl->http_request().uri().GetQuery("view") == NULL) {
  435. tmp.append(prof_name);
  436. tmp.append("[addToProfEnd]");
  437. }
  438. if (prof_result.cut_until(&before_label, ",label=\"") == 0) {
  439. tmp.append(before_label);
  440. tmp.append(",label=\"[");
  441. tmp.append(GetBaseName(prof_name));
  442. if (base_name) {
  443. tmp.append(" - ");
  444. tmp.append(GetBaseName(base_name));
  445. }
  446. tmp.append("]\\l");
  447. tmp.append(prof_result);
  448. tmp.swap(prof_result);
  449. } else {
  450. // Assume it's text. append before result directly.
  451. tmp.append("[");
  452. tmp.append(GetBaseName(prof_name));
  453. if (base_name) {
  454. tmp.append(" - ");
  455. tmp.append(GetBaseName(base_name));
  456. }
  457. tmp.append("]\n");
  458. tmp.append(prof_result);
  459. tmp.swap(prof_result);
  460. }
  461. if (!WriteSmallFile(result_name, prof_result)) {
  462. LOG(ERROR) << "Fail to write " << result_name;
  463. CHECK(butil::DeleteFile(butil::FilePath(result_name), false));
  464. }
  465. break;
  466. }
  467. CHECK(!use_html);
  468. // NOTE: not send prof_result to os first which does copying.
  469. os.move_to(resp);
  470. if (use_html) {
  471. resp.append("<pre>");
  472. }
  473. resp.append(prof_result);
  474. if (use_html) {
  475. resp.append("</pre></body></html>");
  476. }
  477. }
  478. static void DoProfiling(ProfilingType type,
  479. ::google::protobuf::RpcController* cntl_base,
  480. ::google::protobuf::Closure* done) {
  481. ClosureGuard done_guard(done);
  482. Controller *cntl = static_cast<Controller*>(cntl_base);
  483. butil::IOBuf& resp = cntl->response_attachment();
  484. const bool use_html = UseHTML(cntl->http_request());
  485. cntl->http_response().set_content_type(use_html ? "text/html" : "text/plain");
  486. butil::IOBufBuilder os;
  487. if (use_html) {
  488. os << "<!DOCTYPE html><html><head>\n"
  489. "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
  490. "<script language=\"javascript\" type=\"text/javascript\" src=\"/js/jquery_min\"></script>\n"
  491. << TabsHead()
  492. << "<style type=\"text/css\">\n"
  493. ".logo {position: fixed; bottom: 0px; right: 0px; }\n"
  494. ".logo_text {color: #B0B0B0; }\n"
  495. "</style>\n"
  496. "</head>\n"
  497. "<body>\n";
  498. cntl->server()->PrintTabsBody(os, ProfilingType2String(type));
  499. }
  500. const std::string* view = cntl->http_request().uri().GetQuery("view");
  501. if (view) {
  502. if (!ValidProfilePath(*view)) {
  503. return cntl->SetFailed(EINVAL, "Invalid query `view'");
  504. }
  505. if (!butil::PathExists(butil::FilePath(*view))) {
  506. return cntl->SetFailed(
  507. EINVAL, "The profile denoted by `view' does not exist");
  508. }
  509. DisplayResult(cntl, done_guard.release(), view->c_str(), os.buf());
  510. return;
  511. }
  512. const int seconds = ReadSeconds(cntl);
  513. if ((type == PROFILING_CPU || type == PROFILING_CONTENTION)) {
  514. if (seconds < 0) {
  515. os << "Invalid seconds" << (use_html ? "</body></html>" : "\n");
  516. os.move_to(cntl->response_attachment());
  517. cntl->http_response().set_status_code(HTTP_STATUS_BAD_REQUEST);
  518. return;
  519. }
  520. }
  521. // Log requester
  522. std::ostringstream client_info;
  523. client_info << cntl->remote_side();
  524. if (cntl->auth_context()) {
  525. client_info << "(auth=" << cntl->auth_context()->user() << ')';
  526. } else {
  527. client_info << "(no auth)";
  528. }
  529. client_info << " requests for profiling " << ProfilingType2String(type);
  530. if (type == PROFILING_CPU || type == PROFILING_CONTENTION) {
  531. LOG(INFO) << client_info.str() << " for " << seconds << " seconds";
  532. } else {
  533. LOG(INFO) << client_info.str();
  534. }
  535. int64_t prof_id = 0;
  536. const std::string* prof_id_str =
  537. cntl->http_request().uri().GetQuery("profiling_id");
  538. if (prof_id_str != NULL) {
  539. char* endptr = NULL;
  540. prof_id = strtoll(prof_id_str->c_str(), &endptr, 10);
  541. LOG_IF(ERROR, *endptr != '\0') << "Invalid profiling_id=" << prof_id;
  542. }
  543. {
  544. BAIDU_SCOPED_LOCK(g_env[type].mutex);
  545. if (g_env[type].client) {
  546. if (NULL == g_env[type].waiters) {
  547. g_env[type].waiters = new std::vector<ProfilingWaiter>;
  548. }
  549. ProfilingWaiter waiter = { cntl, done_guard.release() };
  550. g_env[type].waiters->push_back(waiter);
  551. RPC_VLOG << "Queue request from " << cntl->remote_side();
  552. return;
  553. }
  554. if (g_env[type].cached_result != NULL &&
  555. g_env[type].cached_result->id == prof_id) {
  556. cntl->http_response().set_status_code(
  557. g_env[type].cached_result->status_code);
  558. cntl->response_attachment().append(
  559. g_env[type].cached_result->result);
  560. RPC_VLOG << "Hit cached result, id=" << prof_id;
  561. return;
  562. }
  563. CHECK(NULL == g_env[type].client);
  564. g_env[type].client = new ProfilingClient;
  565. g_env[type].client->end_us = butil::cpuwide_time_us() + seconds * 1000000L;
  566. g_env[type].client->seconds = seconds;
  567. // This id work arounds an issue of chrome (or jquery under chrome) that
  568. // the ajax call in another tab may be delayed until ajax call in
  569. // current tab finishes. We assign a increasing-only id to each
  570. // profiling and save last profiling result along with the assigned id.
  571. // If the delay happens, the viewr should send the ajax call with an
  572. // id matching the id in cached result, then the result will be returned
  573. // directly instead of running another profiling which may take long
  574. // time.
  575. if (0 == ++ g_env[type].cur_id) { // skip 0
  576. ++ g_env[type].cur_id;
  577. }
  578. g_env[type].client->id = g_env[type].cur_id;
  579. g_env[type].client->point = cntl->remote_side();
  580. }
  581. RPC_VLOG << "Apply request from " << cntl->remote_side();
  582. char prof_name[128];
  583. if (MakeProfName(type, prof_name, sizeof(prof_name)) != 0) {
  584. os << "Fail to create prof name: " << berror()
  585. << (use_html ? "</body></html>" : "\n");
  586. os.move_to(resp);
  587. cntl->http_response().set_status_code(HTTP_STATUS_INTERNAL_SERVER_ERROR);
  588. return NotifyWaiters(type, cntl, view);
  589. }
  590. #if defined(OS_MACOSX)
  591. if (!has_GOOGLE_PPROF_BINARY_PATH()) {
  592. os << "no GOOGLE_PPROF_BINARY_PATH in env"
  593. << (use_html ? "</body></html>" : "\n");
  594. os.move_to(resp);
  595. cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
  596. return NotifyWaiters(type, cntl, view);
  597. }
  598. #endif
  599. if (type == PROFILING_CPU) {
  600. if ((void*)ProfilerStart == NULL || (void*)ProfilerStop == NULL) {
  601. os << "CPU profiler is not enabled"
  602. << (use_html ? "</body></html>" : "\n");
  603. os.move_to(resp);
  604. cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
  605. return NotifyWaiters(type, cntl, view);
  606. }
  607. butil::File::Error error;
  608. const butil::FilePath dir = butil::FilePath(prof_name).DirName();
  609. if (!butil::CreateDirectoryAndGetError(dir, &error)) {
  610. os << "Fail to create directory=`" << dir.value() << ", "
  611. << error << (use_html ? "</body></html>" : "\n");
  612. os.move_to(resp);
  613. cntl->http_response().set_status_code(
  614. HTTP_STATUS_INTERNAL_SERVER_ERROR);
  615. return NotifyWaiters(type, cntl, view);
  616. }
  617. if (!ProfilerStart(prof_name)) {
  618. os << "Another profiler (not via /hotspots/cpu) is running, "
  619. "try again later" << (use_html ? "</body></html>" : "\n");
  620. os.move_to(resp);
  621. cntl->http_response().set_status_code(HTTP_STATUS_SERVICE_UNAVAILABLE);
  622. return NotifyWaiters(type, cntl, view);
  623. }
  624. if (bthread_usleep(seconds * 1000000L) != 0) {
  625. PLOG(WARNING) << "Profiling has been interrupted";
  626. }
  627. ProfilerStop();
  628. } else if (type == PROFILING_CONTENTION) {
  629. if (!bthread::ContentionProfilerStart(prof_name)) {
  630. os << "Another profiler (not via /hotspots/contention) is running, "
  631. "try again later" << (use_html ? "</body></html>" : "\n");
  632. os.move_to(resp);
  633. cntl->http_response().set_status_code(HTTP_STATUS_SERVICE_UNAVAILABLE);
  634. return NotifyWaiters(type, cntl, view);
  635. }
  636. if (bthread_usleep(seconds * 1000000L) != 0) {
  637. PLOG(WARNING) << "Profiling has been interrupted";
  638. }
  639. bthread::ContentionProfilerStop();
  640. } else if (type == PROFILING_HEAP) {
  641. MallocExtension* malloc_ext = MallocExtension::instance();
  642. if (malloc_ext == NULL || !has_TCMALLOC_SAMPLE_PARAMETER()) {
  643. os << "Heap profiler is not enabled";
  644. if (malloc_ext != NULL) {
  645. os << " (no TCMALLOC_SAMPLE_PARAMETER in env)";
  646. }
  647. os << '.' << (use_html ? "</body></html>" : "\n");
  648. os.move_to(resp);
  649. cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
  650. return NotifyWaiters(type, cntl, view);
  651. }
  652. std::string obj;
  653. malloc_ext->GetHeapSample(&obj);
  654. if (!WriteSmallFile(prof_name, obj)) {
  655. os << "Fail to write " << prof_name
  656. << (use_html ? "</body></html>" : "\n");
  657. os.move_to(resp);
  658. cntl->http_response().set_status_code(
  659. HTTP_STATUS_INTERNAL_SERVER_ERROR);
  660. return NotifyWaiters(type, cntl, view);
  661. }
  662. } else if (type == PROFILING_GROWTH) {
  663. MallocExtension* malloc_ext = MallocExtension::instance();
  664. if (malloc_ext == NULL) {
  665. os << "Growth profiler is not enabled."
  666. << (use_html ? "</body></html>" : "\n");
  667. os.move_to(resp);
  668. cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
  669. return NotifyWaiters(type, cntl, view);
  670. }
  671. std::string obj;
  672. malloc_ext->GetHeapGrowthStacks(&obj);
  673. if (!WriteSmallFile(prof_name, obj)) {
  674. os << "Fail to write " << prof_name
  675. << (use_html ? "</body></html>" : "\n");
  676. os.move_to(resp);
  677. cntl->http_response().set_status_code(
  678. HTTP_STATUS_INTERNAL_SERVER_ERROR);
  679. return NotifyWaiters(type, cntl, view);
  680. }
  681. } else {
  682. os << "Unknown ProfilingType=" << type
  683. << (use_html ? "</body></html>" : "\n");
  684. os.move_to(resp);
  685. cntl->http_response().set_status_code(
  686. HTTP_STATUS_INTERNAL_SERVER_ERROR);
  687. return NotifyWaiters(type, cntl, view);
  688. }
  689. std::vector<ProfilingWaiter> waiters;
  690. // NOTE: Must be called before DisplayResult which calls done->Run() and
  691. // deletes cntl.
  692. ConsumeWaiters(type, cntl, &waiters);
  693. DisplayResult(cntl, done_guard.release(), prof_name, os.buf());
  694. for (size_t i = 0; i < waiters.size(); ++i) {
  695. DisplayResult(waiters[i].cntl, waiters[i].done, prof_name, os.buf());
  696. }
  697. }
  698. static void StartProfiling(ProfilingType type,
  699. ::google::protobuf::RpcController* cntl_base,
  700. ::google::protobuf::Closure* done) {
  701. ClosureGuard done_guard(done);
  702. Controller *cntl = static_cast<Controller*>(cntl_base);
  703. butil::IOBuf& resp = cntl->response_attachment();
  704. const bool use_html = UseHTML(cntl->http_request());
  705. butil::IOBufBuilder os;
  706. bool enabled = false;
  707. const char* extra_desc = "";
  708. if (type == PROFILING_CPU) {
  709. enabled = cpu_profiler_enabled;
  710. } else if (type == PROFILING_CONTENTION) {
  711. enabled = true;
  712. } else if (type == PROFILING_HEAP) {
  713. enabled = IsHeapProfilerEnabled();
  714. if (enabled && !has_TCMALLOC_SAMPLE_PARAMETER()) {
  715. enabled = false;
  716. extra_desc = " (no TCMALLOC_SAMPLE_PARAMETER in env)";
  717. }
  718. } else if (type == PROFILING_GROWTH) {
  719. enabled = IsHeapProfilerEnabled();
  720. }
  721. const char* const type_str = ProfilingType2String(type);
  722. #if defined(OS_MACOSX)
  723. if (!has_GOOGLE_PPROF_BINARY_PATH()) {
  724. enabled = false;
  725. extra_desc = "(no GOOGLE_PPROF_BINARY_PATH in env)";
  726. }
  727. #endif
  728. if (!use_html) {
  729. if (!enabled) {
  730. os << "Error: " << type_str << " profiler is not enabled."
  731. << extra_desc << "\n"
  732. "Read the docs: docs/cn/{cpu_profiler.md,heap_profiler.md}\n";
  733. os.move_to(cntl->response_attachment());
  734. cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
  735. return;
  736. }
  737. // Console can only use non-responsive version, namely the curl
  738. // blocks until profiling is done.
  739. return DoProfiling(type, cntl, done_guard.release());
  740. }
  741. const int seconds = ReadSeconds(cntl);
  742. const std::string* view = cntl->http_request().uri().GetQuery("view");
  743. const bool use_text = cntl->http_request().uri().GetQuery("text");
  744. const bool show_ccount = cntl->http_request().uri().GetQuery("ccount");
  745. const std::string* base_name = cntl->http_request().uri().GetQuery("base");
  746. ProfilingClient profiling_client;
  747. size_t nwaiters = 0;
  748. ProfilingEnvironment & env = g_env[type];
  749. if (view == NULL) {
  750. BAIDU_SCOPED_LOCK(env.mutex);
  751. if (env.client) {
  752. profiling_client = *env.client;
  753. nwaiters = (env.waiters ? env.waiters->size() : 0);
  754. }
  755. }
  756. cntl->http_response().set_content_type("text/html");
  757. os << "<!DOCTYPE html><html><head>\n"
  758. "<script language=\"javascript\" type=\"text/javascript\""
  759. " src=\"/js/jquery_min\"></script>\n"
  760. << TabsHead()
  761. << "<style type=\"text/css\">\n"
  762. ".logo {position: fixed; bottom: 0px; right: 0px; }\n"
  763. ".logo_text {color: #B0B0B0; }\n"
  764. " </style>\n"
  765. "<script type=\"text/javascript\">\n"
  766. "function generateURL() {\n"
  767. " var past_prof = document.getElementById('view_prof').value;\n"
  768. " var base_prof = document.getElementById('base_prof').value;\n"
  769. " var use_text = document.getElementById('text_cb').checked;\n";
  770. if (type == PROFILING_CONTENTION) {
  771. os << " var show_ccount = document.getElementById('ccount_cb').checked;\n";
  772. }
  773. os << " var targetURL = '/hotspots/" << type_str << "';\n"
  774. " var first = true;\n"
  775. " if (past_prof != '') {\n"
  776. " if (first) {\n"
  777. " targetURL += '?';\n"
  778. " first = false;\n"
  779. " } else {\n"
  780. " targetURL += '&';\n"
  781. " }\n"
  782. " targetURL += 'view=' + past_prof;\n"
  783. " }\n"
  784. " if (base_prof != '') {\n"
  785. " if (first) {\n"
  786. " targetURL += '?';\n"
  787. " first = false;\n"
  788. " } else {\n"
  789. " targetURL += '&';\n"
  790. " }\n"
  791. " targetURL += 'base=' + base_prof;\n"
  792. " }\n"
  793. " if (use_text) {\n"
  794. " if (first) {\n"
  795. " targetURL += '?';\n"
  796. " first = false;\n"
  797. " } else {\n"
  798. " targetURL += '&';\n"
  799. " }\n"
  800. " targetURL += 'text';\n"
  801. " }\n";
  802. if (type == PROFILING_CONTENTION) {
  803. os <<
  804. " if (show_ccount) {\n"
  805. " if (first) {\n"
  806. " targetURL += '?';\n"
  807. " first = false;\n"
  808. " } else {\n"
  809. " targetURL += '&';\n"
  810. " }\n"
  811. " targetURL += 'ccount';\n"
  812. " }\n";
  813. }
  814. os << " return targetURL;\n"
  815. "}\n"
  816. "$(function() {\n"
  817. " function onDataReceived(data) {\n";
  818. if (view == NULL) {
  819. os <<
  820. " var selEnd = data.indexOf('[addToProfEnd]');\n"
  821. " if (selEnd != -1) {\n"
  822. " var sel = document.getElementById('view_prof');\n"
  823. " var option = document.createElement('option');\n"
  824. " option.value = data.substring(0, selEnd);\n"
  825. " option.text = option.value;\n"
  826. " var slash_index = option.value.lastIndexOf('/');\n"
  827. " if (slash_index != -1) {\n"
  828. " option.text = option.value.substring(slash_index + 1);\n"
  829. " }\n"
  830. " var option1 = sel.options[1];\n"
  831. " if (option1 == null || option1.text != option.text) {\n"
  832. " sel.add(option, 1);\n"
  833. " } else if (option1 != null) {\n"
  834. " console.log('merged ' + option.text);\n"
  835. " }\n"
  836. " sel.selectedIndex = 1;\n"
  837. " window.history.pushState('', '', generateURL());\n"
  838. " data = data.substring(selEnd + '[addToProfEnd]'.length);\n"
  839. " }\n";
  840. }
  841. os <<
  842. " var index = data.indexOf('digraph ');\n"
  843. " if (index == -1) {\n"
  844. " var selEnd = data.indexOf('[addToProfEnd]');\n"
  845. " if (selEnd != -1) {\n"
  846. " data = data.substring(selEnd + '[addToProfEnd]'.length);\n"
  847. " }\n"
  848. " $(\"#profiling-result\").html('<pre>' + data + '</pre>');\n"
  849. " } else {\n"
  850. " $(\"#profiling-result\").html('Plotting ...');\n"
  851. " var svg = Viz(data.substring(index), \"svg\");\n"
  852. " $(\"#profiling-result\").html(svg);\n"
  853. " }\n"
  854. " }\n"
  855. " function onErrorReceived(xhr, ajaxOptions, thrownError) {\n"
  856. " $(\"#profiling-result\").html(xhr.responseText);\n"
  857. " }\n"
  858. " $.ajax({\n"
  859. " url: \"/hotspots/" << type_str << "_non_responsive?console=1";
  860. if (type == PROFILING_CPU || type == PROFILING_CONTENTION) {
  861. os << "&seconds=" << seconds;
  862. }
  863. if (profiling_client.id != 0) {
  864. os << "&profiling_id=" << profiling_client.id;
  865. }
  866. if (use_text) {
  867. os << "&text";
  868. }
  869. if (show_ccount) {
  870. os << "&ccount";
  871. }
  872. if (view) {
  873. os << "&view=" << *view;
  874. }
  875. if (base_name) {
  876. os << "&base=" << *base_name;
  877. }
  878. os << "\",\n"
  879. " type: \"GET\",\n"
  880. " dataType: \"html\",\n"
  881. " success: onDataReceived,\n"
  882. " error: onErrorReceived\n"
  883. " });\n"
  884. "});\n"
  885. "function onSelectProf() {\n"
  886. " window.location.href = generateURL();\n"
  887. "}\n"
  888. "function onChangedCB(cb) {\n"
  889. " onSelectProf();\n"
  890. "}\n"
  891. "</script>\n"
  892. "</head>\n"
  893. "<body>\n";
  894. cntl->server()->PrintTabsBody(os, type_str);
  895. TRACEPRINTF("Begin to enumerate profiles");
  896. std::vector<std::string> past_profs;
  897. butil::FilePath prof_dir(FLAGS_rpc_profiling_dir);
  898. prof_dir = prof_dir.Append(GetProgramChecksum());
  899. std::string file_pattern;
  900. file_pattern.reserve(15);
  901. file_pattern.append("*.");
  902. file_pattern.append(type_str);
  903. butil::FileEnumerator prof_enum(prof_dir, false/*non recursive*/,
  904. butil::FileEnumerator::FILES,
  905. file_pattern);
  906. std::string file_path;
  907. for (butil::FilePath name = prof_enum.Next(); !name.empty();
  908. name = prof_enum.Next()) {
  909. // NOTE: name already includes dir.
  910. if (past_profs.empty()) {
  911. past_profs.reserve(16);
  912. }
  913. past_profs.push_back(name.value());
  914. }
  915. if (!past_profs.empty()) {
  916. TRACEPRINTF("Sort %lu profiles in decending order", past_profs.size());
  917. std::sort(past_profs.begin(), past_profs.end(), std::greater<std::string>());
  918. int max_profiles = FLAGS_max_profiles_kept/*may be reloaded*/;
  919. if (max_profiles < 0) {
  920. max_profiles = 0;
  921. }
  922. if (past_profs.size() > (size_t)max_profiles) {
  923. TRACEPRINTF("Remove %lu profiles",
  924. past_profs.size() - (size_t)max_profiles);
  925. for (size_t i = max_profiles; i < past_profs.size(); ++i) {
  926. CHECK(butil::DeleteFile(butil::FilePath(past_profs[i]), false));
  927. std::string cache_path;
  928. cache_path.reserve(past_profs[i].size() + 7);
  929. cache_path += past_profs[i];
  930. cache_path += ".cache";
  931. CHECK(butil::DeleteFile(butil::FilePath(cache_path), true));
  932. }
  933. past_profs.resize(max_profiles);
  934. }
  935. }
  936. TRACEPRINTF("End enumeration");
  937. os << "<pre style='display:inline'>View: </pre>"
  938. "<select id='view_prof' onchange='onSelectProf()'>";
  939. os << "<option value=''>&lt;new profile&gt;</option>";
  940. for (size_t i = 0; i < past_profs.size(); ++i) {
  941. os << "<option value='" << past_profs[i] << "' ";
  942. if (view != NULL && past_profs[i] == *view) {
  943. os << "selected";
  944. }
  945. os << '>' << GetBaseName(&past_profs[i]);
  946. }
  947. os << "</select>"
  948. "&nbsp;&nbsp;&nbsp;<label for='text_cb'>"
  949. "<input id='text_cb' type='checkbox'"
  950. << (use_text ? " checked=''" : "") <<
  951. " onclick='onChangedCB(this);'>text</label>";
  952. if (type == PROFILING_CONTENTION) {
  953. os << "&nbsp;&nbsp;&nbsp;<label for='ccount_cb'>"
  954. "<input id='ccount_cb' type='checkbox'"
  955. << (show_ccount ? " checked=''" : "") <<
  956. " onclick='onChangedCB(this);'>count</label>";
  957. }
  958. os << "<br><pre style='display:inline'>Diff: </pre>"
  959. "<select id='base_prof' onchange='onSelectProf()'>"
  960. "<option value=''>&lt;none&gt;</option>";
  961. for (size_t i = 0; i < past_profs.size(); ++i) {
  962. os << "<option value='" << past_profs[i] << "' ";
  963. if (base_name != NULL && past_profs[i] == *base_name) {
  964. os << "selected";
  965. }
  966. os << '>' << GetBaseName(&past_profs[i]);
  967. }
  968. os << "</select>";
  969. if (!enabled && view == NULL) {
  970. os << "<p><span style='color:red'>Error:</span> "
  971. << type_str << " profiler is not enabled." << extra_desc << "</p>"
  972. "<p>To enable all profilers, link tcmalloc and define macros BRPC_ENABLE_CPU_PROFILER"
  973. "</p><p>Or read docs: <a href='https://github.com/brpc/brpc/blob/master/docs/cn/cpu_profiler.md'>cpu_profiler</a>"
  974. " and <a href='https://github.com/brpc/brpc/blob/master/docs/cn/heap_profiler.md'>heap_profiler</a>"
  975. "</p></body></html>";
  976. os.move_to(cntl->response_attachment());
  977. cntl->http_response().set_status_code(HTTP_STATUS_FORBIDDEN);
  978. return;
  979. }
  980. if ((type == PROFILING_CPU || type == PROFILING_CONTENTION) && view == NULL) {
  981. if (seconds < 0) {
  982. os << "Invalid seconds</body></html>";
  983. os.move_to(cntl->response_attachment());
  984. cntl->http_response().set_status_code(HTTP_STATUS_BAD_REQUEST);
  985. return;
  986. }
  987. }
  988. if (nwaiters >= CONCURRENT_PROFILING_LIMIT) {
  989. os << "Your profiling request is rejected because of "
  990. "too many concurrent profiling requests</body></html>";
  991. os.move_to(cntl->response_attachment());
  992. cntl->http_response().set_status_code(HTTP_STATUS_SERVICE_UNAVAILABLE);
  993. return;
  994. }
  995. os << "<div id=\"profiling-result\">";
  996. if (profiling_client.seconds != 0) {
  997. const int wait_seconds =
  998. (int)ceil((profiling_client.end_us - butil::cpuwide_time_us())
  999. / 1000000.0);
  1000. os << "Your request is merged with the request from "
  1001. << profiling_client.point;
  1002. if (type == PROFILING_CPU || type == PROFILING_CONTENTION) {
  1003. os << ", showing in about " << wait_seconds << " seconds ...";
  1004. }
  1005. } else {
  1006. if ((type == PROFILING_CPU || type == PROFILING_CONTENTION) && view == NULL) {
  1007. os << "Profiling " << ProfilingType2String(type) << " for "
  1008. << seconds << " seconds ...";
  1009. } else {
  1010. os << "Generating " << type_str << " profile ...";
  1011. }
  1012. }
  1013. os << "</div><pre class='logo'><span class='logo_text'>" << logo()
  1014. << "</span></pre></body>\n";
  1015. if (!use_text) {
  1016. // don't need viz.js in text mode.
  1017. os << "<script language=\"javascript\" type=\"text/javascript\""
  1018. " src=\"/js/viz_min\"></script>\n";
  1019. }
  1020. os << "</html>";
  1021. os.move_to(resp);
  1022. }
  1023. void HotspotsService::cpu(
  1024. ::google::protobuf::RpcController* cntl_base,
  1025. const ::brpc::HotspotsRequest*,
  1026. ::brpc::HotspotsResponse*,
  1027. ::google::protobuf::Closure* done) {
  1028. return StartProfiling(PROFILING_CPU, cntl_base, done);
  1029. }
  1030. void HotspotsService::heap(
  1031. ::google::protobuf::RpcController* cntl_base,
  1032. const ::brpc::HotspotsRequest*,
  1033. ::brpc::HotspotsResponse*,
  1034. ::google::protobuf::Closure* done) {
  1035. return StartProfiling(PROFILING_HEAP, cntl_base, done);
  1036. }
  1037. void HotspotsService::growth(
  1038. ::google::protobuf::RpcController* cntl_base,
  1039. const ::brpc::HotspotsRequest*,
  1040. ::brpc::HotspotsResponse*,
  1041. ::google::protobuf::Closure* done) {
  1042. return StartProfiling(PROFILING_GROWTH, cntl_base, done);
  1043. }
  1044. void HotspotsService::contention(
  1045. ::google::protobuf::RpcController* cntl_base,
  1046. const ::brpc::HotspotsRequest*,
  1047. ::brpc::HotspotsResponse*,
  1048. ::google::protobuf::Closure* done) {
  1049. return StartProfiling(PROFILING_CONTENTION, cntl_base, done);
  1050. }
  1051. void HotspotsService::cpu_non_responsive(
  1052. ::google::protobuf::RpcController* cntl_base,
  1053. const ::brpc::HotspotsRequest*,
  1054. ::brpc::HotspotsResponse*,
  1055. ::google::protobuf::Closure* done) {
  1056. return DoProfiling(PROFILING_CPU, cntl_base, done);
  1057. }
  1058. void HotspotsService::heap_non_responsive(
  1059. ::google::protobuf::RpcController* cntl_base,
  1060. const ::brpc::HotspotsRequest*,
  1061. ::brpc::HotspotsResponse*,
  1062. ::google::protobuf::Closure* done) {
  1063. return DoProfiling(PROFILING_HEAP, cntl_base, done);
  1064. }
  1065. void HotspotsService::growth_non_responsive(
  1066. ::google::protobuf::RpcController* cntl_base,
  1067. const ::brpc::HotspotsRequest*,
  1068. ::brpc::HotspotsResponse*,
  1069. ::google::protobuf::Closure* done) {
  1070. return DoProfiling(PROFILING_GROWTH, cntl_base, done);
  1071. }
  1072. void HotspotsService::contention_non_responsive(
  1073. ::google::protobuf::RpcController* cntl_base,
  1074. const ::brpc::HotspotsRequest*,
  1075. ::brpc::HotspotsResponse*,
  1076. ::google::protobuf::Closure* done) {
  1077. return DoProfiling(PROFILING_CONTENTION, cntl_base, done);
  1078. }
  1079. void HotspotsService::GetTabInfo(TabInfoList* info_list) const {
  1080. TabInfo* info = info_list->add();
  1081. info->path = "/hotspots/cpu";
  1082. info->tab_name = "cpu";
  1083. info = info_list->add();
  1084. info->path = "/hotspots/heap";
  1085. info->tab_name = "heap";
  1086. info = info_list->add();
  1087. info->path = "/hotspots/growth";
  1088. info->tab_name = "growth";
  1089. info = info_list->add();
  1090. info->path = "/hotspots/contention";
  1091. info->tab_name = "contention";
  1092. }
  1093. } // namespace brpc