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