From e4d44919aa90f16285a9966dcc632c128b932374 Mon Sep 17 00:00:00 2001 From: Primiano Tucci Date: Wed, 10 Jan 2018 12:14:50 +0000 Subject: [PATCH] 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 --- Android.mk | 1 + cmds/statsd/Android.mk | 3 + cmds/statsd/src/anomaly/AnomalyTracker.cpp | 3 +- cmds/statsd/src/external/Perfetto.cpp | 111 ++++++++++++++++++ cmds/statsd/src/external/Perfetto.h | 35 ++++++ .../statsd/src/perfetto/perfetto_config.proto | 61 ++++++++++ cmds/statsd/src/statsd_config.proto | 4 +- cmds/statsd/tools/dogfood/Android.mk | 2 + cmds/statsd/tools/loadtest/Android.mk | 1 + 9 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 cmds/statsd/src/external/Perfetto.cpp create mode 100644 cmds/statsd/src/external/Perfetto.h create mode 100644 cmds/statsd/src/perfetto/perfetto_config.proto diff --git a/Android.mk b/Android.mk index 298b85ddf084b..866d2a7b0e540 100644 --- a/Android.mk +++ b/Android.mk @@ -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) diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 5eff54887be12..01f4a84bbb93f 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -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 diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index f10b2cf618cdd..9b65acf866045 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "AnomalyTracker.h" +#include "external/Perfetto.h" #include "guardrail/StatsdStats.h" #include @@ -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; diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp new file mode 100644 index 0000000000000..f7b33e77c5574 --- /dev/null +++ b/cmds/statsd/src/external/Perfetto.cpp @@ -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 +#include +#include +#include +#include + +#include + +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 diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h new file mode 100644 index 0000000000000..e2e02533f17fd --- /dev/null +++ b/cmds/statsd/src/external/Perfetto.h @@ -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 diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto new file mode 100644 index 0000000000000..dc868f997a633 --- /dev/null +++ b/cmds/statsd/src/perfetto/perfetto_config.proto @@ -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; +} diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 624785486043d..cd60ee74e3add 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -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 { diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk index a4c0800632845..73fbaa4e2ad00 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -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 diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk index 0a0fd6647fbbd..f5722c2c3d19a 100644 --- a/cmds/statsd/tools/loadtest/Android.mk +++ b/cmds/statsd/tools/loadtest/Android.mk @@ -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