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