be/src/service/http/action/jeprofile_actions.cpp
Line | Count | Source |
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 | | |
18 | | #include "service/http/action/jeprofile_actions.h" |
19 | | |
20 | | #include <jemalloc/jemalloc.h> |
21 | | #include <unistd.h> |
22 | | |
23 | | #include <string> |
24 | | |
25 | | #include "agent/utils.h" |
26 | | #include "runtime/memory/heap_profiler.h" |
27 | | #include "service/http/ev_http_server.h" |
28 | | #include "service/http/http_channel.h" |
29 | | #include "service/http/http_handler.h" |
30 | | #include "service/http/http_handler_with_auth.h" |
31 | | #include "service/http/http_headers.h" |
32 | | #include "service/http/http_request.h" |
33 | | |
34 | | namespace doris { |
35 | | |
36 | | const static std::string HEADER_JSON = "application/json"; |
37 | | const static std::string START_HEAP_PROFILE_NOTICE = |
38 | | "`curl http://be_host:be_webport/jeheap/active/true` to start heap profiler, note that " |
39 | | "`JEMALLOC_CONF` in `be/conf/be.conf` must contain `prof:true`, will only track and sample " |
40 | | "the memory " |
41 | | "allocated and freed after the heap profiler started, it cannot analyze the " |
42 | | "memory allocated and freed before. Therefore, dumping the heap profile " |
43 | | "immediately after start heap profiler may prompt `No nodes to print`, try rerun your " |
44 | | "query and dump the heap profile. Sometimes restarting BE and then immediately executing " |
45 | | "`curl http://be_host:be_webport/jeheap/active/true` makes it easier to analyze the " |
46 | | "problem.\n" |
47 | | "If you want to analyze the memory during the BE process startup, need to modify be.conf " |
48 | | "and restart the BE process, open `be/conf/be.conf` and add `,prof_active:true` after " |
49 | | "`JEMALLOC_CONF`, or modify `prof_active:false` to `prof:true`.\n"; |
50 | | |
51 | 0 | static bool compile_check(HttpRequest* req) { |
52 | 0 | #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || defined(THREAD_SANITIZER) |
53 | 0 | HttpChannel::send_reply( |
54 | 0 | req, HttpStatus::INTERNAL_SERVER_ERROR, |
55 | 0 | "Jemalloc heap dump is not available with ASAN(address sanitizer) builds.\n"); |
56 | 0 | return false; |
57 | | #elif !defined(USE_JEMALLOC) |
58 | | HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, |
59 | | "jemalloc heap dump is not available without setting USE_JEMALLOC.\n"); |
60 | | return false; |
61 | | #else |
62 | | return true; |
63 | | #endif |
64 | 0 | } |
65 | | |
66 | 0 | static bool conf_check(HttpRequest* req) { |
67 | 0 | if (!HeapProfiler::instance()->check_active_heap_profiler()) { |
68 | 0 | HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, |
69 | 0 | "Jemalloc heap profiler is not enabled, refer to the following " |
70 | 0 | "method to enable it.\n" + |
71 | 0 | START_HEAP_PROFILE_NOTICE); |
72 | 0 | return false; |
73 | 0 | } |
74 | 0 | return true; |
75 | 0 | } |
76 | | |
77 | 0 | void SetJeHeapProfileActiveActions::handle(HttpRequest* req) { |
78 | 0 | req->add_output_header(HttpHeaders::CONTENT_TYPE, HEADER_JSON.c_str()); |
79 | 0 | if (compile_check(req)) { |
80 | 0 | if (req->param("prof_value") == "true") { |
81 | 0 | HeapProfiler::instance()->heap_profiler_start(); |
82 | 0 | HttpChannel::send_reply(req, HttpStatus::OK, |
83 | 0 | "Jemalloc heap profiler started\n" + START_HEAP_PROFILE_NOTICE); |
84 | 0 | } else { |
85 | 0 | HeapProfiler::instance()->heap_profiler_stop(); |
86 | 0 | HttpChannel::send_reply(req, HttpStatus::OK, "heap profiler stoped\n"); |
87 | 0 | } |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | 0 | void SetJeHeapProfileResetActions::handle(HttpRequest* req) { |
92 | 0 | req->add_output_header(HttpHeaders::CONTENT_TYPE, HEADER_JSON.c_str()); |
93 | 0 | if (compile_check(req)) { |
94 | 0 | const auto& lg_sample_str = req->param("reset_value"); |
95 | 0 | size_t lg_sample = std::stol(lg_sample_str); |
96 | 0 | if (lg_sample > 0 && HeapProfiler::instance()->check_enable_heap_profiler() && |
97 | 0 | HeapProfiler::instance()->heap_profiler_reset(lg_sample)) { |
98 | 0 | HttpChannel::send_reply(req, HttpStatus::OK, |
99 | 0 | fmt::format("Jemalloc reset all memory profile statistics and " |
100 | 0 | "update the sample rate:{}\n", |
101 | 0 | lg_sample_str)); |
102 | 0 | } else { |
103 | 0 | HttpChannel::send_reply(req, HttpStatus::OK, |
104 | 0 | "Jemalloc have not reset.\nThe `JEMALLOC_CONF` in " |
105 | 0 | "`be/conf/be.conf` must contain `prof:true`.\n"); |
106 | 0 | } |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | 0 | void DumpJeHeapProfileToDotActions::handle(HttpRequest* req) { |
111 | 0 | req->add_output_header(HttpHeaders::CONTENT_TYPE, HEADER_JSON.c_str()); |
112 | 0 | if (compile_check(req) && conf_check(req)) { |
113 | 0 | std::string dot = HeapProfiler::instance()->dump_heap_profile_to_dot(); |
114 | 0 | if (dot.empty()) { |
115 | 0 | HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, |
116 | 0 | "dump heap profile to dot failed, see be.INFO\n"); |
117 | 0 | } else { |
118 | 0 | std::string msg; |
119 | 0 | AgentUtils util; |
120 | 0 | dot += "\n-------------------------------------------------------\n"; |
121 | 0 | util.exec_cmd("type addr2line", &msg); |
122 | 0 | dot += "addr2line: " + msg + "\n"; |
123 | 0 | dot += "Copy the text after `digraph` in the above output to " |
124 | 0 | "http://www.webgraphviz.com to generate a dot graph.\n" |
125 | 0 | "after start heap profiler, if there is no operation, will print `No nodes to " |
126 | 0 | "print`." |
127 | 0 | "If there are many errors: `addr2line: Dwarf Error`," |
128 | 0 | "or other FAQ, reference doc: " |
129 | 0 | "https://doris.apache.org/community/developer-guide/debug-tool/#4-qa\n"; |
130 | 0 | HttpChannel::send_reply(req, HttpStatus::OK, dot); |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | 0 | void DumpJeHeapProfileActions::handle(HttpRequest* req) { |
136 | 0 | req->add_output_header(HttpHeaders::CONTENT_TYPE, HEADER_JSON.c_str()); |
137 | 0 | if (compile_check(req) && conf_check(req)) { |
138 | 0 | std::string profile_file_name = HeapProfiler::instance()->dump_heap_profile(); |
139 | 0 | if (profile_file_name.empty()) { |
140 | 0 | HttpChannel::send_reply(req, HttpStatus::INTERNAL_SERVER_ERROR, |
141 | 0 | "jemalloc heap dump failed\n"); |
142 | 0 | } else { |
143 | 0 | HttpChannel::send_reply(req, HttpStatus::OK, |
144 | 0 | fmt::format("jemalloc heap dump success, dump file path: {}\n", |
145 | 0 | profile_file_name)); |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | } // namespace doris |