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 "src/profiling/perf/proc_descriptors.h"
18
19 #include <fcntl.h>
20 #include <signal.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23
24 #include "perfetto/ext/base/string_utils.h"
25
26 namespace perfetto {
27
~ProcDescriptorDelegate()28 ProcDescriptorDelegate::~ProcDescriptorDelegate() {}
29
~ProcDescriptorGetter()30 ProcDescriptorGetter::~ProcDescriptorGetter() {}
31
32 // DirectDescriptorGetter:
33
~DirectDescriptorGetter()34 DirectDescriptorGetter::~DirectDescriptorGetter() {}
35
SetDelegate(ProcDescriptorDelegate * delegate)36 void DirectDescriptorGetter::SetDelegate(ProcDescriptorDelegate* delegate) {
37 delegate_ = delegate;
38 }
39
GetDescriptorsForPid(pid_t pid)40 void DirectDescriptorGetter::GetDescriptorsForPid(pid_t pid) {
41 base::StackString<128> dir_buf("/proc/%d", pid);
42 auto dir_fd = base::ScopedFile(
43 open(dir_buf.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
44 if (!dir_fd) {
45 if (errno != ENOENT) // not surprising if the process has quit
46 PERFETTO_PLOG("Failed to open [%s]", dir_buf.c_str());
47
48 return;
49 }
50
51 struct stat stat_buf;
52 if (fstat(dir_fd.get(), &stat_buf) == -1) {
53 PERFETTO_PLOG("Failed to stat [%s]", dir_buf.c_str());
54 return;
55 }
56
57 auto maps_fd =
58 base::ScopedFile{openat(dir_fd.get(), "maps", O_RDONLY | O_CLOEXEC)};
59 if (!maps_fd) {
60 if (errno != ENOENT) // not surprising if the process has quit
61 PERFETTO_PLOG("Failed to open %s/maps", dir_buf.c_str());
62
63 return;
64 }
65
66 auto mem_fd =
67 base::ScopedFile{openat(dir_fd.get(), "mem", O_RDONLY | O_CLOEXEC)};
68 if (!mem_fd) {
69 if (errno != ENOENT) // not surprising if the process has quit
70 PERFETTO_PLOG("Failed to open %s/mem", dir_buf.c_str());
71
72 return;
73 }
74
75 delegate_->OnProcDescriptors(pid, stat_buf.st_uid, std::move(maps_fd),
76 std::move(mem_fd));
77 }
78
79 // AndroidRemoteDescriptorGetter:
80
81 AndroidRemoteDescriptorGetter::~AndroidRemoteDescriptorGetter() = default;
82
SetDelegate(ProcDescriptorDelegate * delegate)83 void AndroidRemoteDescriptorGetter::SetDelegate(
84 ProcDescriptorDelegate* delegate) {
85 delegate_ = delegate;
86 }
87
88 #if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
GetDescriptorsForPid(pid_t)89 void AndroidRemoteDescriptorGetter::GetDescriptorsForPid(pid_t) {
90 PERFETTO_FATAL("Unexpected build type for AndroidRemoteDescriptorGetter");
91 }
92 #else
GetDescriptorsForPid(pid_t pid)93 void AndroidRemoteDescriptorGetter::GetDescriptorsForPid(pid_t pid) {
94 constexpr static int kPerfProfilerSignalValue = 1;
95 constexpr static int kProfilerSignal = __SIGRTMIN + 4;
96
97 PERFETTO_DLOG("Sending signal to pid [%d]", pid);
98 union sigval signal_value;
99 signal_value.sival_int = kPerfProfilerSignalValue;
100 if (sigqueue(pid, kProfilerSignal, signal_value) != 0 && errno != ESRCH) {
101 PERFETTO_DPLOG("Failed sigqueue(%d)", pid);
102 }
103 }
104 #endif
105
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> new_connection)106 void AndroidRemoteDescriptorGetter::OnNewIncomingConnection(
107 base::UnixSocket*,
108 std::unique_ptr<base::UnixSocket> new_connection) {
109 PERFETTO_DLOG("remote fds: new connection from pid [%d]",
110 static_cast<int>(new_connection->peer_pid_linux()));
111
112 active_connections_.emplace(new_connection.get(), std::move(new_connection));
113 }
114
OnDisconnect(base::UnixSocket * self)115 void AndroidRemoteDescriptorGetter::OnDisconnect(base::UnixSocket* self) {
116 PERFETTO_DLOG("remote fds: disconnect from pid [%d]",
117 static_cast<int>(self->peer_pid_linux()));
118
119 auto it = active_connections_.find(self);
120 PERFETTO_CHECK(it != active_connections_.end());
121 active_connections_.erase(it);
122 }
123
124 // Note: this callback will fire twice for a given connection. Once for the file
125 // descriptors, and once during the disconnect (with 0 bytes available in the
126 // socket).
OnDataAvailable(base::UnixSocket * self)127 void AndroidRemoteDescriptorGetter::OnDataAvailable(base::UnixSocket* self) {
128 // Expect two file descriptors (maps, followed by mem).
129 base::ScopedFile fds[2];
130 char buf[1];
131 size_t received_bytes =
132 self->Receive(buf, sizeof(buf), fds, base::ArraySize(fds));
133
134 PERFETTO_DLOG("remote fds: received %zu bytes", received_bytes);
135 if (!received_bytes)
136 return;
137
138 delegate_->OnProcDescriptors(self->peer_pid_linux(), self->peer_uid_posix(),
139 std::move(fds[0]), std::move(fds[1]));
140 }
141
142 } // namespace perfetto
143