Merge "Update shell open command to have a mode."
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index 5149b7f..83e30a2 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -176,7 +176,6 @@
}
}
}
- close(fd);
if (!newline) dprintf(out_fd, "\n");
if (!title.empty()) dprintf(out_fd, "\n");
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index ea7109b..ede4254 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -30,6 +30,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <log/log.h>
#include "DumpstateInternal.h"
@@ -182,8 +183,8 @@
}
int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
+ if (fd.get() < 0) {
int err = errno;
if (title.empty()) {
dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
@@ -194,7 +195,7 @@
fsync(out_fd);
return -1;
}
- return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
+ return DumpFileFromFdToFd(title, path, fd.get(), out_fd, PropertiesHelper::IsDryRun());
}
int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 0d3cb32..380921d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -207,20 +207,36 @@
const std::string& name = it->name;
const int fd = it->fd;
dumped = true;
+
+ // Seek to the beginning of the file before dumping any data. A given
+ // DumpData entry might be dumped multiple times in the report.
+ //
+ // For example, the most recent ANR entry is dumped to the body of the
+ // main entry and it also shows up as a separate entry in the bugreport
+ // ZIP file.
+ if (lseek(fd, 0, SEEK_SET) != static_cast<off_t>(0)) {
+ MYLOGE("Unable to add %s to zip file, lseek failed: %s\n", name.c_str(),
+ strerror(errno));
+ }
+
if (ds.IsZipping() && add_to_zip) {
if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
- MYLOGE("Unable to add %s %s to zip file\n", name.c_str(), type_name);
+ MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str());
}
} else {
dump_file_from_fd(type_name, name.c_str(), fd);
}
-
- close(fd);
}
return dumped;
}
+static void CloseDumpFds(const std::vector<DumpData>* dumps) {
+ for (auto it = dumps->begin(); it != dumps->end(); ++it) {
+ close(it->fd);
+ }
+}
+
// for_each_pid() callback to get mount info about a process.
void do_mountinfo(int pid, const char* name __attribute__((unused))) {
char path[PATH_MAX];
@@ -887,9 +903,9 @@
MYLOGD("AddGlobalAnrTraceFile(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
- int fd = TEMP_FAILURE_RETRY(
- open(anr_traces_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
- if (fd < 0) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(anr_traces_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK)));
+ if (fd.get() < 0) {
printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_file.c_str(), strerror(errno));
} else {
if (add_to_zip) {
@@ -901,7 +917,7 @@
} else {
MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
anr_traces_file.c_str());
- dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_file.c_str(), fd);
+ dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_file.c_str(), fd.get());
}
}
}
@@ -934,12 +950,12 @@
AddDumps(anr_data->begin(), anr_data->begin() + 1,
"VM TRACES AT LAST ANR", add_to_zip);
- if (anr_data->size() > 1) {
- // NOTE: Historical ANRs are always added as separate entries in the
- // bugreport zip file.
- AddDumps(anr_data->begin() + 1, anr_data->end(),
- "HISTORICAL ANR", true /* add_to_zip */);
- }
+ // The "last" ANR will always be included as separate entry in the zip file. In addition,
+ // it will be present in the body of the main entry if |add_to_zip| == false.
+ //
+ // Historical ANRs are always included as separate entries in the bugreport zip file.
+ AddDumps(anr_data->begin() + ((add_to_zip) ? 1 : 0), anr_data->end(),
+ "HISTORICAL ANR", true /* add_to_zip */);
} else {
printf("*** NO ANRs to dump in %s\n\n", ANR_DIR.c_str());
}
@@ -2034,5 +2050,8 @@
close(ds.control_socket_fd_);
}
+ CloseDumpFds(tombstone_data.get());
+ CloseDumpFds(anr_data.get());
+
return 0;
}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index c2c9071..6ff0dae 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -632,7 +632,7 @@
struct dirent *d;
char *newpath = NULL;
const char *slash = "/";
- int fd, retval = 0;
+ int retval = 0;
if (!title.empty()) {
printf("------ %s (%s) ------\n", title.c_str(), dir);
@@ -674,13 +674,13 @@
}
continue;
}
- fd = TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
- retval = fd;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
+ if (fd.get() < 0) {
+ retval = -1;
printf("*** %s: %s\n", newpath, strerror(errno));
continue;
}
- (*dump_from_fd)(NULL, newpath, fd);
+ (*dump_from_fd)(NULL, newpath, fd.get());
}
closedir(dirp);
if (!title.empty()) {
@@ -699,11 +699,9 @@
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
- close(fd);
return -1;
} else if (!(flags & O_NONBLOCK)) {
printf("*** %s: fd must have O_NONBLOCK set.\n", path);
- close(fd);
return -1;
}
return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun());
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index bfc6f28..b5295f2 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -136,5 +136,101 @@
return NO_ERROR;
}
+void DisplayState::merge(const DisplayState& other) {
+ if (other.what & eSurfaceChanged) {
+ what |= eSurfaceChanged;
+ surface = other.surface;
+ }
+ if (other.what & eLayerStackChanged) {
+ what |= eLayerStackChanged;
+ layerStack = other.layerStack;
+ }
+ if (other.what & eDisplayProjectionChanged) {
+ what |= eDisplayProjectionChanged;
+ orientation = other.orientation;
+ viewport = other.viewport;
+ frame = other.frame;
+ }
+ if (other.what & eDisplaySizeChanged) {
+ what |= eDisplaySizeChanged;
+ width = other.width;
+ height = other.height;
+ }
+}
+
+void layer_state_t::merge(const layer_state_t& other) {
+ if (other.what & ePositionChanged) {
+ what |= ePositionChanged;
+ x = other.x;
+ y = other.y;
+ }
+ if (other.what & eLayerChanged) {
+ what |= eLayerChanged;
+ z = other.z;
+ }
+ if (other.what & eSizeChanged) {
+ what |= eSizeChanged;
+ w = other.w;
+ h = other.h;
+ }
+ if (other.what & eAlphaChanged) {
+ what |= eAlphaChanged;
+ alpha = other.alpha;
+ }
+ if (other.what & eMatrixChanged) {
+ what |= eMatrixChanged;
+ matrix = other.matrix;
+ }
+ if (other.what & eTransparentRegionChanged) {
+ what |= eTransparentRegionChanged;
+ transparentRegion = other.transparentRegion;
+ }
+ if (other.what & eFlagsChanged) {
+ what |= eFlagsChanged;
+ flags = other.flags;
+ mask = other.mask;
+ }
+ if (other.what & eLayerStackChanged) {
+ what |= eLayerStackChanged;
+ layerStack = other.layerStack;
+ }
+ if (other.what & eCropChanged) {
+ what |= eCropChanged;
+ crop = other.crop;
+ }
+ if (other.what & eDeferTransaction) {
+ what |= eDeferTransaction;
+ barrierHandle = other.barrierHandle;
+ barrierGbp = other.barrierGbp;
+ frameNumber = other.frameNumber;
+ }
+ if (other.what & eFinalCropChanged) {
+ what |= eFinalCropChanged;
+ finalCrop = other.finalCrop;
+ }
+ if (other.what & eOverrideScalingModeChanged) {
+ what |= eOverrideScalingModeChanged;
+ overrideScalingMode = other.overrideScalingMode;
+ }
+ if (other.what & eGeometryAppliesWithResize) {
+ what |= eGeometryAppliesWithResize;
+ }
+ if (other.what & eReparentChildren) {
+ what |= eReparentChildren;
+ reparentHandle = other.reparentHandle;
+ }
+ if (other.what & eDetachChildren) {
+ what |= eDetachChildren;
+ }
+ if (other.what & eRelativeLayerChanged) {
+ what |= eRelativeLayerChanged;
+ z = other.z;
+ relativeLayerHandle = other.relativeLayerHandle;
+ }
+ if (other.what & eReparent) {
+ what |= eReparent;
+ parentHandleForChild = other.parentHandleForChild;
+ }
+}
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 15c4c9a..2adc273 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -104,6 +104,30 @@
mComposerStates = other.mComposerStates;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
+ for (auto const& state : other.mComposerStates) {
+ ssize_t index = mComposerStates.indexOf(state);
+ if (index < 0) {
+ mComposerStates.add(state);
+ } else {
+ mComposerStates.editItemAt(static_cast<size_t>(index)).state.merge(state.state);
+ }
+ }
+ other.mComposerStates.clear();
+
+ for (auto const& state : other.mDisplayStates) {
+ ssize_t index = mDisplayStates.indexOf(state);
+ if (index < 0) {
+ mDisplayStates.add(state);
+ } else {
+ mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state);
+ }
+ }
+ other.mDisplayStates.clear();
+
+ return *this;
+}
+
status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
if (mStatus != NO_ERROR) {
return mStatus;
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index ae6965a..f3fb82f 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -77,6 +77,7 @@
matrix.dsdy = matrix.dtdx = 0.0f;
}
+ void merge(const layer_state_t& other);
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
@@ -144,6 +145,7 @@
};
DisplayState();
+ void merge(const DisplayState& other);
uint32_t what;
sp<IBinder> token;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index d63dafe..87fdfae 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -144,7 +144,9 @@
Transaction(Transaction const& other);
status_t apply(bool synchronous = false);
-
+ // Merge another transaction in to this one, clearing other
+ // as if it had been applied.
+ Transaction& merge(Transaction&& other);
Transaction& show(const sp<SurfaceControl>& sc);
Transaction& hide(const sp<SurfaceControl>& sc);
Transaction& setPosition(const sp<SurfaceControl>& sc,
@@ -175,8 +177,6 @@
float alpha);
Transaction& setMatrix(const sp<SurfaceControl>& sc,
float dsdx, float dtdx, float dtdy, float dsdy);
- Transaction& setOrientation(const sp<SurfaceControl>& sc,
- const Rect& crop);
Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index c4ffb41..5e4df84 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -54,7 +54,7 @@
tags: ["optional"],
}
-cc_test {
+cc_benchmark {
srcs: ["buffer_transport_benchmark.cpp"],
static_libs: static_libraries,
shared_libs: shared_libraries,
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
index 5b580df..658b496 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
@@ -1,24 +1,24 @@
#include <android/native_window.h>
-#include <base/logging.h>
+#include <android-base/logging.h>
+#include <benchmark/benchmark.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <dvr/dvr_api.h>
#include <dvr/performance_client_api.h>
-#include <gtest/gtest.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
#include <gui/Surface.h>
#include <private/dvr/buffer_hub_queue_producer.h>
#include <utils/Trace.h>
+#include <chrono>
#include <functional>
-#include <mutex>
+#include <iostream>
#include <thread>
#include <vector>
#include <poll.h>
#include <sys/wait.h>
-#include <unistd.h> // for pipe
// Use ALWAYS at the tag level. Control is performed manually during command
// line processing.
@@ -29,6 +29,7 @@
using namespace android;
using namespace android::dvr;
+using ::benchmark::State;
static const String16 kBinderService = String16("bufferTransport");
static const uint32_t kBufferWidth = 100;
@@ -38,82 +39,13 @@
static const uint64_t kBufferUsage =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
static const int kMaxAcquiredImages = 1;
+static const int kQueueDepth = 2; // We are double buffering for this test.
static const size_t kMaxQueueCounts = 128;
-static int gConcurrency = 1; // 1 writer at a time
-static int gIterations = 1000; // 1K times
-static int gSleepIntervalUs = 16 * 1000; // 16ms
-
enum BufferTransportServiceCode {
CREATE_BUFFER_QUEUE = IBinder::FIRST_CALL_TRANSACTION,
};
-// A mininal cross process helper class based on a bidirectional pipe pair. This
-// is used to signal that Binder-based BufferTransportService has finished
-// initialization.
-class Pipe {
- public:
- static std::tuple<Pipe, Pipe> CreatePipePair() {
- int a[2] = {-1, -1};
- int b[2] = {-1, -1};
-
- pipe(a);
- pipe(b);
-
- return std::make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
- }
-
- Pipe() = default;
-
- Pipe(Pipe&& other) {
- read_fd_ = other.read_fd_;
- write_fd_ = other.write_fd_;
- other.read_fd_ = 0;
- other.write_fd_ = 0;
- }
-
- Pipe& operator=(Pipe&& other) {
- Reset();
- read_fd_ = other.read_fd_;
- write_fd_ = other.write_fd_;
- other.read_fd_ = 0;
- other.write_fd_ = 0;
- return *this;
- }
-
- ~Pipe() { Reset(); }
-
- Pipe(const Pipe&) = delete;
- Pipe& operator=(const Pipe&) = delete;
- Pipe& operator=(const Pipe&&) = delete;
-
- bool IsValid() { return read_fd_ > 0 && write_fd_ > 0; }
-
- void Signal() {
- bool val = true;
- int error = write(write_fd_, &val, sizeof(val));
- ASSERT_GE(error, 0);
- };
-
- void Wait() {
- bool val = false;
- int error = read(read_fd_, &val, sizeof(val));
- ASSERT_GE(error, 0);
- }
-
- void Reset() {
- if (read_fd_)
- close(read_fd_);
- if (write_fd_)
- close(write_fd_);
- }
-
- private:
- int read_fd_ = -1;
- int write_fd_ = -1;
- Pipe(int read_fd, int write_fd) : read_fd_{read_fd}, write_fd_{write_fd} {}
-};
-
// A binder services that minics a compositor that consumes buffers. It provides
// one Binder interface to create a new Surface for buffer producer to write
// into; while itself will carry out no-op buffer consuming by acquiring then
@@ -149,8 +81,6 @@
buffer_item_consumer_(buffer_item_consumer) {}
void onFrameAvailable(const BufferItem& /*item*/) override {
- std::unique_lock<std::mutex> autolock(service_->reader_mutex_);
-
BufferItem buffer;
status_t ret = 0;
{
@@ -197,7 +127,6 @@
sp<FrameListener> frame_listener_;
};
- std::mutex reader_mutex_;
std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
};
@@ -225,32 +154,19 @@
public:
BinderBufferTransport() {}
- ~BinderBufferTransport() {
- if (client_pipe_.IsValid()) {
- client_pipe_.Signal();
- LOG(INFO) << "Client signals service to shut down.";
- }
- }
-
int Start() override {
- // Fork a process to run a binder server. The parent process will return
- // a pipe here, and we use the pipe to signal the binder server to exit.
- client_pipe_ = CreateBinderServer();
+ sp<IServiceManager> sm = defaultServiceManager();
+ service_ = sm->getService(kBinderService);
+ if (service_ == nullptr) {
+ LOG(ERROR) << "Failed to get the benchmark service.";
+ return -EIO;
+ }
- // Wait until service is ready.
- LOG(INFO) << "Service is ready for client.";
- client_pipe_.Wait();
+ LOG(INFO) << "Binder server is ready for client.";
return 0;
}
sp<Surface> CreateSurface() override {
- sp<IServiceManager> sm = defaultServiceManager();
- service_ = sm->getService(kBinderService);
- if (service_ == nullptr) {
- LOG(ERROR) << "Failed to set the benchmark service.";
- return nullptr;
- }
-
Parcel data;
Parcel reply;
int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
@@ -283,38 +199,7 @@
}
private:
- static Pipe CreateBinderServer() {
- std::tuple<Pipe, Pipe> pipe_pair = Pipe::CreatePipePair();
- pid_t pid = fork();
- if (pid) {
- // parent, i.e. the client side.
- ProcessState::self()->startThreadPool();
- LOG(INFO) << "Binder server pid: " << pid;
- return std::move(std::get<0>(pipe_pair));
- } else {
- // child, i.e. the service side.
- Pipe service_pipe = std::move(std::get<1>(pipe_pair));
-
- ProcessState::self()->startThreadPool();
- sp<IServiceManager> sm = defaultServiceManager();
- sp<BufferTransportService> service = new BufferTransportService;
- sm->addService(kBinderService, service, false);
-
- LOG(INFO) << "Binder Service Running...";
-
- service_pipe.Signal();
- service_pipe.Wait();
-
- LOG(INFO) << "Service Exiting...";
- exit(EXIT_SUCCESS);
-
- /* never get here */
- return {};
- }
- }
-
sp<IBinder> service_;
- Pipe client_pipe_;
};
// BufferHub/PDX-based buffer transport.
@@ -378,7 +263,6 @@
const int num_events = ret;
for (int i = 0; i < num_events; i++) {
uint32_t surface_index = events[i].data.u32;
- // LOG(INFO) << "!!! handle queue events index: " << surface_index;
buffer_queues_[surface_index]->consumer_queue_->HandleQueueEvents();
}
}
@@ -390,8 +274,6 @@
}
sp<Surface> CreateSurface() override {
- std::lock_guard<std::mutex> autolock(queue_mutex_);
-
auto new_queue = std::make_shared<BufferQueueHolder>();
if (new_queue->producer_ == nullptr) {
LOG(ERROR) << "Failed to create buffer producer.";
@@ -472,8 +354,6 @@
std::atomic<bool> stopped_;
std::thread reader_thread_;
- // Mutex to guard epoll_fd_ and buffer_queues_.
- std::mutex queue_mutex_;
EpollFileDescriptor epoll_fd_;
std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
};
@@ -486,124 +366,155 @@
// Main test suite, which supports two transport backend: 1) BinderBufferQueue,
// 2) BufferHubQueue. The test case drives the producer end of both transport
// backend by queuing buffers into the buffer queue by using ANativeWindow API.
-class BufferTransportBenchmark
- : public ::testing::TestWithParam<TransportType> {
+class BufferTransportBenchmark : public ::benchmark::Fixture {
public:
- void SetUp() override {
- switch (GetParam()) {
- case kBinderBufferTransport:
- transport_.reset(new BinderBufferTransport);
- break;
- case kBufferHubTransport:
- transport_.reset(new BufferHubTransport);
- break;
- default:
- FAIL() << "Unknown test case.";
- break;
+ void SetUp(State& state) override {
+ if (state.thread_index == 0) {
+ const int transport = state.range(0);
+ switch (transport) {
+ case kBinderBufferTransport:
+ transport_.reset(new BinderBufferTransport);
+ break;
+ case kBufferHubTransport:
+ transport_.reset(new BufferHubTransport);
+ break;
+ default:
+ CHECK(false) << "Unknown test case.";
+ break;
+ }
+
+ CHECK(transport_);
+ const int ret = transport_->Start();
+ CHECK_EQ(ret, 0);
+
+ LOG(INFO) << "Transport backend running, transport=" << transport << ".";
+
+ // Create surfaces for each thread.
+ surfaces_.resize(state.threads);
+ for (int i = 0; i < state.threads; i++) {
+ // Common setup every thread needs.
+ surfaces_[i] = transport_->CreateSurface();
+ CHECK(surfaces_[i]);
+
+ LOG(INFO) << "Surface initialized on thread " << i << ".";
+ }
+ }
+ }
+
+ void TearDown(State& state) override {
+ if (state.thread_index == 0) {
+ surfaces_.clear();
+ transport_.reset();
+ LOG(INFO) << "Tear down benchmark.";
}
}
protected:
- void ProduceBuffers(sp<Surface> surface, int iterations, int sleep_usec) {
- ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
- ANativeWindow_Buffer buffer;
- int32_t error = 0;
+ std::unique_ptr<BufferTransport> transport_;
+ std::vector<sp<Surface>> surfaces_;
+};
- for (int i = 0; i < iterations; i++) {
- usleep(sleep_usec);
+BENCHMARK_DEFINE_F(BufferTransportBenchmark, Producers)(State& state) {
+ ANativeWindow* window = nullptr;
+ ANativeWindow_Buffer buffer;
+ int32_t error = 0;
+ double total_gain_buffer_us = 0;
+ double total_post_buffer_us = 0;
+ int iterations = 0;
- {
- ATRACE_NAME("GainBuffer");
+ while (state.KeepRunning()) {
+ if (window == nullptr) {
+ CHECK(surfaces_[state.thread_index]);
+ window = static_cast<ANativeWindow*>(surfaces_[state.thread_index].get());
+
+ // Lock buffers a couple time from the queue, so that we have the buffer
+ // allocated.
+ for (int i = 0; i < kQueueDepth; i++) {
error = ANativeWindow_lock(window, &buffer,
/*inOutDirtyBounds=*/nullptr);
- }
- ASSERT_EQ(error, 0);
-
- {
- ATRACE_NAME("PostBuffer");
+ CHECK_EQ(error, 0);
error = ANativeWindow_unlockAndPost(window);
+ CHECK_EQ(error, 0);
}
- ASSERT_EQ(error, 0);
+ }
+
+ {
+ ATRACE_NAME("GainBuffer");
+ auto t1 = std::chrono::high_resolution_clock::now();
+ error = ANativeWindow_lock(window, &buffer,
+ /*inOutDirtyBounds=*/nullptr);
+ auto t2 = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double, std::micro> delta_us = t2 - t1;
+ total_gain_buffer_us += delta_us.count();
+ }
+ CHECK_EQ(error, 0);
+
+ {
+ ATRACE_NAME("PostBuffer");
+ auto t1 = std::chrono::high_resolution_clock::now();
+ error = ANativeWindow_unlockAndPost(window);
+ auto t2 = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double, std::micro> delta_us = t2 - t1;
+ total_post_buffer_us += delta_us.count();
+ }
+ CHECK_EQ(error, 0);
+
+ iterations++;
+ }
+
+ state.counters["gain_buffer_us"] = ::benchmark::Counter(
+ total_gain_buffer_us / iterations, ::benchmark::Counter::kAvgThreads);
+ state.counters["post_buffer_us"] = ::benchmark::Counter(
+ total_post_buffer_us / iterations, ::benchmark::Counter::kAvgThreads);
+ state.counters["producer_us"] = ::benchmark::Counter(
+ (total_gain_buffer_us + total_post_buffer_us) / iterations,
+ ::benchmark::Counter::kAvgThreads);
+}
+
+BENCHMARK_REGISTER_F(BufferTransportBenchmark, Producers)
+ ->Unit(::benchmark::kMicrosecond)
+ ->Ranges({{kBinderBufferTransport, kBufferHubTransport}})
+ ->ThreadRange(1, 32);
+
+static void runBinderServer() {
+ ProcessState::self()->setThreadPoolMaxThreadCount(0);
+ ProcessState::self()->startThreadPool();
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<BufferTransportService> service = new BufferTransportService;
+ sm->addService(kBinderService, service, false);
+
+ LOG(INFO) << "Binder server running...";
+
+ while (true) {
+ int stat, retval;
+ retval = wait(&stat);
+ if (retval == -1 && errno == ECHILD) {
+ break;
}
}
- std::unique_ptr<BufferTransport> transport_;
-};
-
-TEST_P(BufferTransportBenchmark, ContinuousLoad) {
- ASSERT_NE(transport_, nullptr);
- const int ret = transport_->Start();
- ASSERT_EQ(ret, 0);
-
- LOG(INFO) << "Start Running.";
-
- std::vector<std::thread> writer_threads;
- for (int i = 0; i < gConcurrency; i++) {
- std::thread writer_thread = std::thread([this]() {
- sp<Surface> surface = transport_->CreateSurface();
- ASSERT_NE(surface, nullptr);
-
- ASSERT_NO_FATAL_FAILURE(
- ProduceBuffers(surface, gIterations, gSleepIntervalUs));
-
- usleep(1000 * 100);
- });
-
- writer_threads.push_back(std::move(writer_thread));
- }
-
- for (auto& writer_thread : writer_threads) {
- writer_thread.join();
- }
-
- LOG(INFO) << "All done.";
-};
-
-INSTANTIATE_TEST_CASE_P(BufferTransportBenchmarkInstance,
- BufferTransportBenchmark,
- ::testing::ValuesIn({kBinderBufferTransport,
- kBufferHubTransport}));
+ LOG(INFO) << "Service Exiting...";
+}
// To run binder-based benchmark, use:
// adb shell buffer_transport_benchmark \
-// --gtest_filter="BufferTransportBenchmark.ContinuousLoad/0"
+// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/0/"
//
// To run bufferhub-based benchmark, use:
// adb shell buffer_transport_benchmark \
-// --gtest_filter="BufferTransportBenchmark.ContinuousLoad/1"
+// --benchmark_filter="BufferTransportBenchmark/ContinuousLoad/1/"
int main(int argc, char** argv) {
bool tracing_enabled = false;
- // Parse arguments in addition to "--gtest_filter" paramters.
+ // Parse arguments in addition to "--benchmark_filter" paramters.
for (int i = 1; i < argc; i++) {
if (std::string(argv[i]) == "--help") {
std::cout << "Usage: binderThroughputTest [OPTIONS]" << std::endl;
- std::cout << "\t-c N: Specify number of concurrent writer threads, "
- "(default: 1, max: 128)."
- << std::endl;
- std::cout << "\t-i N: Specify number of iterations, (default: 1000)."
- << std::endl;
- std::cout << "\t-s N: Specify sleep interval in usec, (default: 16000)."
- << std::endl;
std::cout << "\t--trace: Enable systrace logging."
<< std::endl;
return 0;
}
- if (std::string(argv[i]) == "-c") {
- gConcurrency = atoi(argv[i + 1]);
- i++;
- continue;
- }
- if (std::string(argv[i]) == "-s") {
- gSleepIntervalUs = atoi(argv[i + 1]);
- i++;
- continue;
- }
- if (std::string(argv[i]) == "-i") {
- gIterations = atoi(argv[i + 1]);
- i++;
- continue;
- }
if (std::string(argv[i]) == "--trace") {
tracing_enabled = true;
continue;
@@ -614,6 +525,15 @@
atrace_setup();
atrace_set_tracing_enabled(tracing_enabled);
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ pid_t pid = fork();
+ if (pid == 0) {
+ // parent, i.e. the client side.
+ ProcessState::self()->startThreadPool();
+
+ ::benchmark::Initialize(&argc, argv);
+ ::benchmark::RunSpecifiedBenchmarks();
+ } else {
+ LOG(INFO) << "Benchmark process pid: " << pid;
+ runBinderServer();
+ }
}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index ec87eee..16a16a5 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -891,6 +891,30 @@
}
}
+TEST_F(LayerUpdateTest, MergingTransactions) {
+ sp<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before move");
+ ScreenCapture::captureScreen(&sc);
+ sc->expectBGColor(0, 12);
+ sc->expectFGColor(75, 75);
+ sc->expectBGColor(145, 145);
+ }
+
+ Transaction t1, t2;
+ t1.setPosition(mFGSurfaceControl, 128, 128);
+ t2.setPosition(mFGSurfaceControl, 0, 0);
+ // We expect that the position update from t2 now
+ // overwrites the position update from t1.
+ t1.merge(std::move(t2));
+ t1.apply();
+
+ {
+ ScreenCapture::captureScreen(&sc);
+ sc->expectFGColor(1, 1);
+ }
+}
+
class ChildLayerTest : public LayerUpdateTest {
protected:
void SetUp() override {