blob: 5c027387c761f40500088f2363d39be104f7a088 [file] [log] [blame]
Josh Gao9c02dc52016-06-15 17:29:00 -07001/*
Josh Gaocbe70cb2016-10-18 18:17:52 -07002 * Copyright 2016, The Android Open Source Project
Josh Gao9c02dc52016-06-15 17:29:00 -07003 *
Josh Gaocbe70cb2016-10-18 18:17:52 -07004 * 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
Josh Gao9c02dc52016-06-15 17:29:00 -07007 *
Josh Gaocbe70cb2016-10-18 18:17:52 -07008 * 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.
Josh Gao9c02dc52016-06-15 17:29:00 -070015 */
16
Josh Gaocbe70cb2016-10-18 18:17:52 -070017#include <debuggerd/client.h>
Josh Gao9c02dc52016-06-15 17:29:00 -070018
Josh Gaocbe70cb2016-10-18 18:17:52 -070019#include <fcntl.h>
Josh Gao9c02dc52016-06-15 17:29:00 -070020#include <signal.h>
Josh Gao9c02dc52016-06-15 17:29:00 -070021#include <stdlib.h>
Josh Gaoae9d7672017-03-24 16:26:03 -070022#include <sys/poll.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070023#include <sys/stat.h>
24#include <sys/types.h>
Kalesh Singh1489e272019-07-14 12:15:35 -070025#include <time.h>
Josh Gao9c02dc52016-06-15 17:29:00 -070026#include <unistd.h>
27
Josh Gaocbe70cb2016-10-18 18:17:52 -070028#include <chrono>
Kalesh Singh1489e272019-07-14 12:15:35 -070029#include <iomanip>
Josh Gao9c02dc52016-06-15 17:29:00 -070030
Josh Gao5f87bbd2019-01-09 17:01:49 -080031#include <android-base/cmsg.h>
Josh Gaoae9d7672017-03-24 16:26:03 -070032#include <android-base/file.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070033#include <android-base/logging.h>
Josh Gao5675f3c2017-06-01 12:19:53 -070034#include <android-base/parseint.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070035#include <android-base/stringprintf.h>
Josh Gao5675f3c2017-06-01 12:19:53 -070036#include <android-base/strings.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070037#include <android-base/unique_fd.h>
Josh Gaoa48b41b2019-12-13 14:11:04 -080038#include <bionic/reserved_signals.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070039#include <cutils/sockets.h>
Josh Gao4175cee2019-01-04 13:57:09 -080040#include <procinfo/process.h>
Narayan Kamath2d377cd2017-05-10 10:58:59 +010041
42#include "debuggerd/handler.h"
43#include "protocol.h"
44#include "util.h"
Josh Gao9c02dc52016-06-15 17:29:00 -070045
Josh Gaoae9d7672017-03-24 16:26:03 -070046using namespace std::chrono_literals;
47
Kalesh Singh1489e272019-07-14 12:15:35 -070048using android::base::ReadFileToString;
Josh Gao5f87bbd2019-01-09 17:01:49 -080049using android::base::SendFileDescriptors;
Josh Gaocbe70cb2016-10-18 18:17:52 -070050using android::base::unique_fd;
Kalesh Singh1489e272019-07-14 12:15:35 -070051using android::base::WriteStringToFd;
Josh Gao9c02dc52016-06-15 17:29:00 -070052
Narayan Kamatha73df602017-05-24 15:07:25 +010053static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
Josh Gaoa48b41b2019-12-13 14:11:04 -080054 const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;
Josh Gaocbe70cb2016-10-18 18:17:52 -070055 sigval val;
Narayan Kamatha73df602017-05-24 15:07:25 +010056 val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
57
58 if (sigqueue(pid, signal, val) != 0) {
Josh Gaocbe70cb2016-10-18 18:17:52 -070059 PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
Josh Gao9c02dc52016-06-15 17:29:00 -070060 return false;
61 }
Josh Gaocbe70cb2016-10-18 18:17:52 -070062 return true;
Josh Gao9c02dc52016-06-15 17:29:00 -070063}
64
Josh Gaoae9d7672017-03-24 16:26:03 -070065template <typename Duration>
66static void populate_timeval(struct timeval* tv, const Duration& duration) {
67 auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
68 auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration - seconds);
69 tv->tv_sec = static_cast<long>(seconds.count());
70 tv->tv_usec = static_cast<long>(microseconds.count());
71}
72
Kalesh Singh1489e272019-07-14 12:15:35 -070073static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
74 struct tm now;
75 time_t t = time(nullptr);
76 localtime_r(&t, &now);
77 char timestamp[32];
78 strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
79 std::string time_now(timestamp);
80
81 std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
82
83 char proc_name_buf[1024];
84 const char* proc_name = nullptr;
85 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
86
87 if (fp) {
88 proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
89 }
90
91 if (!proc_name) {
92 proc_name = "<unknown>";
93 }
94
95 buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
96 << "Cmd line: " << proc_name << "\n";
97}
98
99static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
100 buffer << "----- end " << std::to_string(pid) << " -----\n";
101}
102
103/**
104 * Returns the wchan data for each thread in the process,
105 * or empty string if unable to obtain any data.
106 */
107static std::string get_wchan_data(pid_t pid) {
108 std::stringstream buffer;
109 std::vector<pid_t> tids;
110
111 if (!android::procinfo::GetProcessTids(pid, &tids)) {
112 LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
113 return buffer.str();
114 }
115
116 std::stringstream data;
117 for (int tid : tids) {
118 std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
119 std::string wchan_str;
120 if (!ReadFileToString(path, &wchan_str, true)) {
121 PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
122 continue;
123 }
124 data << "sysTid=" << std::left << std::setw(10) << tid << wchan_str << "\n";
125 }
126
127 if (std::string str = data.str(); !str.empty()) {
128 get_wchan_header(pid, buffer);
129 buffer << "\n" << str << "\n";
130 get_wchan_footer(pid, buffer);
131 buffer << "\n";
132 }
133
134 return buffer.str();
135}
136
137static void dump_wchan_data(const std::string& data, int fd, pid_t pid) {
138 if (!WriteStringToFd(data, fd)) {
139 LOG(WARNING) << "libdebuggerd_client: Failed to dump wchan data for pid: " << pid;
140 }
141}
142
Josh Gao4175cee2019-01-04 13:57:09 -0800143bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
Narayan Kamatha73df602017-05-24 15:07:25 +0100144 unique_fd output_fd) {
Josh Gao4175cee2019-01-04 13:57:09 -0800145 pid_t pid = tid;
146 if (dump_type == kDebuggerdJavaBacktrace) {
147 // Java dumps always get sent to the tgid, so we need to resolve our tid to a tgid.
148 android::procinfo::ProcessInfo procinfo;
149 std::string error;
150 if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {
151 LOG(ERROR) << "libdebugged_client: failed to get process info: " << error;
Greg Kaiser3fa9a592019-01-08 06:29:22 -0800152 return false;
Josh Gao4175cee2019-01-04 13:57:09 -0800153 }
154 pid = procinfo.pid;
155 }
156
Josh Gaocbe70cb2016-10-18 18:17:52 -0700157 LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
158 unique_fd sockfd;
159 const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
Josh Gao287d50d2017-04-04 13:43:21 -0700160 auto time_left = [&end]() { return end - std::chrono::steady_clock::now(); };
Josh Gaoae9d7672017-03-24 16:26:03 -0700161 auto set_timeout = [timeout_ms, &time_left](int sockfd) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700162 if (timeout_ms <= 0) {
Josh Gao287d50d2017-04-04 13:43:21 -0700163 return sockfd;
Josh Gao9c02dc52016-06-15 17:29:00 -0700164 }
Josh Gao9c02dc52016-06-15 17:29:00 -0700165
Josh Gaoae9d7672017-03-24 16:26:03 -0700166 auto remaining = time_left();
167 if (remaining < decltype(remaining)::zero()) {
Josh Gao287d50d2017-04-04 13:43:21 -0700168 LOG(ERROR) << "libdebuggerd_client: timeout expired";
Josh Gao460b3362017-03-30 16:40:47 -0700169 return -1;
Josh Gao9c02dc52016-06-15 17:29:00 -0700170 }
Josh Gao287d50d2017-04-04 13:43:21 -0700171
Josh Gaoae9d7672017-03-24 16:26:03 -0700172 struct timeval timeout;
173 populate_timeval(&timeout, remaining);
Josh Gao9c02dc52016-06-15 17:29:00 -0700174
Josh Gaocbe70cb2016-10-18 18:17:52 -0700175 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
Josh Gao287d50d2017-04-04 13:43:21 -0700176 PLOG(ERROR) << "libdebuggerd_client: failed to set receive timeout";
Josh Gao460b3362017-03-30 16:40:47 -0700177 return -1;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700178 }
179 if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
Josh Gao287d50d2017-04-04 13:43:21 -0700180 PLOG(ERROR) << "libdebuggerd_client: failed to set send timeout";
Josh Gao460b3362017-03-30 16:40:47 -0700181 return -1;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700182 }
Josh Gao218f7fb2016-10-07 16:42:05 -0700183
Josh Gao460b3362017-03-30 16:40:47 -0700184 return sockfd;
Josh Gao218f7fb2016-10-07 16:42:05 -0700185 };
Josh Gao9c02dc52016-06-15 17:29:00 -0700186
Josh Gaocbe70cb2016-10-18 18:17:52 -0700187 sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
188 if (sockfd == -1) {
189 PLOG(ERROR) << "libdebugger_client: failed to create socket";
190 return false;
Josh Gao9c02dc52016-06-15 17:29:00 -0700191 }
192
Josh Gao460b3362017-03-30 16:40:47 -0700193 if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
Josh Gaocbe70cb2016-10-18 18:17:52 -0700194 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
195 PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
196 return false;
197 }
198
Nick Desaulniers67d52aa2019-10-07 23:28:15 -0700199 InterceptRequest req = {
200 .dump_type = dump_type,
201 .pid = pid,
202 };
Josh Gaoae9d7672017-03-24 16:26:03 -0700203 if (!set_timeout(sockfd)) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700204 PLOG(ERROR) << "libdebugger_client: failed to set timeout";
Josh Gaoae9d7672017-03-24 16:26:03 -0700205 return false;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700206 }
207
Josh Gaoae9d7672017-03-24 16:26:03 -0700208 // Create an intermediate pipe to pass to the other end.
209 unique_fd pipe_read, pipe_write;
210 if (!Pipe(&pipe_read, &pipe_write)) {
211 PLOG(ERROR) << "libdebuggerd_client: failed to create pipe";
212 return false;
213 }
214
Josh Gao5675f3c2017-06-01 12:19:53 -0700215 std::string pipe_size_str;
216 int pipe_buffer_size = 1024 * 1024;
217 if (android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
218 pipe_size_str = android::base::Trim(pipe_size_str);
219
220 if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
221 LOG(FATAL) << "failed to parse pipe max size '" << pipe_size_str << "'";
222 }
223 }
224
225 if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
226 PLOG(ERROR) << "failed to set pipe buffer size";
227 }
228
Josh Gao5f87bbd2019-01-09 17:01:49 -0800229 ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
230 pipe_write.reset();
231 if (rc != sizeof(req)) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700232 PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
233 return false;
234 }
235
Josh Gao460b3362017-03-30 16:40:47 -0700236 // Check to make sure we've successfully registered.
237 InterceptResponse response;
Josh Gao5f87bbd2019-01-09 17:01:49 -0800238 rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
Josh Gao460b3362017-03-30 16:40:47 -0700239 if (rc == 0) {
Josh Gaobe0c1af2018-09-06 13:00:57 -0700240 LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
241 << "timeout reached?";
242 return false;
243 } else if (rc == -1) {
244 PLOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned";
Josh Gao460b3362017-03-30 16:40:47 -0700245 return false;
246 } else if (rc != sizeof(response)) {
Josh Gaobe0c1af2018-09-06 13:00:57 -0700247 LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
248 "reading initial response: expected "
249 << sizeof(response) << ", received " << rc;
Josh Gao460b3362017-03-30 16:40:47 -0700250 return false;
251 }
252
253 if (response.status != InterceptStatus::kRegistered) {
254 LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
255 << static_cast<int>(response.status);
256 return false;
257 }
258
Josh Gao4175cee2019-01-04 13:57:09 -0800259 if (!send_signal(tid, dump_type)) {
Liu Changcheng34922212017-04-06 11:20:14 +0800260 return false;
261 }
Josh Gaocbe70cb2016-10-18 18:17:52 -0700262
Josh Gao460b3362017-03-30 16:40:47 -0700263 rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
Josh Gaocbe70cb2016-10-18 18:17:52 -0700264 if (rc == 0) {
Josh Gaobe0c1af2018-09-06 13:00:57 -0700265 LOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned: "
266 "timeout reached?";
267 return false;
268 } else if (rc == -1) {
269 PLOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned";
Josh Gaocbe70cb2016-10-18 18:17:52 -0700270 return false;
271 } else if (rc != sizeof(response)) {
Josh Gaobe0c1af2018-09-06 13:00:57 -0700272 LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
273 "reading confirmation response: expected "
274 << sizeof(response) << ", received " << rc;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700275 return false;
276 }
277
Josh Gao460b3362017-03-30 16:40:47 -0700278 if (response.status != InterceptStatus::kStarted) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700279 response.error_message[sizeof(response.error_message) - 1] = '\0';
280 LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
Josh Gaoae9d7672017-03-24 16:26:03 -0700281 return false;
282 }
283
284 // Forward output from the pipe to the output fd.
285 while (true) {
Josh Gao287d50d2017-04-04 13:43:21 -0700286 auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left()).count();
287 if (timeout_ms <= 0) {
288 remaining_ms = -1;
289 } else if (remaining_ms < 0) {
Josh Gaoae9d7672017-03-24 16:26:03 -0700290 LOG(ERROR) << "libdebuggerd_client: timeout expired";
291 return false;
292 }
293
294 struct pollfd pfd = {
295 .fd = pipe_read.get(), .events = POLLIN, .revents = 0,
296 };
297
Josh Gao287d50d2017-04-04 13:43:21 -0700298 rc = poll(&pfd, 1, remaining_ms);
Josh Gaoae9d7672017-03-24 16:26:03 -0700299 if (rc == -1) {
300 if (errno == EINTR) {
301 continue;
302 } else {
303 PLOG(ERROR) << "libdebuggerd_client: error while polling";
304 return false;
305 }
306 } else if (rc == 0) {
307 LOG(ERROR) << "libdebuggerd_client: timeout expired";
308 return false;
309 }
310
311 char buf[1024];
312 rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), buf, sizeof(buf)));
313 if (rc == 0) {
314 // Done.
315 break;
316 } else if (rc == -1) {
317 PLOG(ERROR) << "libdebuggerd_client: error while reading";
318 return false;
319 }
320
321 if (!android::base::WriteFully(output_fd.get(), buf, rc)) {
322 PLOG(ERROR) << "libdebuggerd_client: error while writing";
323 return false;
324 }
Josh Gaocbe70cb2016-10-18 18:17:52 -0700325 }
326
327 LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
328
329 return true;
Josh Gao9c02dc52016-06-15 17:29:00 -0700330}
331
Narayan Kamatha73df602017-05-24 15:07:25 +0100332int dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) {
333 return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700334}
335
Narayan Kamatha73df602017-05-24 15:07:25 +0100336int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs,
337 int fd) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700338 android::base::unique_fd copy(dup(fd));
339 if (copy == -1) {
340 return -1;
Josh Gao9c02dc52016-06-15 17:29:00 -0700341 }
Kalesh Singh1489e272019-07-14 12:15:35 -0700342
343 // debuggerd_trigger_dump results in every thread in the process being interrupted
344 // by a signal, so we need to fetch the wchan data before calling that.
345 std::string wchan_data = get_wchan_data(tid);
346
Josh Gaocbe70cb2016-10-18 18:17:52 -0700347 int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
Kalesh Singh1489e272019-07-14 12:15:35 -0700348 int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
349
350 // Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read
351 // kernel stack traces (/proc/*/stack).
352 dump_wchan_data(wchan_data, fd, tid);
353
354 return ret;
Josh Gao9c02dc52016-06-15 17:29:00 -0700355}