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

Commit 56b45522 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Add text dumpsys section to incidentd" into rvc-dev am: 5bbfd653 am:...

Merge "Add text dumpsys section to incidentd" into rvc-dev am: 5bbfd653 am: d889b5ff am: 6c7619b7

Change-Id: I4b0763407361772e3db66e1a4de1b99ad8e5179a
parents ebba0f3c 6c7619b7
Loading
Loading
Loading
Loading
+97 −61
Original line number Diff line number Diff line
@@ -20,9 +20,9 @@

#include <dirent.h>
#include <errno.h>

#include <mutex>
#include <set>
#include <thread>

#include <android-base/file.h>
#include <android-base/properties.h>
@@ -42,6 +42,7 @@
#include "frameworks/base/core/proto/android/os/backtrace.proto.h"
#include "frameworks/base/core/proto/android/os/data.proto.h"
#include "frameworks/base/core/proto/android/util/log.proto.h"
#include "frameworks/base/core/proto/android/util/textdump.proto.h"
#include "incidentd_util.h"

namespace android {
@@ -135,7 +136,7 @@ status_t FileSection::Execute(ReportWriter* writer) const {
    status_t ihStatus = wait_child(pid);
    if (ihStatus != NO_ERROR) {
        ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus));
        return ihStatus;
        return OK; // Not a fatal error.
    }

    return writer->writeSection(buffer);
@@ -234,7 +235,7 @@ struct WorkerThreadData : public virtual RefBase {
    Fpipe pipe;

    // Lock protects these fields
    mutex lock;
    std::mutex lock;
    bool workerDone;
    status_t workerError;

@@ -261,83 +262,47 @@ void sigpipe_handler(int signum) {
    }
}

static void* worker_thread_func(void* cookie) {
    // Don't crash the service if we write to a closed pipe (which can happen if
    // dumping times out).
    signal(SIGPIPE, sigpipe_handler);

    WorkerThreadData* data = (WorkerThreadData*)cookie;
    status_t err = data->section->BlockingCall(data->pipe.writeFd());

    {
        unique_lock<mutex> lock(data->lock);
        data->workerDone = true;
        data->workerError = err;
    }

    data->pipe.writeFd().reset();
    data->decStrong(data->section);
    // data might be gone now. don't use it after this point in this thread.
    return NULL;
}

status_t WorkerThreadSection::Execute(ReportWriter* writer) const {
    status_t err = NO_ERROR;
    pthread_t thread;
    pthread_attr_t attr;
    bool workerDone = false;
    FdBuffer buffer;

    // Data shared between this thread and the worker thread.
    sp<WorkerThreadData> data = new WorkerThreadData(this);

    // Create the pipe
    if (!data->pipe.init()) {
    // Create shared data and pipe
    WorkerThreadData data(this);
    if (!data.pipe.init()) {
        return -errno;
    }

    // Create the thread
    err = pthread_attr_init(&attr);
    if (err != 0) {
        return -err;
    }
    // TODO: Do we need to tweak thread priority?
    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (err != 0) {
        pthread_attr_destroy(&attr);
        return -err;
    }

    // The worker thread needs a reference and we can't let the count go to zero
    // if that thread is slow to start.
    data->incStrong(this);

    err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
    pthread_attr_destroy(&attr);
    if (err != 0) {
        data->decStrong(this);
        return -err;
    std::thread([&]() {
        // Don't crash the service if writing to a closed pipe (may happen if dumping times out)
        signal(SIGPIPE, sigpipe_handler);
        status_t err = data.section->BlockingCall(data.pipe.writeFd());
        {
            std::unique_lock<std::mutex> lock(data.lock);
            data.workerDone = true;
            data.workerError = err;
            // unique_fd is not thread safe. If we don't lock it, reset() may pause half way while
            // the other thread executes to the end, calling ~Fpipe, which is a race condition.
            data.pipe.writeFd().reset();
        }
    }).detach();

    // Loop reading until either the timeout or the worker side is done (i.e. eof).
    err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
    err = buffer.read(data.pipe.readFd().get(), this->timeoutMs);
    if (err != NO_ERROR) {
        ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
    }

    // Done with the read fd. The worker thread closes the write one so
    // we never race and get here first.
    data->pipe.readFd().reset();

    // If the worker side is finished, then return its error (which may overwrite
    // our possible error -- but it's more interesting anyway). If not, then we timed out.
    {
        unique_lock<mutex> lock(data->lock);
        if (data->workerError != NO_ERROR) {
            err = data->workerError;
        std::unique_lock<std::mutex> lock(data.lock);
        data.pipe.close();
        if (data.workerError != NO_ERROR) {
            err = data.workerError;
            ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
        }
        workerDone = data->workerDone;
        workerDone = data.workerDone;
    }

    writer->setSectionStats(buffer);
@@ -472,6 +437,77 @@ status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
    return NO_ERROR;
}

// ================================================================================
TextDumpsysSection::TextDumpsysSection(int id, const char* service, ...)
    : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), mService(service) {
    name = "dumpsys ";
    name += service;

    va_list args;
    va_start(args, service);
    while (true) {
        const char* arg = va_arg(args, const char*);
        if (arg == NULL) {
            break;
        }
        mArgs.add(String16(arg));
        name += " ";
        name += arg;
    }
    va_end(args);
}

TextDumpsysSection::~TextDumpsysSection() {}

status_t TextDumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
    // checkService won't wait for the service to show up like getService will.
    sp<IBinder> service = defaultServiceManager()->checkService(mService);
    if (service == NULL) {
        ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).string());
        return NAME_NOT_FOUND;
    }

    // Create pipe
    Fpipe dumpPipe;
    if (!dumpPipe.init()) {
        ALOGW("[%s] failed to setup pipe", this->name.string());
        return -errno;
    }

    // Run dumping thread
    const uint64_t start = Nanotime();
    std::thread worker([&]() {
        // Don't crash the service if writing to a closed pipe (may happen if dumping times out)
        signal(SIGPIPE, sigpipe_handler);
        status_t err = service->dump(dumpPipe.writeFd().get(), mArgs);
        if (err != OK) {
            ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err));
        }
        dumpPipe.writeFd().reset();
    });

    // Collect dump content
    std::string content;
    bool success = ReadFdToString(dumpPipe.readFd(), &content);
    worker.join(); // Wait for worker to finish
    dumpPipe.readFd().reset();
    if (!success) {
        ALOGW("[%s] failed to read data from pipe", this->name.string());
        return -1;
    }

    ProtoOutputStream proto;
    proto.write(util::TextDumpProto::COMMAND, std::string(name.string()));
    proto.write(util::TextDumpProto::CONTENT, content);
    proto.write(util::TextDumpProto::DUMP_DURATION_NS, int64_t(Nanotime() - start));

    if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
        return EPIPE;
    }
    return OK;
}

