xref: /aosp_15_r20/external/bcc/src/cc/common.cc (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /*
2  * Copyright (c) 2016 Catalysts GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <fstream>
17 #include <sstream>
18 
19 #include "common.h"
20 #include "bcc_libbpf_inc.h"
21 #include "vendor/optional.hpp"
22 #include "vendor/tinyformat.hpp"
23 
24 namespace ebpf {
25 
26 using std::experimental::optional;
27 
28 // Get enum value from BTF, since the enum may be anonymous, like:
29 //   [608] ENUM '(anon)' size=4 vlen=1
30 //   	'TASK_COMM_LEN' val=16
31 // we have to traverse the whole BTF.
32 // Though there is a BTF_KIND_ENUM64, but it is unlikely that it will
33 // be used as array size, we don't handle it here.
get_enum_val_from_btf(const char * name)34 static optional<int32_t> get_enum_val_from_btf(const char *name) {
35   optional<int32_t> val;
36 
37   auto btf = btf__load_vmlinux_btf();
38   if (libbpf_get_error(btf))
39     return {};
40 
41   for (size_t i = 1; i < btf__type_cnt(btf); i++) {
42     auto t = btf__type_by_id(btf, i);
43     if (btf_kind(t) != BTF_KIND_ENUM)
44       continue;
45 
46     auto m = btf_enum(t);
47     for (int j = 0, n = btf_vlen(t); j < n; j++, m++) {
48       if (!strcmp(btf__name_by_offset(btf, m->name_off), name)) {
49         val = m->val;
50         break;
51       }
52     }
53 
54     if (val)
55       break;
56   }
57 
58   btf__free(btf);
59   return val;
60 }
61 
read_cpu_range(std::string path)62 std::vector<int> read_cpu_range(std::string path) {
63   std::ifstream cpus_range_stream { path };
64   std::vector<int> cpus;
65   std::string cpu_range;
66 
67   while (std::getline(cpus_range_stream, cpu_range, ',')) {
68     std::size_t rangeop = cpu_range.find('-');
69     if (rangeop == std::string::npos) {
70       cpus.push_back(std::stoi(cpu_range));
71     }
72     else {
73       int start = std::stoi(cpu_range.substr(0, rangeop));
74       int end = std::stoi(cpu_range.substr(rangeop + 1));
75       for (int i = start; i <= end; i++)
76         cpus.push_back(i);
77     }
78   }
79   return cpus;
80 }
81 
get_online_cpus()82 std::vector<int> get_online_cpus() {
83   return read_cpu_range("/sys/devices/system/cpu/online");
84 }
85 
get_possible_cpus()86 std::vector<int> get_possible_cpus() {
87   return read_cpu_range("/sys/devices/system/cpu/possible");
88 }
89 
get_pid_exe(pid_t pid)90 std::string get_pid_exe(pid_t pid) {
91   char exe_path[4096];
92   int res;
93 
94   std::string exe_link = tfm::format("/proc/%d/exe", pid);
95   res = readlink(exe_link.c_str(), exe_path, sizeof(exe_path));
96   if (res == -1)
97     return "";
98   if (res >= static_cast<int>(sizeof(exe_path)))
99     res = sizeof(exe_path) - 1;
100   exe_path[res] = '\0';
101   return std::string(exe_path);
102 }
103 
104 enum class field_kind_t {
105     common,
106     data_loc,
107     regular,
108     invalid,
109     pad,
110 };
111 
_get_field_kind(std::string const & line,std::string & field_type,std::string & field_name,int * last_offset)112 static inline field_kind_t _get_field_kind(std::string const& line,
113                                            std::string& field_type,
114                                            std::string& field_name,
115                                            int *last_offset) {
116   auto field_pos = line.find("field:");
117   if (field_pos == std::string::npos)
118     return field_kind_t::invalid;
119 
120   auto field_semi_pos = line.find(';', field_pos);
121   if (field_semi_pos == std::string::npos)
122     return field_kind_t::invalid;
123 
124   auto offset_pos = line.find("offset:", field_semi_pos);
125   if (offset_pos == std::string::npos)
126     return field_kind_t::invalid;
127 
128   auto semi_pos = line.find(';', offset_pos);
129   if (semi_pos == std::string::npos)
130     return field_kind_t::invalid;
131 
132   auto offset_str = line.substr(offset_pos + 7,
133                               semi_pos - offset_pos - 7);
134   int offset = std::stoi(offset_str, nullptr);
135 
136   auto size_pos = line.find("size:", semi_pos);
137   if (size_pos == std::string::npos)
138     return field_kind_t::invalid;
139 
140   semi_pos = line.find(';', size_pos);
141   if (semi_pos == std::string::npos)
142     return field_kind_t::invalid;
143 
144   auto size_str = line.substr(size_pos + 5,
145                               semi_pos - size_pos - 5);
146   int size = std::stoi(size_str, nullptr);
147 
148   if (*last_offset < offset) {
149     *last_offset += 1;
150     return field_kind_t::pad;
151   }
152 
153   *last_offset = offset + size;
154 
155   auto field = line.substr(field_pos + 6/*"field:"*/,
156                            field_semi_pos - field_pos - 6);
157   auto pos = field.find_last_of("\t ");
158   if (pos == std::string::npos)
159     return field_kind_t::invalid;
160 
161   field_type = field.substr(0, pos);
162   field_name = field.substr(pos + 1);
163   if (field_type.find("__data_loc") != std::string::npos)
164     return field_kind_t::data_loc;
165   if (field_name.find("common_") == 0)
166     return field_kind_t::common;
167 
168   // We may have `char comm[TASK_COMM_LEN];` on kernel v5.18+
169   // Let's replace `TASK_COMM_LEN` with value extracted from BTF
170   if (field_name.find("[") != std::string::npos) {
171     auto pos1 = field_name.find("[");
172     auto pos2 = field_name.find("]");
173     auto dim = field_name.substr(pos1 + 1, pos2 - pos1 - 1);
174     if (!dim.empty() && !isdigit(dim[0])) {
175       auto v = get_enum_val_from_btf(dim.c_str());
176       if (v)
177         dim = std::to_string(*v);
178       field_name.replace(pos1 + 1, pos2 - pos1 - 1, dim, 0);
179     }
180     return field_kind_t::regular;
181   }
182 
183   // adjust the field_type based on the size of field
184   // otherwise, incorrect value may be retrieved for big endian
185   // and the field may have incorrect structure offset.
186   if (size == 2) {
187     if (field_type == "char" || field_type == "int8_t")
188       field_type = "s16";
189     if (field_type == "unsigned char" || field_type == "uint8_t")
190       field_type = "u16";
191   } else if (size == 4) {
192     if (field_type == "char" || field_type == "short" ||
193         field_type == "int8_t" || field_type == "int16_t")
194       field_type = "s32";
195     if (field_type == "unsigned char" || field_type == "unsigned short" ||
196         field_type == "uint8_t" || field_type == "uint16_t")
197       field_type = "u32";
198   } else if (size == 8) {
199     if (field_type == "char" || field_type == "short" || field_type == "int" ||
200         field_type == "int8_t" || field_type == "int16_t" ||
201         field_type == "int32_t" || field_type == "pid_t")
202       field_type = "s64";
203     if (field_type == "unsigned char" || field_type == "unsigned short" ||
204         field_type == "unsigned int" || field_type == "uint8_t" ||
205         field_type == "uint16_t" || field_type == "uint32_t" ||
206         field_type == "unsigned" || field_type == "u32" ||
207         field_type == "uid_t" || field_type == "gid_t")
208       field_type = "u64";
209   }
210 
211   return field_kind_t::regular;
212 }
213 
214 #define DEBUGFS_TRACEFS "/sys/kernel/debug/tracing"
215 #define TRACEFS "/sys/kernel/tracing"
216 
tracefs_path()217 std::string tracefs_path() {
218   static bool use_debugfs = access(DEBUGFS_TRACEFS, F_OK) == 0;
219   return use_debugfs ? DEBUGFS_TRACEFS : TRACEFS;
220 }
221 
tracepoint_format_file(std::string const & category,std::string const & event)222 std::string tracepoint_format_file(std::string const& category,
223                                    std::string const& event) {
224   return tracefs_path() + "/events/" + category + "/" + event + "/format";
225 }
226 
parse_tracepoint(std::istream & input,std::string const & category,std::string const & event)227 std::string parse_tracepoint(std::istream &input, std::string const& category,
228                              std::string const& event) {
229   std::string tp_struct = "struct tracepoint__" + category + "__" + event + " {\n";
230   tp_struct += "\tu64 __do_not_use__;\n";
231   int last_offset = 0, common_offset = 8;
232   for (std::string line; getline(input, line); ) {
233     std::string field_type, field_name;
234     field_kind_t kind;
235 
236     do {
237       kind = _get_field_kind(line, field_type, field_name, &last_offset);
238 
239       switch (kind) {
240       case field_kind_t::invalid:
241           continue;
242       case field_kind_t::common:
243             for (;common_offset < last_offset; common_offset++)
244             {
245               tp_struct += "\tchar __do_not_use__" + std::to_string(common_offset) + ";\n";
246             }
247           continue;
248       case field_kind_t::data_loc:
249           tp_struct += "\tint data_loc_" + field_name + ";\n";
250           break;
251       case field_kind_t::regular:
252           tp_struct += "\t" + field_type + " " + field_name + ";\n";
253           break;
254       case field_kind_t::pad:
255           tp_struct += "\tchar __pad_" + std::to_string(last_offset - 1) + ";\n";
256           break;
257       }
258     } while (kind == field_kind_t::pad);
259   }
260 
261   tp_struct += "};\n";
262   return tp_struct;
263 }
264 } // namespace ebpf
265