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