// ================================================================================
// initialization only once in Section.cpp.
map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
+17 −1
Original line number Diff line number Diff line
@@ -112,7 +112,8 @@ private:
};

/**
 * Section that calls dumpsys on a system service.
 * Section that calls protobuf dumpsys on a system service, usually
 * "dumpsys [service_name] --proto".
 */
class DumpsysSection : public WorkerThreadSection {
public:
@@ -126,6 +127,21 @@ private:
    Vector<String16> mArgs;
};

/**
 * Section that calls text dumpsys on a system service, usually "dumpsys [service_name]".
 */
class TextDumpsysSection : public WorkerThreadSection {
public:
    TextDumpsysSection(int id, const char* service, ...);
    virtual ~TextDumpsysSection();

    virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;

private:
    String16 mService;
    Vector<String16> mArgs;
};

/**
 * Section that calls dumpsys on a system service.
 */
+12 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import "frameworks/base/core/proto/android/service/sensor_service.proto";
import "frameworks/base/core/proto/android/service/usb.proto";
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
import "frameworks/base/core/proto/android/util/textdump.proto";
import "frameworks/base/core/proto/android/privacy.proto";
import "frameworks/base/core/proto/android/section.proto";
import "frameworks/base/proto/src/ipconnectivity.proto";
@@ -510,6 +511,17 @@ message IncidentProto {
        (section).args = "sensorservice --proto"
    ];

    // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
    optional android.util.TextDumpProto textdump_wifi = 4000 [
        (section).type = SECTION_TEXT_DUMPSYS,
        (section).args = "wifi"
    ];

    optional android.util.TextDumpProto textdump_bluetooth = 4001 [
        (section).type = SECTION_TEXT_DUMPSYS,
        (section).args = "bluetooth_manager"
    ];

    // Reserved for OEMs.
    extensions 50000 to 100000;
}
+4 −0
Original line number Diff line number Diff line
@@ -46,6 +46,10 @@ enum SectionType {

    // incidentd calls tombstoned for annotated field
    SECTION_TOMBSTONE = 6;

    // incidentd calls legacy text dumpsys for annotated field. The section will only be generated
    // on userdebug and eng builds.
    SECTION_TEXT_DUMPSYS = 7;
}

message SectionFlags {
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

syntax = "proto2";
package android.util;

import "frameworks/base/core/proto/android/privacy.proto";

option java_multiple_files = true;

message TextDumpProto {
    option (android.msg_privacy).dest = DEST_EXPLICIT;

    // The command that was executed
    optional string command = 1;
    // The content that was dumped
    optional string content = 2;
    // The duration of the dump process
    optional int64 dump_duration_ns = 3;
}
Loading