• 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 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             field.elem_count = elem_count;
315           }
316         }
317       } else {
318         // Parse value like "int common_pid".
319         field.name = last_value_part;
320         field.elem_count = 1;
321       }
322     } else if (name == "offset") {
323       field.offset = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
324     } else if (name == "size") {
325       size_t size = static_cast<size_t>(strtoull(value.c_str(), nullptr, 10));
326       CHECK_EQ(size % field.elem_count, 0u);
327       field.elem_size = size / field.elem_count;
328     } else if (name == "signed") {
329       int is_signed = static_cast<int>(strtoull(value.c_str(), nullptr, 10));
330       field.is_signed = (is_signed == 1);
331     }
332   }
333   return field;
334 }
335 
ParseTracingFormat(const std::string & data)336 TracingFormat ParseTracingFormat(const std::string& data) {
337   TracingFormat format;
338   std::vector<std::string> strs = Split(data, "\n");
339   FormatParsingState state = FormatParsingState::READ_NAME;
340   for (const auto& s : strs) {
341     if (state == FormatParsingState::READ_NAME) {
342       if (size_t pos = s.find("name:"); pos != std::string::npos) {
343         format.name = android::base::Trim(s.substr(pos + strlen("name:")));
344         state = FormatParsingState::READ_ID;
345       }
346     } else if (state == FormatParsingState::READ_ID) {
347       if (size_t pos = s.find("ID:"); pos != std::string::npos) {
348         format.id = strtoull(s.substr(pos + strlen("ID:")).c_str(), nullptr, 10);
349         state = FormatParsingState::READ_FIELDS;
350       }
351     } else if (state == FormatParsingState::READ_FIELDS) {
352       if (size_t pos = s.find("field:"); pos != std::string::npos) {
353         TracingField field = ParseTracingField(s);
354         format.fields.push_back(field);
355       }
356     }
357   }
358   return format;
359 }
360 
LoadTracingFormatsFromEventFiles() const361 std::vector<TracingFormat> TracingFile::LoadTracingFormatsFromEventFiles() const {
362   std::vector<TracingFormat> formats;
363   for (const auto& pair : event_format_files) {
364     TracingFormat format = ParseTracingFormat(pair.second);
365     format.system_name = pair.first;
366     formats.push_back(format);
367   }
368   return formats;
369 }
370 
Create(const std::vector<char> & data)371 std::unique_ptr<Tracing> Tracing::Create(const std::vector<char>& data) {
372   std::unique_ptr<Tracing> tracing(new Tracing);
373   if (!tracing->tracing_file_->LoadFromBinary(data)) {
374     LOG(ERROR) << "Failed to load tracing data";
375     return nullptr;
376   }
377   return tracing;
378 }
379 
Tracing()380 Tracing::Tracing() : tracing_file_(new TracingFile) {}
381 
~Tracing()382 Tracing::~Tracing() {}
383 
Dump(size_t indent)384 void Tracing::Dump(size_t indent) {
385   tracing_file_->Dump(indent);
386 }
387 
GetTracingFormatHavingId(uint64_t trace_event_id)388 TracingFormat Tracing::GetTracingFormatHavingId(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 format;
395     }
396   }
397   LOG(FATAL) << "no tracing format for id " << trace_event_id;
398   return TracingFormat();
399 }
400 
GetTracingEventNameHavingId(uint64_t trace_event_id)401 std::string Tracing::GetTracingEventNameHavingId(uint64_t trace_event_id) {
402   if (tracing_formats_.empty()) {
403     tracing_formats_ = tracing_file_->LoadTracingFormatsFromEventFiles();
404   }
405   for (const auto& format : tracing_formats_) {
406     if (format.id == trace_event_id) {
407       return android::base::StringPrintf("%s:%s", format.system_name.c_str(), format.name.c_str());
408     }
409   }
410   return "";
411 }
412 
GetKallsyms() const413 const std::string& Tracing::GetKallsyms() const {
414   return tracing_file_->GetKallsymsFile();
415 }
416 
GetPageSize() const417 uint32_t Tracing::GetPageSize() const {
418   return tracing_file_->GetPageSize();
419 }
420 
GetTracingData(const std::vector<const EventType * > & event_types,std::vector<char> * data)421 bool GetTracingData(const std::vector<const EventType*>& event_types, std::vector<char>* data) {
422   data->clear();
423   std::vector<TraceType> trace_types;
424   for (const auto& type : event_types) {
425     CHECK_EQ(static_cast<uint32_t>(PERF_TYPE_TRACEPOINT), type->type);
426     size_t pos = type->name.find(':');
427     TraceType trace_type;
428     trace_type.system = type->name.substr(0, pos);
429     trace_type.name = type->name.substr(pos + 1);
430     trace_types.push_back(trace_type);
431   }
432   TracingFile tracing_file;
433   if (!tracing_file.RecordHeaderFiles()) {
434     return false;
435   }
436   tracing_file.RecordFtraceFiles(trace_types);
437   if (!tracing_file.RecordEventFiles(trace_types)) {
438     return false;
439   }
440   // Don't record /proc/kallsyms here, as it will be contained in
441   // KernelSymbolRecord.
442   if (!tracing_file.RecordPrintkFormatsFile()) {
443     return false;
444   }
445   *data = tracing_file.BinaryFormat();
446   return true;
447 }
448 
449 namespace {
450 
451 // Briefly check if the filter format is acceptable by the kernel, which is described in
452 // Documentation/trace/events.rst in the kernel. Also adjust quotes in string operands.
453 //
454 // filter := predicate_expr [logical_operator predicate_expr]*
455 // predicate_expr := predicate | '!' predicate_expr | '(' filter ')'
456 // predicate := field_name relational_operator value
457 //
458 // logical_operator := '&&' | '||'
459 // relational_operator := numeric_operator | string_operator
460 // numeric_operator := '==' | '!=' | '<' | '<=' | '>' | '>=' | '&'
461 // string_operator := '==' | '!=' | '~'
462 // value := int or string
463 struct FilterFormatAdjuster {
FilterFormatAdjustersimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster464   FilterFormatAdjuster(bool use_quote) : use_quote(use_quote) {}
465 
MatchFiltersimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster466   bool MatchFilter(const char*& p) {
467     bool ok = MatchPredicateExpr(p);
468     while (ok && *p != '\0') {
469       RemoveSpace(p);
470       if (strncmp(p, "||", 2) == 0 || strncmp(p, "&&", 2) == 0) {
471         CopyBytes(p, 2);
472         ok = MatchPredicateExpr(p);
473       } else {
474         break;
475       }
476     }
477     RemoveSpace(p);
478     return ok;
479   }
480 
RemoveSpacesimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster481   void RemoveSpace(const char*& p) {
482     size_t i = 0;
483     while (isspace(p[i])) {
484       i++;
485     }
486     if (i > 0) {
487       CopyBytes(p, i);
488     }
489   }
490 
MatchPredicateExprsimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster491   bool MatchPredicateExpr(const char*& p) {
492     RemoveSpace(p);
493     if (*p == '!') {
494       CopyBytes(p, 1);
495       return MatchPredicateExpr(p);
496     }
497     if (*p == '(') {
498       CopyBytes(p, 1);
499       bool ok = MatchFilter(p);
500       if (!ok) {
501         return false;
502       }
503       RemoveSpace(p);
504       if (*p != ')') {
505         return false;
506       }
507       CopyBytes(p, 1);
508       return true;
509     }
510     return MatchPredicate(p);
511   }
512 
MatchPredicatesimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster513   bool MatchPredicate(const char*& p) {
514     return MatchFieldName(p) && MatchRelationalOperator(p) && MatchValue(p);
515   }
516 
MatchFieldNamesimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster517   bool MatchFieldName(const char*& p) {
518     RemoveSpace(p);
519     std::string name;
520     for (size_t i = 0; isalnum(p[i]) || p[i] == '_'; i++) {
521       name.push_back(p[i]);
522     }
523     CopyBytes(p, name.size());
524     if (name.empty()) {
525       return false;
526     }
527     used_fields.emplace(std::move(name));
528     return true;
529   }
530 
MatchRelationalOperatorsimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster531   bool MatchRelationalOperator(const char*& p) {
532     RemoveSpace(p);
533     // "==", "!=", "<", "<=", ">", ">=", "&", "~"
534     if (*p == '=' || *p == '!' || *p == '<' || *p == '>') {
535       if (p[1] == '=') {
536         CopyBytes(p, 2);
537         return true;
538       }
539     }
540     if (*p == '<' || *p == '>' || *p == '&' || *p == '~') {
541       CopyBytes(p, 1);
542       return true;
543     }
544     return false;
545   }
546 
MatchValuesimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster547   bool MatchValue(const char*& p) {
548     RemoveSpace(p);
549     // Match a string with quotes.
550     if (*p == '\'' || *p == '"') {
551       char quote = *p;
552       size_t len = 1;
553       while (p[len] != quote && p[len] != '\0') {
554         len++;
555       }
556       if (p[len] != quote) {
557         return false;
558       }
559       len++;
560       if (use_quote) {
561         CopyBytes(p, len);
562       } else {
563         p++;
564         CopyBytes(p, len - 2);
565         p++;
566       }
567       return true;
568     }
569     // Match an int value.
570     char* end;
571     errno = 0;
572     if (*p == '-') {
573       strtoll(p, &end, 0);
574     } else {
575       strtoull(p, &end, 0);
576     }
577     if (errno == 0 && end != p) {
578       CopyBytes(p, end - p);
579       return true;
580     }
581     // Match a string without quotes, stopping at ), &&, || or space.
582     size_t len = 0;
583     while (p[len] != '\0' && strchr(")&| \t", p[len]) == nullptr) {
584       len++;
585     }
586     if (len == 0) {
587       return false;
588     }
589     if (use_quote) {
590       adjusted_filter += '"';
591     }
592     CopyBytes(p, len);
593     if (use_quote) {
594       adjusted_filter += '"';
595     }
596     return true;
597   }
598 
CopyBytessimpleperf::__anonc9af3a3d0111::FilterFormatAdjuster599   void CopyBytes(const char*& p, size_t len) {
600     adjusted_filter.append(p, len);
601     p += len;
602   }
603 
604   const bool use_quote;
605   std::string adjusted_filter;
606   FieldNameSet used_fields;
607 };
608 
609 }  // namespace
610 
AdjustTracepointFilter(const std::string & filter,bool use_quote,FieldNameSet * used_fields)611 std::optional<std::string> AdjustTracepointFilter(const std::string& filter, bool use_quote,
612                                                   FieldNameSet* used_fields) {
613   FilterFormatAdjuster adjuster(use_quote);
614   const char* p = filter.c_str();
615   if (!adjuster.MatchFilter(p) || *p != '\0') {
616     LOG(ERROR) << "format error in filter \"" << filter << "\" starting from \"" << p << "\"";
617     return std::nullopt;
618   }
619   *used_fields = std::move(adjuster.used_fields);
620   return std::move(adjuster.adjusted_filter);
621 }
622 
GetFieldNamesForTracepointEvent(const EventType & event)623 std::optional<FieldNameSet> GetFieldNamesForTracepointEvent(const EventType& event) {
624   std::vector<std::string> strs = Split(event.name, ":");
625   if (strs.size() != 2) {
626     return {};
627   }
628   std::string data;
629   if (!ReadTraceFsFile("/events/" + strs[0] + "/" + strs[1] + "/format", &data, false)) {
630     return {};
631   }
632   TracingFormat format = ParseTracingFormat(data);
633   FieldNameSet names;
634   for (auto& field : format.fields) {
635     names.emplace(std::move(field.name));
636   }
637   return names;
638 }
639 
640 }  // namespace simpleperf
641