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

Commit e4d44919 authored by Primiano Tucci's avatar Primiano Tucci
Browse files

Statsd -> Perfetto integration

This CL invokes the perfetto client utility to start the
collection of a trace when an anomaly that subscribed to
Perfetto is detected. The code simply spawns the
/system/bin/perfetto client and passes the trace config via
stdin. The client takes care of the dropbox upload.
The CollectPerfettoTraceAndUploadToDropbox() function does
NOT wait for the full trace collection (in order to avoid
blocking statsd) and instead returns immediately after having
spawned perfetto.

Change-Id: I4f02067bad7a46ede7b6e4841cdcf381c1a4e2a7
Bug: 71795552
parent 5e1f8546
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -781,6 +781,7 @@ LOCAL_PROTOC_FLAGS := \
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
    cmds/am/proto/instrumentation_data.proto \
    cmds/statsd/src/perfetto/perfetto_config.proto \
    $(call all-proto-files-under, core/proto) \
    $(call all-proto-files-under, libs/incident/proto) \
    $(call all-proto-files-under, cmds/statsd/src)
+3 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ statsd_common_src := \
    src/config/ConfigKey.cpp \
    src/config/ConfigListener.cpp \
    src/config/ConfigManager.cpp \
    src/external/Perfetto.cpp \
    src/external/StatsPuller.cpp \
    src/external/StatsCompanionServicePuller.cpp \
    src/external/SubsystemSleepStatePuller.cpp \
@@ -57,6 +58,7 @@ statsd_common_src := \
    src/metrics/MetricsManager.cpp \
    src/metrics/metrics_manager_util.cpp \
    src/packages/UidMap.cpp \
    src/perfetto/perfetto_config.proto \
    src/storage/StorageManager.cpp \
    src/StatsLogProcessor.cpp \
    src/StatsService.cpp \
@@ -209,6 +211,7 @@ LOCAL_MODULE := statsdprotolite
LOCAL_SRC_FILES := \
    src/stats_log.proto \
    src/statsd_config.proto \
    src/perfetto/perfetto_config.proto \
    src/atoms.proto

LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+2 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include "Log.h"

#include "AnomalyTracker.h"
#include "external/Perfetto.h"
#include "guardrail/StatsdStats.h"

#include <android/os/IIncidentManager.h>
@@ -239,7 +240,7 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
                }
                break;
            case Subscription::SubscriberInformationCase::kPerfettoDetails:
                ALOGW("Perfetto reports not implemented.");
                CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
                break;
            default:
                break;
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

#include "Log.h"

#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert

#include <android-base/unique_fd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <string>

namespace {
const char kDropboxTag[] = "perfetto";
}

namespace android {
namespace os {
namespace statsd {

bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) {
    ALOGD("Starting trace collection through perfetto");

    if (!config.has_trace_config()) {
        ALOGE("The perfetto trace config is empty, aborting");
        return false;
    }

    android::base::unique_fd readPipe;
    android::base::unique_fd writePipe;
    if (!android::base::Pipe(&readPipe, &writePipe)) {
        ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno));
        return false;
    }

    pid_t pid = fork();
    if (pid < 0) {
        ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno));
        return false;
    }

    if (pid == 0) {
        // Child process.

        // No malloc calls or library calls after this point. Remember that even
        // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf().

        writePipe.reset();  // Close the write end (owned by the main process).

        // Replace stdin with |readPipe| so the main process can write into it.
        if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1);
        execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
              kDropboxTag, nullptr);

        // execl() doesn't return in case of success, if we get here something failed.
        _exit(1);
    }

    // Main process.

    readPipe.reset();  // Close the read end (owned by the child process).

    // Using fopen() because fwrite() has the right logic to chunking write()
    // over a pipe (see __sfvwrite()).
    FILE* writePipeStream = fdopen(writePipe.get(), "wb");
    if (!writePipeStream) {
        ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
        return false;
    }

    std::string cfgProto = config.trace_config().SerializeAsString();
    size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
    fclose(writePipeStream);
    if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
        ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten,
              strerror(errno));
        return false;
    }

    // This does NOT wait for the full duration of the trace. It just waits until the process
    // has read the config from stdin and detached.
    int childStatus = 0;
    waitpid(pid, &childStatus, 0);
    if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) {
        ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus);
        return false;
    }

    ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded");
    return true;
}

}  // namespace statsd
}  // namespace os
}  // namespace android
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.
 */

#pragma once

using android::os::StatsLogEventWrapper;

namespace android {
namespace os {
namespace statsd {

class PerfettoDetails;  // Declared in statsd_config.pb.h

// Starts the collection of a Perfetto trace with the given |config|.
// The trace is uploaded to Dropbox by the perfetto cmdline util once done.
// This method returns immediately after passing the config and does NOT wait
// for the full duration of the trace.
bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config);

}  // namespace statsd
}  // namespace os
}  // namespace android
Loading