Coverage Report

Created: 2026-04-23 11:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/pprof_utils.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 "util/pprof_utils.h"
19
20
#include <stdio.h>
21
#include <unistd.h>
22
23
#include <cstdlib>
24
#include <fstream> // IWYU pragma: keep
25
#include <memory>
26
27
#include "absl/strings/substitute.h"
28
#include "agent/utils.h"
29
#include "io/fs/local_file_system.h"
30
31
namespace doris {
32
namespace config {
33
extern std::string pprof_profile_dir;
34
}
35
36
0
Status PprofUtils::get_pprof_cmd(std::string* cmd) {
37
0
    AgentUtils util;
38
    // check if pprof cmd exist
39
0
    const static std::string tools_path = std::string(std::getenv("DORIS_HOME")) + "/tools/bin/";
40
0
    std::string pprof_cmd = tools_path + "pprof";
41
0
    std::string msg;
42
0
    bool rc = util.exec_cmd(pprof_cmd + " --version", &msg);
43
0
    if (!rc) {
44
        // not found in BE tools dir, found in system
45
0
        pprof_cmd = "pprof";
46
0
        rc = util.exec_cmd(pprof_cmd + " --version", &msg);
47
0
        if (!rc) {
48
0
            return Status::NotSupported(
49
0
                    "pprof: command not found in systemp PATH or be/tools/bin/. Install gperftools "
50
0
                    "first.");
51
0
        }
52
0
    }
53
0
    *cmd = pprof_cmd;
54
0
    return Status::OK();
55
0
}
56
57
0
Status PprofUtils::get_perf_cmd(std::string* cmd) {
58
0
    AgentUtils util;
59
    // check if perf cmd exist
60
0
    std::string perf_cmd = "perf";
61
0
    std::string msg;
62
0
    bool rc = util.exec_cmd(perf_cmd + " --version", &msg);
63
0
    if (!rc) {
64
0
        return Status::NotSupported("perf: command not found in systemp PATH");
65
0
    }
66
0
    *cmd = perf_cmd;
67
0
    return Status::OK();
68
0
}
69
70
0
Status PprofUtils::get_self_cmdline(std::string* cmd) {
71
    // get cmdline
72
0
    FILE* fp = fopen("/proc/self/cmdline", "r");
73
0
    if (fp == nullptr) {
74
0
        return Status::InternalError("Unable to open file: /proc/self/cmdline");
75
0
    }
76
0
    char buf[1024];
77
78
0
    Status res = Status::OK();
79
80
0
    if (fscanf(fp, "%1023s ", buf) != 1) {
81
0
        res = Status::InternalError("get_self_cmdline read buffer failed");
82
0
    }
83
0
    fclose(fp);
84
0
    *cmd = buf;
85
0
    return res;
86
0
}
87
88
Status PprofUtils::get_readable_profile(const std::string& file_or_content, bool is_file,
89
0
                                        std::stringstream* output) {
90
    // get pprof cmd
91
0
    std::string pprof_cmd;
92
0
    RETURN_IF_ERROR(PprofUtils::get_pprof_cmd(&pprof_cmd));
93
94
    // get self cmdline
95
0
    std::string self_cmdline;
96
0
    RETURN_IF_ERROR(PprofUtils::get_self_cmdline(&self_cmdline));
97
98
    // save file if necessary
99
0
    std::string final_file;
100
0
    if (!is_file) {
101
0
        std::stringstream tmp_file;
102
0
        tmp_file << config::pprof_profile_dir << "/pprof_profile." << getpid() << "." << rand();
103
0
        std::ofstream outfile;
104
0
        outfile.open(tmp_file.str().c_str());
105
0
        outfile << file_or_content;
106
0
        outfile.close();
107
0
        final_file = tmp_file.str();
108
0
    } else {
109
0
        final_file = file_or_content;
110
0
    }
111
112
    // parse raw with "pprof --text cmdline raw_file"
113
0
    std::string cmd_output;
114
0
    std::string final_cmd = pprof_cmd + absl::Substitute(" --text $0 $1", self_cmdline, final_file);
115
0
    AgentUtils util;
116
0
    LOG(INFO) << "begin to run command: " << final_cmd;
117
0
    bool rc = util.exec_cmd(final_cmd, &cmd_output, false);
118
119
    // delete raw file
120
0
    static_cast<void>(io::global_local_filesystem()->delete_file(file_or_content));
121
122
0
    if (!rc) {
123
0
        return Status::InternalError("Failed to execute command: {}", cmd_output);
124
0
    }
125
126
0
    (*output) << "Profile(Sample 30 seconds)" << std::endl;
127
0
    (*output) << cmd_output << std::endl;
128
0
    return Status::OK();
129
0
}
130
131
Status PprofUtils::generate_flamegraph(int32_t sample_seconds,
132
                                       const std::string& flame_graph_tool_dir, bool return_file,
133
0
                                       std::string* svg_file_or_content) {
134
    // get perf cmd
135
0
    std::string perf_cmd;
136
0
    RETURN_IF_ERROR(PprofUtils::get_perf_cmd(&perf_cmd));
137
138
    // check if FlameGraph has been installed
139
    // check stackcollapse-perf.pl and flamegraph.pl exist
140
0
    std::string stackcollapse_perf_pl = flame_graph_tool_dir + "/stackcollapse-perf.pl";
141
0
    std::string flamegraph_pl = flame_graph_tool_dir + "/flamegraph.pl";
142
0
    bool exists = false;
143
0
    RETURN_IF_ERROR(io::global_local_filesystem()->exists(stackcollapse_perf_pl, &exists));
144
0
    RETURN_IF_ERROR(io::global_local_filesystem()->exists(flamegraph_pl, &exists));
145
0
    if (!exists) {
146
0
        return Status::InternalError(
147
0
                "Missing stackcollapse-perf.pl or flamegraph.pl in FlameGraph");
148
0
    }
149
150
    // tmp output profile file
151
0
    std::stringstream tmp_file;
152
0
    tmp_file << config::pprof_profile_dir << "/cpu_perf." << getpid() << "." << rand();
153
154
    // sample
155
0
    std::stringstream cmd;
156
0
    cmd << perf_cmd << " record -m 2 -g -p " << getpid() << " -o " << tmp_file.str() << " -- sleep "
157
0
        << sample_seconds;
158
159
0
    AgentUtils util;
160
0
    std::string cmd_output;
161
0
    LOG(INFO) << "begin to run command: " << cmd.str();
162
0
    bool rc = util.exec_cmd(cmd.str(), &cmd_output);
163
0
    if (!rc) {
164
0
        static_cast<void>(io::global_local_filesystem()->delete_file(tmp_file.str()));
165
0
        return Status::InternalError("Failed to execute perf command: {}", cmd_output);
166
0
    }
167
168
    // generate flamegraph
169
170
0
    std::string res_content;
171
0
    if (return_file) {
172
0
        std::stringstream graph_file;
173
0
        graph_file << config::pprof_profile_dir << "/flamegraph." << getpid() << "." << rand()
174
0
                   << ".svg";
175
0
        std::stringstream gen_cmd;
176
0
        gen_cmd << perf_cmd << " script -i " << tmp_file.str() << " | " << stackcollapse_perf_pl
177
0
                << " | " << flamegraph_pl << " > " << graph_file.str();
178
0
        LOG(INFO) << "begin to run command: " << gen_cmd.str();
179
0
        rc = util.exec_cmd(gen_cmd.str(), &res_content);
180
0
        if (!rc) {
181
0
            static_cast<void>(io::global_local_filesystem()->delete_file(tmp_file.str()));
182
0
            static_cast<void>(io::global_local_filesystem()->delete_file(graph_file.str()));
183
0
            return Status::InternalError("Failed to execute perf script command: {}", res_content);
184
0
        }
185
0
        *svg_file_or_content = graph_file.str();
186
0
    } else {
187
0
        std::stringstream gen_cmd;
188
0
        gen_cmd << perf_cmd << " script -i " << tmp_file.str() << " | " << stackcollapse_perf_pl
189
0
                << " | " << flamegraph_pl;
190
0
        LOG(INFO) << "begin to run command: " << gen_cmd.str();
191
0
        rc = util.exec_cmd(gen_cmd.str(), &res_content, false);
192
0
        if (!rc) {
193
0
            static_cast<void>(io::global_local_filesystem()->delete_file(tmp_file.str()));
194
0
            return Status::InternalError("Failed to execute perf script command: {}", res_content);
195
0
        }
196
0
        *svg_file_or_content = res_content;
197
0
    }
198
0
    return Status::OK();
199
0
}
200
201
} // namespace doris