1 /*
2 * Copyright (c) 2016 Sasha Goldshtein
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 <linux/bpf.h>
17 #include <linux/version.h>
18 #include <sys/utsname.h>
19 #include <unistd.h>
20
21 #include <fstream>
22 #include <regex>
23
24 #include <clang/AST/ASTConsumer.h>
25 #include <clang/AST/ASTContext.h>
26 #include <clang/AST/RecordLayout.h>
27 #include <clang/Frontend/CompilerInstance.h>
28 #include <clang/Frontend/MultiplexConsumer.h>
29 #include <clang/Rewrite/Core/Rewriter.h>
30
31 #include "frontend_action_common.h"
32 #include "tp_frontend_action.h"
33
34 namespace ebpf {
35
36 using std::map;
37 using std::set;
38 using std::string;
39 using std::to_string;
40 using std::unique_ptr;
41 using std::vector;
42 using std::regex;
43 using std::smatch;
44 using std::regex_search;
45 using std::ifstream;
46 using namespace clang;
47
TracepointTypeVisitor(ASTContext & C,Rewriter & rewriter)48 TracepointTypeVisitor::TracepointTypeVisitor(ASTContext &C, Rewriter &rewriter)
49 : C(C), diag_(C.getDiagnostics()), rewriter_(rewriter), out_(llvm::errs()) {
50 }
51
52 enum class field_kind_t {
53 common,
54 data_loc,
55 regular,
56 invalid
57 };
58
_get_field_kind(string const & line,string & field_type,string & field_name)59 static inline field_kind_t _get_field_kind(string const& line,
60 string& field_type,
61 string& field_name) {
62 auto field_pos = line.find("field:");
63 if (field_pos == string::npos)
64 return field_kind_t::invalid;
65
66 auto field_semi_pos = line.find(';', field_pos);
67 if (field_semi_pos == string::npos)
68 return field_kind_t::invalid;
69
70 auto offset_pos = line.find("offset:", field_semi_pos);
71 if (offset_pos == string::npos)
72 return field_kind_t::invalid;
73
74 auto semi_pos = line.find(';', offset_pos);
75 if (semi_pos == string::npos)
76 return field_kind_t::invalid;
77
78 auto size_pos = line.find("size:", semi_pos);
79 if (size_pos == string::npos)
80 return field_kind_t::invalid;
81
82 semi_pos = line.find(';', size_pos);
83 if (semi_pos == string::npos)
84 return field_kind_t::invalid;
85
86 auto size_str = line.substr(size_pos + 5,
87 semi_pos - size_pos - 5);
88 int size = std::stoi(size_str, nullptr);
89
90 auto field = line.substr(field_pos + 6/*"field:"*/,
91 field_semi_pos - field_pos - 6);
92 auto pos = field.find_last_of("\t ");
93 if (pos == string::npos)
94 return field_kind_t::invalid;
95
96 field_type = field.substr(0, pos);
97 field_name = field.substr(pos + 1);
98 if (field_type.find("__data_loc") != string::npos)
99 return field_kind_t::data_loc;
100 if (field_name.find("common_") == 0)
101 return field_kind_t::common;
102 // do not change type definition for array
103 if (field_name.find("[") != string::npos)
104 return field_kind_t::regular;
105
106 // adjust the field_type based on the size of field
107 // otherwise, incorrect value may be retrieved for big endian
108 // and the field may have incorrect structure offset.
109 if (size == 2) {
110 if (field_type == "char" || field_type == "int8_t")
111 field_type = "s16";
112 if (field_type == "unsigned char" || field_type == "uint8_t")
113 field_type = "u16";
114 } else if (size == 4) {
115 if (field_type == "char" || field_type == "short" ||
116 field_type == "int8_t" || field_type == "int16_t")
117 field_type = "s32";
118 if (field_type == "unsigned char" || field_type == "unsigned short" ||
119 field_type == "uint8_t" || field_type == "uint16_t")
120 field_type = "u32";
121 } else if (size == 8) {
122 if (field_type == "char" || field_type == "short" || field_type == "int" ||
123 field_type == "int8_t" || field_type == "int16_t" ||
124 field_type == "int32_t" || field_type == "pid_t")
125 field_type = "s64";
126 if (field_type == "unsigned char" || field_type == "unsigned short" ||
127 field_type == "unsigned int" || field_type == "uint8_t" ||
128 field_type == "uint16_t" || field_type == "uint32_t" ||
129 field_type == "unsigned" || field_type == "u32" ||
130 field_type == "uid_t" || field_type == "gid_t")
131 field_type = "u64";
132 }
133
134 return field_kind_t::regular;
135 }
136
GenerateTracepointStruct(SourceLocation loc,string const & category,string const & event)137 string TracepointTypeVisitor::GenerateTracepointStruct(
138 SourceLocation loc, string const& category, string const& event) {
139 string format_file = "/sys/kernel/debug/tracing/events/" +
140 category + "/" + event + "/format";
141 ifstream input(format_file.c_str());
142 if (!input)
143 return "";
144
145 string tp_struct = "struct tracepoint__" + category + "__" + event + " {\n";
146 tp_struct += "\tu64 __do_not_use__;\n";
147 for (string line; getline(input, line); ) {
148 string field_type, field_name;
149 switch (_get_field_kind(line, field_type, field_name)) {
150 case field_kind_t::invalid:
151 case field_kind_t::common:
152 continue;
153 case field_kind_t::data_loc:
154 tp_struct += "\tint data_loc_" + field_name + ";\n";
155 break;
156 case field_kind_t::regular:
157 tp_struct += "\t" + field_type + " " + field_name + ";\n";
158 break;
159 }
160 }
161
162 tp_struct += "};\n";
163 return tp_struct;
164 }
165
_is_tracepoint_struct_type(string const & type_name,string & tp_category,string & tp_event)166 static inline bool _is_tracepoint_struct_type(string const& type_name,
167 string& tp_category,
168 string& tp_event) {
169 // We are looking to roughly match the regex:
170 // (?:struct|class)\s+tracepoint__(\S+)__(\S+)
171 // Not using std::regex because older versions of GCC don't support it yet.
172 // E.g., the libstdc++ that ships with Ubuntu 14.04.
173
174 auto first_space_pos = type_name.find_first_of("\t ");
175 if (first_space_pos == string::npos)
176 return false;
177 auto first_tok = type_name.substr(0, first_space_pos);
178 if (first_tok != "struct" && first_tok != "class")
179 return false;
180
181 auto non_space_pos = type_name.find_first_not_of("\t ", first_space_pos);
182 auto second_space_pos = type_name.find_first_of("\t ", non_space_pos);
183 auto second_tok = type_name.substr(non_space_pos,
184 second_space_pos - non_space_pos);
185 if (second_tok.find("tracepoint__") != 0)
186 return false;
187
188 auto tp_event_pos = second_tok.rfind("__");
189 if (tp_event_pos == string::npos)
190 return false;
191 tp_event = second_tok.substr(tp_event_pos + 2);
192
193 auto tp_category_pos = second_tok.find("__");
194 if (tp_category_pos == tp_event_pos)
195 return false;
196 tp_category = second_tok.substr(tp_category_pos + 2,
197 tp_event_pos - tp_category_pos - 2);
198 return true;
199 }
200
201
VisitFunctionDecl(FunctionDecl * D)202 bool TracepointTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
203 if (D->isExternallyVisible() && D->hasBody()) {
204 // If this function has a tracepoint structure as an argument,
205 // add that structure declaration based on the structure name.
206 for (auto it = D->param_begin(); it != D->param_end(); ++it) {
207 auto arg = *it;
208 auto type = arg->getType();
209 if (type->isPointerType() &&
210 type->getPointeeType()->isStructureOrClassType()) {
211 auto type_name = type->getPointeeType().getAsString();
212 string tp_cat, tp_evt;
213 if (_is_tracepoint_struct_type(type_name, tp_cat, tp_evt)) {
214 string tp_struct = GenerateTracepointStruct(
215 GET_BEGINLOC(D), tp_cat, tp_evt);
216 // Get the actual function declaration point (the macro instantiation
217 // point if using the TRACEPOINT_PROBE macro instead of the macro
218 // declaration point in bpf_helpers.h).
219 auto insert_loc = GET_BEGINLOC(D);
220 insert_loc = rewriter_.getSourceMgr().getFileLoc(insert_loc);
221 rewriter_.InsertText(insert_loc, tp_struct);
222 }
223 }
224 }
225 }
226 return true;
227 }
228
TracepointTypeConsumer(ASTContext & C,Rewriter & rewriter)229 TracepointTypeConsumer::TracepointTypeConsumer(ASTContext &C, Rewriter &rewriter)
230 : visitor_(C, rewriter) {
231 }
232
HandleTopLevelDecl(DeclGroupRef Group)233 bool TracepointTypeConsumer::HandleTopLevelDecl(DeclGroupRef Group) {
234 for (auto D : Group)
235 visitor_.TraverseDecl(D);
236 return true;
237 }
238
TracepointFrontendAction(llvm::raw_ostream & os)239 TracepointFrontendAction::TracepointFrontendAction(llvm::raw_ostream &os)
240 : os_(os), rewriter_(new Rewriter) {
241 }
242
EndSourceFileAction()243 void TracepointFrontendAction::EndSourceFileAction() {
244 rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_);
245 os_.flush();
246 }
247
CreateASTConsumer(CompilerInstance & Compiler,llvm::StringRef InFile)248 unique_ptr<ASTConsumer> TracepointFrontendAction::CreateASTConsumer(
249 CompilerInstance &Compiler, llvm::StringRef InFile) {
250 rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts());
251 return unique_ptr<ASTConsumer>(new TracepointTypeConsumer(
252 Compiler.getASTContext(), *rewriter_));
253 }
254
255 }
256