1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_TRACED_PROBES_FTRACE_PROTO_TRANSLATION_TABLE_H_
18 #define SRC_TRACED_PROBES_FTRACE_PROTO_TRANSLATION_TABLE_H_
19 
20 #include <stdint.h>
21 
22 #include <map>
23 #include <memory>
24 #include <set>
25 #include <string>
26 #include <vector>
27 
28 #include "perfetto/base/scoped_file.h"
29 #include "src/traced/probes/ftrace/event_info.h"
30 #include "src/traced/probes/ftrace/format_parser.h"
31 
32 namespace perfetto {
33 
34 class FtraceProcfs;
35 
36 namespace protos {
37 namespace pbzero {
38 class FtraceEventBundle;
39 }  // namespace pbzero
40 }  // namespace protos
41 
42 // Used when reading the config to store the group and name info for the
43 // ftrace event.
44 class GroupAndName {
45  public:
GroupAndName(const std::string & group,const std::string & name)46   GroupAndName(const std::string& group, const std::string& name)
47       : group_(group), name_(name) {}
48 
49   bool operator==(const GroupAndName& other) const {
50     return std::tie(group_, name_) == std::tie(other.group(), other.name());
51   }
52 
53   bool operator<(const GroupAndName& other) const {
54     return std::tie(group_, name_) < std::tie(other.group(), other.name());
55   }
56 
name()57   const std::string& name() const { return name_; }
group()58   const std::string& group() const { return group_; }
59 
ToString()60   std::string ToString() const { return group_ + "/" + name_; }
61 
62  private:
63   std::string group_;
64   std::string name_;
65 };
66 
67 bool InferFtraceType(const std::string& type_and_name,
68                      size_t size,
69                      bool is_signed,
70                      FtraceFieldType* out);
71 
72 class ProtoTranslationTable {
73  public:
74   struct FtracePageHeaderSpec {
75     FtraceEvent::Field timestamp{};
76     FtraceEvent::Field overwrite{};
77     FtraceEvent::Field size{};
78   };
79 
80   static FtracePageHeaderSpec DefaultPageHeaderSpecForTesting();
81 
82   // This method mutates the |events| and |common_fields| vectors to
83   // fill some of the fields and to delete unused events/fields
84   // before std:move'ing them into the ProtoTranslationTable.
85   static std::unique_ptr<ProtoTranslationTable> Create(
86       const FtraceProcfs* ftrace_procfs,
87       std::vector<Event> events,
88       std::vector<Field> common_fields);
89   virtual ~ProtoTranslationTable();
90 
91   ProtoTranslationTable(const FtraceProcfs* ftrace_procfs,
92                         const std::vector<Event>& events,
93                         std::vector<Field> common_fields,
94                         FtracePageHeaderSpec ftrace_page_header_spec);
95 
largest_id()96   size_t largest_id() const { return largest_id_; }
97 
common_fields()98   const std::vector<Field>& common_fields() const { return common_fields_; }
99 
100   // Virtual for testing.
GetEvent(const GroupAndName & group_and_name)101   virtual const Event* GetEvent(const GroupAndName& group_and_name) const {
102     if (!group_and_name_to_event_.count(group_and_name))
103       return nullptr;
104     return group_and_name_to_event_.at(group_and_name);
105   }
106 
GetEventsByGroup(const std::string & group)107   const std::vector<const Event*>* GetEventsByGroup(
108       const std::string& group) const {
109     if (!group_to_events_.count(group))
110       return nullptr;
111     return &group_to_events_.at(group);
112   }
113 
GetEventById(size_t id)114   const Event* GetEventById(size_t id) const {
115     if (id == 0 || id > largest_id_)
116       return nullptr;
117     if (!events_.at(id).ftrace_event_id)
118       return nullptr;
119     return &events_.at(id);
120   }
121 
EventToFtraceId(const GroupAndName & group_and_name)122   size_t EventToFtraceId(const GroupAndName& group_and_name) const {
123     if (!group_and_name_to_event_.count(group_and_name))
124       return 0;
125     return group_and_name_to_event_.at(group_and_name)->ftrace_event_id;
126   }
127 
events()128   const std::vector<Event>& events() { return events_; }
ftrace_page_header_spec()129   const FtracePageHeaderSpec& ftrace_page_header_spec() const {
130     return ftrace_page_header_spec_;
131   }
132 
133   // Returns the size in bytes of the "size" field in the ftrace header. This
134   // usually matches sizeof(void*) in the kernel (which can be != sizeof(void*)
135   // of user space on 32bit-user + 64-bit-kernel configurations).
page_header_size_len()136   inline uint16_t page_header_size_len() const {
137     // TODO(fmayer): Do kernel deepdive to double check this.
138     return ftrace_page_header_spec_.size.size;
139   }
140 
141   // Retrieves the ftrace event from the proto translation
142   // table. If it does not exist, reads the format file and creates a
143   // new event with the proto id set to generic. Virtual for testing.
144   virtual const Event* GetOrCreateEvent(const GroupAndName&);
145 
146   // This is for backwards compatibility. If a group is not specified in the
147   // config then the first event with that name will be returned.
GetEventByName(const std::string & name)148   const Event* GetEventByName(const std::string& name) const {
149     if (!name_to_events_.count(name))
150       return nullptr;
151     return name_to_events_.at(name)[0];
152   }
153 
154  private:
155   ProtoTranslationTable(const ProtoTranslationTable&) = delete;
156   ProtoTranslationTable& operator=(const ProtoTranslationTable&) = delete;
157 
158   // Store strings so they can be read when writing the trace output.
159   const char* InternString(const std::string& str);
160 
161   uint16_t CreateGenericEventField(const FtraceEvent::Field& ftrace_field,
162                                    Event& event);
163 
164   const FtraceProcfs* ftrace_procfs_;
165   std::vector<Event> events_;
166   size_t largest_id_;
167   std::map<GroupAndName, const Event*> group_and_name_to_event_;
168   std::map<std::string, std::vector<const Event*>> name_to_events_;
169   std::map<std::string, std::vector<const Event*>> group_to_events_;
170   std::vector<Field> common_fields_;
171   FtracePageHeaderSpec ftrace_page_header_spec_{};
172   std::set<std::string> interned_strings_;
173 };
174 
175 // Class for efficient 'is event with id x enabled?' checks.
176 // Mirrors the data in a FtraceConfig but in a format better suited
177 // to be consumed by CpuReader.
178 class EventFilter {
179  public:
180   EventFilter();
181   ~EventFilter();
182   EventFilter(EventFilter&&) = default;
183   EventFilter& operator=(EventFilter&&) = default;
184 
185   void AddEnabledEvent(size_t ftrace_event_id);
186   void DisableEvent(size_t ftrace_event_id);
187   bool IsEventEnabled(size_t ftrace_event_id) const;
188   std::set<size_t> GetEnabledEvents() const;
189   void EnableEventsFrom(const EventFilter&);
190 
191  private:
192   EventFilter(const EventFilter&) = delete;
193   EventFilter& operator=(const EventFilter&) = delete;
194 
195   std::vector<bool> enabled_ids_;
196 };
197 
198 }  // namespace perfetto
199 
200 #endif  // SRC_TRACED_PROBES_FTRACE_PROTO_TRANSLATION_TABLE_H_
201