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 <deque>
23 #include <iostream>
24 #include <map>
25 #include <memory>
26 #include <set>
27 #include <string>
28 #include <vector>
29
30 #include "perfetto/ext/base/scoped_file.h"
31 #include "src/traced/probes/ftrace/compact_sched.h"
32 #include "src/traced/probes/ftrace/event_info.h"
33 #include "src/traced/probes/ftrace/format_parser.h"
34
35 namespace perfetto {
36
37 class FtraceProcfs;
38
39 namespace protos {
40 namespace pbzero {
41 class FtraceEventBundle;
42 } // namespace pbzero
43 } // namespace protos
44
45 // Used when reading the config to store the group and name info for the
46 // ftrace event.
47 class GroupAndName {
48 public:
GroupAndName(const std::string & group,const std::string & name)49 GroupAndName(const std::string& group, const std::string& name)
50 : group_(group), name_(name) {}
51
52 bool operator==(const GroupAndName& other) const {
53 return std::tie(group_, name_) == std::tie(other.group(), other.name());
54 }
55
56 bool operator<(const GroupAndName& other) const {
57 return std::tie(group_, name_) < std::tie(other.group(), other.name());
58 }
59
name()60 const std::string& name() const { return name_; }
group()61 const std::string& group() const { return group_; }
62
ToString()63 std::string ToString() const { return group_ + "/" + name_; }
64
65 private:
66 std::string group_;
67 std::string name_;
68 };
69
PrintTo(const GroupAndName & event,::std::ostream * os)70 inline void PrintTo(const GroupAndName& event, ::std::ostream* os) {
71 *os << "GroupAndName(" << event.group() << ", " << event.name() << ")";
72 }
73
74 bool InferFtraceType(const std::string& type_and_name,
75 size_t size,
76 bool is_signed,
77 FtraceFieldType* out);
78
79 class ProtoTranslationTable {
80 public:
81 struct FtracePageHeaderSpec {
82 FtraceEvent::Field timestamp{};
83 FtraceEvent::Field overwrite{};
84 FtraceEvent::Field size{};
85 };
86
87 static FtracePageHeaderSpec DefaultPageHeaderSpecForTesting();
88
89 // This method mutates the |events| and |common_fields| vectors to
90 // fill some of the fields and to delete unused events/fields
91 // before std:move'ing them into the ProtoTranslationTable.
92 static std::unique_ptr<ProtoTranslationTable> Create(
93 const FtraceProcfs* ftrace_procfs,
94 std::vector<Event> events,
95 std::vector<Field> common_fields);
96 virtual ~ProtoTranslationTable();
97
98 ProtoTranslationTable(const FtraceProcfs* ftrace_procfs,
99 const std::vector<Event>& events,
100 std::vector<Field> common_fields,
101 FtracePageHeaderSpec ftrace_page_header_spec,
102 CompactSchedEventFormat compact_sched_format);
103
largest_id()104 size_t largest_id() const { return largest_id_; }
105
common_fields()106 const std::vector<Field>& common_fields() const { return common_fields_; }
107
108 // Virtual for testing.
GetEvent(const GroupAndName & group_and_name)109 virtual const Event* GetEvent(const GroupAndName& group_and_name) const {
110 if (!group_and_name_to_event_.count(group_and_name))
111 return nullptr;
112 return group_and_name_to_event_.at(group_and_name);
113 }
114
GetEventsByGroup(const std::string & group)115 const std::vector<const Event*>* GetEventsByGroup(
116 const std::string& group) const {
117 if (!group_to_events_.count(group))
118 return nullptr;
119 return &group_to_events_.at(group);
120 }
121
GetEventById(size_t id)122 const Event* GetEventById(size_t id) const {
123 if (id == 0 || id > largest_id_)
124 return nullptr;
125 const Event* evt = &events_[id];
126 if (!evt->ftrace_event_id)
127 return nullptr;
128 return evt;
129 }
130
EventToFtraceId(const GroupAndName & group_and_name)131 size_t EventToFtraceId(const GroupAndName& group_and_name) const {
132 if (!group_and_name_to_event_.count(group_and_name))
133 return 0;
134 return group_and_name_to_event_.at(group_and_name)->ftrace_event_id;
135 }
136
events()137 const std::deque<Event>& events() { return events_; }
ftrace_page_header_spec()138 const FtracePageHeaderSpec& ftrace_page_header_spec() const {
139 return ftrace_page_header_spec_;
140 }
141
142 // Returns the size in bytes of the "size" field in the ftrace header. This
143 // usually matches sizeof(void*) in the kernel (which can be != sizeof(void*)
144 // of user space on 32bit-user + 64-bit-kernel configurations).
page_header_size_len()145 inline uint16_t page_header_size_len() const {
146 // TODO(fmayer): Do kernel deepdive to double check this.
147 return ftrace_page_header_spec_.size.size;
148 }
149
150 // Retrieves the ftrace event from the proto translation
151 // table. If it does not exist, reads the format file and creates a
152 // new event with the proto id set to generic. Virtual for testing.
153 virtual const Event* GetOrCreateEvent(const GroupAndName&);
154
155 // This is for backwards compatibility. If a group is not specified in the
156 // config then the first event with that name will be returned.
GetEventByName(const std::string & name)157 const Event* GetEventByName(const std::string& name) const {
158 if (!name_to_events_.count(name))
159 return nullptr;
160 return name_to_events_.at(name)[0];
161 }
162
compact_sched_format()163 const CompactSchedEventFormat& compact_sched_format() const {
164 return compact_sched_format_;
165 }
166
167 private:
168 ProtoTranslationTable(const ProtoTranslationTable&) = delete;
169 ProtoTranslationTable& operator=(const ProtoTranslationTable&) = delete;
170
171 // Store strings so they can be read when writing the trace output.
172 const char* InternString(const std::string& str);
173
174 uint16_t CreateGenericEventField(const FtraceEvent::Field& ftrace_field,
175 Event& event);
176
177 const FtraceProcfs* ftrace_procfs_;
178 std::deque<Event> events_;
179 size_t largest_id_;
180 std::map<GroupAndName, const Event*> group_and_name_to_event_;
181 std::map<std::string, std::vector<const Event*>> name_to_events_;
182 std::map<std::string, std::vector<const Event*>> group_to_events_;
183 std::vector<Field> common_fields_;
184 FtracePageHeaderSpec ftrace_page_header_spec_{};
185 std::set<std::string> interned_strings_;
186 CompactSchedEventFormat compact_sched_format_;
187 };
188
189 // Class for efficient 'is event with id x enabled?' checks.
190 // Mirrors the data in a FtraceConfig but in a format better suited
191 // to be consumed by CpuReader.
192 class EventFilter {
193 public:
194 EventFilter();
195 ~EventFilter();
196 EventFilter(EventFilter&&) = default;
197 EventFilter& operator=(EventFilter&&) = default;
198
199 void AddEnabledEvent(size_t ftrace_event_id);
200 void DisableEvent(size_t ftrace_event_id);
201 bool IsEventEnabled(size_t ftrace_event_id) const;
202 std::set<size_t> GetEnabledEvents() const;
203 void EnableEventsFrom(const EventFilter&);
204
205 private:
206 EventFilter(const EventFilter&) = delete;
207 EventFilter& operator=(const EventFilter&) = delete;
208
209 std::vector<bool> enabled_ids_;
210 };
211
212 } // namespace perfetto
213
214 #endif // SRC_TRACED_PROBES_FTRACE_PROTO_TRANSLATION_TABLE_H_
215