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 "vendor/tinyformat.hpp"
21
22 namespace ebpf {
23
read_cpu_range(std::string path)24 std::vector<int> read_cpu_range(std::string path) {
25 std::ifstream cpus_range_stream { path };
26 std::vector<int> cpus;
27 std::string cpu_range;
28
29 while (std::getline(cpus_range_stream, cpu_range, ',')) {
30 std::size_t rangeop = cpu_range.find('-');
31 if (rangeop == std::string::npos) {
32 cpus.push_back(std::stoi(cpu_range));
33 }
34 else {
35 int start = std::stoi(cpu_range.substr(0, rangeop));
36 int end = std::stoi(cpu_range.substr(rangeop + 1));
37 for (int i = start; i <= end; i++)
38 cpus.push_back(i);
39 }
40 }
41 return cpus;
42 }
43
get_online_cpus()44 std::vector<int> get_online_cpus() {
45 return read_cpu_range("/sys/devices/system/cpu/online");
46 }
47
get_possible_cpus()48 std::vector<int> get_possible_cpus() {
49 return read_cpu_range("/sys/devices/system/cpu/possible");
50 }
51
get_pid_exe(pid_t pid)52 std::string get_pid_exe(pid_t pid) {
53 char exe_path[4096];
54 int res;
55
56 std::string exe_link = tfm::format("/proc/%d/exe", pid);
57 res = readlink(exe_link.c_str(), exe_path, sizeof(exe_path));
58 if (res == -1)
59 return "";
60 if (res >= static_cast<int>(sizeof(exe_path)))
61 res = sizeof(exe_path) - 1;
62 exe_path[res] = '\0';
63 return std::string(exe_path);
64 }
65
66 enum class field_kind_t {
67 common,
68 data_loc,
69 regular,
70 invalid,
71 pad,
72 };
73
_get_field_kind(std::string const & line,std::string & field_type,std::string & field_name,int * last_offset)74 static inline field_kind_t _get_field_kind(std::string const& line,
75 std::string& field_type,
76 std::string& field_name,
77 int *last_offset) {
78 auto field_pos = line.find("field:");
79 if (field_pos == std::string::npos)
80 return field_kind_t::invalid;
81
82 auto field_semi_pos = line.find(';', field_pos);
83 if (field_semi_pos == std::string::npos)
84 return field_kind_t::invalid;
85
86 auto offset_pos = line.find("offset:", field_semi_pos);
87 if (offset_pos == std::string::npos)
88 return field_kind_t::invalid;
89
90 auto semi_pos = line.find(';', offset_pos);
91 if (semi_pos == std::string::npos)
92 return field_kind_t::invalid;
93
94 auto offset_str = line.substr(offset_pos + 7,
95 semi_pos - offset_pos - 7);
96 int offset = std::stoi(offset_str, nullptr);
97
98 auto size_pos = line.find("size:", semi_pos);
99 if (size_pos == std::string::npos)
100 return field_kind_t::invalid;
101
102 semi_pos = line.find(';', size_pos);
103 if (semi_pos == std::string::npos)
104 return field_kind_t::invalid;
105
106 auto size_str = line.substr(size_pos + 5,
107 semi_pos - size_pos - 5);
108 int size = std::stoi(size_str, nullptr);
109
110 if (*last_offset < offset) {
111 *last_offset += 1;
112 return field_kind_t::pad;
113 }
114
115 *last_offset = offset + size;
116
117 auto field = line.substr(field_pos + 6/*"field:"*/,
118 field_semi_pos - field_pos - 6);
119 auto pos = field.find_last_of("\t ");
120 if (pos == std::string::npos)
121 return field_kind_t::invalid;
122
123 field_type = field.substr(0, pos);
124 field_name = field.substr(pos + 1);
125 if (field_type.find("__data_loc") != std::string::npos)
126 return field_kind_t::data_loc;
127 if (field_name.find("common_") == 0)
128 return field_kind_t::common;
129 // do not change type definition for array
130 if (field_name.find("[") != std::string::npos)
131 return field_kind_t::regular;
132
133 // adjust the field_type based on the size of field
134 // otherwise, incorrect value may be retrieved for big endian
135 // and the field may have incorrect structure offset.
136 if (size == 2) {
137 if (field_type == "char" || field_type == "int8_t")
138 field_type = "s16";
139 if (field_type == "unsigned char" || field_type == "uint8_t")
140 field_type = "u16";
141 } else if (size == 4) {
142 if (field_type == "char" || field_type == "short" ||
143 field_type == "int8_t" || field_type == "int16_t")
144 field_type = "s32";
145 if (field_type == "unsigned char" || field_type == "unsigned short" ||
146 field_type == "uint8_t" || field_type == "uint16_t")
147 field_type = "u32";
148 } else if (size == 8) {
149 if (field_type == "char" || field_type == "short" || field_type == "int" ||
150 field_type == "int8_t" || field_type == "int16_t" ||
151 field_type == "int32_t" || field_type == "pid_t")
152 field_type = "s64";
153 if (field_type == "unsigned char" || field_type == "unsigned short" ||
154 field_type == "unsigned int" || field_type == "uint8_t" ||
155 field_type == "uint16_t" || field_type == "uint32_t" ||
156 field_type == "unsigned" || field_type == "u32" ||
157 field_type == "uid_t" || field_type == "gid_t")
158 field_type = "u64";
159 }
160
161 return field_kind_t::regular;
162 }
163
parse_tracepoint(std::istream & input,std::string const & category,std::string const & event)164 std::string parse_tracepoint(std::istream &input, std::string const& category,
165 std::string const& event) {
166 std::string tp_struct = "struct tracepoint__" + category + "__" + event + " {\n";
167 tp_struct += "\tu64 __do_not_use__;\n";
168 int last_offset = 0, common_offset = 8;
169 for (std::string line; getline(input, line); ) {
170 std::string field_type, field_name;
171 field_kind_t kind;
172
173 do {
174 kind = _get_field_kind(line, field_type, field_name, &last_offset);
175
176 switch (kind) {
177 case field_kind_t::invalid:
178 continue;
179 case field_kind_t::common:
180 for (;common_offset < last_offset; common_offset++)
181 {
182 tp_struct += "\tchar __do_not_use__" + std::to_string(common_offset) + ";\n";
183 }
184 continue;
185 case field_kind_t::data_loc:
186 tp_struct += "\tint data_loc_" + field_name + ";\n";
187 break;
188 case field_kind_t::regular:
189 tp_struct += "\t" + field_type + " " + field_name + ";\n";
190 break;
191 case field_kind_t::pad:
192 tp_struct += "\tchar __pad_" + std::to_string(last_offset - 1) + ";\n";
193 break;
194 }
195 } while (kind == field_kind_t::pad);
196 }
197
198 tp_struct += "};\n";
199 return tp_struct;
200 }
201 } // namespace ebpf
202