Home | History | Annotate | Download | only in clang
      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