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 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 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 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 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 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 229 TracepointTypeConsumer::TracepointTypeConsumer(ASTContext &C, Rewriter &rewriter) 230 : visitor_(C, rewriter) { 231 } 232 233 bool TracepointTypeConsumer::HandleTopLevelDecl(DeclGroupRef Group) { 234 for (auto D : Group) 235 visitor_.TraverseDecl(D); 236 return true; 237 } 238 239 TracepointFrontendAction::TracepointFrontendAction(llvm::raw_ostream &os) 240 : os_(os), rewriter_(new Rewriter) { 241 } 242 243 void TracepointFrontendAction::EndSourceFileAction() { 244 rewriter_->getEditBuffer(rewriter_->getSourceMgr().getMainFileID()).write(os_); 245 os_.flush(); 246 } 247 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