1 /*
2 * Copyright (C) 2020 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 "ProbeEvents.h"
18
19 #include <inttypes.h>
20
21 #include <filesystem>
22 #include <memory>
23 #include <string>
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 #include <android-base/unique_fd.h>
31
32 #include "RegEx.h"
33 #include "environment.h"
34 #include "event_selection_set.h"
35 #include "event_type.h"
36 #include "utils.h"
37
38 namespace simpleperf {
39
40 using android::base::ParseInt;
41 using android::base::ParseUint;
42 using android::base::Split;
43 using android::base::StringPrintf;
44 using android::base::unique_fd;
45 using android::base::WriteStringToFd;
46
47 static const std::string kKprobeEventPrefix = "kprobes:";
48
ParseProbeEventName(ProbeEventType type,const std::string & probe_cmd,ProbeEvent * event)49 bool ProbeEvents::ParseProbeEventName(ProbeEventType type, const std::string& probe_cmd,
50 ProbeEvent* event) {
51 // kprobe_cmd is in formats described in <kernel>/Documentation/trace/kprobetrace.rst:
52 // p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]
53 // r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+offs] [FETCHARGS]
54 // uprobe_cmd is in formats described in <kernel>/Documentation/trace/uprobetracer.rst:
55 // p[:[GRP/][EVENT]] PATH:OFFSET [FETCHARGS] : Set a uprobe
56 // r[:[GRP/][EVENT]] PATH:OFFSET [FETCHARGS] : Set a return uprobe (uretprobe)
57 std::vector<std::string> args = Split(probe_cmd, " ");
58 if (args.size() < 2) {
59 return false;
60 }
61
62 event->type = type;
63 event->group_name = type == ProbeEventType::kKprobe ? "kprobes" : "uprobes";
64
65 // Parse given name.
66 auto name_reg = RegEx::Create(R"(:([a-zA-Z_][\w_]*/)?([a-zA-Z_][\w_]*))");
67 auto match = name_reg->SearchAll(args[0]);
68 if (match->IsValid()) {
69 if (match->GetField(1).length() > 0) {
70 event->group_name = match->GetField(1);
71 event->group_name.pop_back();
72 }
73 event->event_name = match->GetField(2);
74 return true;
75 }
76
77 if (type == ProbeEventType::kKprobe) {
78 // Generate name from MEMADDR.
79 char probe_type = args[0][0];
80 uint64_t kaddr;
81 if (ParseUint(args[1], &kaddr)) {
82 event->event_name = StringPrintf("%c_0x%" PRIx64, probe_type, kaddr);
83 return true;
84 }
85
86 // Generate name from [MOD:]SYM[+offs].
87 std::string symbol;
88 int64_t offset;
89 size_t split_pos = args[1].find_first_of("+-");
90 if (split_pos == std::string::npos) {
91 symbol = args[1];
92 offset = 0;
93 } else {
94 symbol = args[1].substr(0, split_pos);
95 if (!ParseInt(args[1].substr(split_pos), &offset) || offset < 0) {
96 return false;
97 }
98 }
99 std::string s = StringPrintf("%c_%s_%" PRId64, probe_type, symbol.c_str(), offset);
100 event->event_name = RegEx::Create(R"(\.|:)")->Replace(s, "_").value();
101 return true;
102 } else {
103 // Generate name from PATH:OFFSET.
104 uint64_t offset;
105 std::vector<std::string> target = Split(args[1], ":");
106 if (target.size() == 2 && ParseUint(target[1], &offset)) {
107 std::filesystem::path path(target[0]);
108 std::string filename = path.filename().string();
109 auto pos = filename.find_first_of(".-_");
110 if (pos != std::string::npos) {
111 filename = filename.substr(0, pos);
112 }
113 // 'p' is used in the event name even if it's a uretprobe.
114 event->event_name = StringPrintf("p_%s_0x%" PRIx64, filename.c_str(), offset);
115 return true;
116 }
117
118 return false;
119 }
120 }
121
~ProbeEvents()122 ProbeEvents::~ProbeEvents() {
123 if (!IsEmpty()) {
124 // Probe events can be deleted only when no perf event file is using them.
125 event_selection_set_.CloseEventFiles();
126 Clear();
127 }
128 }
129
IsProbeSupported(ProbeEventType type)130 bool ProbeEvents::IsProbeSupported(ProbeEventType type) {
131 const char* file_name = type == ProbeEventType::kKprobe ? "kprobe_events" : "uprobe_events";
132 auto& path = GetProbeControlPath(type);
133 if (!path.has_value()) {
134 path = "";
135 if (const char* tracefs_dir = GetTraceFsDir(); tracefs_dir != nullptr) {
136 std::string probe_event_path = std::string(tracefs_dir) + "/" + file_name;
137 if (IsRegularFile(probe_event_path)) {
138 path = std::move(probe_event_path);
139 }
140 }
141 }
142 return !path.value().empty();
143 }
144
AddProbe(ProbeEventType type,const std::string & probe_cmd)145 bool ProbeEvents::AddProbe(ProbeEventType type, const std::string& probe_cmd) {
146 ProbeEvent event;
147 if (!IsProbeSupported(type)) {
148 LOG(ERROR) << "probe events isn't supported by the kernel.";
149 return false;
150 }
151 if (!ParseProbeEventName(type, probe_cmd, &event)) {
152 LOG(ERROR) << "invalid probe cmd: " << probe_cmd;
153 return false;
154 }
155 if (!WriteProbeCmd(type, probe_cmd)) {
156 return false;
157 }
158 probe_events_.emplace_back(std::move(event));
159 return true;
160 }
161
IsKprobeEvent(const std::string & event_name)162 bool ProbeEvents::IsKprobeEvent(const std::string& event_name) {
163 return android::base::StartsWith(event_name, kKprobeEventPrefix);
164 }
165
CreateProbeEventIfNotExist(const std::string & event_name)166 bool ProbeEvents::CreateProbeEventIfNotExist(const std::string& event_name) {
167 // uprobes aren't supported in this function because we can't identify the binary from an event
168 // name.
169 if (!IsKprobeEvent(event_name) ||
170 (EventTypeManager::Instance().FindType(event_name) != nullptr)) {
171 // No need to create a probe event.
172 return true;
173 }
174 std::string function_name = event_name.substr(kKprobeEventPrefix.size());
175 return AddProbe(ProbeEventType::kKprobe,
176 StringPrintf("p:%s %s", function_name.c_str(), function_name.c_str()));
177 }
178
Clear()179 void ProbeEvents::Clear() {
180 for (const auto& probe_event : probe_events_) {
181 if (!WriteProbeCmd(probe_event.type,
182 "-:" + probe_event.group_name + "/" + probe_event.event_name)) {
183 LOG(WARNING) << "failed to delete probe event " << probe_event.group_name << ":"
184 << probe_event.event_name;
185 }
186 EventTypeManager::Instance().RemoveProbeType(probe_event.group_name + ":" +
187 probe_event.event_name);
188 }
189 probe_events_.clear();
190 }
191
WriteProbeCmd(ProbeEventType type,const std::string & probe_cmd)192 bool ProbeEvents::WriteProbeCmd(ProbeEventType type, const std::string& probe_cmd) {
193 const std::string& file_path = GetProbeControlPath(type).value();
194 unique_fd fd(open(file_path.c_str(), O_APPEND | O_WRONLY | O_CLOEXEC));
195 if (!fd.ok()) {
196 PLOG(ERROR) << "failed to open " << file_path;
197 return false;
198 }
199 if (!WriteStringToFd(probe_cmd, fd)) {
200 PLOG(ERROR) << "failed to write '" << probe_cmd << "' to " << file_path;
201 return false;
202 }
203 return true;
204 }
205
GetProbeControlPath(ProbeEventType type)206 std::optional<std::string>& ProbeEvents::GetProbeControlPath(ProbeEventType type) {
207 switch (type) {
208 case ProbeEventType::kKprobe:
209 return kprobe_control_path_;
210 case ProbeEventType::kUprobe:
211 return uprobe_control_path_;
212 }
213 }
214
215 } // namespace simpleperf
216