• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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