Home | History | Annotate | Download | only in ftrace_reader
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      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 
     17 #include "perfetto/ftrace_reader/format_parser.h"
     18 
     19 #include <string.h>
     20 
     21 #include <iosfwd>
     22 #include <iostream>
     23 #include <memory>
     24 #include <regex>
     25 #include <string>
     26 #include <vector>
     27 
     28 #include "perfetto/base/string_splitter.h"
     29 #include "perfetto/base/utils.h"
     30 
     31 namespace perfetto {
     32 namespace {
     33 
     34 #define MAX_FIELD_LENGTH 127
     35 #define STRINGIFY(x) STRINGIFY2(x)
     36 #define STRINGIFY2(x) #x
     37 
     38 const char* kCommonFieldPrefix = "common_";
     39 
     40 bool IsCommonFieldName(const std::string& name) {
     41   return name.compare(0, strlen(kCommonFieldPrefix), kCommonFieldPrefix) == 0;
     42 }
     43 
     44 bool IsCIdentifier(const std::string& s) {
     45   for (const char c : s) {
     46     if (!(std::isalnum(c) || c == '_'))
     47       return false;
     48   }
     49   return !s.empty() && !std::isdigit(s[0]);
     50 }
     51 
     52 }  // namespace
     53 
     54 // For example:
     55 // "int foo" -> "foo"
     56 // "u8 foo[(int)sizeof(struct blah)]" -> "foo"
     57 // "char[] foo[16]" -> "foo"
     58 // "something_went_wrong" -> ""
     59 // "" -> ""
     60 std::string GetNameFromTypeAndName(const std::string& type_and_name) {
     61   size_t right = type_and_name.size();
     62   if (right == 0)
     63     return "";
     64 
     65   if (type_and_name[type_and_name.size() - 1] == ']') {
     66     right = type_and_name.rfind('[');
     67     if (right == std::string::npos)
     68       return "";
     69   }
     70 
     71   size_t left = type_and_name.rfind(' ', right);
     72   if (left == std::string::npos)
     73     return "";
     74   left++;
     75 
     76   std::string result = type_and_name.substr(left, right - left);
     77   if (!IsCIdentifier(result))
     78     return "";
     79 
     80   return result;
     81 }
     82 
     83 bool ParseFtraceEvent(const std::string& input, FtraceEvent* output) {
     84   char buffer[MAX_FIELD_LENGTH + 1];
     85 
     86   bool has_id = false;
     87   bool has_name = false;
     88 
     89   uint32_t id = 0;
     90   std::string name;
     91   std::vector<FtraceEvent::Field> common_fields;
     92   std::vector<FtraceEvent::Field> fields;
     93 
     94   for (base::StringSplitter ss(input, '\n'); ss.Next();) {
     95     const char* line = ss.cur_token();
     96     if (!has_id && sscanf(line, "ID: %d", &id) == 1) {
     97       has_id = true;
     98       continue;
     99     }
    100 
    101     if (!has_name &&
    102         sscanf(line, "name: %" STRINGIFY(MAX_FIELD_LENGTH) "s", buffer) == 1) {
    103       name = std::string(buffer);
    104       has_name = true;
    105       continue;
    106     }
    107 
    108     if (strcmp("format:", line) == 0) {
    109       continue;
    110     }
    111 
    112     uint16_t offset = 0;
    113     uint16_t size = 0;
    114     int is_signed = 0;
    115     if (sscanf(line,
    116                "\tfield:%" STRINGIFY(MAX_FIELD_LENGTH) "[^;];\toffset: "
    117                                                        "%hu;\tsize: "
    118                                                        "%hu;\tsigned: %d;",
    119                buffer, &offset, &size, &is_signed) == 4) {
    120       std::string type_and_name(buffer);
    121 
    122       FtraceEvent::Field field{type_and_name, offset, size, is_signed == 1};
    123 
    124       if (IsCommonFieldName(GetNameFromTypeAndName(type_and_name))) {
    125         common_fields.push_back(field);
    126       } else {
    127         fields.push_back(field);
    128       }
    129 
    130       continue;
    131     }
    132 
    133     if (strncmp(line, "print fmt:", 10) == 0) {
    134       break;
    135     }
    136 
    137     if (output)
    138       fprintf(stderr, "Cannot parse line: \"%s\"\n", line);
    139     return false;
    140   }
    141 
    142   if (!has_id || !has_name || fields.empty()) {
    143     if (output)
    144       fprintf(stderr, "Could not parse format file: %s.\n",
    145               !has_id ? "no ID found"
    146                       : !has_name ? "no name found" : "no fields found");
    147     return false;
    148   }
    149 
    150   if (!output)
    151     return true;
    152 
    153   output->id = id;
    154   output->name = name;
    155   output->fields = std::move(fields);
    156   output->common_fields = std::move(common_fields);
    157 
    158   return true;
    159 }
    160 
    161 ::std::ostream& operator<<(::std::ostream& os,
    162                            const FtraceEvent::Field& field) {
    163   PrintTo(field, &os);
    164   return os;
    165 }
    166 
    167 // Allow gtest to pretty print FtraceEvent::Field.
    168 void PrintTo(const FtraceEvent::Field& field, ::std::ostream* os) {
    169   *os << "FtraceEvent::Field(" << field.type_and_name << ", " << field.offset
    170       << ", " << field.size << ", " << field.is_signed << ")";
    171 }
    172 
    173 }  // namespace perfetto
    174