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/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
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 continue;
78 }
79
80 if (strncmp(line, "print fmt:", 10) == 0) {
81 break;
82 }
83
84 if (!disable_logging_for_testing)
85 PERFETTO_DLOG("Cannot parse line: \"%s\"\n", line);
86 return false;
87 }
88 return true;
89 }
90
91 } // namespace
92
93 // For example:
94 // "int foo" -> "foo"
95 // "u8 foo[(int)sizeof(struct blah)]" -> "foo"
96 // "char[] foo[16]" -> "foo"
97 // "something_went_wrong" -> ""
98 // "" -> ""
GetNameFromTypeAndName(const std::string & type_and_name)99 std::string GetNameFromTypeAndName(const std::string& type_and_name) {
100 size_t right = type_and_name.size();
101 if (right == 0)
102 return "";
103
104 if (type_and_name[type_and_name.size() - 1] == ']') {
105 right = type_and_name.rfind('[');
106 if (right == std::string::npos)
107 return "";
108 }
109
110 size_t left = type_and_name.rfind(' ', right);
111 if (left == std::string::npos)
112 return "";
113 left++;
114
115 std::string result = type_and_name.substr(left, right - left);
116 if (!IsCIdentifier(result))
117 return "";
118
119 return result;
120 }
121
ParseFtraceEventBody(std::string input,std::vector<FtraceEvent::Field> * common_fields,std::vector<FtraceEvent::Field> * fields,bool disable_logging_for_testing)122 bool ParseFtraceEventBody(std::string input,
123 std::vector<FtraceEvent::Field>* common_fields,
124 std::vector<FtraceEvent::Field>* fields,
125 bool disable_logging_for_testing) {
126 base::StringSplitter ss(std::move(input), '\n');
127 return ParseFtraceEventBody(&ss, common_fields, fields,
128 disable_logging_for_testing);
129 }
130
ParseFtraceEvent(std::string input,FtraceEvent * output)131 bool ParseFtraceEvent(std::string input, FtraceEvent* output) {
132 char buffer[MAX_FIELD_LENGTH + 1];
133
134 bool has_id = false;
135 bool has_name = false;
136
137 uint32_t id = 0;
138 std::string name;
139 std::vector<FtraceEvent::Field> common_fields;
140 std::vector<FtraceEvent::Field> fields;
141
142 for (base::StringSplitter ss(std::move(input), '\n'); ss.Next();) {
143 const char* line = ss.cur_token();
144 if (!has_id && sscanf(line, "ID: %d", &id) == 1) {
145 has_id = true;
146 continue;
147 }
148
149 if (!has_name &&
150 sscanf(line, "name: %" STRINGIFY(MAX_FIELD_LENGTH) "s", buffer) == 1) {
151 name = std::string(buffer);
152 has_name = true;
153 continue;
154 }
155
156 if (strcmp("format:", line) == 0) {
157 ParseFtraceEventBody(&ss, &common_fields, &fields,
158 /*disable_logging_for_testing=*/output == nullptr);
159 break;
160 }
161
162 if (output)
163 PERFETTO_DLOG("Cannot parse line: \"%s\"\n", line);
164 return false;
165 }
166
167 if (!has_id || !has_name || fields.empty()) {
168 if (output)
169 PERFETTO_DLOG("Could not parse format file: %s.\n",
170 !has_id ? "no ID found"
171 : !has_name ? "no name found" : "no fields found");
172 return false;
173 }
174
175 if (!output)
176 return true;
177
178 output->id = id;
179 output->name = name;
180 output->fields = std::move(fields);
181 output->common_fields = std::move(common_fields);
182
183 return true;
184 }
185
operator <<(::std::ostream & os,const FtraceEvent::Field & field)186 ::std::ostream& operator<<(::std::ostream& os,
187 const FtraceEvent::Field& field) {
188 PrintTo(field, &os);
189 return os;
190 }
191
192 // Allow gtest to pretty print FtraceEvent::Field.
PrintTo(const FtraceEvent::Field & field,::std::ostream * os)193 void PrintTo(const FtraceEvent::Field& field, ::std::ostream* os) {
194 *os << "FtraceEvent::Field(" << field.type_and_name << ", " << field.offset
195 << ", " << field.size << ", " << field.is_signed << ")";
196 }
197
198 } // namespace perfetto
199