1 /*
2  * Copyright (C) 2015 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 "event_type.h"
18 
19 #include <inttypes.h>
20 #include <unistd.h>
21 #include <algorithm>
22 #include <string>
23 #include <vector>
24 
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/parseint.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 
31 #include "ETMRecorder.h"
32 #include "event_attr.h"
33 #include "utils.h"
34 
35 using namespace simpleperf;
36 
37 #define EVENT_TYPE_TABLE_ENTRY(name, type, config, description, limited_arch) \
38           {name, type, config, description, limited_arch},
39 
40 static const std::vector<EventType> static_event_type_array = {
41 #include "event_type_table.h"
42 };
43 
44 static std::string tracepoint_events;
45 static std::set<EventType> g_event_types;
46 static uint32_t g_etm_event_type;
47 
SetTracepointEventsFilePath(const std::string & filepath)48 bool SetTracepointEventsFilePath(const std::string& filepath) {
49   if (!android::base::ReadFileToString(filepath, &tracepoint_events)) {
50     PLOG(ERROR) << "Failed to read " << filepath;
51     return false;
52   }
53   return true;
54 }
55 
GetTracepointEvents()56 std::string GetTracepointEvents() {
57   std::string result;
58   for (auto& event : GetAllEventTypes()) {
59     if (event.type != PERF_TYPE_TRACEPOINT) {
60       continue;
61     }
62     if (!result.empty()) {
63       result.push_back('\n');
64     }
65     result += android::base::StringPrintf("%s %" PRIu64, event.name.c_str(), event.config);
66   }
67   return result;
68 }
69 
GetTracepointEventTypesFromString(const std::string & s)70 static std::vector<EventType> GetTracepointEventTypesFromString(const std::string& s) {
71   std::vector<EventType> result;
72   for (auto& line : android::base::Split(s, "\n")) {
73     std::vector<std::string> items = android::base::Split(line, " ");
74     CHECK_EQ(items.size(), 2u);
75     std::string event_name = items[0];
76     uint64_t id;
77     CHECK(android::base::ParseUint(items[1].c_str(), &id));
78     result.push_back(EventType(event_name, PERF_TYPE_TRACEPOINT, id, "", ""));
79   }
80   return result;
81 }
82 
GetTracepointEventTypesFromTraceFs()83 static std::vector<EventType> GetTracepointEventTypesFromTraceFs() {
84   std::vector<EventType> result;
85   const std::string tracepoint_dirname = "/sys/kernel/debug/tracing/events";
86   for (const auto& system_name : GetSubDirs(tracepoint_dirname)) {
87     std::string system_path = tracepoint_dirname + "/" + system_name;
88     for (const auto& event_name : GetSubDirs(system_path)) {
89       std::string id_path = system_path + "/" + event_name + "/id";
90       std::string id_content;
91       if (!android::base::ReadFileToString(id_path, &id_content)) {
92         continue;
93       }
94       char* endptr;
95       uint64_t id = strtoull(id_content.c_str(), &endptr, 10);
96       if (endptr == id_content.c_str()) {
97         LOG(DEBUG) << "unexpected id '" << id_content << "' in " << id_path;
98         continue;
99       }
100       result.push_back(EventType(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id, "", ""));
101     }
102   }
103   return result;
104 }
105 
GetTracepointEventTypes()106 static std::vector<EventType> GetTracepointEventTypes() {
107   std::vector<EventType> result;
108   if (!tracepoint_events.empty()) {
109     result = GetTracepointEventTypesFromString(tracepoint_events);
110   } else {
111     result = GetTracepointEventTypesFromTraceFs();
112   }
113   std::sort(result.begin(), result.end(),
114             [](const EventType& type1, const EventType& type2) { return type1.name < type2.name; });
115   return result;
116 }
117 
BuildString(const std::vector<const EventType * > & event_types)118 std::string ScopedEventTypes::BuildString(const std::vector<const EventType*>& event_types) {
119   std::string result;
120   for (auto type : event_types) {
121     if (!result.empty()) {
122       result.push_back('\n');
123     }
124     result += android::base::StringPrintf("%s,%u,%" PRIu64, type->name.c_str(), type->type,
125                                           type->config);
126   }
127   return result;
128 }
129 
ScopedEventTypes(const std::string & event_type_str)130 ScopedEventTypes::ScopedEventTypes(const std::string& event_type_str) {
131   saved_event_types_ = std::move(g_event_types);
132   g_event_types.clear();
133   for (auto& s : android::base::Split(event_type_str, "\n")) {
134     std::string name = s.substr(0, s.find(','));
135     uint32_t type;
136     uint64_t config;
137     sscanf(s.c_str() + name.size(), ",%u,%" PRIu64, &type, &config);
138     if (name == "cs-etm") {
139       g_etm_event_type = type;
140     }
141     g_event_types.emplace(name, type, config, "", "");
142   }
143 }
144 
~ScopedEventTypes()145 ScopedEventTypes::~ScopedEventTypes() {
146   g_event_types = std::move(saved_event_types_);
147 }
148 
GetAllEventTypes()149 const std::set<EventType>& GetAllEventTypes() {
150   if (g_event_types.empty()) {
151     g_event_types.insert(static_event_type_array.begin(), static_event_type_array.end());
152     std::vector<EventType> tracepoint_array = GetTracepointEventTypes();
153     g_event_types.insert(tracepoint_array.begin(), tracepoint_array.end());
154 #if defined(__linux__)
155     std::unique_ptr<EventType> etm_type = ETMRecorder::GetInstance().BuildEventType();
156     if (etm_type) {
157       g_etm_event_type = etm_type->type;
158       g_event_types.emplace(std::move(*etm_type));
159     }
160 #endif
161   }
162   return g_event_types;
163 }
164 
FindEventTypeByName(const std::string & name,bool report_error)165 const EventType* FindEventTypeByName(const std::string& name, bool report_error) {
166   const auto& event_types = GetAllEventTypes();
167   auto it = event_types.find(EventType(name, 0, 0, "", ""));
168   if (it != event_types.end()) {
169     return &*it;
170   }
171   if (!name.empty() && name[0] == 'r') {
172     char* end;
173     uint64_t config = strtoull(&name[1], &end, 16);
174     if (end != &name[1] && *end == '\0') {
175       auto result = g_event_types.emplace(name, PERF_TYPE_RAW, config, "", "");
176       CHECK(result.second);
177       return &*(result.first);
178     }
179   }
180   if (report_error) {
181     LOG(ERROR) << "Unknown event_type '" << name
182                << "', try `simpleperf list` to list all possible event type names";
183   }
184   return nullptr;
185 }
186 
ParseEventType(const std::string & event_type_str)187 std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str) {
188   static std::string modifier_characters = "ukhGHp";
189   std::unique_ptr<EventTypeAndModifier> event_type_modifier(new EventTypeAndModifier);
190   event_type_modifier->name = event_type_str;
191   std::string event_type_name = event_type_str;
192   std::string modifier;
193   size_t comm_pos = event_type_str.rfind(':');
194   if (comm_pos != std::string::npos) {
195     bool match_modifier = true;
196     for (size_t i = comm_pos + 1; i < event_type_str.size(); ++i) {
197       char c = event_type_str[i];
198       if (c != ' ' && modifier_characters.find(c) == std::string::npos) {
199         match_modifier = false;
200         break;
201       }
202     }
203     if (match_modifier) {
204       event_type_name = event_type_str.substr(0, comm_pos);
205       modifier = event_type_str.substr(comm_pos + 1);
206     }
207   }
208   const EventType* event_type = FindEventTypeByName(event_type_name);
209   if (event_type == nullptr) {
210     // Try if the modifier belongs to the event type name, like some tracepoint events.
211     if (!modifier.empty()) {
212       event_type_name = event_type_str;
213       modifier.clear();
214       event_type = FindEventTypeByName(event_type_name);
215     }
216     if (event_type == nullptr) {
217       return nullptr;
218     }
219   }
220   event_type_modifier->event_type = *event_type;
221   if (modifier.find_first_of("ukh") != std::string::npos) {
222     event_type_modifier->exclude_user = true;
223     event_type_modifier->exclude_kernel = true;
224     event_type_modifier->exclude_hv = true;
225   }
226   if (modifier.find_first_of("GH") != std::string::npos) {
227     event_type_modifier->exclude_guest = true;
228     event_type_modifier->exclude_host = true;
229   }
230 
231   for (auto& c : modifier) {
232     switch (c) {
233       case 'u':
234         event_type_modifier->exclude_user = false;
235         break;
236       case 'k':
237         event_type_modifier->exclude_kernel = false;
238         break;
239       case 'h':
240         event_type_modifier->exclude_hv = false;
241         break;
242       case 'G':
243         event_type_modifier->exclude_guest = false;
244         break;
245       case 'H':
246         event_type_modifier->exclude_host = false;
247         break;
248       case 'p':
249         event_type_modifier->precise_ip++;
250         break;
251       case ' ':
252         break;
253       default:
254         LOG(ERROR) << "Unknown event type modifier '" << c << "'";
255     }
256   }
257   event_type_modifier->modifier = modifier;
258   return event_type_modifier;
259 }
260 
IsEtmEventType(uint32_t type)261 bool IsEtmEventType(uint32_t type) {
262   return g_etm_event_type != 0 && type == g_etm_event_type;
263 }