Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e282a14 authored by Josh Gao's avatar Josh Gao Committed by android-build-merger
Browse files

dumpsys: use a socket for dumping, add timeout support.

am: 4b8f6f9a

* commit '4b8f6f9a':
  dumpsys: use a socket for dumping, add timeout support.
parents 054ce05a 4b8f6f9a
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
	dumpsys.cpp

LOCAL_SHARED_LIBRARIES := \
	libbase \
	libutils \
	liblog \
	libbinder
+110 −13
Original line number Diff line number Diff line
@@ -5,21 +5,33 @@

#define LOG_TAG "dumpsys"

#include <utils/Log.h>
#include <algorithm>
#include <chrono>
#include <thread>

#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/TextOutput.h>
#include <utils/Log.h>
#include <utils/Vector.h>

#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

using namespace android;
using android::base::unique_fd;
using android::base::WriteFully;

static int sort_func(const String16* lhs, const String16* rhs)
{
@@ -122,22 +134,107 @@ int main(int argc, char* const argv[])
    }

    for (size_t i = 0; i < N; i++) {
        if (IsSkipped(skippedServices, services[i])) continue;
        String16 service_name = std::move(services[i]);
        if (IsSkipped(skippedServices, service_name)) continue;

        sp<IBinder> service = sm->checkService(services[i]);
        sp<IBinder> service = sm->checkService(service_name);
        if (service != NULL) {
            int sfd[2];

            // Use a socketpair instead of a pipe to avoid sending SIGPIPE to services that timeout.
            if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) != 0) {
                aerr << "Failed to create socketpair to dump service info for " << service_name
                     << ": " << strerror(errno) << endl;
                continue;
            }

            unique_fd local_end(sfd[0]);
            unique_fd remote_end(sfd[1]);
            sfd[0] = sfd[1] = -1;

            if (N > 1) {
                aout << "------------------------------------------------------------"
                        "-------------------" << endl;
                aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
                aout << "DUMP OF SERVICE " << service_name << ":" << endl;
            }
            int err = service->dump(STDOUT_FILENO, args);

            // dump blocks until completion, so spawn a thread..
            std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
                int err = service->dump(remote_end.get(), args);

                // It'd be nice to be able to close the remote end of the socketpair before the dump
                // call returns, to terminate our reads if the other end closes their copy of the
                // file descriptor, but then hangs for some reason. There doesn't seem to be a good
                // way to do this, though.
                remote_end.clear();

                if (err != 0) {
                aerr << "Error dumping service info: (" << strerror(err)
                        << ") " << services[i] << endl;
                    aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
                         << endl;
                }
            });

            // TODO: Make this configurable at runtime.
            constexpr auto timeout = std::chrono::seconds(10);
            auto end = std::chrono::steady_clock::now() + timeout;

            struct pollfd pfd = {
                .fd = local_end.get(),
                .events = POLLIN
            };

            bool timed_out = false;
            bool error = false;
            while (true) {
                // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
                auto time_left_ms = [end]() {
                    auto now = std::chrono::steady_clock::now();
                    auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
                    return std::max(diff.count(), 0ll);
                };

                int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
                if (rc < 0) {
                    aerr << "Error in poll while dumping service " << service_name << " : "
                         << strerror(errno) << endl;
                    error = true;
                    break;
                } else if (rc == 0) {
                    timed_out = true;
                    break;
                }

                char buf[4096];
                rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
                if (rc < 0) {
                    aerr << "Failed to read while dumping service " << service_name << ": "
                         << strerror(errno) << endl;
                    error = true;
                    break;
                } else if (rc == 0) {
                    // EOF.
                    break;
                }

                if (!WriteFully(STDOUT_FILENO, buf, rc)) {
                    aerr << "Failed to write while dumping service " << service_name << ": "
                         << strerror(errno) << endl;
                    error = true;
                    break;
                }
            }

            if (timed_out) {
                aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
            }

            if (timed_out || error) {
                dump_thread.detach();
            } else {
                dump_thread.join();
            }
        } else {
            aerr << "Can't find service: " << services[i] << endl;
            aerr << "Can't find service: " << service_name << endl;
        }
    }