You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
12 KiB
418 lines
12 KiB
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <memory>
|
|
|
|
#include "base/message_loop/message_loop.h"
|
|
#include "base/process/process_metrics.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "base/synchronization/waitable_event.h"
|
|
#include "base/test/perf_log.h"
|
|
#include "base/timer/timer.h"
|
|
#include "ipc/ipc_channel_proxy.h"
|
|
#include "ipc/ipc_perftest_messages.h"
|
|
#include "ipc/ipc_perftest_util.h"
|
|
#include "ipc/ipc_sync_channel.h"
|
|
#include "ipc/ipc_test.mojom.h"
|
|
#include "ipc/ipc_test_base.h"
|
|
#include "mojo/core/test/mojo_test_base.h"
|
|
#include "mojo/core/test/multiprocess_test_helper.h"
|
|
#include "mojo/public/cpp/bindings/binding.h"
|
|
#include "mojo/public/cpp/system/message_pipe.h"
|
|
|
|
namespace IPC {
|
|
namespace {
|
|
|
|
struct TestParams {
|
|
TestParams() = default;
|
|
TestParams(size_t in_message_size,
|
|
size_t in_frames_per_second,
|
|
size_t in_messages_per_frame,
|
|
size_t in_duration_in_seconds)
|
|
: message_size(in_message_size),
|
|
frames_per_second(in_frames_per_second),
|
|
messages_per_frame(in_messages_per_frame),
|
|
duration_in_seconds(in_duration_in_seconds) {}
|
|
|
|
size_t message_size;
|
|
size_t frames_per_second;
|
|
size_t messages_per_frame;
|
|
size_t duration_in_seconds;
|
|
};
|
|
|
|
std::vector<TestParams> GetDefaultTestParams() {
|
|
std::vector<TestParams> list;
|
|
list.push_back({144, 20, 10, 10});
|
|
list.push_back({144, 60, 10, 10});
|
|
return list;
|
|
}
|
|
|
|
std::string GetLogTitle(const std::string& label, const TestParams& params) {
|
|
return base::StringPrintf(
|
|
"%s_MsgSize_%zu_FrmPerSec_%zu_MsgPerFrm_%zu", label.c_str(),
|
|
params.message_size, params.frames_per_second, params.messages_per_frame);
|
|
}
|
|
|
|
base::TimeDelta GetFrameTime(size_t frames_per_second) {
|
|
return base::TimeDelta::FromSecondsD(1.0 / frames_per_second);
|
|
}
|
|
|
|
class PerfCpuLogger {
|
|
public:
|
|
explicit PerfCpuLogger(base::StringPiece test_name)
|
|
: test_name_(test_name),
|
|
process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
|
|
process_metrics_->GetPlatformIndependentCPUUsage();
|
|
}
|
|
|
|
~PerfCpuLogger() {
|
|
double result = process_metrics_->GetPlatformIndependentCPUUsage();
|
|
base::LogPerfResult(test_name_.c_str(), result, "%");
|
|
}
|
|
|
|
private:
|
|
std::string test_name_;
|
|
std::unique_ptr<base::ProcessMetrics> process_metrics_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(PerfCpuLogger);
|
|
};
|
|
|
|
MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
|
|
MojoPerfTestClient client;
|
|
int rv = mojo::core::test::MultiprocessTestHelper::RunClientMain(
|
|
base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)),
|
|
true /* pass_pipe_ownership_to_main */);
|
|
|
|
base::RunLoop run_loop;
|
|
run_loop.RunUntilIdle();
|
|
|
|
return rv;
|
|
}
|
|
|
|
class ChannelSteadyPingPongListener : public Listener {
|
|
public:
|
|
ChannelSteadyPingPongListener() = default;
|
|
|
|
~ChannelSteadyPingPongListener() override = default;
|
|
|
|
void Init(Sender* sender) {
|
|
DCHECK(!sender_);
|
|
sender_ = sender;
|
|
}
|
|
|
|
void SetTestParams(const TestParams& params,
|
|
const std::string& label,
|
|
bool sync,
|
|
const base::Closure& quit_closure) {
|
|
params_ = params;
|
|
label_ = label;
|
|
sync_ = sync;
|
|
quit_closure_ = quit_closure;
|
|
payload_ = std::string(params.message_size, 'a');
|
|
}
|
|
|
|
bool OnMessageReceived(const Message& message) override {
|
|
CHECK(sender_);
|
|
|
|
bool handled = true;
|
|
IPC_BEGIN_MESSAGE_MAP(ChannelSteadyPingPongListener, message)
|
|
IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
|
|
IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
|
|
IPC_MESSAGE_UNHANDLED(handled = false)
|
|
IPC_END_MESSAGE_MAP()
|
|
return handled;
|
|
}
|
|
|
|
void OnHello() {
|
|
cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
|
|
|
|
frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
|
|
|
|
timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
|
|
&ChannelSteadyPingPongListener::StartPingPong);
|
|
}
|
|
|
|
void StartPingPong() {
|
|
if (sync_) {
|
|
base::TimeTicks before = base::TimeTicks::Now();
|
|
for (count_down_ = params_.messages_per_frame; count_down_ > 0;
|
|
--count_down_) {
|
|
std::string response;
|
|
sender_->Send(new TestMsg_SyncPing(payload_, &response));
|
|
DCHECK_EQ(response, payload_);
|
|
}
|
|
|
|
if (base::TimeTicks::Now() - before >
|
|
GetFrameTime(params_.frames_per_second)) {
|
|
LOG(ERROR) << "Frame " << frame_count_down_
|
|
<< " wasn't able to complete on time!";
|
|
}
|
|
|
|
CHECK_GT(frame_count_down_, 0);
|
|
frame_count_down_--;
|
|
if (frame_count_down_ == 0)
|
|
StopPingPong();
|
|
} else {
|
|
if (count_down_ != 0) {
|
|
LOG(ERROR) << "Frame " << frame_count_down_
|
|
<< " wasn't able to complete on time!";
|
|
} else {
|
|
SendPong();
|
|
}
|
|
count_down_ = params_.messages_per_frame;
|
|
}
|
|
}
|
|
|
|
void StopPingPong() {
|
|
cpu_logger_.reset();
|
|
timer_.AbandonAndStop();
|
|
quit_closure_.Run();
|
|
}
|
|
|
|
void OnPing(const std::string& payload) {
|
|
// Include message deserialization in latency.
|
|
DCHECK_EQ(payload_.size(), payload.size());
|
|
|
|
CHECK_GT(count_down_, 0);
|
|
count_down_--;
|
|
if (count_down_ > 0) {
|
|
SendPong();
|
|
} else {
|
|
CHECK_GT(frame_count_down_, 0);
|
|
frame_count_down_--;
|
|
if (frame_count_down_ == 0)
|
|
StopPingPong();
|
|
}
|
|
}
|
|
|
|
void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
|
|
|
|
private:
|
|
Sender* sender_ = nullptr;
|
|
TestParams params_;
|
|
std::string payload_;
|
|
std::string label_;
|
|
bool sync_ = false;
|
|
|
|
int count_down_ = 0;
|
|
int frame_count_down_ = 0;
|
|
|
|
base::RepeatingTimer timer_;
|
|
std::unique_ptr<PerfCpuLogger> cpu_logger_;
|
|
|
|
base::Closure quit_closure_;
|
|
};
|
|
|
|
class ChannelSteadyPingPongTest : public IPCChannelMojoTestBase {
|
|
public:
|
|
ChannelSteadyPingPongTest() = default;
|
|
~ChannelSteadyPingPongTest() override = default;
|
|
|
|
void RunPingPongServer(const std::string& label, bool sync) {
|
|
Init("MojoPerfTestClient");
|
|
|
|
// Set up IPC channel and start client.
|
|
ChannelSteadyPingPongListener listener;
|
|
|
|
std::unique_ptr<ChannelProxy> channel_proxy;
|
|
std::unique_ptr<base::WaitableEvent> shutdown_event;
|
|
|
|
if (sync) {
|
|
shutdown_event = std::make_unique<base::WaitableEvent>(
|
|
base::WaitableEvent::ResetPolicy::MANUAL,
|
|
base::WaitableEvent::InitialState::NOT_SIGNALED);
|
|
channel_proxy = IPC::SyncChannel::Create(
|
|
TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
|
|
GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get(), false,
|
|
shutdown_event.get());
|
|
} else {
|
|
channel_proxy = IPC::ChannelProxy::Create(
|
|
TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
|
|
GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get());
|
|
}
|
|
listener.Init(channel_proxy.get());
|
|
|
|
LockThreadAffinity thread_locker(kSharedCore);
|
|
std::vector<TestParams> params_list = GetDefaultTestParams();
|
|
for (const auto& params : params_list) {
|
|
base::RunLoop run_loop;
|
|
|
|
listener.SetTestParams(params, label, sync,
|
|
run_loop.QuitWhenIdleClosure());
|
|
|
|
// This initial message will kick-start the ping-pong of messages.
|
|
channel_proxy->Send(new TestMsg_Hello);
|
|
|
|
run_loop.Run();
|
|
}
|
|
|
|
// Send quit message.
|
|
channel_proxy->Send(new TestMsg_Quit);
|
|
|
|
EXPECT_TRUE(WaitForClientShutdown());
|
|
channel_proxy.reset();
|
|
}
|
|
};
|
|
|
|
TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
|
|
RunPingPongServer("IPC_CPU_Async", false);
|
|
}
|
|
|
|
TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
|
|
RunPingPongServer("IPC_CPU_Sync", true);
|
|
}
|
|
|
|
class MojoSteadyPingPongTest : public mojo::core::test::MojoTestBase {
|
|
public:
|
|
MojoSteadyPingPongTest() = default;
|
|
|
|
protected:
|
|
void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
|
|
label_ = label;
|
|
sync_ = sync;
|
|
|
|
mojo::MessagePipeHandle mp_handle(mp);
|
|
mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
|
|
ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));
|
|
|
|
LockThreadAffinity thread_locker(kSharedCore);
|
|
std::vector<TestParams> params_list = GetDefaultTestParams();
|
|
for (const auto& params : params_list) {
|
|
params_ = params;
|
|
payload_ = std::string(params.message_size, 'a');
|
|
|
|
ping_receiver_->Ping("hello", base::Bind(&MojoSteadyPingPongTest::OnHello,
|
|
base::Unretained(this)));
|
|
base::RunLoop run_loop;
|
|
quit_closure_ = run_loop.QuitWhenIdleClosure();
|
|
run_loop.Run();
|
|
}
|
|
|
|
ping_receiver_->Quit();
|
|
|
|
ignore_result(ping_receiver_.PassInterface().PassHandle().release());
|
|
}
|
|
|
|
void OnHello(const std::string& value) {
|
|
cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
|
|
|
|
frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
|
|
|
|
timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
|
|
&MojoSteadyPingPongTest::StartPingPong);
|
|
}
|
|
|
|
void StartPingPong() {
|
|
if (sync_) {
|
|
base::TimeTicks before = base::TimeTicks::Now();
|
|
for (count_down_ = params_.messages_per_frame; count_down_ > 0;
|
|
--count_down_) {
|
|
std::string response;
|
|
ping_receiver_->SyncPing(payload_, &response);
|
|
DCHECK_EQ(response, payload_);
|
|
}
|
|
|
|
if (base::TimeTicks::Now() - before >
|
|
GetFrameTime(params_.frames_per_second)) {
|
|
LOG(ERROR) << "Frame " << frame_count_down_
|
|
<< " wasn't able to complete on time!";
|
|
}
|
|
|
|
CHECK_GT(frame_count_down_, 0);
|
|
frame_count_down_--;
|
|
if (frame_count_down_ == 0)
|
|
StopPingPong();
|
|
} else {
|
|
if (count_down_ != 0) {
|
|
LOG(ERROR) << "Frame " << frame_count_down_
|
|
<< " wasn't able to complete on time!";
|
|
} else {
|
|
SendPing();
|
|
}
|
|
count_down_ = params_.messages_per_frame;
|
|
}
|
|
}
|
|
|
|
void StopPingPong() {
|
|
cpu_logger_.reset();
|
|
timer_.AbandonAndStop();
|
|
quit_closure_.Run();
|
|
}
|
|
|
|
void OnPong(const std::string& value) {
|
|
// Include message deserialization in latency.
|
|
DCHECK_EQ(payload_.size(), value.size());
|
|
|
|
CHECK_GT(count_down_, 0);
|
|
count_down_--;
|
|
if (count_down_ > 0) {
|
|
SendPing();
|
|
} else {
|
|
CHECK_GT(frame_count_down_, 0);
|
|
frame_count_down_--;
|
|
if (frame_count_down_ == 0)
|
|
StopPingPong();
|
|
}
|
|
}
|
|
|
|
void SendPing() {
|
|
ping_receiver_->Ping(payload_, base::Bind(&MojoSteadyPingPongTest::OnPong,
|
|
base::Unretained(this)));
|
|
}
|
|
|
|
static int RunPingPongClient(MojoHandle mp) {
|
|
mojo::MessagePipeHandle mp_handle(mp);
|
|
mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
|
|
|
|
LockThreadAffinity thread_locker(kSharedCore);
|
|
base::RunLoop run_loop;
|
|
ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
|
|
run_loop.Run();
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
TestParams params_;
|
|
std::string payload_;
|
|
std::string label_;
|
|
bool sync_ = false;
|
|
|
|
IPC::mojom::ReflectorPtr ping_receiver_;
|
|
|
|
int count_down_ = 0;
|
|
int frame_count_down_ = 0;
|
|
|
|
base::RepeatingTimer timer_;
|
|
std::unique_ptr<PerfCpuLogger> cpu_logger_;
|
|
|
|
base::Closure quit_closure_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(MojoSteadyPingPongTest);
|
|
};
|
|
|
|
DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
|
|
base::MessageLoop main_message_loop;
|
|
return RunPingPongClient(h);
|
|
}
|
|
|
|
// Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
|
|
// instead of raw IPC::Messages.
|
|
TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
|
|
RunTestClient("PingPongClient", [&](MojoHandle h) {
|
|
base::MessageLoop main_message_loop;
|
|
RunPingPongServer(h, "Mojo_CPU_Async", false);
|
|
});
|
|
}
|
|
|
|
TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
|
|
RunTestClient("PingPongClient", [&](MojoHandle h) {
|
|
base::MessageLoop main_message_loop;
|
|
RunPingPongServer(h, "Mojo_CPU_Sync", true);
|
|
});
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace IPC
|