• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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