• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/traced/probes/ftrace/format_parser.h"
18 
19 #include <string.h>
20 
21 #include <iosfwd>
22 #include <iostream>
23 #include <memory>
24 #include <string>
25 #include <vector>
26 
27 #include "perfetto/base/logging.h"
28 #include "perfetto/ext/base/string_splitter.h"
29 #include "perfetto/ext/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 
IsCommonFieldName(const std::string & name)40 bool IsCommonFieldName(const std::string& name) {
41   return name.compare(0, strlen(kCommonFieldPrefix), kCommonFieldPrefix) == 0;
42 }
43 
IsCIdentifier(const std::string & s)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 
ParseFtraceEventBody(base::StringSplitter * ss,std::vector<FtraceEvent::Field> * common_fields,std::vector<FtraceEvent::Field> * fields,bool disable_logging_for_testing)52 bool ParseFtraceEventBody(base::StringSplitter* ss,
53                           std::vector<FtraceEvent::Field>* common_fields,
54                           std::vector<FtraceEvent::Field>* fields,
55                           bool disable_logging_for_testing) {
56   PERFETTO_DCHECK(common_fields || fields);
57   char buffer[MAX_FIELD_LENGTH + 1];
58   while (ss->Next()) {
59     const char* line = ss->cur_token();
60     uint16_t offset = 0;
61     uint16_t size = 0;
62     int is_signed = 0;
63     if (sscanf(line,
64                "\tfield:%" STRINGIFY(MAX_FIELD_LENGTH) "[^;];\toffset: "
65                                                        "%hu;\tsize: "
66                                                        "%hu;\tsigned: %d;",
67                buffer, &offset, &size, &is_signed) == 4) {
68       std::string type_and_name(buffer);
69 
70       FtraceEvent::Field field{type_and_name, offset, size, is_signed == 1};
71 
72       if (IsCommonFieldName(GetNameFromTypeAndName(type_and_name))) {
73         if (common_fields)
74           common_fields->push_back(field);
75       } else if (fields) {
76         fields->push_back(field);
77       }
78       continue;
79     }
80 
81     if (strncmp(line, "print fmt:", 10) == 0) {
82       break;
83     }
84 
85     if (!disable_logging_for_testing)
86       PERFETTO_DLOG("Cannot parse line: \"%s\"\n", line);
87     return false;
88   }
89   return true;
90 }
91 
92 }  // namespace
93 
94 // For example:
95 // "int foo" -> "foo"
96 // "u8 foo[(int)sizeof(struct blah)]" -> "foo"
97 // "char[] foo[16]" -> "foo"
98 // "something_went_wrong" -> ""
99 // "" -> ""
GetNameFromTypeAndName(const std::string & type_and_name)100 std::string GetNameFromTypeAndName(const std::string& type_and_name) {
101   size_t right = type_and_name.size();
102   if (right == 0)
103     return "";
104 
105   if (type_and_name[type_and_name.size() - 1] == ']') {
106     right = type_and_name.rfind('[');
107     if (right == std::string::npos)
108       return "";
109   }
110 
111   size_t left = type_and_name.rfind(' ', right);
112   if (left == std::string::npos)
113     return "";
114   left++;
115 
116   std::string result = type_and_name.substr(left, right - left);
117   if (!IsCIdentifier(result))
118     return "";
119 
120   return result;
121 }
122 
ParseFtraceEventBody(std::string input,std::vector<FtraceEvent::Field> * common_fields,std::vector<FtraceEvent::Field> * fields,bool disable_logging_for_testing)123 bool ParseFtraceEventBody(std::string input,
124                           std::vector<FtraceEvent::Field>* common_fields,
125                           std::vector<FtraceEvent::Field>* fields,
126                           bool disable_logging_for_testing) {
127   base::StringSplitter ss(std::move(input), '\n');
128   return ParseFtraceEventBody(&ss, common_fields, fields,
129                               disable_logging_for_testing);
130 }
131 
ParseFtraceEvent(std::string input,FtraceEvent * output)132 bool ParseFtraceEvent(std::string input, FtraceEvent* output) {
133   char buffer[MAX_FIELD_LENGTH + 1];
134 
135   bool has_id = false;
136   bool has_name = false;
137 
138   uint32_t id = 0;
139   std::string name;
140   std::vector<FtraceEvent::Field> common_fields;
141   std::vector<FtraceEvent::Field> fields;
142 
143   for (base::StringSplitter ss(std::move(input), '\n'); ss.Next();) {
144     const char* line = ss.cur_token();
145     if (!has_id && sscanf(line, "ID: %u", &id) == 1) {
146       has_id = true;
147       continue;
148     }
149 
150     if (!has_name &&
151         sscanf(line, "name: %" STRINGIFY(MAX_FIELD_LENGTH) "s", buffer) == 1) {
152       name = std::string(buffer);
153       has_name = true;
154       continue;
155     }
156 
157     if (strcmp("format:", line) == 0) {
158       ParseFtraceEventBody(&ss, &common_fields, &fields,
159                            /*disable_logging_for_testing=*/output == nullptr);
160       break;
161     }
162 
163     if (output)
164       PERFETTO_DLOG("Cannot parse line: \"%s\"\n", line);
165     return false;
166   }
167 
168   if (!has_id || !has_name || fields.empty()) {
169     if (output)
170       PERFETTO_DLOG("Could not parse format file: %s.\n",
171                     !has_id ? "no ID found"
172                             : !has_name ? "no name found" : "no fields found");
173     return false;
174   }
175 
176   if (!output)
177     return true;
178 
179   output->id = id;
180   output->name = name;
181   output->fields = std::move(fields);
182   output->common_fields = std::move(common_fields);
183 
184   return true;
185 }
186 
operator <<(::std::ostream & os,const FtraceEvent::Field & field)187 ::std::ostream& operator<<(::std::ostream& os,
188                            const FtraceEvent::Field& field) {
189   PrintTo(field, &os);
190   return os;
191 }
192 
193 // Allow gtest to pretty print FtraceEvent::Field.
PrintTo(const FtraceEvent::Field & field,::std::ostream * os)194 void PrintTo(const FtraceEvent::Field& field, ::std::ostream* os) {
195   *os << "FtraceEvent::Field(" << field.type_and_name << ", " << field.offset
196       << ", " << field.size << ", " << field.is_signed << ")";
197 }
198 
199 }  // namespace perfetto
200