GpuMem perfetto producer
To overcome the problem of ftrace gpumem events not emitting anything
during MEC (with apps that do launch-time allocations), this change adds
a perfetto producer to emit initial counter values at the start of a
trace, so that the visualization won't so zero usage.
Test: adb shell perfetto --query | grep gpu.
Test: Take a perfetto trace with android.gpumem enabled
Bug: 157142645
Change-Id: I5c8278754549399ffa51e6e6fc2ca69b51976cb2
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 04fe1e6..1bcaab4 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -22,6 +22,7 @@
"libcutils",
"libgfxstats",
"libgpumem",
+ "libgpumemtracer",
"libgraphicsenv",
"liblog",
"libutils",
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index c36e4b9..18c819f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -27,6 +27,7 @@
#include <gpumem/GpuMem.h>
#include <gpustats/GpuStats.h>
#include <private/android_filesystem_config.h>
+#include <tracing/GpuMemTracer.h>
#include <utils/String8.h>
#include <utils/Trace.h>
#include <vkjson.h>
@@ -48,8 +49,13 @@
const char* const GpuService::SERVICE_NAME = "gpu";
GpuService::GpuService()
- : mGpuMem(std::make_unique<GpuMem>()), mGpuStats(std::make_unique<GpuStats>()) {
- std::thread asyncInitThread([this]() { mGpuMem->initialize(); });
+ : mGpuMem(std::make_shared<GpuMem>()),
+ mGpuStats(std::make_unique<GpuStats>()),
+ mGpuMemTracer(std::make_unique<GpuMemTracer>()) {
+ std::thread asyncInitThread([this]() {
+ mGpuMem->initialize();
+ mGpuMemTracer->initialize(mGpuMem);
+ });
asyncInitThread.detach();
};
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index 9a0460d..43faa3e 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -30,6 +30,7 @@
class GpuMem;
class GpuStats;
+class GpuMemTracer;
class GpuService : public BnGpuService, public PriorityDumper {
public:
@@ -75,8 +76,9 @@
/*
* Attributes
*/
- std::unique_ptr<GpuMem> mGpuMem;
+ std::shared_ptr<GpuMem> mGpuMem;
std::unique_ptr<GpuStats> mGpuStats;
+ std::unique_ptr<GpuMemTracer> mGpuMemTracer;
std::string developerDriverPath;
};
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
index ff8b4bc..49a9f95 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -31,6 +31,29 @@
void initialize();
// dumpsys interface
void dump(const Vector<String16>& args, std::string* result);
+ bool isInitialized() { return mInitialized.load(); }
+
+ // Traverse the map and send each value read back to the callback function.
+ // Used for tracing.
+ template <typename lambda>
+ void traceGpuMemTotals(lambda tracerCallback) {
+ auto res = mGpuMemTotalMap.getFirstKey();
+ if (!res.ok()) return;
+ uint64_t key = res.value();
+ while (true) {
+ uint32_t gpu_id = key >> 32;
+ uint32_t pid = key;
+
+ res = mGpuMemTotalMap.readValue(key);
+ if (!res.ok()) break;
+ uint64_t size = res.value();
+
+ tracerCallback(gpu_id, pid, size);
+ res = mGpuMemTotalMap.getNextKey(key);
+ if (!res.ok()) break;
+ key = res.value();
+ }
+ }
private:
// Friend class for testing.
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
new file mode 100644
index 0000000..919fed3
--- /dev/null
+++ b/services/gpuservice/tracing/Android.bp
@@ -0,0 +1,41 @@
+// Copyright 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.
+
+cc_library_shared {
+ name: "libgpumemtracer",
+ srcs: [
+ "GpuMemTracer.cpp",
+ ],
+ shared_libs: [
+ "libgpumem",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: [
+ "libperfetto_client_experimental",
+ ],
+ export_include_dirs: ["include"],
+ export_static_lib_headers: [
+ "libperfetto_client_experimental",
+ ],
+ cppflags: [
+ "-Wall",
+ "-Werror",
+ "-Wformat",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
new file mode 100644
index 0000000..c9bfa57
--- /dev/null
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GpuMemTracer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "tracing/GpuMemTracer.h"
+
+#include <gpumem/GpuMem.h>
+#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
+#include <unistd.h>
+#include <utils/Timers.h>
+
+#include <algorithm>
+#include <thread>
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::GpuMemTracer::GpuMemDataSource);
+
+namespace android {
+
+std::mutex GpuMemTracer::sTraceMutex;
+std::condition_variable GpuMemTracer::sCondition;
+bool GpuMemTracer::sTraceStarted;
+
+void GpuMemTracer::initialize(std::shared_ptr<GpuMem> gpuMem) {
+ if (!gpuMem->isInitialized()) {
+ ALOGE("Cannot initialize GpuMemTracer before GpuMem");
+ return;
+ }
+ mGpuMem = gpuMem;
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ // TODO(b/160016498): Find a better way to wait for traced
+ // Sleep for 30 seconds to make sure the data source is registered only
+ // after traced starts.
+ sleep(30);
+ perfetto::Tracing::Initialize(args);
+ registerDataSource();
+ std::thread tracerThread(&GpuMemTracer::threadLoop, this);
+ pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThread");
+ tracerThread.detach();
+}
+
+void GpuMemTracer::registerDataSource() {
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name(kGpuMemDataSource);
+ GpuMemDataSource::Register(dsd);
+}
+
+void GpuMemTracer::threadLoop() {
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ while (!sTraceStarted) {
+ sCondition.wait(lock);
+ }
+ }
+ traceInitialCounters();
+ {
+ std::lock_guard<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = false;
+ }
+ }
+}
+
+void GpuMemTracer::traceInitialCounters() {
+ if (!mGpuMem->isInitialized()) {
+ // This should never happen.
+ ALOGE("Cannot trace without GpuMem initialization");
+ return;
+ }
+ mGpuMem->traceGpuMemTotals([](uint32_t gpuId, uint32_t pid, uint64_t size) {
+ GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp(systemTime());
+ auto* event = packet->set_gpu_mem_total_event();
+ event->set_gpu_id(gpuId);
+ event->set_pid(pid);
+ event->set_size(size);
+ });
+ });
+ // Flush the TraceContext. The last packet in the above loop will go
+ // missing without this flush.
+ GpuMemDataSource::Trace([](GpuMemDataSource::TraceContext ctx) { ctx.Flush(); });
+}
+
+} // namespace android
diff --git a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
new file mode 100644
index 0000000..40deb4c
--- /dev/null
+++ b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <perfetto/tracing.h>
+
+#include <mutex>
+
+namespace android {
+
+class GpuMem;
+
+class GpuMemTracer {
+public:
+ class GpuMemDataSource : public perfetto::DataSource<GpuMemDataSource> {
+ virtual void OnSetup(const SetupArgs&) override{};
+ virtual void OnStart(const StartArgs&) override {
+ std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
+ sTraceStarted = true;
+ sCondition.notify_all();
+ }
+ virtual void OnStop(const StopArgs&) override{};
+ };
+
+ ~GpuMemTracer() = default;
+
+ // Sets up the perfetto tracing backend and data source.
+ void initialize(std::shared_ptr<GpuMem>);
+ // Registers the data source with the perfetto backend. Called as part of initialize()
+ // and should not be called manually outside of tests. Public to allow for substituting a
+ // perfetto::kInProcessBackend in tests.
+ void registerDataSource();
+
+ static constexpr char kGpuMemDataSource[] = "android.gpu.memory";
+ static std::condition_variable sCondition;
+ static std::mutex sTraceMutex;
+ static bool sTraceStarted;
+
+private:
+ void traceInitialCounters();
+ void threadLoop();
+
+ std::shared_ptr<GpuMem> mGpuMem;
+};
+
+} // namespace android