1 /*
2 * Copyright (C) 2016 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 "tracing.h"
18
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include <map>
23 #include <optional>
24 #include <string>
25 #include <vector>
26
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/parseint.h>
30 #include <android-base/stringprintf.h>
31 #include <android-base/strings.h>
32
33 #include "environment.h"
34 #include "perf_event.h"
35 #include "utils.h"
36
37 const char TRACING_INFO_MAGIC[10] = {23, 8, 68, 't', 'r',
38 'a', 'c', 'i', 'n', 'g'};
39
40 template <class T>
AppendData(std::vector<char> & data,const T & s)41 void AppendData(std::vector<char>& data, const T& s) {
42 const char* p = reinterpret_cast<const char*>(&s);
43 data.insert(data.end(), p, p + sizeof(T));
44 }
45
AppendData(std::vector<char> & data,const char * s)46 static void AppendData(std::vector<char>& data, const char* s) {
47 data.insert(data.end(), s, s + strlen(s) + 1);
48 }
49
50 template <>
AppendData(std::vector<char> & data,const std::string & s)51 void AppendData(std::vector<char>& data, const std::string& s) {
52 data.insert(data.end(), s.c_str(), s.c_str() + s.size() + 1);
53 }
54
55 template <>
MoveFromBinaryFormat(std::string & data,const char * & p)56 void MoveFromBinaryFormat(std::string& data, const char*& p) {
57 data.clear();
58 while (*p != '\0') {
59 data.push_back(*p++);
60 }
61 p++;
62 }
63
AppendFile(std::vector<char> & data,const std::string & file,uint32_t file_size_bytes=8)64 static void AppendFile(std::vector<char>& data, const std::string& file,
65 uint32_t file_size_bytes = 8) {
66 if (file_size_bytes == 8) {
67 uint64_t file_size = file.size();
68 AppendData(data, file_size);
69 } else if (file_size_bytes == 4) {
70 uint32_t file_size = file.size();
71 AppendData(data, file_size);
72 }
73 data.insert(data.end(), file.begin(), file.end());
74 }
75
DetachFile(const char * & p,std::string & file,uint32_t file_size_bytes=8)76 static void DetachFile(const char*& p, std::string& file,
77 uint32_t file_size_bytes = 8) {
78 uint64_t file_size = ConvertBytesToValue(p, file_size_bytes);
79 p += file_size_bytes;
80 file.clear();
81 file.insert(file.end(), p, p + file_size);
82 p += file_size;
83 }
84
ReadTraceFsFile(const std::string & path,std::string * content,bool report_error=true)85 static bool ReadTraceFsFile(const std::string& path, std::string* content, bool report_error = true) {
86 const char* tracefs_dir = GetTraceFsDir();
87 if (tracefs_dir == nullptr) {
88 if (report_error) {
89 LOG(ERROR) << "tracefs doesn't exist";
90 }
91 return false;
92 }
93 std::string full_path = tracefs_dir + path;
94 if (!android::base::ReadFileToString(full_path, content)) {
95 if (report_error) {
96 PLOG(ERROR) << "failed to read " << full_path;
97 }
98 return false;
99 }
100 return true;
101 }
102
103 struct TraceType {
104 std::string system;
105 std::string name;
106 };
107
108 class TracingFile {
109 public:
110 TracingFile();
111 bool RecordHeaderFiles();
112 void RecordFtraceFiles(const std::vector<TraceType>& trace_types);
113 bool RecordEventFiles(const std::vector<TraceType>& trace_types);
114 bool RecordKallsymsFile();
115 bool RecordPrintkFormatsFile();
116 std::vector<char> BinaryFormat() const;
117 void LoadFromBinary(const std::vector<char>& data);
118 void Dump(size_t indent) const;
119 std::vector<TracingFormat> LoadTracingFormatsFromEventFiles() const;
GetKallsymsFile() const120 const std::string& GetKallsymsFile() const { return kallsyms_file; }
GetPageSize() const121 uint32_t GetPageSize() const { return page_size; }
122
123 private:
124 char magic[10];
125 std::string version;
126 char endian;
127 uint8_t size_of_long;
128 uint32_t page_size;
129 std::string header_page_file;
130 std::string header_event_file;
131
132 std::vector<std::string> ftrace_format_files;
133 // pair of system, format_file_data.
134 std::vector<std::pair<std::string, std::string>> event_format_files;
135
136 std::string kallsyms_file;
137 std::string printk_formats_file;
138 };
139
TracingFile()140 TracingFile::TracingFile() {
141 memcpy(magic, TRACING_INFO_MAGIC, sizeof(TRACING_INFO_MAGIC));
142 version = "0.5";
143 endian = 0;
144 size_of_long = static_cast<int>(sizeof(long)); // NOLINT(google-runtime-int)
145 page_size = static_cast<uint32_t>(::GetPageSize());
146 }
147
RecordHeaderFiles()148 bool TracingFile::RecordHeaderFiles() {
149 return ReadTraceFsFile("/events/header_page", &header_page_file) &&
150 ReadTraceFsFile("/events/header_event", &header_event_file);
151 }
152
RecordFtraceFiles(const std::vector<TraceType> & trace_types)153 void TracingFile::RecordFtraceFiles(const std::vector<TraceType>& trace_types) {
154 for (const auto& type : trace_types) {
155 std::string format_data;
156 if (ReadTraceFsFile("/events/ftrace/" + type.name + "/format", &format_data, false)) {
157 ftrace_format_files.emplace_back(std::move(format_data));
158 }
159 }
160 }
161
RecordEventFiles(const std::vector<TraceType> & trace_types)162 bool TracingFile::RecordEventFiles(const std::vector<TraceType>& trace_types) {
163 for (const auto& type : trace_types) {
164 std::string format_data;
165 if (!ReadTraceFsFile("/events/" + type.system + "/" + type.name + "/format", &format_data)) {
166 return false;
167 }
168 event_format_files.emplace_back(type.system, std::move(format_data));
169 }
170 return true;
171 }
172
RecordPrintkFormatsFile()173 bool TracingFile::RecordPrintkFormatsFile() {
174 return ReadTraceFsFile("/printk_formats", &printk_formats_file);
175 }
176
BinaryFormat() const177 std::vector<char> TracingFile::BinaryFormat() const {
178 std::vector<char> ret;
179 ret.insert(ret.end(), magic, magic + sizeof(magic));
180 AppendData(ret, version);
181 ret.push_back(endian);
182 AppendData(ret, size_of_long);
183 AppendData(ret, page_size);
184 AppendData(ret, "header_page");
185 AppendFile(ret, header_page_file);
186 AppendData(ret, "header_event");
187 AppendFile(ret, header_event_file);
188 int count = static_cast<int>(ftrace_format_files.size());
189 AppendData(ret, count);
190 for (const auto& format : ftrace_format_files) {
191 AppendFile(ret, format);
192 }
193 count = static_cast<int>(event_format_files.size());
194 AppendData(ret, count);
195 for (const auto& pair : event_format_files) {
196 AppendData(ret, pair.first);
197 AppendData(ret, 1);
198 AppendFile(ret, pair.second);
199 }
200 AppendFile(ret, kallsyms_file, 4);
201 AppendFile(ret, printk_formats_file, 4);
202 return ret;
203 }
204
LoadFromBinary(const std::vector<char> & data)205 void TracingFile::LoadFromBinary(const std::vector<char>& data) {
206 const char* p = data.data();
207 const char* end = data.data() + data.size();
208 CHECK(memcmp(p, magic, sizeof(magic)) == 0);
209 p += sizeof(magic);
210 MoveFromBinaryFormat(version, p);
211 MoveFromBinaryFormat(endian, p);
212 MoveFromBinaryFormat(size_of_long, p);
213 MoveFromBinaryFormat(page_size, p);
214 std::string filename;
215 MoveFromBinaryFormat(filename, p);
216 CHECK_EQ(filename, "header_page");
217 DetachFile(p, header_page_file);
218 MoveFromBinaryFormat(filename, p);
219 CHECK_EQ(filename, "header_event");
220 DetachFile(p, header_event_file);
221 uint32_t count;
222 MoveFromBinaryFormat(count, p);
223 ftrace_format_files.resize(count);
224 for (uint32_t i = 0; i < count; ++i) {
225 DetachFile(p, ftrace_format_files[i]);
226 }
227 MoveFromBinaryFormat(count, p);
228 event_format_files.clear();
229 for (uint32_t i = 0; i < count; ++i) {
230 std::string system;
231 MoveFromBinaryFormat(system, p);
232 uint32_t count_in_system;
233 MoveFromBinaryFormat(count_in_system, p);
234 for (uint32_t i = 0; i < count_in_system; ++i) {
235 std::string format;
236 DetachFile(p, format);
237 event_format_files.push_back(std::make_pair(system, std::move(format)));
238 }
239 }
240 DetachFile(p, kallsyms_file, 4);
241 DetachFile(p, printk_formats_file, 4);
242 CHECK_EQ(p, end);
243 }
244
Dump(size_t indent) const245 void TracingFile::Dump(size_t indent) const {
246 PrintIndented(indent, "tracing data:\n");
247 PrintIndented(indent + 1, "magic: ");
248 for (size_t i = 0; i < 3u; ++i) {
249 printf("0x%x ", magic[i]);
250 }
251 for (size_t i = 3; i < sizeof(magic); ++i) {
252 printf("%c", magic[i]);
253 }
254 printf("\n");
255 PrintIndented(indent + 1, "version: %s\n", version.c_str());
256 PrintIndented(indent + 1, "endian: %d\n", endian);
257 PrintIndented(indent + 1, "header_page:\n%s\n\n", header_page_file.c_str());
258 PrintIndented(indent + 1, "header_event:\n%s\n\n", header_event_file.c_str());
259 for (size_t i = 0; i < ftrace_format_files.size(); ++i) {
260 PrintIndented(indent + 1, "ftrace format file %zu/%zu:\n%s\n\n", i + 1,
261 ftrace_format_files.size(), ftrace_format_files[i].c_str());
262 }
263 for (size_t i = 0; i < event_format_files.size(); ++i) {
264 PrintIndented(indent + 1, "event format file %zu/%zu %s:\n%s\n\n", i + 1,
265 event_format_files.size(),
266 event_format_files[i].first.c_str(),
267 event_format_files[i].second.c_str());
268 }
269 PrintIndented(indent + 1, "kallsyms:\n%s\n\n", kallsyms_file.c_str());
270 PrintIndented(indent + 1, "printk_formats:\n%s\n\n",
271 printk_formats_file.c_str());
272 }
273
274 enum class FormatParsingState {
275 READ_NAME,
276 READ_ID,
277 READ_FIELDS,
278 READ_PRINTFMT,
279 };
280
281 // Parse lines like: field:char comm[16]; offset:8; size:16; signed:1;
ParseTracingField(const std::string & s)282 static TracingField ParseTracingField(const std::string& s) {
283 TracingField field;
284 size_t start = 0;
285 std::string name;
286 std::string value;
287 for (size_t i = 0; i < s.size(); ++i) {
288 if (!isspace(s[i]) && (i == 0 || isspace(s[i - 1]))) {
289 start = i;
290 } else if (s[i] == ':') {
291 name = s.substr(start, i - start);
292 start = i + 1;
293 } else if (s[i] == ';') {
294 value = s.substr(start, i - start);
295 if (name == "field") {
296 // Parse value with brackets like "comm[16]", or just a field name.
297 size_t left_bracket_pos = value.find('[');
298 if (left_bracket_pos == std::string::npos) {
299 field.name = value;
300 field.elem_count = 1;
301 } else {
302 field.name = value.substr(0, left_bracket_pos);
303 field.elem_count = 1;
304 size_t right_bracket_pos = value.find(']', left_bracket_pos);
305 if (right_bracket_pos != std::string::npos) {
306 size_t len = right_bracket_pos - left_bracket_pos - 1;
307 size_t elem_count;
308 // Array size may not be a number, like field:u32 rates[IEEE80211_NUM_BANDS].
309 if (android::base::ParseUint(value.substr(left_bracket_pos + 1, len), &elem_count)) {
310 field.elem_count = elem_count;
311 }
312 }
313 }
314 } else if (name == "offset") {
315 field.offset =
316 static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
317 } else if (name == "size") {
318 size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
319 CHECK_EQ(size % field.elem_count, 0u);
320 field.elem_size = size / field.elem_count;
321 } else if (name == "signed") {
322 int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10));
323 field.is_signed = (is_signed == 1);
324 }
325 }
326 }
327 return field;
328 }
329
ParseTracingFormat(const std::string & data)330 static TracingFormat ParseTracingFormat(const std::string& data) {
331 TracingFormat format;
332 std::vector<std::string> strs = android::base::Split(data, "\n");
333 FormatParsingState state = FormatParsingState::READ_NAME;
334 for (const auto& s : strs) {
335 if (state == FormatParsingState::READ_NAME) {
336 if (size_t pos = s.find("name:"); pos != std::string::npos) {
337 format.name = android::base::Trim(s.substr(pos + strlen("name:")));
338 state = FormatParsingState::READ_ID;
339 }
340 } else if (state == FormatParsingState::READ_ID) {
341 if (size_t pos = s.find("ID:"); pos != std::string::npos) {
342 format.id = strtoull(s.substr(pos + strlen("ID:")).c_str(), nullptr, 10);
343 state = FormatParsingState::READ_FIELDS;
344 }
345 } else if (state == FormatParsingState::READ_FIELDS) {
346 if (size_t pos = s.find("field:"); pos != std::string::npos) {
347 TracingField field = ParseTracingField(s);
348 format.fields.push_back(field);
349 }
350 }
351 }
352 return format;
353 }
354
LoadTracingFormatsFromEventFiles() const355 std::vector<TracingFormat> TracingFile::LoadTracingFormatsFromEventFiles()
356 const {
357 std::vector<TracingFormat> formats;
358 for (const auto& pair : event_format_files) {
359 TracingFormat format = ParseTracingFormat(pair.second);
360 format.system_name = pair.first;
361 formats.push_back(format);
362 }
363 return formats;
364 }
365
Tracing(const std::vector<char> & data)366 Tracing::Tracing(const std::vector<char>& data) {
367 tracing_file_ = new TracingFile;
368 tracing_file_->LoadFromBinary(data);
369 }
370
~Tracing()371 Tracing::~Tracing() { delete tracing_file_; }
372
Dump(size_t indent)373 void Tracing::Dump(size_t indent) { tracing_file_->Dump(indent); }
374
GetTracingFormatHavingId(uint64_t trace_event_id)375 TracingFormat Tracing::GetTracingFormatHavingId(uint64_t trace_event_id) {
376 if (tracing_formats_.empty()) {
377 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
378 }
379 for (const auto& format : tracing_formats_) {
380 if (format.id == trace_event_id) {
381 return format;
382 }
383 }
384 LOG(FATAL) << "no tracing format for id " << trace_event_id;
385 return TracingFormat();
386 }
387
GetTracingEventNameHavingId(uint64_t trace_event_id)388 std::string Tracing::GetTracingEventNameHavingId(uint64_t trace_event_id) {
389 if (tracing_formats_.empty()) {
390 tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
391 }
392 for (const auto& format : tracing_formats_) {
393 if (format.id == trace_event_id) {
394 return android::base::StringPrintf("%s:%s", format.system_name.c_str(),
395 format.name.c_str());
396 }
397 }
398 return "";
399 }
400
GetKallsyms() const401 const std::string& Tracing::GetKallsyms() const {
402 return tracing_file_->GetKallsymsFile();
403 }
404
GetPageSize() const405 uint32_t Tracing::GetPageSize() const { return tracing_file_->GetPageSize(); }
406
GetTracingData(const std::vector<const EventType * > & event_types,std::vector<char> * data)407 bool GetTracingData(const std::vector<const EventType*>& event_types,
408 std::vector<char>* data) {
409 data->clear();
410 std::vector<TraceType> trace_types;
411 for (const auto& type : event_types) {
412 CHECK_EQ(static_cast<uint32_t>(PERF_TYPE_TRACEPOINT), type->type);
413 size_t pos = type->name.find(':');
414 TraceType trace_type;
415 trace_type.system = type->name.substr(0, pos);
416 trace_type.name = type->name.substr(pos + 1);
417 trace_types.push_back(trace_type);
418 }
419 TracingFile tracing_file;
420 if (!tracing_file.RecordHeaderFiles()) {
421 return false;
422 }
423 tracing_file.RecordFtraceFiles(trace_types);
424 if (!tracing_file.RecordEventFiles(trace_types)) {
425 return false;
426 }
427 // Don't record /proc/kallsyms here, as it will be contained in
428 // KernelSymbolRecord.
429 if (!tracing_file.RecordPrintkFormatsFile()) {
430 return false;
431 }
432 *data = tracing_file.BinaryFormat();
433 return true;
434 }
435
436 namespace {
437
438 // Briefly check if the filter format is acceptable by the kernel, which is described in
439 // Documentation/trace/events.rst in the kernel. Also adjust quotes in string operands.
440 //
441 // filter := predicate_expr [logical_operator predicate_expr]*
442 // predicate_expr := predicate | '!' predicate_expr | '(' filter ')'
443 // predicate := field_name relational_operator value
444 //
445 // logical_operator := '&&' | '||'
446 // relational_operator := numeric_operator | string_operator
447 // numeric_operator := '==' | '!=' | '<' | '<=' | '>' | '>=' | '&'
448 // string_operator := '==' | '!=' | '~'
449 // value := int or string
450 struct FilterFormatAdjuster {
FilterFormatAdjuster__anone00b59380111::FilterFormatAdjuster451 FilterFormatAdjuster(bool use_quote) : use_quote(use_quote) {}
452
MatchFilter__anone00b59380111::FilterFormatAdjuster453 bool MatchFilter(const char*& p) {
454 bool ok = MatchPredicateExpr(p);
455 while (ok && *p != '\0') {
456 RemoveSpace(p);
457 if (strncmp(p, "||", 2) == 0 || strncmp(p, "&&", 2) == 0) {
458 CopyBytes(p, 2);
459 ok = MatchPredicateExpr(p);
460 } else {
461 break;
462 }
463 }
464 RemoveSpace(p);
465 return ok;
466 }
467
RemoveSpace__anone00b59380111::FilterFormatAdjuster468 void RemoveSpace(const char*& p) {
469 size_t i = 0;
470 while (isspace(p[i])) {
471 i++;
472 }
473 if (i > 0) {
474 CopyBytes(p, i);
475 }
476 }
477
MatchPredicateExpr__anone00b59380111::FilterFormatAdjuster478 bool MatchPredicateExpr(const char*& p) {
479 RemoveSpace(p);
480 if (*p == '!') {
481 CopyBytes(p, 1);
482 return MatchPredicateExpr(p);
483 }
484 if (*p == '(') {
485 CopyBytes(p, 1);
486 bool ok = MatchFilter(p);
487 if (!ok) {
488 return false;
489 }
490 RemoveSpace(p);
491 if (*p != ')') {
492 return false;
493 }
494 CopyBytes(p, 1);
495 return true;
496 }
497 return MatchPredicate(p);
498 }
499
MatchPredicate__anone00b59380111::FilterFormatAdjuster500 bool MatchPredicate(const char*& p) {
501 return MatchFieldName(p) && MatchRelationalOperator(p) && MatchValue(p);
502 }
503
MatchFieldName__anone00b59380111::FilterFormatAdjuster504 bool MatchFieldName(const char*& p) {
505 RemoveSpace(p);
506 std::string name;
507 for (size_t i = 0; isalnum(p[i]) || p[i] == '_'; i++) {
508 name.push_back(p[i]);
509 }
510 CopyBytes(p, name.size());
511 if (name.empty()) {
512 return false;
513 }
514 used_fields.emplace(std::move(name));
515 return true;
516 }
517
MatchRelationalOperator__anone00b59380111::FilterFormatAdjuster518 bool MatchRelationalOperator(const char*& p) {
519 RemoveSpace(p);
520 // "==", "!=", "<", "<=", ">", ">=", "&", "~"
521 if (*p == '=' || *p == '!' || *p == '<' || *p == '>') {
522 if (p[1] == '=') {
523 CopyBytes(p, 2);
524 return true;
525 }
526 }
527 if (*p == '<' || *p == '>' || *p == '&' || *p == '~') {
528 CopyBytes(p, 1);
529 return true;
530 }
531 return false;
532 }
533
MatchValue__anone00b59380111::FilterFormatAdjuster534 bool MatchValue(const char*& p) {
535 RemoveSpace(p);
536 // Match a string with quotes.
537 if (*p == '\'' || *p == '"') {
538 char quote = *p;
539 size_t len = 1;
540 while (p[len] != quote && p[len] != '\0') {
541 len++;
542 }
543 if (p[len] != quote) {
544 return false;
545 }
546 len++;
547 if (use_quote) {
548 CopyBytes(p, len);
549 } else {
550 p++;
551 CopyBytes(p, len - 2);
552 p++;
553 }
554 return true;
555 }
556 // Match an int value.
557 char* end;
558 errno = 0;
559 if (*p == '-') {
560 strtoll(p, &end, 0);
561 } else {
562 strtoull(p, &end, 0);
563 }
564 if (errno == 0 && end != p) {
565 CopyBytes(p, end - p);
566 return true;
567 }
568 // Match a string without quotes, stopping at ), &&, || or space.
569 size_t len = 0;
570 while (p[len] != '\0' && strchr(")&| \t", p[len]) == nullptr) {
571 len++;
572 }
573 if (len == 0) {
574 return false;
575 }
576 if (use_quote) {
577 adjusted_filter += '"';
578 }
579 CopyBytes(p, len);
580 if (use_quote) {
581 adjusted_filter += '"';
582 }
583 return true;
584 }
585
CopyBytes__anone00b59380111::FilterFormatAdjuster586 void CopyBytes(const char*& p, size_t len) {
587 adjusted_filter.append(p, len);
588 p += len;
589 }
590
591 const bool use_quote;
592 std::string adjusted_filter;
593 FieldNameSet used_fields;
594 };
595
596 } // namespace
597
AdjustTracepointFilter(const std::string & filter,bool use_quote,FieldNameSet * used_fields)598 std::optional<std::string> AdjustTracepointFilter(const std::string& filter, bool use_quote,
599 FieldNameSet* used_fields) {
600 FilterFormatAdjuster adjuster(use_quote);
601 const char* p = filter.c_str();
602 if (!adjuster.MatchFilter(p) || *p != '\0') {
603 LOG(ERROR) << "format error in filter \"" << filter << "\" starting from \"" << p << "\"";
604 return std::nullopt;
605 }
606 *used_fields = std::move(adjuster.used_fields);
607 return std::move(adjuster.adjusted_filter);
608 }
609
GetFieldNamesForTracepointEvent(const EventType & event)610 std::optional<FieldNameSet> GetFieldNamesForTracepointEvent(const EventType& event) {
611 std::vector<std::string> strs = android::base::Split(event.name, ":");
612 if (strs.size() != 2) {
613 return {};
614 }
615 std::string data;
616 if (!ReadTraceFsFile("/events/" + strs[0] + "/" + strs[1] + "/format", &data, false)) {
617 return {};
618 }
619 TracingFormat format = ParseTracingFormat(data);
620 FieldNameSet names;
621 for (auto& field : format.fields) {
622 names.emplace(std::move(field.name));
623 }
624 return names;
625 }
626