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
This commit is contained in:
Primiano Tucci
2018-01-10 12:14:50 +00:00
parent 5e1f854620
commit e4d44919aa
9 changed files with 219 additions and 2 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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
cmds/statsd/src/external/Perfetto.cpp vendored Normal file
View File

@@ -0,0 +1,111 @@
/*
* 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
cmds/statsd/src/external/Perfetto.h vendored Normal file
View File

@@ -0,0 +1,35 @@
/*
* 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

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2017 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";
option optimize_for = LITE_RUNTIME;
package perfetto.protos;
message DataSourceConfig {
message FtraceConfig {
repeated string event_names = 1;
}
optional string name = 1;
optional uint32 target_buffer = 2;
optional FtraceConfig ftrace_config = 100;
}
message TraceConfig {
message BufferConfig {
optional uint32 size_kb = 1;
enum OptimizeFor {
DEFAULT = 0;
ONE_SHOT_READ = 1;
}
optional OptimizeFor optimize_for = 3;
enum FillPolicy {
UNSPECIFIED = 0;
RING_BUFFER = 1;
}
optional FillPolicy fill_policy = 4;
}
repeated BufferConfig buffers = 1;
message DataSource {
optional protos.DataSourceConfig config = 1;
repeated string producer_name_filter = 2;
}
repeated DataSource data_sources = 2;
optional uint32 duration_ms = 3;
}

View File

@@ -22,6 +22,8 @@ package android.os.statsd;
option java_package = "com.android.internal.os";
option java_outer_classname = "StatsdConfigProto";
import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
enum Position {
POSITION_UNKNOWN = 0;
FIRST = 1;
@@ -272,7 +274,7 @@ message IncidentdDetails {
}
message PerfettoDetails {
optional int32 perfetto_stuff = 1;
optional perfetto.protos.TraceConfig trace_config = 1;
}
message Subscription {

View File

@@ -21,9 +21,11 @@ LOCAL_PACKAGE_NAME := StatsdDogfood
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += ../../src/stats_log.proto \
../../src/atoms.proto \
../../src/perfetto/perfetto_config.proto \
../../src/statsd_config.proto
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite

View File

@@ -21,6 +21,7 @@ LOCAL_PACKAGE_NAME := StatsdLoadtest
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += ../../src/stats_log.proto \
../../src/atoms.proto \
../../src/perfetto/perfetto_config.proto \
../../src/statsd_config.proto
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res