Throughput profiling

PiperOrigin-RevId: 477752265
This commit is contained in:
hai007
2022-09-29 10:02:11 -07:00
committed by Copybara-Service
parent a584dad4a9
commit 8f681dece0
22 changed files with 940 additions and 47 deletions
+1
View File
@@ -426,6 +426,7 @@ let package = Package(
"connections/implementation/service_controller_router_test.cc",
"connections/implementation/wifi_hotspot_test.cc",
"connections/implementation/analytics/analytics_recorder_test.cc",
"connections/implementation/analytics/throughput_recorder_test.cc",
"connections/implementation/mediums/ble_v2_test.cc",
"connections/implementation/mediums/ble_v2/bloom_filter_test.cc",
"connections/implementation/mediums/ble_v2/ble_packet_test.cc",
@@ -17,10 +17,13 @@ cc_library(
name = "analytics",
srcs = [
"analytics_recorder.cc",
"throughput_recorder.cc",
],
hdrs = [
"analytics_recorder.h",
"connection_attempt_metadata_params.h",
"packet_meta_data.h",
"throughput_recorder.h",
],
copts = ["-DCORE_ADAPTER_DLL"],
visibility = [
@@ -38,6 +41,7 @@ cc_library(
"//proto:connections_enums_cc_proto",
"//proto/errorcode:error_code_enums_cc_proto",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/time",
],
)
@@ -47,6 +51,7 @@ cc_test(
size = "small",
srcs = [
"analytics_recorder_test.cc",
"throughput_recorder_test.cc",
],
shard_count = 16,
deps = [
@@ -0,0 +1,104 @@
// Copyright 2022 Google LLC
//
// 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
//
// https://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.
#ifndef NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_PACKET_META_DATA_H_
#define NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_PACKET_META_DATA_H_
#include <cstdint>
#include <string>
#include "absl/time/time.h"
#include "internal/platform/system_clock.h"
namespace location {
namespace nearby {
namespace analytics {
struct PacketMetaData {
int packet_size;
absl::Time file_io_start_time;
absl::Time file_io_end_time;
absl::Time encryption_start_time;
absl::Time encryption_end_time;
absl::Time socket_io_start_time;
absl::Time socket_io_end_time;
void Reset() {
file_io_start_time = SystemClock::ElapsedRealtime();
socket_io_start_time = SystemClock::ElapsedRealtime();
socket_io_start_time = SystemClock::ElapsedRealtime();
packet_size = 0;
}
void SetPacketSize(int packet_size) {
this->packet_size = packet_size;
}
int GetPacketSize() {
return packet_size;
}
void StartFileIo() {
file_io_start_time = SystemClock::ElapsedRealtime();
}
void StopFileIo() {
file_io_end_time = SystemClock::ElapsedRealtime();
}
void StartEncryption() {
encryption_start_time = SystemClock::ElapsedRealtime();
}
void StopEncryption() {
encryption_end_time = SystemClock::ElapsedRealtime();
}
void StartSocketIo() {
socket_io_start_time = SystemClock::ElapsedRealtime();
}
void StopSocketIo() {
socket_io_end_time = SystemClock::ElapsedRealtime();
}
int64_t GetEncryptionTimeInMillis() {
if (encryption_end_time > encryption_start_time) {
return absl::ToInt64Milliseconds(encryption_end_time -
encryption_start_time);
}
return 0L;
}
int64_t GetFileIoTimeInMillis() {
if (file_io_end_time > file_io_start_time) {
return absl::ToInt64Milliseconds(file_io_end_time - file_io_start_time);
}
return 0L;
}
int64_t GetSocketIoTimeInMillis() {
if (socket_io_end_time > socket_io_start_time) {
return absl::ToInt64Milliseconds(socket_io_end_time -
socket_io_start_time);
}
return 0L;
}
};
} // namespace analytics
} // namespace nearby
} // namespace location
#endif // NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_PACKET_META_DATA_H_
@@ -0,0 +1,303 @@
// Copyright 2022 Google LLC
//
// 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
//
// https://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 "connections/implementation/analytics/throughput_recorder.h"
#include <string>
#include <utility>
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
namespace location {
namespace nearby {
namespace analytics {
namespace {
constexpr int kDefaultThroughoutKbps = 0;
constexpr int kKbInBytes = 1024;
constexpr int kSecInMs = 1000;
} // namespace
ThroughputRecorder::ThroughputRecorder(int64_t payload_id)
: payload_id_(payload_id) {}
ThroughputRecorderContainer& ThroughputRecorderContainer::GetInstance() {
static std::aligned_storage_t<sizeof(ThroughputRecorderContainer),
alignof(ThroughputRecorderContainer)>
storage;
static ThroughputRecorderContainer* env =
new (&storage) ThroughputRecorderContainer();
return *env;
}
void ThroughputRecorder::Start(PayloadType payload_type,
const bool is_incoming) {
NEARBY_LOGS(INFO) << "Start TP profiling for payload_id:" << payload_id_;
// MutexLock lock(&mutex_);
if (payload_type == PayloadType::kUnknown) {
NEARBY_LOGS(INFO)
<< "Ignore ThroughputRecorder::start for Unknown Payload type";
return;
}
start_timestamp_ = SystemClock::ElapsedRealtime();
payload_type_ = payload_type;
is_incoming_ = is_incoming;
// Add packetLostAlarm later
}
bool ThroughputRecorder::Stop() {
NEARBY_LOGS(INFO) << "Stop TP profiling for payload_id:" << payload_id_;
if (payload_type_ == PayloadType::kUnknown) {
NEARBY_LOGS(INFO) << "Ignore ThroughputRecorder::stop as it never start";
return false;
}
{
MutexLock lock(&mutex_);
// Add packetLostAlarm stop process later
absl::Time stop_timestamp = SystemClock::ElapsedRealtime();
int64_t total_byte_size = 0;
int medium_size = throughputs_.size();
// The worse case is the socket/connect blocking the write request, never
// got return when writing a frame out, it would get a very good data rate
// for this case. e.g. use 60 seconds to send a file and failed, the counter
// only get the duration as 30 seconds because the last write request
// blocked.
if (!success_) {
if (!throughputs_.empty()) {
for (auto& tp : throughputs_) {
tp.second.SetLastTimestamp(stop_timestamp);
}
}
}
// calculate throughput by medium
for (auto& tp : throughputs_) {
tp.second.dump();
total_byte_size += tp.second.GetTotalByteSize();
}
throughputs_.clear();
int64_t total_millis =
absl::ToInt64Milliseconds(stop_timestamp - start_timestamp_);
throughput_kbps_ = CalculateThroughputKBps(total_byte_size, total_millis);
int throughput_mbps = CalculateThroughputMBps(throughput_kbps_);
// calculate overall throughput if there are multiple mediums
if (medium_size > 1) {
if (throughput_kbps_ != kDefaultThroughoutKbps) {
std::string dump_content = absl::StrFormat(
"%s %s data(%d bytes) %s, overall used %d milliseconds, "
"throughput "
"is %d MB/s (%d KB/s), File IO takes %d ms, %s takes %d "
"ms, "
"Socket IO takes %d ms",
is_incoming_ ? "Received" : "Sent", ToString(payload_type_),
total_byte_size, success_ ? "SUCCEEDED" : "FAILED", total_millis,
throughput_mbps, throughput_kbps_, file_io_time_,
is_incoming_ ? "Decryption" : "Encryption", encryption_time_,
socket_io_time_);
NEARBY_LOGS(INFO) << dump_content;
}
}
}
return true;
}
int ThroughputRecorder::CalculateThroughputKBps(int64_t total_byte_size,
int64_t total_millis) {
if (total_millis > 0) {
return (int)(total_byte_size * kSecInMs / kKbInBytes / total_millis);
}
return kDefaultThroughoutKbps;
}
int ThroughputRecorder::CalculateThroughputMBps(int throughputKBps) {
return throughputKBps / kKbInBytes;
}
void ThroughputRecorder::Throughput::Add(int frame_size,
int64_t file_io_time,
int64_t encryption_time,
int64_t socket_io_time) {
total_byte_size_ += frame_size;
// reset the last timestamp
last_timestamp_ = SystemClock::ElapsedRealtime();
file_io_time_ += file_io_time;
encryption_time_ += encryption_time;
socket_io_time_ += socket_io_time;
}
bool ThroughputRecorder::Throughput::dump() {
int64_t total_millis =
absl::ToInt64Milliseconds(last_timestamp_ - start_timestamp_);
int throughput_kbps = CalculateThroughputKBps(total_byte_size_, total_millis);
if (throughput_kbps == kDefaultThroughoutKbps) {
return false;
}
int throughpu_mbps = CalculateThroughputMBps(throughput_kbps);
int64_t other =
total_millis - file_io_time_ - encryption_time_ - socket_io_time_;
std::string dump_content = absl::StrFormat(
"%s %s data(%ld bytes) via %s used %ld milliseconds, throughput is %d "
"MB/s (%d KB/s), File IO takes %ld ms, %s takes %ld ms, "
"Socket IO takes %ld ms, "
"Other takes %ld ms",
is_incoming_ ? "Received" : "Sent", ToString(payload_type_),
total_byte_size_, proto::connections::Medium_Name(medium_), total_millis,
throughpu_mbps, throughput_kbps, file_io_time_,
is_incoming_ ? "Decryption" : "Encryption", encryption_time_,
socket_io_time_, other);
NEARBY_LOGS(INFO) << dump_content;
return true;
}
ThroughputRecorder::Throughput& ThroughputRecorder::GetThroughput(
Medium medium, int64_t duration_millis) {
MutexLock lock(&mutex_);
auto it = throughputs_.find(medium);
if (it == throughputs_.end()) {
auto throughput = new Throughput(
medium,
SystemClock::ElapsedRealtime() - absl::Milliseconds(duration_millis),
payload_type_, is_incoming_);
throughputs_.emplace(medium, std::move(*throughput));
delete throughput;
return throughputs_.find(medium)->second;
}
return it->second;
}
int ThroughputRecorder::GetThroughputsSize() {
MutexLock lock(&mutex_);
return throughputs_.size();
}
int ThroughputRecorder::GetThroughputKbps() { return throughput_kbps_; }
int64_t ThroughputRecorder::GetDurationMillis() {
return duration_millis_;
}
void ThroughputRecorder::OnFrameSent(Medium medium,
PacketMetaData& packetMetaData) {
if (payload_type_ == PayloadType::kUnknown) {
NEARBY_LOGS(INFO) << "PayloadType is invalid, return";
return;
}
duration_millis_ = packetMetaData.GetEncryptionTimeInMillis() +
packetMetaData.GetFileIoTimeInMillis() +
packetMetaData.GetSocketIoTimeInMillis();
GetThroughput(medium, duration_millis_)
.Add(packetMetaData.packet_size, packetMetaData.GetFileIoTimeInMillis(),
packetMetaData.GetEncryptionTimeInMillis(),
packetMetaData.GetSocketIoTimeInMillis());
CalculateDurationTimes(packetMetaData);
}
void ThroughputRecorder::OnFrameReceived(Medium medium,
PacketMetaData& packetMetaData) {
if (payload_type_ == PayloadType::kUnknown) {
NEARBY_LOGS(INFO) << "PayloadType is invalid, return";
return;
}
// Add packetLostAlarm process later
duration_millis_ = packetMetaData.GetEncryptionTimeInMillis() +
packetMetaData.GetFileIoTimeInMillis() +
packetMetaData.GetSocketIoTimeInMillis();
GetThroughput(medium, duration_millis_)
.Add(packetMetaData.packet_size, packetMetaData.GetFileIoTimeInMillis(),
packetMetaData.GetEncryptionTimeInMillis(),
packetMetaData.GetSocketIoTimeInMillis());
CalculateDurationTimes(packetMetaData);
}
void ThroughputRecorder::CalculateDurationTimes(PacketMetaData packetMetaData) {
encryption_time_ += packetMetaData.GetEncryptionTimeInMillis();
socket_io_time_ += packetMetaData.GetSocketIoTimeInMillis();
file_io_time_ += packetMetaData.GetFileIoTimeInMillis();
}
std::string ThroughputRecorder::ToString(PayloadType type) {
switch (type) {
case PayloadType::kBytes:
return std::string("Bytes");
case PayloadType::kStream:
return std::string("Stream");
case PayloadType::kFile:
return std::string("File");
case PayloadType::kUnknown:
return std::string("Unknown");
}
}
// Inplementation for ThroughputRecorderContainer
void ThroughputRecorderContainer::Shutdown() {
MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << __func__
<< ". Num of Instance:" << throughput_recorders_.size();
for (auto& throughput_recorder : throughput_recorders_) {
NEARBY_LOGS(INFO) << "Stop instance: " << throughput_recorder.second;
throughput_recorder.second->Stop();
delete throughput_recorder.second;
}
throughput_recorders_.clear();
}
ThroughputRecorder* ThroughputRecorderContainer::GetTPRecorder(
const int64_t payload_id) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(payload_id);
if (it == throughput_recorders_.end()) {
auto instance = new ThroughputRecorder(payload_id);
NEARBY_LOGS(INFO) << "Add ThroughputRecorder instance : " << instance
<< " for payload_id:" << payload_id;
throughput_recorders_.emplace(payload_id, instance);
return instance;
}
return it->second;
}
void ThroughputRecorderContainer::StopTPRecorder(
const int64_t payload_id) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(payload_id);
if (it != throughput_recorders_.end()) {
NEARBY_LOGS(INFO) << "Found and stop/delete ThroughputRecorder instance : "
<< &(it->second) << " for payload_id:" << payload_id;
it->second->Stop();
delete it->second;
throughput_recorders_.erase(payload_id);
return;
}
NEARBY_LOGS(INFO) << "No ThroughputRecorder found for :" << payload_id;
}
int ThroughputRecorderContainer::GetSize() {
MutexLock lock(&mutex_);
return throughput_recorders_.size();
}
} // namespace analytics
} // namespace nearby
} // namespace location
@@ -0,0 +1,139 @@
// Copyright 2022 Google LLC
//
// 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
//
// https://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.
#ifndef NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_THROUGHPUT_RECORDER_H_
#define NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_THROUGHPUT_RECORDER_H_
#include <cstdint>
#include <string>
#include "absl/container/flat_hash_map.h"
#include "connections/implementation/analytics/packet_meta_data.h"
#include "connections/payload_type.h"
#include "internal/platform/mutex.h"
#include "proto/connections_enums.pb.h"
namespace location {
namespace nearby {
namespace analytics {
using Medium = location::nearby::proto::connections::Medium;
using PayloadType = connections::PayloadType;
class ThroughputRecorder {
public:
explicit ThroughputRecorder(int64_t payload_id);
~ThroughputRecorder() = default;
void Start(PayloadType payload_type, const bool is_incoming);
bool Stop() ABSL_LOCKS_EXCLUDED(mutex_);
static int CalculateThroughputKBps(int64_t total_byte_size,
int64_t total_millis);
static int CalculateThroughputMBps(int throughputKBps);
class Throughput {
public:
Throughput() = default;
~Throughput() = default;
Throughput(Medium medium, absl::Time start_timestamp,
PayloadType payload_type, bool is_incoming)
: medium_(medium),
start_timestamp_(start_timestamp),
payload_type_(payload_type),
is_incoming_(is_incoming) {}
void Add(int frame_size, int64_t file_io_time,
int64_t encryption_time, int64_t socket_io_time);
void SetLastTimestamp(absl::Time time_stamp) {
last_timestamp_ = time_stamp;
}
int64_t GetTotalByteSize() { return total_byte_size_; }
bool dump();
private:
Medium medium_;
absl::Time start_timestamp_;
PayloadType payload_type_;
int64_t total_byte_size_ = 0;
absl::Time last_timestamp_;
bool is_incoming_ = false;
int64_t file_io_time_ = 0;
int64_t encryption_time_ = 0;
int64_t socket_io_time_ = 0;
};
Throughput& GetThroughput(Medium medium, int64_t duration_millis)
ABSL_LOCKS_EXCLUDED(mutex_);
int GetThroughputsSize() ABSL_LOCKS_EXCLUDED(mutex_);
int GetThroughputKbps();
int64_t GetDurationMillis();
void OnFrameSent(Medium medium, PacketMetaData& packetMetaData);
void OnFrameReceived(Medium medium, PacketMetaData& packetMetaData);
void MarkAsSuccess() { success_ = true; }
private:
void CalculateDurationTimes(PacketMetaData packetMetaData);
static std::string ToString(PayloadType type);
Mutex mutex_;
int64_t payload_id_;
absl::Time start_timestamp_;
PayloadType payload_type_ = PayloadType::kUnknown;
bool is_incoming_;
absl::flat_hash_map<Medium, Throughput> throughputs_ ABSL_GUARDED_BY(mutex_);
bool success_ = false;
int64_t file_io_time_ = 0;
int64_t encryption_time_ = 0;
int64_t socket_io_time_ = 0;
int64_t duration_millis_ = 0;
int throughput_kbps_;
};
class ThroughputRecorderContainer {
public:
ThroughputRecorderContainer(const ThroughputRecorderContainer&) = delete;
ThroughputRecorderContainer& operator=(const ThroughputRecorderContainer&) =
delete;
static ThroughputRecorderContainer& GetInstance();
void Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
ThroughputRecorder* GetTPRecorder(const int64_t payload_id)
ABSL_LOCKS_EXCLUDED(mutex_);
void StopTPRecorder(const int64_t payload_id)
ABSL_LOCKS_EXCLUDED(mutex_);
int GetSize() ABSL_LOCKS_EXCLUDED(mutex_);
private:
// This is a singleton object, for which destructor will never be called.
// Constructor will be invoked once from Instance() static method.
// Object is create in-place (with a placement new) to guarantee that
// destructor is not scheduled for execution at exit.
ThroughputRecorderContainer() = default;
~ThroughputRecorderContainer() = default;
Mutex mutex_;
absl::flat_hash_map<int64_t, ThroughputRecorder*> throughput_recorders_
ABSL_GUARDED_BY(mutex_);
};
} // namespace analytics
} // namespace nearby
} // namespace location
#endif // NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_THROUGHPUT_RECORDER_H_
@@ -0,0 +1,212 @@
// Copyright 2022 Google LLC
//
// 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
//
// https://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 "connections/implementation/analytics/throughput_recorder.h"
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "internal/platform/logging.h"
#include "proto/connections_enums.proto.h"
namespace location {
namespace nearby {
namespace analytics {
namespace {
// TODO(b/246693797): Add unit tests coverage for throughput recorder code
constexpr int64_t kPayloadIdA = 123456789;
constexpr int64_t kPayloadIdB = 987654321;
constexpr int kFrameSize = 10 * 64 * 1024;
constexpr int64_t kTotalByteSize1GB = 1024 * 1024 * 1024;
constexpr int64_t kTotalMillis10Sec = 10 * 1000;
constexpr int kTPResultKBPerSec = 1024 * 1024 / 10;
constexpr int kTPKBPerSec = 100 * 1024;
constexpr int kTPResultMBPerSec = 100;
// class ThroughputRecorderTest : public testing::Test {
class ThroughputRecorderTest : public testing::TestWithParam<bool> {
protected:
ThroughputRecorderTest() = default;
~ThroughputRecorderTest() override {
ThroughputRecorderContainer::GetInstance().Shutdown();
}
ThroughputRecorderContainer& tp_recorder_container_ =
ThroughputRecorderContainer::GetInstance();
};
INSTANTIATE_TEST_SUITE_P(ParametrisedTestThroughputRecorderTest,
ThroughputRecorderTest, testing::Values(true, false));
TEST(ThroughputRecorder, CalculateThroughputKBps) {
EXPECT_EQ(ThroughputRecorder::CalculateThroughputKBps(kTotalByteSize1GB,
kTotalMillis10Sec),
kTPResultKBPerSec);
EXPECT_EQ(ThroughputRecorder::CalculateThroughputKBps(kTotalByteSize1GB, 0),
0);
}
TEST(ThroughputRecorder, CalculateThroughputMBps) {
EXPECT_EQ(ThroughputRecorder::CalculateThroughputMBps(kTPKBPerSec),
kTPResultMBPerSec);
}
TEST(ThroughputRecorderContainer, InstanceCreate_ContainerSize) {
ThroughputRecorderContainer& TPRecorderContainer =
ThroughputRecorderContainer::GetInstance();
TPRecorderContainer.GetTPRecorder(kPayloadIdA);
TPRecorderContainer.GetTPRecorder(kPayloadIdB);
EXPECT_EQ(ThroughputRecorderContainer::GetInstance().GetSize(), 2);
ThroughputRecorderContainer::GetInstance().Shutdown();
EXPECT_EQ(ThroughputRecorderContainer::GetInstance().GetSize(), 0);
}
TEST_F(ThroughputRecorderTest, OnFrameSentSaveTransferredSize) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(kPayloadIdA);
TPRecorder->Start(PayloadType::kFile, /*isIncoming=*/false);
PacketMetaData packet_meta_data;
packet_meta_data.SetPacketSize(kFrameSize);
TPRecorder->OnFrameSent(proto::connections::BLE, packet_meta_data);
TPRecorder->OnFrameSent(proto::connections::BLE, packet_meta_data);
TPRecorder->OnFrameSent(proto::connections::BLE, packet_meta_data);
auto throughput = TPRecorder->GetThroughput(proto::connections::BLE, 0);
EXPECT_EQ(throughput.GetTotalByteSize(), kFrameSize * 3);
}
TEST_F(ThroughputRecorderTest, OnIgnoreUnkownPaylaodType) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(kPayloadIdA);
TPRecorder->Start(PayloadType::kUnknown, /*isIncoming=*/false);
PacketMetaData packet_meta_data;
TPRecorder->OnFrameSent(proto::connections::BLE, packet_meta_data);
EXPECT_EQ(TPRecorder->GetThroughputsSize(), 0);
TPRecorder->Start(PayloadType::kUnknown, /*isIncoming=*/true);
TPRecorder->OnFrameReceived(proto::connections::BLE, packet_meta_data);
EXPECT_EQ(TPRecorder->GetThroughputsSize(), 0);
}
TEST_P(ThroughputRecorderTest, OnFrameSentStopAndDump) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(kPayloadIdA);
TPRecorder->Start(PayloadType::kFile, /*isIncoming=*/false);
PacketMetaData packet_meta_data;
packet_meta_data.SetPacketSize(kFrameSize);
packet_meta_data.StartFileIo();
absl::SleepFor(absl::Milliseconds(5));
packet_meta_data.StopFileIo();
packet_meta_data.StartEncryption();
absl::SleepFor(absl::Milliseconds(6));
packet_meta_data.StopEncryption();
packet_meta_data.StartSocketIo();
absl::SleepFor(absl::Milliseconds(7));
packet_meta_data.StopSocketIo();
TPRecorder->OnFrameSent(proto::connections::BLE, packet_meta_data);
EXPECT_EQ(TPRecorder->GetDurationMillis(),
packet_meta_data.GetEncryptionTimeInMillis() +
packet_meta_data.GetFileIoTimeInMillis() +
packet_meta_data.GetSocketIoTimeInMillis());
packet_meta_data.SetPacketSize(kFrameSize);
packet_meta_data.StartFileIo();
absl::SleepFor(absl::Milliseconds(15));
packet_meta_data.StopFileIo();
packet_meta_data.StartEncryption();
absl::SleepFor(absl::Milliseconds(16));
packet_meta_data.StopEncryption();
packet_meta_data.StartSocketIo();
absl::SleepFor(absl::Milliseconds(17));
packet_meta_data.StopSocketIo();
TPRecorder->OnFrameSent(proto::connections::BLE, packet_meta_data);
if (GetParam() == true) {
NEARBY_LOGS(INFO) << "MarkAsSuccess";
TPRecorder->MarkAsSuccess();
}
EXPECT_TRUE(TPRecorder->Stop());
EXPECT_NE(TPRecorder->GetThroughputKbps(), 0);
}
TEST_F(ThroughputRecorderTest, OnFrameSentStopAndDumpForMultiMeadium) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(kPayloadIdA);
TPRecorder->Start(PayloadType::kFile, /*isIncoming=*/false);
PacketMetaData packet_meta_data1;
packet_meta_data1.SetPacketSize(kFrameSize);
packet_meta_data1.StartFileIo();
absl::SleepFor(absl::Milliseconds(5));
packet_meta_data1.StopFileIo();
packet_meta_data1.StartEncryption();
absl::SleepFor(absl::Milliseconds(6));
packet_meta_data1.StopEncryption();
packet_meta_data1.StartSocketIo();
absl::SleepFor(absl::Milliseconds(7));
packet_meta_data1.StopSocketIo();
TPRecorder->OnFrameSent(proto::connections::BLE, packet_meta_data1);
PacketMetaData packet_meta_data2;
packet_meta_data2.SetPacketSize(kFrameSize);
packet_meta_data2.StartFileIo();
absl::SleepFor(absl::Milliseconds(15));
packet_meta_data2.StopFileIo();
packet_meta_data2.StartEncryption();
absl::SleepFor(absl::Milliseconds(16));
packet_meta_data2.StopEncryption();
packet_meta_data2.StartSocketIo();
absl::SleepFor(absl::Milliseconds(17));
packet_meta_data2.StopSocketIo();
TPRecorder->OnFrameSent(proto::connections::WIFI_LAN, packet_meta_data2);
TPRecorder->MarkAsSuccess();
EXPECT_TRUE(TPRecorder->Stop());
EXPECT_NE(TPRecorder->GetThroughputKbps(), 0);
}
TEST_F(ThroughputRecorderTest, OnFrameReceivedCheckDurationMillis) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(kPayloadIdA);
TPRecorder->Start(PayloadType::kFile, /*isIncoming=*/true);
PacketMetaData packet_meta_data;
packet_meta_data.SetPacketSize(kFrameSize);
packet_meta_data.StartFileIo();
absl::SleepFor(absl::Milliseconds(5));
packet_meta_data.StopFileIo();
packet_meta_data.StartEncryption();
absl::SleepFor(absl::Milliseconds(6));
packet_meta_data.StopEncryption();
packet_meta_data.StartSocketIo();
absl::SleepFor(absl::Milliseconds(7));
packet_meta_data.StopSocketIo();
TPRecorder->OnFrameReceived(proto::connections::BLE, packet_meta_data);
EXPECT_EQ(TPRecorder->GetDurationMillis(),
packet_meta_data.GetEncryptionTimeInMillis() +
packet_meta_data.GetFileIoTimeInMillis() +
packet_meta_data.GetSocketIoTimeInMillis());
}
TEST_F(ThroughputRecorderTest, OnTPRecorderNotStarted) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(kPayloadIdA);
auto throughput = TPRecorder->GetThroughput(proto::connections::BLE, 0);
EXPECT_FALSE(throughput.dump());
}
} // namespace
} // namespace analytics
} // namespace nearby
} // namespace location
@@ -15,9 +15,10 @@
#include "connections/implementation/base_endpoint_channel.h"
#include <cassert>
#include <memory>
#include <string>
#include <utility>
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "connections/implementation/offline_frames.h"
#include "internal/platform/byte_array.h"
@@ -122,10 +123,17 @@ BaseEndpointChannel::BaseEndpointChannel(
try_count_(try_count) {}
ExceptionOr<ByteArray> BaseEndpointChannel::Read() {
PacketMetaData packet_meta_data;
return Read(packet_meta_data);
}
ExceptionOr<ByteArray> BaseEndpointChannel::Read(
PacketMetaData& packet_meta_data) {
ByteArray result;
{
MutexLock lock(&reader_mutex_);
packet_meta_data.StartSocketIo();
ExceptionOr<std::int32_t> read_int = ReadInt(reader_);
if (!read_int.ok()) {
return ExceptionOr<ByteArray>(read_int.exception());
@@ -141,6 +149,8 @@ ExceptionOr<ByteArray> BaseEndpointChannel::Read() {
if (!read_bytes.ok()) {
return read_bytes;
}
packet_meta_data.StopSocketIo();
packet_meta_data.SetPacketSize(read_int.result() + sizeof(std::int32_t));
result = std::move(read_bytes.result());
}
@@ -149,6 +159,7 @@ ExceptionOr<ByteArray> BaseEndpointChannel::Read() {
if (IsEncryptionEnabledLocked()) {
// If encryption is enabled, decode the message.
std::string input(std::move(result));
packet_meta_data.StartEncryption();
std::unique_ptr<std::string> decrypted_data =
crypto_context_->DecodeMessageFromPeer(input);
if (decrypted_data) {
@@ -178,6 +189,7 @@ ExceptionOr<ByteArray> BaseEndpointChannel::Read() {
<< __func__ << ": Unable to parse data as unencrypted message.";
}
}
packet_meta_data.StopEncryption();
if (result.Empty()) {
NEARBY_LOGS(WARNING) << __func__ << ": Unable to parse read result.";
return ExceptionOr<ByteArray>(Exception::kInvalidProtocolBuffer);
@@ -193,6 +205,12 @@ ExceptionOr<ByteArray> BaseEndpointChannel::Read() {
}
Exception BaseEndpointChannel::Write(const ByteArray& data) {
PacketMetaData packet_meta_data;
return Write(data, packet_meta_data);
}
Exception BaseEndpointChannel::Write(const ByteArray& data,
PacketMetaData& packet_meta_data) {
{
MutexLock pause_lock(&is_paused_mutex_);
if (is_paused_) {
@@ -212,8 +230,10 @@ Exception BaseEndpointChannel::Write(const ByteArray& data) {
MutexLock crypto_lock(&crypto_mutex_);
if (IsEncryptionEnabledLocked()) {
// If encryption is enabled, encode the message.
packet_meta_data.StartEncryption();
std::unique_ptr<std::string> encrypted =
crypto_context_->EncodeMessageToPeer(std::string(data));
packet_meta_data.StopEncryption();
if (!encrypted) {
NEARBY_LOGS(WARNING) << __func__ << ": Failed to encrypt data.";
return {Exception::kIo};
@@ -230,6 +250,7 @@ Exception BaseEndpointChannel::Write(const ByteArray& data) {
return {Exception::kIo};
}
packet_meta_data.StartSocketIo();
Exception write_exception =
WriteInt(writer_, static_cast<std::int32_t>(data_size));
if (write_exception.Raised()) {
@@ -249,6 +270,8 @@ Exception BaseEndpointChannel::Write(const ByteArray& data) {
<< flush_exception.value;
return flush_exception;
}
packet_meta_data.StopSocketIo();
packet_meta_data.SetPacketSize(data_size + sizeof(std::uint32_t));
}
{
@@ -19,22 +19,21 @@
#include <memory>
#include <string>
#include "securegcm/d2d_connection_context_v1.h"
#include "absl/base/thread_annotations.h"
#include "connections/implementation/analytics/analytics_recorder.h"
#include "connections/implementation/endpoint_channel.h"
#include "internal/platform/atomic_reference.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/condition_variable.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/mutex.h"
#include "internal/platform/output_stream.h"
#include "internal/platform/system_clock.h"
namespace location {
namespace nearby {
namespace connections {
using analytics::PacketMetaData;
class BaseEndpointChannel : public EndpointChannel {
public:
BaseEndpointChannel(const std::string& service_id,
@@ -49,10 +48,12 @@ class BaseEndpointChannel : public EndpointChannel {
~BaseEndpointChannel() override = default;
// EndpointChannel:
ExceptionOr<ByteArray> Read()
ExceptionOr<ByteArray> Read() override;
ExceptionOr<ByteArray> Read(PacketMetaData& packet_meta_data)
ABSL_LOCKS_EXCLUDED(reader_mutex_, crypto_mutex_,
last_read_mutex_) override;
Exception Write(const ByteArray& data)
Exception Write(const ByteArray& data) override;
Exception Write(const ByteArray& data, PacketMetaData& packet_meta_data)
ABSL_LOCKS_EXCLUDED(writer_mutex_, crypto_mutex_) override;
void Close() ABSL_LOCKS_EXCLUDED(is_paused_mutex_) override;
void Close(proto::connections::DisconnectionReason reason) override;
@@ -905,7 +905,8 @@ Status BasePcpHandler::RejectConnection(ClientProxy* client,
void BasePcpHandler::OnIncomingFrame(OfflineFrame& frame,
const std::string& endpoint_id,
ClientProxy* client,
proto::connections::Medium medium) {
proto::connections::Medium medium,
PacketMetaData& packet_meta_data) {
CountDownLatch latch(1);
RunOnPcpHandlerThread(
"incoming-frame",
@@ -134,7 +134,8 @@ class BasePcpHandler : public PcpHandler,
// @EndpointManagerReaderThread
void OnIncomingFrame(OfflineFrame& frame, const std::string& endpoint_id,
ClientProxy* client,
proto::connections::Medium medium) override;
proto::connections::Medium medium,
analytics::PacketMetaData& packet_meta_data) override;
// Called when an endpoint disconnects while we're waiting for both sides to
// approve/reject the connection.
@@ -627,6 +627,7 @@ TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) {
EndpointManager em(&ecm);
BwuManager bwu(m, em, ecm, {}, {});
MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu);
analytics::PacketMetaData packet_meta_data;
StartDiscovery(&client, &pcp_handler);
auto mediums = pcp_handler.GetDiscoveryMediums(&client);
auto connect_medium = mediums[mediums.size() - 1];
@@ -647,7 +648,7 @@ TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) {
auto frame =
parser::FromBytes(parser::ForConnectionResponse(Status::kSuccess));
pcp_handler.OnIncomingFrame(frame.result(), endpoint_id, &client,
connect_medium);
connect_medium, packet_meta_data);
NEARBY_LOGS(INFO) << "Closing connection: id=" << endpoint_id;
channel_b->Close();
bwu.Shutdown();
+2 -1
View File
@@ -291,7 +291,8 @@ void BwuManager::InitiateBwuForEndpoint(ClientProxy* client,
void BwuManager::OnIncomingFrame(OfflineFrame& frame,
const std::string& endpoint_id,
ClientProxy* client, Medium medium) {
ClientProxy* client, Medium medium,
PacketMetaData& packet_meta_data) {
V1Frame::FrameType frame_type = parser::GetFrameType(frame);
if (frame_type != V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION) return;
+2 -1
View File
@@ -86,7 +86,8 @@ class BwuManager : public EndpointManager::FrameProcessor {
// inbound BWU protocol.
// @EndpointManagerReaderThread
void OnIncomingFrame(OfflineFrame& frame, const std::string& endpoint_id,
ClientProxy* client, Medium medium) override;
ClientProxy* client, Medium medium,
PacketMetaData& packet_meta_data) override;
// Cleans up in-progress upgrades after endpoint disconnection.
// @EndpointManagerReaderThread
+10 -8
View File
@@ -14,6 +14,7 @@
#include "connections/implementation/bwu_manager.h"
#include <memory>
#include <string>
#include <utility>
@@ -119,12 +120,12 @@ class BwuManagerTest : public ::testing::Test {
parser::FromBytes(parser::ForBwuLastWrite());
bwu_manager_->OnIncomingFrame(last_write_frame.result(),
std::string(endpoint_id), &client_,
initial_medium);
initial_medium, packet_meta_data_);
ExceptionOr<OfflineFrame> safe_to_close_frame =
parser::FromBytes(parser::ForBwuSafeToClose());
bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(),
std::string(endpoint_id), &client_,
initial_medium);
initial_medium, packet_meta_data_);
return upgraded_channel;
}
@@ -139,6 +140,7 @@ class BwuManagerTest : public ::testing::Test {
FakeBwuHandler* fake_wifi_lan_bwu_handler_ = nullptr;
FakeBwuHandler* fake_wifi_hotspot_bwu_handler_ = nullptr;
std::unique_ptr<BwuManager> bwu_manager_;
PacketMetaData packet_meta_data_;
};
class BwuManagerTestParam : public BwuManagerTest,
@@ -193,12 +195,12 @@ TEST_P(BwuManagerTestParam, InitiateBwu_Success) {
parser::FromBytes(parser::ForBwuLastWrite());
bwu_manager_->OnIncomingFrame(last_write_frame.result(),
std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
Medium::BLUETOOTH, packet_meta_data_);
ExceptionOr<OfflineFrame> safe_to_close_frame =
parser::FromBytes(parser::ForBwuSafeToClose());
bwu_manager_->OnIncomingFrame(safe_to_close_frame.result(),
std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
Medium::BLUETOOTH, packet_meta_data_);
// Confirm that upgrade channel is resumed after initial channel is shut down.
// Note: If we didn't grab the shared initial channel pointer above, this
@@ -626,7 +628,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnUpgradeFailure_FlagEnabled) {
parser::FromBytes(parser::ForBwuFailure(info));
bwu_manager_->OnIncomingFrame(upgrade_failure.result(),
std::string(kEndpointId3), &client_,
Medium::WEB_RTC);
Medium::WEB_RTC, packet_meta_data_);
// With the flag enabled, we can safely revert WebRTC just for service B
// because service B has no active WebRTC endpoints.
@@ -661,7 +663,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnUpgradeFailure_FlagDisabled) {
parser::FromBytes(parser::ForBwuFailure(info));
bwu_manager_->OnIncomingFrame(upgrade_failure.result(),
std::string(kEndpointId3), &client_,
Medium::WEB_RTC);
Medium::WEB_RTC, packet_meta_data_);
// With the flag disabled, we don't revert if there are still connected
// endpoints for _any_ service. We don't have service-level bookkeeping; we
@@ -688,7 +690,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Hotspot) {
upgrade_path_info->set_supports_client_introduction_ack(false);
upgrade_path_info->set_supports_disabling_encryption(true);
bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
Medium::BLUETOOTH, packet_meta_data_);
CountDownLatch latch(1);
bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA,
std::string(kEndpointId1), latch);
@@ -714,7 +716,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Wlan) {
upgrade_path_info->set_supports_client_introduction_ack(false);
bwu_manager_->OnIncomingFrame(frame, std::string(kEndpointId1), &client_,
Medium::BLUETOOTH);
Medium::BLUETOOTH, packet_meta_data_);
CountDownLatch latch(1);
bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA,
std::string(kEndpointId1), latch);
@@ -42,10 +42,20 @@ class FakeEndpointChannel : public EndpointChannel {
return in_ ? in_->Read(Pipe::kChunkSize)
: ExceptionOr<ByteArray>{Exception::kIo};
}
ExceptionOr<ByteArray> Read(PacketMetaData& packet_meta_data) override {
read_timestamp_ = SystemClock::ElapsedRealtime();
return in_ ? in_->Read(Pipe::kChunkSize)
: ExceptionOr<ByteArray>{Exception::kIo};
}
Exception Write(const ByteArray& data) override {
write_timestamp_ = SystemClock::ElapsedRealtime();
return out_ ? out_->Write(data) : Exception{Exception::kIo};
}
Exception Write(const ByteArray& data,
PacketMetaData& packet_meta_data) override {
write_timestamp_ = SystemClock::ElapsedRealtime();
return out_ ? out_->Write(data) : Exception{Exception::kIo};
}
void Close() override {
if (in_) in_->Close();
if (out_) out_->Close();
@@ -19,17 +19,18 @@
#include <string>
#include "securegcm/d2d_connection_context_v1.h"
#include "absl/time/clock.h"
#include "connections/implementation/analytics/analytics_recorder.h"
#include "connections/implementation/analytics/packet_meta_data.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/mutex.h"
#include "proto/connections_enums.pb.h"
namespace location {
namespace nearby {
namespace connections {
using analytics::PacketMetaData;
class EndpointChannel {
public:
virtual ~EndpointChannel() = default;
@@ -39,9 +40,15 @@ class EndpointChannel {
virtual ExceptionOr<ByteArray>
Read() = 0; // throws Exception::IO, Exception::INTERRUPTED
virtual ExceptionOr<ByteArray> Read(PacketMetaData& packet_meta_data) = 0;
virtual Exception Write(const ByteArray& data) = 0; // throws Exception::IO
virtual Exception Write(
const ByteArray& data,
PacketMetaData& packet_meta_data) = 0; // throws Exception::IO
// Closes this EndpointChannel, without tracking the closure in analytics.
virtual void Close() = 0;
// Closes this EndpointChannel and records the closure with the given reason.
+19 -7
View File
@@ -19,7 +19,9 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "connections/implementation/analytics/throughput_recorder.h"
#include "connections/implementation/endpoint_channel.h"
#include "connections/implementation/offline_frames.h"
#include "connections/implementation/proto/offline_wire_formats.pb.h"
@@ -167,7 +169,8 @@ ExceptionOr<bool> EndpointManager::HandleData(
// a replacement for this endpoint since we last checked with the
// EndpointChannelManager.
while (true) {
ExceptionOr<ByteArray> bytes = endpoint_channel->Read();
PacketMetaData packet_meta_data;
ExceptionOr<ByteArray> bytes = endpoint_channel->Read(packet_meta_data);
if (!bytes.ok()) {
NEARBY_LOG(INFO, "Stop reading on read-time exception: %d",
bytes.exception());
@@ -210,7 +213,8 @@ ExceptionOr<bool> EndpointManager::HandleData(
}
frame_processor->OnIncomingFrame(frame, endpoint_id, client,
endpoint_channel->GetMedium());
endpoint_channel->GetMedium(),
packet_meta_data);
}
}
@@ -279,6 +283,7 @@ EndpointManager::EndpointManager(EndpointChannelManager* manager)
EndpointManager::~EndpointManager() {
NEARBY_LOG(INFO, "Initiating shutdown of EndpointManager.");
analytics::ThroughputRecorderContainer::GetInstance().Shutdown();
CountDownLatch latch(1);
RunOnEndpointManagerThread("bring-down-endpoints", [this, &latch]() {
NEARBY_LOG(INFO, "Bringing down endpoints");
@@ -493,7 +498,8 @@ int EndpointManager::GetMaxTransmitPacketSize(const std::string& endpoint_id) {
std::vector<std::string> EndpointManager::SendPayloadChunk(
const PayloadTransferFrame::PayloadHeader& payload_header,
const PayloadTransferFrame::PayloadChunk& payload_chunk,
const std::vector<std::string>& endpoint_ids) {
const std::vector<std::string>& endpoint_ids,
PacketMetaData& packet_meta_data) {
ByteArray bytes =
parser::ForDataPayloadTransfer(payload_header, payload_chunk);
@@ -501,7 +507,8 @@ std::vector<std::string> EndpointManager::SendPayloadChunk(
endpoint_ids, bytes, payload_header.id(),
/*offset=*/payload_chunk.offset(),
/*packet_type=*/
PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::DATA));
PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::DATA),
packet_meta_data);
}
// Designed to run asynchronously. It is called from IO thread pools, and
@@ -521,12 +528,14 @@ std::vector<std::string> EndpointManager::SendControlMessage(
const PayloadTransferFrame::ControlMessage& control,
const std::vector<std::string>& endpoint_ids) {
ByteArray bytes = parser::ForControlPayloadTransfer(header, control);
PacketMetaData packet_meta_data;
return SendTransferFrameBytes(
endpoint_ids, bytes, header.id(),
/*offset=*/control.offset(),
/*packet_type=*/
PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::CONTROL));
PayloadTransferFrame::PacketType_Name(PayloadTransferFrame::CONTROL),
packet_meta_data);
}
// @EndpointManagerThread
@@ -616,7 +625,7 @@ CountDownLatch EndpointManager::NotifyFrameProcessorsOnEndpointDisconnect(
std::vector<std::string> EndpointManager::SendTransferFrameBytes(
const std::vector<std::string>& endpoint_ids, const ByteArray& bytes,
std::int64_t payload_id, std::int64_t offset,
const std::string& packet_type) {
const std::string& packet_type, PacketMetaData& packet_meta_data) {
std::vector<std::string> failed_endpoint_ids;
for (const std::string& endpoint_id : endpoint_ids) {
std::shared_ptr<EndpointChannel> channel =
@@ -635,12 +644,15 @@ std::vector<std::string> EndpointManager::SendTransferFrameBytes(
continue;
}
Exception write_exception = channel->Write(bytes);
Exception write_exception = channel->Write(bytes, packet_meta_data);
if (!write_exception.Ok()) {
failed_endpoint_ids.push_back(endpoint_id);
NEARBY_LOGS(INFO) << "Failed to send packet; endpoint_id=" << endpoint_id;
continue;
}
analytics::ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_id)
->OnFrameSent(channel->GetMedium(), packet_meta_data);
}
return failed_endpoint_ids;
+10 -5
View File
@@ -20,7 +20,9 @@
#include <memory>
#include <utility>
#include <string>
#include <vector>
#include "connections/implementation/analytics/packet_meta_data.h"
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
@@ -33,10 +35,8 @@
#include "internal/platform/byte_array.h"
#include "internal/platform/condition_variable.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/multi_thread_executor.h"
#include "internal/platform/runnable.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/platform/system_clock.h"
namespace location {
namespace nearby {
@@ -60,6 +60,8 @@ namespace connections {
// to PayloadManager::ProcessFrame() (still running on that
// same dedicated reader thread).
using analytics::PacketMetaData;
class EndpointManager {
public:
class FrameProcessor {
@@ -77,7 +79,8 @@ class EndpointManager {
virtual void OnIncomingFrame(OfflineFrame& offline_frame,
const std::string& from_endpoint_id,
ClientProxy* to_client,
proto::connections::Medium current_medium) = 0;
proto::connections::Medium current_medium,
PacketMetaData& packet_meta_data) = 0;
// Implementations must call barrier.CountDown() once
// they're done. This parallelizes the disconnection event across all frame
@@ -125,7 +128,8 @@ class EndpointManager {
std::vector<std::string> SendPayloadChunk(
const PayloadTransferFrame::PayloadHeader& payload_header,
const PayloadTransferFrame::PayloadChunk& payload_chunk,
const std::vector<std::string>& endpoint_ids);
const std::vector<std::string>& endpoint_ids,
PacketMetaData& packet_meta_data);
std::vector<std::string> SendControlMessage(
const PayloadTransferFrame::PayloadHeader& payload_header,
const PayloadTransferFrame::ControlMessage& control_message,
@@ -261,7 +265,8 @@ class EndpointManager {
std::vector<std::string> SendTransferFrameBytes(
const std::vector<std::string>& endpoint_ids,
const ByteArray& payload_transfer_frame_bytes, std::int64_t payload_id,
std::int64_t offset, const std::string& packet_type);
std::int64_t offset, const std::string& packet_type,
PacketMetaData& packet_meta_data);
// Executes all jobs sequentially, on a serial_executor_.
void RunOnEndpointManagerThread(const std::string& name, Runnable runnable);
@@ -18,6 +18,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
@@ -51,7 +52,12 @@ using ::testing::StrictMock;
class MockEndpointChannel : public EndpointChannel {
public:
MOCK_METHOD(ExceptionOr<ByteArray>, Read, (), (override));
MOCK_METHOD(ExceptionOr<ByteArray>, Read, (PacketMetaData & packet_meta_data),
(override));
MOCK_METHOD(Exception, Write, (const ByteArray& data), (override));
MOCK_METHOD(Exception, Write,
(const ByteArray& data, PacketMetaData& packet_meta_data),
(override));
MOCK_METHOD(void, Close, (), (override));
MOCK_METHOD(void, Close, (DisconnectionReason reason), (override));
MOCK_METHOD(proto::connections::ConnectionTechnology, GetTechnology, (),
@@ -95,7 +101,7 @@ class MockFrameProcessor : public EndpointManager::FrameProcessor {
MOCK_METHOD(void, OnIncomingFrame,
(OfflineFrame & offline_frame,
const std::string& from_endpoint_id, ClientProxy* to_client,
Medium current_medium),
Medium current_medium, PacketMetaData& packet_meta_data),
(override));
MOCK_METHOD(void, OnEndpointDisconnect,
@@ -210,7 +216,7 @@ TEST_F(EndpointManagerTest, RegisterFrameProcessorWorks) {
auto read_data = parser::ForConnectionRequest(connection_info);
EXPECT_CALL(*connect_request, OnIncomingFrame);
EXPECT_CALL(*connect_request, OnEndpointDisconnect);
EXPECT_CALL(*endpoint_channel, Read())
EXPECT_CALL(*endpoint_channel, Read(_))
.WillOnce(Return(ExceptionOr<ByteArray>(read_data)))
.WillRepeatedly(Return(ExceptionOr<ByteArray>(Exception::kIo)));
EXPECT_CALL(*endpoint_channel, Write(_))
@@ -256,7 +262,7 @@ TEST_F(EndpointManagerTest, SendControlMessageWorks) {
control.set_offset(150);
control.set_event(PayloadTransferFrame::ControlMessage::PAYLOAD_CANCELED);
ON_CALL(*endpoint_channel, Read())
ON_CALL(*endpoint_channel, Read(_))
.WillByDefault([channel = endpoint_channel.get()]() {
if (channel->IsClosed()) return ExceptionOr<ByteArray>(Exception::kIo);
NEARBY_LOG(INFO, "Simulate read delay: wait");
@@ -271,7 +277,7 @@ TEST_F(EndpointManagerTest, SendControlMessageWorks) {
channel->DoClose();
NEARBY_LOG(INFO, "Channel closed");
});
EXPECT_CALL(*endpoint_channel, Write(_))
EXPECT_CALL(*endpoint_channel, Write(_, _))
.WillRepeatedly(Return(Exception{Exception::kSuccess}));
RegisterEndpoint(std::move(endpoint_channel), false);
@@ -285,7 +291,7 @@ TEST_F(EndpointManagerTest, SendControlMessageWorks) {
TEST_F(EndpointManagerTest, SingleReadOnInvalidPayload) {
auto endpoint_channel = std::make_unique<MockEndpointChannel>();
EXPECT_CALL(*endpoint_channel, Read())
EXPECT_CALL(*endpoint_channel, Read(_))
.WillOnce(
Return(ExceptionOr<ByteArray>(Exception::kInvalidProtocolBuffer)));
EXPECT_CALL(*endpoint_channel, Write(_))
@@ -40,10 +40,19 @@ class FakeEndpointChannel : public EndpointChannel {
read_timestamp_ = SystemClock::ElapsedRealtime();
return read_output_;
}
ExceptionOr<ByteArray> Read(PacketMetaData& packet_meta_data) override {
read_timestamp_ = SystemClock::ElapsedRealtime();
return read_output_;
}
Exception Write(const ByteArray& data) override {
write_timestamp_ = SystemClock::ElapsedRealtime();
return write_output_;
}
Exception Write(const ByteArray& data,
PacketMetaData& packet_meta_data) override {
write_timestamp_ = SystemClock::ElapsedRealtime();
return write_output_;
}
void Close() override { is_closed_ = true; }
void Close(proto::connections::DisconnectionReason reason) override {
is_closed_ = true;
+50 -7
View File
@@ -23,16 +23,20 @@
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/time/time.h"
#include "connections/implementation/analytics/throughput_recorder.h"
#include "connections/implementation/internal_payload_factory.h"
#include "internal/platform/count_down_latch.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/single_thread_executor.h"
#include "internal/platform/system_clock.h"
namespace location {
namespace nearby {
namespace connections {
using analytics::PacketMetaData;
using analytics::ThroughputRecorderContainer;
// C++14 requires to declare this.
// TODO(apolyudov): remove when migration to c++17 is possible.
constexpr const absl::Duration PayloadManager::kWaitCloseTimeout;
@@ -46,6 +50,7 @@ bool PayloadManager::SendPayloadLoop(
const EndpointIds& available_endpoint_ids =
EndpointsToEndpointIds(pair.first);
const Endpoints& unavailable_endpoints = pair.second;
PacketMetaData packet_meta_data;
// First, handle any non-available endpoints.
for (const auto& endpoint : unavailable_endpoints) {
@@ -106,8 +111,10 @@ bool PayloadManager::SendPayloadLoop(
// This will block if there is no data to transfer.
// It will resume when new data arrives, or if Close() is called.
int chunk_size = GetOptimalChunkSize(available_endpoint_ids);
packet_meta_data.StartFileIo();
ByteArray next_chunk =
pending_payload.GetInternalPayload()->DetachNextChunk(chunk_size);
packet_meta_data.StopFileIo();
if (shutdown_.Get()) return false;
// Save chunk size. We'll need it after we move next_chunk.
auto next_chunk_size = next_chunk.size();
@@ -130,7 +137,7 @@ bool PayloadManager::SendPayloadLoop(
PayloadTransferFrame::PayloadChunk payload_chunk(CreatePayloadChunk(
next_chunk_offset - resume_offset, std::move(next_chunk)));
const EndpointIds& failed_endpoint_ids = endpoint_manager_->SendPayloadChunk(
payload_header, payload_chunk, available_endpoint_ids);
payload_header, payload_chunk, available_endpoint_ids, packet_meta_data);
// Check whether at least one endpoint failed.
if (!failed_endpoint_ids.empty()) {
NEARBY_LOGS(INFO) << "Payload xfer: endpoints failed: payload_id="
@@ -163,6 +170,9 @@ bool PayloadManager::SendPayloadLoop(
NEARBY_LOGS(INFO) << "Payload xfer done: payload_id="
<< pending_payload.GetInternalPayload()->GetId()
<< "; size=" << next_chunk_offset;
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(pending_payload.GetInternalPayload()->GetId())
->MarkAsSuccess();
return false;
}
}
@@ -305,6 +315,7 @@ void PayloadManager::DisconnectFromEndpointManager() {
PayloadManager::~PayloadManager() {
NEARBY_LOG(INFO, "PayloadManager: going down; self=%p", this);
ThroughputRecorderContainer::GetInstance().Shutdown();
DisconnectFromEndpointManager();
CancelAllPayloads();
NEARBY_LOG(INFO, "PayloadManager: turn down payload executors; self=%p",
@@ -424,11 +435,17 @@ void PayloadManager::SendPayload(ClientProxy* client,
bool should_continue = true;
std::int64_t next_chunk_offset = 0;
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_id)
->Start(payload_type, /*isIncoming=*/false);
while (should_continue && !shutdown_.Get()) {
should_continue =
SendPayloadLoop(client, *pending_payload, payload_header,
next_chunk_offset, resume_offset);
}
ThroughputRecorderContainer::GetInstance().StopTPRecorder(payload_id);
RunOnStatusUpdateThread("destroy-payload",
[this, payload_id]()
RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() {
@@ -468,9 +485,11 @@ Status PayloadManager::CancelPayload(ClientProxy* client,
}
// @EndpointManagerDataPool
void PayloadManager::OnIncomingFrame(
OfflineFrame& offline_frame, const std::string& from_endpoint_id,
ClientProxy* to_client, proto::connections::Medium current_medium) {
void PayloadManager::OnIncomingFrame(OfflineFrame& offline_frame,
const std::string& from_endpoint_id,
ClientProxy* to_client,
proto::connections::Medium current_medium,
PacketMetaData& packet_meta_data) {
PayloadTransferFrame& frame =
*offline_frame.mutable_v1()->mutable_payload_transfer();
@@ -481,7 +500,8 @@ void PayloadManager::OnIncomingFrame(
ProcessControlPacket(to_client, from_endpoint_id, frame);
break;
case PayloadTransferFrame::DATA:
ProcessDataPacket(to_client, from_endpoint_id, frame);
ProcessDataPacket(to_client, from_endpoint_id, frame, current_medium,
packet_meta_data);
break;
default:
NEARBY_LOGS(WARNING)
@@ -805,6 +825,8 @@ void PayloadManager::HandleFinishedIncomingPayload(
ClientProxy* client, const std::string& endpoint_id,
const PayloadTransferFrame::PayloadHeader& payload_header,
std::int64_t offset_bytes, proto::connections::PayloadStatus status) {
ThroughputRecorderContainer::GetInstance().StopTPRecorder(
payload_header.id());
SendClientCallbacksForFinishedIncomingPayload(
client, endpoint_id, payload_header, offset_bytes, status);
@@ -941,7 +963,8 @@ void PayloadManager::HandleSuccessfulIncomingChunk(
// @EndpointManagerDataPool
void PayloadManager::ProcessDataPacket(
ClientProxy* to_client, const std::string& from_endpoint_id,
PayloadTransferFrame& payload_transfer_frame) {
PayloadTransferFrame& payload_transfer_frame, Medium medium,
PacketMetaData& packet_meta_data) {
PayloadTransferFrame::PayloadHeader& payload_header =
*payload_transfer_frame.mutable_payload_header();
PayloadTransferFrame::PayloadChunk& payload_chunk =
@@ -953,6 +976,10 @@ void PayloadManager::ProcessDataPacket(
PendingPayload* pending_payload;
if (payload_chunk.offset() == 0) {
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_header.id())
->Start((PayloadType)payload_header.type(), /*isIncoming=*/true);
packet_meta_data.Reset();
RunOnStatusUpdateThread(
"process-data-packet", [to_client, from_endpoint_id, payload_header,
this]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() {
@@ -1024,6 +1051,7 @@ void PayloadManager::ProcessDataPacket(
// Save size of packet before we move it.
std::int64_t payload_body_size = payload_chunk.body().size();
packet_meta_data.StartFileIo();
if (pending_payload->GetInternalPayload()
->AttachNextChunk(ByteArray(std::move(*payload_chunk.mutable_body())))
.Raised()) {
@@ -1035,10 +1063,25 @@ void PayloadManager::ProcessDataPacket(
proto::connections::PayloadStatus::LOCAL_ERROR);
return;
}
packet_meta_data.StopFileIo();
HandleSuccessfulIncomingChunk(to_client, from_endpoint_id, payload_header,
payload_chunk.flags(), payload_chunk.offset(),
payload_body_size);
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_header.id())
->OnFrameReceived(medium, packet_meta_data);
bool is_last_chunk = (payload_chunk.flags() &
PayloadTransferFrame::PayloadChunk::LAST_CHUNK) != 0;
if (is_last_chunk) {
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_header.id())
->MarkAsSuccess();
ThroughputRecorderContainer::GetInstance().StopTPRecorder(
payload_header.id());
}
}
// @EndpointManagerDataPool
+8 -2
View File
@@ -16,8 +16,10 @@
#define CORE_INTERNAL_PAYLOAD_MANAGER_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
@@ -37,6 +39,8 @@ namespace location {
namespace nearby {
namespace connections {
using analytics::PacketMetaData;
// Annotations for methods that need to run on PayloadStatusUpdateThread.
// Use only in PayloadManager
#define RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() \
@@ -59,7 +63,8 @@ class PayloadManager : public EndpointManager::FrameProcessor {
void OnIncomingFrame(OfflineFrame& offline_frame,
const std::string& from_endpoint_id,
ClientProxy* to_client,
proto::connections::Medium current_medium) override;
proto::connections::Medium current_medium,
PacketMetaData& packet_meta_data) override;
// @EndpointManagerThread
void OnEndpointDisconnect(ClientProxy* client, const std::string& service_id,
@@ -268,7 +273,8 @@ class PayloadManager : public EndpointManager::FrameProcessor {
void ProcessDataPacket(ClientProxy* to_client,
const std::string& from_endpoint_id,
PayloadTransferFrame& payload_transfer_frame);
PayloadTransferFrame& payload_transfer_frame,
Medium medium, PacketMetaData& packet_meta_data);
void ProcessControlPacket(ClientProxy* to_client,
const std::string& from_endpoint_id,
PayloadTransferFrame& payload_transfer_frame);