Fix tsan error.

PiperOrigin-RevId: 900978318
This commit is contained in:
Edwin Wu
2026-04-16 17:03:53 -07:00
committed by Copybara-Service
parent dacbd03aa1
commit b533dd834c
7 changed files with 428 additions and 364 deletions
+1 -1
View File
@@ -43,9 +43,9 @@ cc_library(
"//proto:connections_enums_cc_proto",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:no_destructor",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/meta:type_traits",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
@@ -19,7 +19,6 @@
#include "absl/time/time.h"
#include "internal/platform/implementation/system_clock.h"
#include "internal/platform/system_clock.h"
namespace nearby {
namespace analytics {
@@ -35,7 +34,7 @@ struct PacketMetaData {
void Reset() {
file_io_start_time = SystemClock::ElapsedRealtime();
socket_io_start_time = SystemClock::ElapsedRealtime();
encryption_start_time = SystemClock::ElapsedRealtime();
socket_io_start_time = SystemClock::ElapsedRealtime();
packet_size = 0;
}
@@ -44,7 +43,7 @@ struct PacketMetaData {
this->packet_size = packet_size;
}
int GetPacketSize() {
int GetPacketSize() const {
return packet_size;
}
@@ -72,27 +71,27 @@ struct PacketMetaData {
socket_io_end_time = SystemClock::ElapsedRealtime();
}
int64_t GetEncryptionTimeInMillis() {
int64_t GetEncryptionTimeInMillis() const {
if (encryption_end_time > encryption_start_time) {
return absl::ToInt64Milliseconds(encryption_end_time -
encryption_start_time);
}
return 0L;
return 0;
}
int64_t GetFileIoTimeInMillis() {
int64_t GetFileIoTimeInMillis() const {
if (file_io_end_time > file_io_start_time) {
return absl::ToInt64Milliseconds(file_io_end_time - file_io_start_time);
}
return 0L;
return 0;
}
int64_t GetSocketIoTimeInMillis() {
int64_t GetSocketIoTimeInMillis() const {
if (socket_io_end_time > socket_io_start_time) {
return absl::ToInt64Milliseconds(socket_io_end_time -
socket_io_start_time);
}
return 0L;
return 0;
}
};
@@ -14,18 +14,17 @@
#include "connections/implementation/analytics/throughput_recorder.h"
#include <stdint.h>
#include <new>
#include <ostream>
#include <cstdint>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/base/no_destructor.h"
#include "absl/container/flat_hash_map.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/str_format.h"
#include "absl/time/time.h"
#include "connections/implementation/analytics/packet_meta_data.h"
#include "connections/payload_type.h"
#include "internal/platform/implementation/system_clock.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
@@ -33,61 +32,73 @@
namespace nearby {
namespace analytics {
using Medium = ::location::nearby::proto::connections::Medium;
using ::nearby::connections::PayloadDirection;
using ::nearby::connections::PayloadType;
namespace {
constexpr int kDefaultThroughoutKbps = 0;
constexpr int kKbInBytes = 1024;
constexpr int kSecInMs = 1000;
int64_t CalculateThroughputKBps(int64_t total_byte_size, int64_t total_millis) {
if (total_millis > 0) {
return total_byte_size * kSecInMs / kKbInBytes / total_millis;
}
return kDefaultThroughoutKbps;
}
int64_t CalculateThroughputMBps(int64_t throughputKBps) {
return throughputKBps / kKbInBytes;
}
std::string 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");
}
}
} // namespace
ThroughputRecorder::ThroughputRecorder(int64_t payload_id)
: payload_id_(payload_id) {}
ThroughputRecorderContainer& ThroughputRecorderContainer::GetInstance() {
alignas(ThroughputRecorderContainer) static char
storage[sizeof(ThroughputRecorderContainer)];
static ThroughputRecorderContainer* env =
new (&storage) ThroughputRecorderContainer();
return *env;
static absl::NoDestructor<ThroughputRecorderContainer> instance;
return *instance;
}
void ThroughputRecorder::Start(PayloadType payload_type,
PayloadDirection payload_direction) {
std::string direction =
(payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "; Receive"
: "; Send";
ThroughputRecorderContainer::ThroughputRecorder::ThroughputRecorder(
int64_t payload_id, PayloadDirection payload_direction,
PayloadType payload_type)
: payload_id_(payload_id),
payload_direction_(payload_direction),
payload_type_(payload_type) {
LOG_IF(DFATAL, payload_type_ == PayloadType::kUnknown)
<< "Invalid payload type";
}
VLOG(1) << "Start TP profiling for payload_id:" << payload_id_ << direction;
if (payload_type == PayloadType::kUnknown) {
VLOG(1) << "Ignore ThroughputRecorder::start for Unknown Payload type";
return;
void ThroughputRecorderContainer::ThroughputRecorder::Start() {
if (VLOG_IS_ON(1)) {
std::string direction =
(payload_direction_ == PayloadDirection::INCOMING_PAYLOAD) ? "; Receive"
: "; Send";
VLOG(1) << "Start TP profiling for payload_id:" << payload_id_ << direction;
}
MutexLock lock(&mutex_);
start_timestamp_ = SystemClock::ElapsedRealtime();
payload_type_ = payload_type;
payload_direction_ = payload_direction;
// Add packetLostAlarm later
}
bool ThroughputRecorder::Stop() {
MutexLock lock(&mutex_);
bool ThroughputRecorderContainer::ThroughputRecorder::Stop() {
VLOG(1) << "Stop TP profiling for payload_id:" << payload_id_;
if (payload_type_ == PayloadType::kUnknown) {
VLOG(1) << "Ignore ThroughputRecorder::stop as it never start";
return false;
}
{
// 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_) {
@@ -96,9 +107,8 @@ bool ThroughputRecorder::Stop() {
}
}
// calculate throughput by medium
for (auto& tp : throughputs_) {
tp.second.dump();
tp.second.dump(payload_direction_, payload_type_);
total_byte_size += tp.second.GetTotalByteSize();
}
@@ -107,17 +117,16 @@ bool ThroughputRecorder::Stop() {
int64_t total_millis =
absl::ToInt64Milliseconds(stop_timestamp - start_timestamp_);
throughput_kbps_ = CalculateThroughputKBps(total_byte_size, total_millis);
int throughput_mbps = CalculateThroughputMBps(throughput_kbps_);
int64_t 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, "
"%s %s data(%lld bytes) %s, overall used %lld milliseconds, "
"throughput "
"is %d MB/s (%d KB/s), File IO takes %d ms, %s takes %d "
"is %lld MB/s (%lld KB/s), File IO takes %lld ms, %s takes %lld "
"ms, "
"Socket IO takes %d ms",
"Socket IO takes %lld ms",
(payload_direction_ == PayloadDirection::INCOMING_PAYLOAD)
? "Received"
: "Sent",
@@ -135,93 +144,79 @@ bool ThroughputRecorder::Stop() {
return true;
}
void ThroughputRecorder::MarkAsSuccess() {
MutexLock lock(&mutex_);
void ThroughputRecorderContainer::ThroughputRecorder::MarkAsSuccess() {
success_ = 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) {
void ThroughputRecorderContainer::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() {
bool ThroughputRecorderContainer::ThroughputRecorder::Throughput::dump(
PayloadDirection payload_direction, PayloadType payload_type) {
int64_t total_millis =
absl::ToInt64Milliseconds(last_timestamp_ - start_timestamp_);
int throughput_kbps = CalculateThroughputKBps(total_byte_size_, total_millis);
int64_t throughput_kbps =
CalculateThroughputKBps(total_byte_size_, total_millis);
if (throughput_kbps == kDefaultThroughoutKbps) {
return false;
}
int throughpu_mbps = CalculateThroughputMBps(throughput_kbps);
int64_t throughput_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",
(payload_direction_ == PayloadDirection::INCOMING_PAYLOAD) ? "Received"
: "Sent",
ToString(payload_type_), total_byte_size_,
"%s %s data(%lld bytes) via %s used %lld ms, throughput is %lld "
"MB/s (%lld KB/s), File IO takes %lld ms, %s takes %lld ms, "
"Socket IO takes %lld ms, "
"Other takes %lld ms",
(payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "Received"
: "Sent",
ToString(payload_type), total_byte_size_,
location::nearby::proto::connections::Medium_Name(medium_), total_millis,
throughpu_mbps, throughput_kbps, file_io_time_,
(payload_direction_ == PayloadDirection::INCOMING_PAYLOAD) ? "Decryption"
: "Encryption",
throughput_mbps, throughput_kbps, file_io_time_,
(payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "Decryption"
: "Encryption",
encryption_time_, socket_io_time_, other);
LOG(INFO) << dump_content;
return true;
}
ThroughputRecorder::Throughput& ThroughputRecorder::GetThroughput(
ThroughputRecorderContainer::ThroughputRecorder::Throughput&
ThroughputRecorderContainer::ThroughputRecorder::GetThroughput(
Medium medium, int64_t duration_millis) {
auto it = throughputs_.find(medium);
if (it == throughputs_.end()) {
auto throughput = new Throughput(
medium,
SystemClock::ElapsedRealtime() - absl::Milliseconds(duration_millis),
payload_type_, payload_direction_);
throughputs_.emplace(medium, std::move(*throughput));
delete throughput;
throughputs_.emplace(
medium, Throughput(medium, SystemClock::ElapsedRealtime() -
absl::Milliseconds(duration_millis)));
return throughputs_.find(medium)->second;
}
return it->second;
}
int ThroughputRecorder::GetThroughputsSize() {
MutexLock lock(&mutex_);
int ThroughputRecorderContainer::ThroughputRecorder::GetThroughputsSize()
const {
return throughputs_.size();
}
int ThroughputRecorder::GetThroughputKbps() { return throughput_kbps_; }
int64_t ThroughputRecorderContainer::ThroughputRecorder::GetThroughputKbps()
const {
return throughput_kbps_;
}
int64_t ThroughputRecorder::GetDurationMillis() { return duration_millis_; }
void ThroughputRecorder::OnFrameSent(Medium medium,
PacketMetaData& packetMetaData) {
MutexLock lock(&mutex_);
if (payload_type_ == PayloadType::kUnknown) {
VLOG(1) << "PayloadType is invalid, return";
return;
}
int64_t ThroughputRecorderContainer::ThroughputRecorder::GetDurationMillis()
const {
return duration_millis_;
}
void ThroughputRecorderContainer::ThroughputRecorder::UpdateFrameData(
Medium medium, PacketMetaData& packetMetaData) {
duration_millis_ = packetMetaData.GetEncryptionTimeInMillis() +
packetMetaData.GetFileIoTimeInMillis() +
packetMetaData.GetSocketIoTimeInMillis();
@@ -232,96 +227,69 @@ void ThroughputRecorder::OnFrameSent(Medium medium,
CalculateDurationTimes(packetMetaData);
}
void ThroughputRecorder::OnFrameReceived(Medium medium,
PacketMetaData& packetMetaData) {
MutexLock lock(&mutex_);
if (payload_type_ == PayloadType::kUnknown) {
VLOG(1) << "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) {
void ThroughputRecorderContainer::ThroughputRecorder::CalculateDurationTimes(
const 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");
// Implementation for ThroughputRecorderContainer
void ThroughputRecorderContainer::Start(int64_t payload_id,
PayloadDirection payload_direction,
PayloadType payload_type) {
if (payload_type == PayloadType::kUnknown) {
return;
}
}
// Inplementation for ThroughputRecorderContainer
void ThroughputRecorderContainer::Shutdown() {
MutexLock lock(&mutex_);
VLOG(1) << __func__ << ". Num of Instance:" << throughput_recorders_.size();
for (auto& throughput_recorder : throughput_recorders_) {
VLOG(1) << "Stop instance: " << throughput_recorder.second;
throughput_recorder.second->Stop();
delete throughput_recorder.second;
}
throughput_recorders_.clear();
}
ThroughputRecorder* ThroughputRecorderContainer::GetTPRecorder(
const int64_t payload_id, PayloadDirection payload_direction) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it == throughput_recorders_.end()) {
auto instance = new ThroughputRecorder(payload_id);
std::string direction =
(payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "; Receive"
: "; Send";
VLOG(1) << "Add ThroughputRecorder instance : " << instance
<< " for payload_id:" << payload_id << direction;
auto instance = std::make_unique<ThroughputRecorder>(
payload_id, payload_direction, payload_type);
instance->Start();
throughput_recorders_.emplace(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction),
instance);
return instance;
std::move(instance));
} else {
it->second->Start();
}
return it->second;
}
void ThroughputRecorderContainer::StopTPRecorder(
const int64_t payload_id, PayloadDirection payload_direction) {
void ThroughputRecorderContainer::UpdateFrameData(
int64_t payload_id, PayloadDirection payload_direction, Medium medium,
PacketMetaData& packet_meta_data) {
MutexLock lock(&mutex_);
std::string direction =
(payload_direction == PayloadDirection::INCOMING_PAYLOAD) ? "; Receive"
: "; Send";
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
VLOG(1) << "Found and stop/delete ThroughputRecorder instance : "
<< &(it->second) << " for payload_id:" << payload_id << direction;
it->second->Stop();
delete it->second;
throughput_recorders_.erase(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
return;
it->second->UpdateFrameData(medium, packet_meta_data);
}
VLOG(1) << "No ThroughputRecorder found for :" << payload_id;
}
void ThroughputRecorderContainer::MarkAsSuccess(
int64_t payload_id, PayloadDirection payload_direction) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
it->second->MarkAsSuccess();
}
}
int64_t ThroughputRecorderContainer::StopTPRecorder(
int64_t payload_id, PayloadDirection payload_direction) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
it->second->Stop();
int64_t throughput_kbps = it->second->GetThroughputKbps();
throughput_recorders_.erase(it);
return throughput_kbps;
}
return 0;
}
int ThroughputRecorderContainer::GetSize() {
@@ -329,5 +297,66 @@ int ThroughputRecorderContainer::GetSize() {
return throughput_recorders_.size();
}
void ThroughputRecorderContainer::ClearForTest() {
MutexLock lock(&mutex_);
throughput_recorders_.clear();
}
int64_t ThroughputRecorderContainer::GetTotalByteSizeForTesting(
int64_t payload_id, PayloadDirection payload_direction, Medium medium) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
return it->second->GetThroughput(medium, 0).GetTotalByteSize();
}
return 0;
}
int ThroughputRecorderContainer::GetThroughputsSizeForTesting(
int64_t payload_id, PayloadDirection payload_direction) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
return it->second->GetThroughputsSize();
}
return 0;
}
int64_t ThroughputRecorderContainer::GetDurationMillisForTesting(
int64_t payload_id, PayloadDirection payload_direction) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
return it->second->GetDurationMillis();
}
return 0;
}
int64_t ThroughputRecorderContainer::GetThroughputKbpsForTesting(
int64_t payload_id, PayloadDirection payload_direction) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
return it->second->GetThroughputKbps();
}
return 0;
}
bool ThroughputRecorderContainer::DumpForTesting(
int64_t payload_id, PayloadDirection payload_direction, Medium medium) {
MutexLock lock(&mutex_);
auto it = throughput_recorders_.find(
std::pair<int64_t, PayloadDirection>(payload_id, payload_direction));
if (it != throughput_recorders_.end()) {
return it->second->GetThroughput(medium, 0).dump(
payload_direction, it->second->GetPayloadType());
}
return false;
}
} // namespace analytics
} // namespace nearby
@@ -16,125 +16,159 @@
#define NEARBY_CONNECTIONS_IMPLEMENTATION_ANALYTICS_THROUGHPUT_RECORDER_H_
#include <cstdint>
#include <string>
#include <memory>
#include <utility>
#include "absl/base/no_destructor.h"
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/time/time.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 nearby {
namespace analytics {
// The following aliases are only for users' convenience.
using ::location::nearby::proto::connections::Medium;
using ::nearby::connections::PayloadType;
// Enum to represent if a payload is incoming or outgoing.
using ::nearby::connections::PayloadDirection;
class ThroughputRecorder {
// Container class to manage ThroughputRecorder instances.
// This class is a singleton and provides thread-safe proxy methods to record
// throughput for different payloads.
class ThroughputRecorderContainer {
public:
explicit ThroughputRecorder(int64_t payload_id);
~ThroughputRecorder() = default;
static ThroughputRecorderContainer& GetInstance();
void Start(PayloadType payload_type, PayloadDirection payload_direction);
bool Stop() ABSL_LOCKS_EXCLUDED(mutex_);
static int CalculateThroughputKBps(int64_t total_byte_size,
int64_t total_millis);
static int CalculateThroughputMBps(int throughputKBps);
// Records the start of a payload transfer.
void Start(int64_t payload_id,
connections::PayloadDirection payload_direction,
connections::PayloadType payload_type) ABSL_LOCKS_EXCLUDED(mutex_);
class Throughput {
// Records when a frame is sent or received.
void UpdateFrameData(int64_t payload_id,
connections::PayloadDirection payload_direction,
location::nearby::proto::connections::Medium medium,
PacketMetaData& packet_meta_data)
ABSL_LOCKS_EXCLUDED(mutex_);
// Marks a payload transfer as successful.
void MarkAsSuccess(int64_t payload_id,
connections::PayloadDirection payload_direction)
ABSL_LOCKS_EXCLUDED(mutex_);
// Stops and removes the throughput recorder for a given payload.
// This calculates and logs the final throughput statistics.
// Returns the throughput in KBps.
int64_t StopTPRecorder(int64_t payload_id,
connections::PayloadDirection payload_direction)
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns the number of active recorder instances.
int GetSize() ABSL_LOCKS_EXCLUDED(mutex_);
// Clear all recorders. Used for testing.
void ClearForTest() ABSL_LOCKS_EXCLUDED(mutex_);
// Testing proxy methods
int64_t GetTotalByteSizeForTesting(
int64_t payload_id, connections::PayloadDirection payload_direction,
location::nearby::proto::connections::Medium medium)
ABSL_LOCKS_EXCLUDED(mutex_);
int GetThroughputsSizeForTesting(
int64_t payload_id, connections::PayloadDirection payload_direction)
ABSL_LOCKS_EXCLUDED(mutex_);
int64_t GetDurationMillisForTesting(
int64_t payload_id, connections::PayloadDirection payload_direction)
ABSL_LOCKS_EXCLUDED(mutex_);
int64_t GetThroughputKbpsForTesting(
int64_t payload_id, connections::PayloadDirection payload_direction)
ABSL_LOCKS_EXCLUDED(mutex_);
bool DumpForTesting(int64_t payload_id,
connections::PayloadDirection payload_direction,
location::nearby::proto::connections::Medium medium)
ABSL_LOCKS_EXCLUDED(mutex_);
private:
friend class absl::NoDestructor<ThroughputRecorderContainer>;
class ThroughputRecorder {
public:
Throughput() = default;
~Throughput() = default;
Throughput(Medium medium, absl::Time start_timestamp,
PayloadType payload_type, PayloadDirection payload_direction)
: medium_(medium),
start_timestamp_(start_timestamp),
payload_type_(payload_type),
payload_direction_(payload_direction) {}
ThroughputRecorder(int64_t payload_id,
connections::PayloadDirection payload_direction,
connections::PayloadType payload_type);
~ThroughputRecorder() = default;
void Add(int frame_size, int64_t file_io_time, int64_t encryption_time,
int64_t socket_io_time);
void Start();
bool Stop() ABSL_LOCKS_EXCLUDED(mutex_);
void SetLastTimestamp(absl::Time time_stamp) {
last_timestamp_ = time_stamp;
}
class Throughput {
public:
Throughput() = default;
~Throughput() = default;
Throughput(location::nearby::proto::connections::Medium medium,
absl::Time start_timestamp)
: medium_(medium), start_timestamp_(start_timestamp) {}
int64_t GetTotalByteSize() { return total_byte_size_; }
void Add(int frame_size, int64_t file_io_time, int64_t encryption_time,
int64_t socket_io_time);
bool dump();
void SetLastTimestamp(absl::Time time_stamp) {
last_timestamp_ = time_stamp;
}
int64_t GetTotalByteSize() const { return total_byte_size_; }
bool dump(connections::PayloadDirection payload_direction,
connections::PayloadType payload_type);
private:
const ::location::nearby::proto::connections::Medium medium_;
const absl::Time start_timestamp_;
int64_t total_byte_size_ = 0;
absl::Time last_timestamp_;
int64_t file_io_time_ = 0;
int64_t encryption_time_ = 0;
int64_t socket_io_time_ = 0;
};
Throughput& GetThroughput(
location::nearby::proto::connections::Medium medium,
int64_t duration_millis);
int GetThroughputsSize() const;
int64_t GetThroughputKbps() const;
int64_t GetDurationMillis() const;
void UpdateFrameData(location::nearby::proto::connections::Medium medium,
PacketMetaData& packetMetaData);
void MarkAsSuccess();
connections::PayloadType GetPayloadType() const { return payload_type_; }
private:
Medium medium_;
void CalculateDurationTimes(const PacketMetaData& packetMetaData);
const int64_t payload_id_;
const connections::PayloadDirection payload_direction_;
const connections::PayloadType payload_type_;
absl::Time start_timestamp_;
PayloadType payload_type_;
int64_t total_byte_size_ = 0;
absl::Time last_timestamp_;
PayloadDirection payload_direction_ = PayloadDirection::INCOMING_PAYLOAD;
absl::flat_hash_map<location::nearby::proto::connections::Medium,
Throughput>
throughputs_;
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;
int64_t throughput_kbps_ = 0;
};
Throughput& GetThroughput(Medium medium, int64_t duration_millis);
int GetThroughputsSize();
int GetThroughputKbps();
int64_t GetDurationMillis();
void OnFrameSent(Medium medium, PacketMetaData& packetMetaData);
void OnFrameReceived(Medium medium, PacketMetaData& packetMetaData);
void MarkAsSuccess();
private:
void CalculateDurationTimes(PacketMetaData packetMetaData);
static std::string ToString(PayloadType type);
Mutex mutex_;
int64_t payload_id_ = 0;
absl::Time start_timestamp_;
PayloadType payload_type_ = PayloadType::kUnknown;
PayloadDirection payload_direction_ = PayloadDirection::INCOMING_PAYLOAD;
absl::flat_hash_map<Medium, Throughput> throughputs_;
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_ = 0;
};
class ThroughputRecorderContainer {
public:
ThroughputRecorderContainer() = default;
ThroughputRecorderContainer(const ThroughputRecorderContainer&) = delete;
ThroughputRecorderContainer& operator=(const ThroughputRecorderContainer&) =
delete;
static ThroughputRecorderContainer& GetInstance();
void Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
ThroughputRecorder* GetTPRecorder(int64_t payload_id,
PayloadDirection payload_direction)
ABSL_LOCKS_EXCLUDED(mutex_);
void StopTPRecorder(int64_t payload_id, PayloadDirection payload_direction)
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_;
// std::pair<int64_t, PayloadDirection> for <payload id, payload direction>
absl::flat_hash_map<std::pair<int64_t, PayloadDirection>, ThroughputRecorder*>
absl::flat_hash_map<std::pair<int64_t, connections::PayloadDirection>,
std::unique_ptr<ThroughputRecorder>>
throughput_recorders_ ABSL_GUARDED_BY(mutex_);
};
@@ -16,35 +16,29 @@
#include <stdint.h>
#include <ostream>
#include <string>
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "connections/implementation/analytics/packet_meta_data.h"
#include "connections/payload_type.h"
#include "internal/platform/logging.h"
#include "proto/connections_enums.pb.h"
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::GetInstance().ClearForTest();
}
ThroughputRecorderContainer& tp_recorder_container_ =
@@ -54,70 +48,71 @@ class ThroughputRecorderTest : public testing::TestWithParam<bool> {
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,
PayloadDirection::OUTGOING_PAYLOAD);
TPRecorderContainer.GetTPRecorder(kPayloadIdB,
PayloadDirection::INCOMING_PAYLOAD);
TPRecorderContainer.Start(kPayloadIdA,
connections::PayloadDirection::OUTGOING_PAYLOAD,
connections::PayloadType::kFile);
TPRecorderContainer.Start(kPayloadIdB,
connections::PayloadDirection::INCOMING_PAYLOAD,
connections::PayloadType::kFile);
EXPECT_EQ(ThroughputRecorderContainer::GetInstance().GetSize(), 2);
ThroughputRecorderContainer::GetInstance().Shutdown();
ThroughputRecorderContainer::GetInstance().ClearForTest();
EXPECT_EQ(ThroughputRecorderContainer::GetInstance().GetSize(), 0);
}
TEST_F(ThroughputRecorderTest, OnFrameSentSaveTransferredSize) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(
kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD);
TPRecorder->Start(PayloadType::kFile, PayloadDirection::OUTGOING_PAYLOAD);
tp_recorder_container_.Start(kPayloadIdA,
connections::PayloadDirection::OUTGOING_PAYLOAD,
connections::PayloadType::kFile);
PacketMetaData packet_meta_data;
packet_meta_data.SetPacketSize(kFrameSize);
TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE,
packet_meta_data);
TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE,
packet_meta_data);
TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE,
packet_meta_data);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
auto throughput =
TPRecorder->GetThroughput(location::nearby::proto::connections::BLE, 0);
EXPECT_EQ(throughput.GetTotalByteSize(), kFrameSize * 3);
EXPECT_EQ(tp_recorder_container_.GetTotalByteSizeForTesting(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE),
kFrameSize * 3);
}
TEST_F(ThroughputRecorderTest, OnIgnoreUnkownPaylaodType) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(
kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD);
TPRecorder->Start(PayloadType::kUnknown, PayloadDirection::OUTGOING_PAYLOAD);
tp_recorder_container_.Start(kPayloadIdA,
connections::PayloadDirection::OUTGOING_PAYLOAD,
connections::PayloadType::kUnknown);
PacketMetaData packet_meta_data;
TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE,
packet_meta_data);
EXPECT_EQ(TPRecorder->GetThroughputsSize(), 0);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
EXPECT_EQ(tp_recorder_container_.GetThroughputsSizeForTesting(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD),
0);
TPRecorder->Start(PayloadType::kUnknown, PayloadDirection::INCOMING_PAYLOAD);
TPRecorder->OnFrameReceived(location::nearby::proto::connections::BLE,
packet_meta_data);
EXPECT_EQ(TPRecorder->GetThroughputsSize(), 0);
tp_recorder_container_.Start(kPayloadIdA,
connections::PayloadDirection::INCOMING_PAYLOAD,
connections::PayloadType::kUnknown);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::INCOMING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
EXPECT_EQ(tp_recorder_container_.GetThroughputsSizeForTesting(
kPayloadIdA, connections::PayloadDirection::INCOMING_PAYLOAD),
0);
}
TEST_P(ThroughputRecorderTest, OnFrameSentStopAndDump) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(
kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD);
TPRecorder->Start(PayloadType::kFile, PayloadDirection::OUTGOING_PAYLOAD);
tp_recorder_container_.Start(kPayloadIdA,
connections::PayloadDirection::OUTGOING_PAYLOAD,
connections::PayloadType::kFile);
PacketMetaData packet_meta_data;
packet_meta_data.SetPacketSize(kFrameSize);
@@ -130,9 +125,11 @@ TEST_P(ThroughputRecorderTest, OnFrameSentStopAndDump) {
packet_meta_data.StartSocketIo();
absl::SleepFor(absl::Milliseconds(7));
packet_meta_data.StopSocketIo();
TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE,
packet_meta_data);
EXPECT_EQ(TPRecorder->GetDurationMillis(),
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
EXPECT_EQ(tp_recorder_container_.GetDurationMillisForTesting(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD),
packet_meta_data.GetEncryptionTimeInMillis() +
packet_meta_data.GetFileIoTimeInMillis() +
packet_meta_data.GetSocketIoTimeInMillis());
@@ -147,21 +144,25 @@ TEST_P(ThroughputRecorderTest, OnFrameSentStopAndDump) {
packet_meta_data.StartSocketIo();
absl::SleepFor(absl::Milliseconds(17));
packet_meta_data.StopSocketIo();
TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE,
packet_meta_data);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
if (GetParam() == true) {
LOG(INFO) << "MarkAsSuccess";
TPRecorder->MarkAsSuccess();
tp_recorder_container_.MarkAsSuccess(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD);
}
EXPECT_TRUE(TPRecorder->Stop());
EXPECT_NE(TPRecorder->GetThroughputKbps(), 0);
int throughput_kbps = tp_recorder_container_.StopTPRecorder(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD);
EXPECT_NE(throughput_kbps, 0);
EXPECT_EQ(tp_recorder_container_.GetSize(), 0);
}
TEST_F(ThroughputRecorderTest, OnFrameSentStopAndDumpForMultiMeadium) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(
kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD);
TPRecorder->Start(PayloadType::kFile, PayloadDirection::OUTGOING_PAYLOAD);
tp_recorder_container_.Start(kPayloadIdA,
connections::PayloadDirection::OUTGOING_PAYLOAD,
connections::PayloadType::kFile);
PacketMetaData packet_meta_data1;
packet_meta_data1.SetPacketSize(kFrameSize);
@@ -174,8 +175,9 @@ TEST_F(ThroughputRecorderTest, OnFrameSentStopAndDumpForMultiMeadium) {
packet_meta_data1.StartSocketIo();
absl::SleepFor(absl::Milliseconds(7));
packet_meta_data1.StopSocketIo();
TPRecorder->OnFrameSent(location::nearby::proto::connections::BLE,
packet_meta_data1);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data1);
PacketMetaData packet_meta_data2;
packet_meta_data2.SetPacketSize(kFrameSize);
@@ -188,18 +190,21 @@ TEST_F(ThroughputRecorderTest, OnFrameSentStopAndDumpForMultiMeadium) {
packet_meta_data2.StartSocketIo();
absl::SleepFor(absl::Milliseconds(17));
packet_meta_data2.StopSocketIo();
TPRecorder->OnFrameSent(location::nearby::proto::connections::WIFI_LAN,
packet_meta_data2);
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::WIFI_LAN, packet_meta_data2);
TPRecorder->MarkAsSuccess();
EXPECT_TRUE(TPRecorder->Stop());
EXPECT_NE(TPRecorder->GetThroughputKbps(), 0);
tp_recorder_container_.MarkAsSuccess(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD);
int throughput_kbps = tp_recorder_container_.StopTPRecorder(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD);
EXPECT_NE(throughput_kbps, 0);
}
TEST_F(ThroughputRecorderTest, OnFrameReceivedCheckDurationMillis) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(
kPayloadIdA, PayloadDirection::INCOMING_PAYLOAD);
TPRecorder->Start(PayloadType::kFile, PayloadDirection::INCOMING_PAYLOAD);
tp_recorder_container_.Start(kPayloadIdA,
connections::PayloadDirection::INCOMING_PAYLOAD,
connections::PayloadType::kFile);
PacketMetaData packet_meta_data;
packet_meta_data.SetPacketSize(kFrameSize);
@@ -212,20 +217,23 @@ TEST_F(ThroughputRecorderTest, OnFrameReceivedCheckDurationMillis) {
packet_meta_data.StartSocketIo();
absl::SleepFor(absl::Milliseconds(7));
packet_meta_data.StopSocketIo();
TPRecorder->OnFrameReceived(location::nearby::proto::connections::BLE,
packet_meta_data);
EXPECT_EQ(TPRecorder->GetDurationMillis(),
tp_recorder_container_.UpdateFrameData(
kPayloadIdA, connections::PayloadDirection::INCOMING_PAYLOAD,
location::nearby::proto::connections::BLE, packet_meta_data);
EXPECT_EQ(tp_recorder_container_.GetDurationMillisForTesting(
kPayloadIdA, connections::PayloadDirection::INCOMING_PAYLOAD),
packet_meta_data.GetEncryptionTimeInMillis() +
packet_meta_data.GetFileIoTimeInMillis() +
packet_meta_data.GetSocketIoTimeInMillis());
}
TEST_F(ThroughputRecorderTest, OnTPRecorderNotStarted) {
auto TPRecorder = tp_recorder_container_.GetTPRecorder(
kPayloadIdA, PayloadDirection::OUTGOING_PAYLOAD);
auto throughput =
TPRecorder->GetThroughput(location::nearby::proto::connections::BLE, 0);
EXPECT_FALSE(throughput.dump());
tp_recorder_container_.Start(kPayloadIdA,
connections::PayloadDirection::OUTGOING_PAYLOAD,
connections::PayloadType::kUnknown);
EXPECT_FALSE(tp_recorder_container_.DumpForTesting(
kPayloadIdA, connections::PayloadDirection::OUTGOING_PAYLOAD,
location::nearby::proto::connections::BLE));
}
} // namespace
@@ -457,7 +457,6 @@ EndpointManager::~EndpointManager() {
MutexLock lock(&mutex_);
is_shutdown_ = true;
}
analytics::ThroughputRecorderContainer::GetInstance().Shutdown();
CountDownLatch latch(1);
RunOnEndpointManagerThread("bring-down-endpoints", [this, &latch]() {
LOG(INFO) << "Bringing down endpoints";
@@ -961,9 +960,9 @@ std::vector<std::string> EndpointManager::SendTransferFrameBytes(
LOG(INFO) << "Failed to send packet; endpoint_id=" << endpoint_id;
continue;
}
analytics::ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_id, PayloadDirection::OUTGOING_PAYLOAD)
->OnFrameSent(channel->GetMedium(), packet_meta_data);
analytics::ThroughputRecorderContainer::GetInstance().UpdateFrameData(
payload_id, PayloadDirection::OUTGOING_PAYLOAD, channel->GetMedium(),
packet_meta_data);
}
return failed_endpoint_ids;
+13 -18
View File
@@ -209,10 +209,9 @@ bool PayloadManager::SendPayloadLoop(
VLOG(1) << "Payload xfer done: payload_id="
<< pending_payload.GetInternalPayload()->GetId()
<< "; size=" << next_chunk_offset;
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(pending_payload.GetInternalPayload()->GetId(),
PayloadDirection::OUTGOING_PAYLOAD)
->MarkAsSuccess();
ThroughputRecorderContainer::GetInstance().MarkAsSuccess(
pending_payload.GetInternalPayload()->GetId(),
PayloadDirection::OUTGOING_PAYLOAD);
return false;
}
}
@@ -364,7 +363,6 @@ void PayloadManager::DisconnectFromEndpointManager() {
PayloadManager::~PayloadManager() {
VLOG(1) << "PayloadManager: going down; self=" << this;
ThroughputRecorderContainer::GetInstance().Shutdown();
DisconnectFromEndpointManager();
CancelAllPayloads();
VLOG(1) << "PayloadManager: turn down payload executors; self=" << this;
@@ -481,9 +479,8 @@ void PayloadManager::SendPayload(ClientProxy* client,
std::int64_t next_chunk_offset = 0;
int index = 0;
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_id, PayloadDirection::OUTGOING_PAYLOAD)
->Start(payload_type, PayloadDirection::OUTGOING_PAYLOAD);
ThroughputRecorderContainer::GetInstance().Start(
payload_id, PayloadDirection::OUTGOING_PAYLOAD, payload_type);
while (should_continue && !shutdown_.Get()) {
should_continue =
SendPayloadLoop(client, *pending_payload, payload_header,
@@ -1326,10 +1323,9 @@ void PayloadManager::ProcessDataPacket(
Payload::Id payload_id = payload_header.id();
PendingPayloadHandle pending_payload;
if (payload_chunk.offset() == 0) {
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_id, PayloadDirection::INCOMING_PAYLOAD)
->Start((PayloadType)payload_header.type(),
PayloadDirection::INCOMING_PAYLOAD);
ThroughputRecorderContainer::GetInstance().Start(
payload_id, PayloadDirection::INCOMING_PAYLOAD,
(PayloadType)payload_header.type());
packet_meta_data.Reset();
RunOnStatusUpdateThread(
"process-data-packet", [to_client, from_endpoint_id, payload_header,
@@ -1439,13 +1435,12 @@ void PayloadManager::ProcessDataPacket(
payload_chunk.flags(), payload_chunk.offset(),
payload_body_size);
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD)
->OnFrameReceived(medium, packet_meta_data);
ThroughputRecorderContainer::GetInstance().UpdateFrameData(
payload_header.id(), PayloadDirection::INCOMING_PAYLOAD, medium,
packet_meta_data);
if (is_last_chunk) {
ThroughputRecorderContainer::GetInstance()
.GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD)
->MarkAsSuccess();
ThroughputRecorderContainer::GetInstance().MarkAsSuccess(
payload_header.id(), PayloadDirection::INCOMING_PAYLOAD);
}
}