Adds functionality to write logs to memory.

This makes it possible to save log outputs from scenario tests to
either files or memory.

Bug: webrtc:9510
Change-Id: I883bd8240ab712d31d54118adf979041bd83481a
Reviewed-on: https://webrtc-review.googlesource.com/c/116321
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26284}
This commit is contained in:
Sebastian Jansson 2019-01-16 17:25:44 +01:00 committed by Commit Bot
parent 375b346b30
commit 52de8b0270
29 changed files with 527 additions and 173 deletions

View file

@ -32,6 +32,9 @@ class RtcEventLogOutput {
// after the first time |false| is returned. Write() may not be called on
// an inactive output sink.
virtual bool Write(const std::string& output) = 0;
// Indicates that buffers should be written to disk if applicable.
virtual void Flush() {}
};
} // namespace webrtc

View file

@ -101,6 +101,7 @@ if (rtc_include_tests) {
"../../api/units:time_delta",
"../../api/units:timestamp",
"../../rtc_base:checks",
"../../test/logging:log_writer",
"//third_party/abseil-cpp/absl/types:optional",
]
}

View file

@ -128,6 +128,7 @@ if (rtc_include_tests) {
"..:test_controller_printer",
"../../../api/transport:network_control",
"../../../api/units:timestamp",
"../../../logging:rtc_event_log_api",
"../../../rtc_base:checks",
]
}

View file

@ -24,15 +24,17 @@ bool BbrStatePrinter::Attached() const {
return controller_ != nullptr;
}
void BbrStatePrinter::PrintHeaders(FILE* out) {
fprintf(out, "bbr_mode bbr_recovery_state round_trip_count gain_cycle_index");
void BbrStatePrinter::PrintHeaders(RtcEventLogOutput* out) {
LogWriteFormat(
out, "bbr_mode bbr_recovery_state round_trip_count gain_cycle_index");
}
void BbrStatePrinter::PrintValues(FILE* out) {
void BbrStatePrinter::PrintValues(RtcEventLogOutput* out) {
RTC_CHECK(controller_);
bbr::BbrNetworkController::DebugState debug(*controller_);
fprintf(out, "%i %i %i %i", debug.mode, debug.recovery_state,
static_cast<int>(debug.round_trip_count), debug.gain_cycle_index);
LogWriteFormat(out, "%i %i %i %i", debug.mode, debug.recovery_state,
static_cast<int>(debug.round_trip_count),
debug.gain_cycle_index);
}
NetworkControlUpdate BbrStatePrinter::GetState(Timestamp at_time) const {

View file

@ -16,6 +16,7 @@
#include "api/transport/network_control.h"
#include "api/transport/network_types.h"
#include "api/units/timestamp.h"
#include "logging/rtc_event_log/rtc_event_log.h"
#include "modules/congestion_controller/bbr/bbr_factory.h"
#include "modules/congestion_controller/bbr/bbr_network_controller.h"
#include "modules/congestion_controller/test/controller_printer.h"
@ -28,8 +29,8 @@ class BbrStatePrinter : public DebugStatePrinter {
void Attach(bbr::BbrNetworkController*);
bool Attached() const override;
void PrintHeaders(FILE* out) override;
void PrintValues(FILE* out) override;
void PrintHeaders(RtcEventLogOutput* out) override;
void PrintValues(RtcEventLogOutput* out) override;
NetworkControlUpdate GetState(Timestamp at_time) const override;

View file

@ -31,25 +31,26 @@ bool GoogCcStatePrinter::Attached() const {
return controller_ != nullptr;
}
void GoogCcStatePrinter::PrintHeaders(FILE* out) {
fprintf(out,
"rate_control_state rate_control_region alr_state"
" trendline trendline_modified_offset trendline_offset_threshold");
void GoogCcStatePrinter::PrintHeaders(RtcEventLogOutput* out) {
out->Write(
"rate_control_state rate_control_region alr_state"
" trendline trendline_modified_offset trendline_offset_threshold");
}
void GoogCcStatePrinter::PrintValues(FILE* out) {
void GoogCcStatePrinter::PrintValues(RtcEventLogOutput* out) {
RTC_CHECK(controller_);
auto* detector = controller_->delay_based_bwe_->delay_detector_.get();
auto* trendline_estimator = reinterpret_cast<TrendlineEstimator*>(detector);
fprintf(out, "%i %f %i %.6lf %.6lf %.6lf",
controller_->delay_based_bwe_->rate_control_.rate_control_state_,
controller_->delay_based_bwe_->rate_control_.link_capacity_
.estimate_kbps_.value_or(NAN) *
1000 / 8,
controller_->alr_detector_->alr_started_time_ms_.has_value(),
trendline_estimator->prev_trend_,
trendline_estimator->prev_modified_trend_,
trendline_estimator->threshold_);
LogWriteFormat(
out, "%i %f %i %.6lf %.6lf %.6lf",
controller_->delay_based_bwe_->rate_control_.rate_control_state_,
controller_->delay_based_bwe_->rate_control_.link_capacity_.estimate_kbps_
.value_or(NAN) *
1000 / 8,
controller_->alr_detector_->alr_started_time_ms_.has_value(),
trendline_estimator->prev_trend_,
trendline_estimator->prev_modified_trend_,
trendline_estimator->threshold_);
}
NetworkControlUpdate GoogCcStatePrinter::GetState(Timestamp at_time) const {

View file

@ -29,8 +29,8 @@ class GoogCcStatePrinter : public DebugStatePrinter {
void Attach(GoogCcNetworkController*);
bool Attached() const override;
void PrintHeaders(FILE* out) override;
void PrintValues(FILE* out) override;
void PrintHeaders(RtcEventLogOutput* out) override;
void PrintValues(RtcEventLogOutput* out) override;
NetworkControlUpdate GetState(Timestamp at_time) const override;

View file

@ -20,20 +20,20 @@
namespace webrtc {
ControlStatePrinter::ControlStatePrinter(
FILE* output,
std::unique_ptr<RtcEventLogOutput> output,
std::unique_ptr<DebugStatePrinter> debug_printer)
: output_(output), debug_printer_(std::move(debug_printer)) {}
: output_(std::move(output)), debug_printer_(std::move(debug_printer)) {}
ControlStatePrinter::~ControlStatePrinter() = default;
void ControlStatePrinter::PrintHeaders() {
fprintf(output_, "time bandwidth rtt target pacing padding window");
output_->Write("time bandwidth rtt target pacing padding window");
if (debug_printer_) {
fprintf(output_, " ");
debug_printer_->PrintHeaders(output_);
output_->Write(" ");
debug_printer_->PrintHeaders(output_.get());
}
fprintf(output_, "\n");
fflush(output_);
output_->Write("\n");
output_->Flush();
}
void ControlStatePrinter::PrintState(const Timestamp time,
@ -48,16 +48,16 @@ void ControlStatePrinter::PrintState(const Timestamp time,
double congestion_window = state.congestion_window
? state.congestion_window->bytes<double>()
: std::numeric_limits<double>::infinity();
fprintf(output_, "%f %f %f %f %f %f %f", timestamp, bandwidth, rtt,
target_rate, pacing_rate, padding_rate, congestion_window);
LogWriteFormat(output_.get(), "%f %f %f %f %f %f %f", timestamp, bandwidth,
rtt, target_rate, pacing_rate, padding_rate,
congestion_window);
if (debug_printer_) {
fprintf(output_, " ");
debug_printer_->PrintValues(output_);
output_->Write(" ");
debug_printer_->PrintValues(output_.get());
}
fprintf(output_, "\n");
fflush(output_);
output_->Write("\n");
output_->Flush();
}
void ControlStatePrinter::PrintState(const Timestamp time) {

View file

@ -10,25 +10,25 @@
#ifndef MODULES_CONGESTION_CONTROLLER_TEST_CONTROLLER_PRINTER_H_
#define MODULES_CONGESTION_CONTROLLER_TEST_CONTROLLER_PRINTER_H_
#include <cstdio>
#include <memory>
#include "api/transport/network_types.h"
#include "api/units/timestamp.h"
#include "test/logging/log_writer.h"
namespace webrtc {
class DebugStatePrinter {
public:
virtual bool Attached() const = 0;
virtual void PrintHeaders(FILE* out) = 0;
virtual void PrintValues(FILE* out) = 0;
virtual void PrintHeaders(RtcEventLogOutput* out) = 0;
virtual void PrintValues(RtcEventLogOutput* out) = 0;
virtual NetworkControlUpdate GetState(Timestamp at_time) const = 0;
virtual ~DebugStatePrinter() = default;
};
class ControlStatePrinter {
public:
ControlStatePrinter(FILE* output,
ControlStatePrinter(std::unique_ptr<RtcEventLogOutput> output,
std::unique_ptr<DebugStatePrinter> debug_printer);
~ControlStatePrinter();
void PrintHeaders();
@ -36,7 +36,7 @@ class ControlStatePrinter {
void PrintState(const Timestamp time);
private:
FILE* output_;
std::unique_ptr<RtcEventLogOutput> output_;
std::unique_ptr<DebugStatePrinter> debug_printer_;
};
} // namespace webrtc

33
test/logging/BUILD.gn Normal file
View file

@ -0,0 +1,33 @@
# Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
#
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
import("../../webrtc.gni")
rtc_source_set("log_writer") {
testonly = true
visibility = [ "*" ]
sources = [
"file_log_writer.cc",
"file_log_writer.h",
"log_writer.cc",
"log_writer.h",
"memory_log_writer.cc",
"memory_log_writer.h",
]
deps = [
"../../api:libjingle_logging_api",
"../../rtc_base:checks",
"../../rtc_base:logging",
"../../rtc_base:rtc_base_tests_utils",
"../../rtc_base:stringutils",
"../../test:fileutils",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/types:optional",
]
}

View file

@ -0,0 +1,62 @@
/*
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/logging/file_log_writer.h"
#include "absl/memory/memory.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "test/testsupport/fileutils.h"
namespace webrtc {
namespace webrtc_impl {
FileLogWriter::FileLogWriter(std::string file_path)
: out_(std::fopen(file_path.c_str(), "wb")) {
RTC_CHECK(out_ != nullptr)
<< "Failed to open file: '" << file_path << "' for writing.";
}
FileLogWriter::~FileLogWriter() {
std::fclose(out_);
}
bool FileLogWriter::IsActive() const {
return true;
}
bool FileLogWriter::Write(const std::string& value) {
// We don't expect the write to fail. If it does, we don't want to risk
// silently ignoring it.
RTC_CHECK_EQ(std::fwrite(value.data(), 1, value.size(), out_), value.size())
<< "fwrite failed unexpectedly: " << errno;
return true;
}
void FileLogWriter::Flush() {
RTC_CHECK_EQ(fflush(out_), 0) << "fflush failed unexpectedly: " << errno;
}
} // namespace webrtc_impl
FileLogWriterFactory::FileLogWriterFactory(std::string base_path)
: base_path_(base_path) {
for (size_t i = 0; i < base_path.size(); ++i) {
if (base_path[i] == '/')
test::CreateDir(base_path.substr(0, i));
}
}
FileLogWriterFactory::~FileLogWriterFactory() {}
std::unique_ptr<RtcEventLogOutput> FileLogWriterFactory::Create(
std::string filename) {
return absl::make_unique<webrtc_impl::FileLogWriter>(base_path_ + filename);
}
} // namespace webrtc

View file

@ -0,0 +1,48 @@
/*
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TEST_LOGGING_FILE_LOG_WRITER_H_
#define TEST_LOGGING_FILE_LOG_WRITER_H_
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
#include "test/logging/log_writer.h"
namespace webrtc {
namespace webrtc_impl {
class FileLogWriter final : public RtcEventLogOutput {
public:
explicit FileLogWriter(std::string file_path);
~FileLogWriter() final;
bool IsActive() const override;
bool Write(const std::string& value) override;
void Flush() override;
private:
std::FILE* const out_;
};
} // namespace webrtc_impl
class FileLogWriterFactory final : public LogWriterFactoryInterface {
public:
explicit FileLogWriterFactory(std::string base_path);
~FileLogWriterFactory() final;
std::unique_ptr<RtcEventLogOutput> Create(std::string filename) override;
private:
const std::string base_path_;
std::vector<std::unique_ptr<webrtc_impl::FileLogWriter>> writers_;
};
} // namespace webrtc
#endif // TEST_LOGGING_FILE_LOG_WRITER_H_

View file

@ -0,0 +1,24 @@
/*
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/logging/log_writer.h"
namespace webrtc {
LogWriterFactoryAddPrefix::LogWriterFactoryAddPrefix(
LogWriterFactoryInterface* base,
std::string prefix)
: base_factory_(base), prefix_(prefix) {}
std::unique_ptr<RtcEventLogOutput> LogWriterFactoryAddPrefix::Create(
std::string filename) {
return base_factory_->Create(prefix_ + filename);
}
} // namespace webrtc

60
test/logging/log_writer.h Normal file
View file

@ -0,0 +1,60 @@
/*
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TEST_LOGGING_LOG_WRITER_H_
#define TEST_LOGGING_LOG_WRITER_H_
#include <memory>
#include <string>
#include <utility>
#include "api/rtceventlogoutput.h"
#include "rtc_base/strings/string_builder.h"
namespace webrtc {
template <class... Args>
inline void LogWriteFormat(RtcEventLogOutput* out_, const char* fmt, ...) {
va_list args, copy;
va_start(args, fmt);
va_copy(copy, args);
const int predicted_length = std::vsnprintf(nullptr, 0, fmt, copy);
va_end(copy);
RTC_DCHECK_GE(predicted_length, 0);
std::string out_str(predicted_length, '\0');
if (predicted_length > 0) {
// Pass "+ 1" to vsnprintf to include space for the '\0'.
const int actual_length =
std::vsnprintf(&out_str.front(), predicted_length + 1, fmt, args);
RTC_DCHECK_GE(actual_length, 0);
}
va_end(args);
out_->Write(out_str);
}
class LogWriterFactoryInterface {
public:
virtual std::unique_ptr<RtcEventLogOutput> Create(std::string filename) = 0;
virtual ~LogWriterFactoryInterface() = default;
};
class LogWriterFactoryAddPrefix : public LogWriterFactoryInterface {
public:
LogWriterFactoryAddPrefix(LogWriterFactoryInterface* base,
std::string prefix);
std::unique_ptr<RtcEventLogOutput> Create(std::string filename) override;
private:
LogWriterFactoryInterface* const base_factory_;
const std::string prefix_;
};
} // namespace webrtc
#endif // TEST_LOGGING_LOG_WRITER_H_

View file

@ -0,0 +1,66 @@
/*
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/logging/memory_log_writer.h"
#include "absl/memory/memory.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
class MemoryLogWriter final : public RtcEventLogOutput {
public:
explicit MemoryLogWriter(std::map<std::string, std::string>* target,
std::string filename)
: target_(target), filename_(filename) {}
~MemoryLogWriter() final {
size_t size;
buffer_.GetSize(&size);
target_->insert({filename_, std::string(buffer_.GetBuffer(), size)});
}
bool IsActive() const override { return true; }
bool Write(const std::string& value) override {
size_t written;
int error;
return buffer_.Write(value.data(), value.size(), &written, &error) ==
rtc::SR_SUCCESS;
}
void Flush() override {}
private:
std::map<std::string, std::string>* const target_;
const std::string filename_;
rtc::MemoryStream buffer_;
};
class MemoryLogWriterFactory : public LogWriterFactoryInterface {
public:
explicit MemoryLogWriterFactory(std::map<std::string, std::string>* target)
: target_(target) {}
~MemoryLogWriterFactory() final {}
std::unique_ptr<RtcEventLogOutput> Create(std::string filename) override {
return absl::make_unique<MemoryLogWriter>(target_, filename);
}
private:
std::map<std::string, std::string>* const target_;
};
} // namespace
MemoryLogStorage::MemoryLogStorage() {}
MemoryLogStorage::~MemoryLogStorage() {}
std::unique_ptr<LogWriterFactoryInterface> MemoryLogStorage::CreateFactory() {
return absl::make_unique<MemoryLogWriterFactory>(&logs_);
}
// namespace webrtc_impl
} // namespace webrtc

View file

@ -0,0 +1,41 @@
/*
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TEST_LOGGING_MEMORY_LOG_WRITER_H_
#define TEST_LOGGING_MEMORY_LOG_WRITER_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "rtc_base/memory_stream.h"
#include "test/logging/log_writer.h"
namespace webrtc {
// Allows creating log writer factories that creates log writers that saves
// their content to memory. When the log writers are destroyed, their content is
// saved to the logs_ member of this class. The intended usage is to keep this
// class alive after the created factories and writers have been destroyed and
// then use logs() to access all the saved logs.
class MemoryLogStorage {
public:
MemoryLogStorage();
~MemoryLogStorage();
std::unique_ptr<LogWriterFactoryInterface> CreateFactory();
const std::map<std::string, std::string>& logs() { return logs_; }
private:
std::map<std::string, std::string> logs_;
};
} // namespace webrtc
#endif // TEST_LOGGING_MEMORY_LOG_WRITER_H_

View file

@ -95,6 +95,7 @@ if (rtc_include_tests) {
"../../system_wrappers",
"../../system_wrappers:field_trial",
"../../video",
"../logging:log_writer",
"network:emulated_network",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/types:optional",

View file

@ -12,7 +12,6 @@
#include <utility>
#include "absl/memory/memory.h"
#include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
#include "modules/audio_mixer/audio_mixer_impl.h"
#include "modules/congestion_controller/goog_cc/test/goog_cc_printer.h"
#include "test/call_test.h"
@ -57,39 +56,38 @@ Call* CreateCall(CallClientConfig config,
}
LoggingNetworkControllerFactory::LoggingNetworkControllerFactory(
std::string filename,
LogWriterFactoryInterface* log_writer_factory,
TransportControllerConfig config) {
if (filename.empty()) {
std::unique_ptr<RtcEventLogOutput> cc_out;
if (!log_writer_factory) {
event_log_ = RtcEventLog::CreateNull();
} else {
event_log_ = RtcEventLog::Create(RtcEventLog::EncodingType::Legacy);
bool success = event_log_->StartLogging(
absl::make_unique<RtcEventLogOutputFile>(filename + ".rtc.dat",
RtcEventLog::kUnlimitedOutput),
RtcEventLog::kImmediateOutput);
log_writer_factory->Create(".rtc.dat"), RtcEventLog::kImmediateOutput);
RTC_CHECK(success);
cc_out_ = fopen((filename + ".cc_state.txt").c_str(), "w");
cc_out = log_writer_factory->Create(".cc_state.txt");
}
switch (config.cc) {
case TransportControllerConfig::CongestionController::kGoogCc:
if (cc_out_) {
if (cc_out) {
auto goog_printer = absl::make_unique<GoogCcStatePrinter>();
owned_cc_factory_.reset(
new GoogCcDebugFactory(event_log_.get(), goog_printer.get()));
cc_printer_.reset(
new ControlStatePrinter(cc_out_, std::move(goog_printer)));
cc_printer_.reset(new ControlStatePrinter(std::move(cc_out),
std::move(goog_printer)));
} else {
owned_cc_factory_.reset(
new GoogCcNetworkControllerFactory(event_log_.get()));
}
break;
case TransportControllerConfig::CongestionController::kGoogCcFeedback:
if (cc_out_) {
if (cc_out) {
auto goog_printer = absl::make_unique<GoogCcStatePrinter>();
owned_cc_factory_.reset(new GoogCcFeedbackDebugFactory(
event_log_.get(), goog_printer.get()));
cc_printer_.reset(
new ControlStatePrinter(cc_out_, std::move(goog_printer)));
cc_printer_.reset(new ControlStatePrinter(std::move(cc_out),
std::move(goog_printer)));
} else {
owned_cc_factory_.reset(
new GoogCcFeedbackNetworkControllerFactory(event_log_.get()));
@ -97,7 +95,7 @@ LoggingNetworkControllerFactory::LoggingNetworkControllerFactory(
break;
case TransportControllerConfig::CongestionController::kInjected:
cc_factory_ = config.cc_factory;
if (cc_out_)
if (cc_out)
RTC_LOG(LS_WARNING)
<< "Can't log controller state for injected network controllers";
break;
@ -111,8 +109,6 @@ LoggingNetworkControllerFactory::LoggingNetworkControllerFactory(
}
LoggingNetworkControllerFactory::~LoggingNetworkControllerFactory() {
if (cc_out_)
fclose(cc_out_);
}
void LoggingNetworkControllerFactory::LogCongestionControllerStats(
@ -134,13 +130,13 @@ TimeDelta LoggingNetworkControllerFactory::GetProcessInterval() const {
return cc_factory_->GetProcessInterval();
}
CallClient::CallClient(Clock* clock,
std::string name,
std::string log_filename,
CallClientConfig config)
CallClient::CallClient(
Clock* clock,
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
CallClientConfig config)
: clock_(clock),
name_(name),
network_controller_factory_(log_filename, config.transport),
log_writer_factory_(std::move(log_writer_factory)),
network_controller_factory_(log_writer_factory_.get(), config.transport),
fake_audio_setup_(InitAudio()),
call_(CreateCall(config,
&network_controller_factory_,
@ -189,6 +185,12 @@ void CallClient::OnPacketReceived(EmulatedIpPacket packet) {
packet.arrival_time.us());
}
std::unique_ptr<RtcEventLogOutput> CallClient::GetLogWriter(std::string name) {
if (!log_writer_factory_ || name.empty())
return nullptr;
return log_writer_factory_->Create(name);
}
uint32_t CallClient::GetNextVideoSsrc() {
RTC_CHECK_LT(next_video_ssrc_index_, CallTest::kNumSsrcs);
return CallTest::kVideoSendSsrcs[next_video_ssrc_index_++];

View file

@ -19,6 +19,7 @@
#include "modules/congestion_controller/test/controller_printer.h"
#include "modules/rtp_rtcp/include/rtp_header_parser.h"
#include "rtc_base/constructor_magic.h"
#include "test/logging/log_writer.h"
#include "test/scenario/column_printer.h"
#include "test/scenario/network/network_emulation.h"
#include "test/scenario/network_node.h"
@ -30,7 +31,7 @@ namespace test {
class LoggingNetworkControllerFactory
: public NetworkControllerFactoryInterface {
public:
LoggingNetworkControllerFactory(std::string filename,
LoggingNetworkControllerFactory(LogWriterFactoryInterface* log_writer_factory,
TransportControllerConfig config);
RTC_DISALLOW_COPY_AND_ASSIGN(LoggingNetworkControllerFactory);
~LoggingNetworkControllerFactory();
@ -46,7 +47,6 @@ class LoggingNetworkControllerFactory
std::unique_ptr<NetworkControllerFactoryInterface> owned_cc_factory_;
NetworkControllerFactoryInterface* cc_factory_ = nullptr;
std::unique_ptr<ControlStatePrinter> cc_printer_;
FILE* cc_out_ = nullptr;
};
struct CallClientFakeAudio {
@ -60,8 +60,7 @@ struct CallClientFakeAudio {
class CallClient : public EmulatedNetworkReceiverInterface {
public:
CallClient(Clock* clock,
std::string name,
std::string log_filename,
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
CallClientConfig config);
RTC_DISALLOW_COPY_AND_ASSIGN(CallClient);
@ -73,6 +72,7 @@ class CallClient : public EmulatedNetworkReceiverInterface {
}
void OnPacketReceived(EmulatedIpPacket packet) override;
std::unique_ptr<RtcEventLogOutput> GetLogWriter(std::string name);
private:
friend class Scenario;
@ -91,7 +91,7 @@ class CallClient : public EmulatedNetworkReceiverInterface {
void AddExtensions(std::vector<RtpExtension> extensions);
Clock* clock_;
const std::string name_;
const std::unique_ptr<LogWriterFactoryInterface> log_writer_factory_;
LoggingNetworkControllerFactory network_controller_factory_;
CallClientFakeAudio fake_audio_setup_;
std::unique_ptr<Call> call_;

View file

@ -34,38 +34,26 @@ ColumnPrinter ColumnPrinter::Lambda(
return ColumnPrinter(headers, printer, max_length);
}
StatesPrinter::StatesPrinter(std::string filename,
StatesPrinter::StatesPrinter(std::unique_ptr<RtcEventLogOutput> writer,
std::vector<ColumnPrinter> printers)
: StatesPrinter(printers) {
if (!filename.empty()) {
output_file_ = fopen(filename.c_str(), "w");
RTC_CHECK(output_file_);
output_ = output_file_;
}
}
StatesPrinter::StatesPrinter(std::vector<ColumnPrinter> printers)
: printers_(printers) {
output_ = stdout;
: writer_(std::move(writer)), printers_(printers) {
RTC_CHECK(!printers_.empty());
for (auto& printer : printers_)
buffer_size_ += printer.max_length_ + 1;
buffer_.resize(buffer_size_);
}
StatesPrinter::~StatesPrinter() {
if (output_file_)
fclose(output_file_);
}
StatesPrinter::~StatesPrinter() = default;
void StatesPrinter::PrintHeaders() {
if (!output_file_)
if (!writer_)
return;
fprintf(output_, "%s", printers_[0].headers_);
writer_->Write(printers_[0].headers_);
for (size_t i = 1; i < printers_.size(); ++i) {
fprintf(output_, " %s", printers_[i].headers_);
writer_->Write(" ");
writer_->Write(printers_[i].headers_);
}
fputs("\n", output_);
writer_->Write("\n");
}
void StatesPrinter::PrintRow() {
@ -77,9 +65,9 @@ void StatesPrinter::PrintRow() {
sb << ' ';
printers_[i].printer_(sb);
}
sb << "\n\0";
if (output_file_)
fputs(&buffer_.front(), output_);
sb << "\n";
if (writer_)
writer_->Write(std::string(sb.str(), sb.size()));
}
} // namespace test
} // namespace webrtc

View file

@ -16,6 +16,7 @@
#include "rtc_base/constructor_magic.h"
#include "rtc_base/strings/string_builder.h"
#include "test/logging/log_writer.h"
namespace webrtc {
namespace test {
@ -43,19 +44,18 @@ class ColumnPrinter {
class StatesPrinter {
public:
StatesPrinter(std::string filename, std::vector<ColumnPrinter> printers);
explicit StatesPrinter(std::vector<ColumnPrinter> printers);
StatesPrinter(std::unique_ptr<RtcEventLogOutput> writer,
std::vector<ColumnPrinter> printers);
RTC_DISALLOW_COPY_AND_ASSIGN(StatesPrinter);
~StatesPrinter();
void PrintHeaders();
void PrintRow();
private:
const std::unique_ptr<RtcEventLogOutput> writer_;
const std::vector<ColumnPrinter> printers_;
size_t buffer_size_ = 0;
std::vector<char> buffer_;
FILE* output_file_ = nullptr;
FILE* output_ = nullptr;
};
} // namespace test
} // namespace webrtc

View file

@ -19,12 +19,10 @@ namespace webrtc {
namespace test {
VideoQualityAnalyzer::VideoQualityAnalyzer(
std::string filename_or_empty,
std::unique_ptr<RtcEventLogOutput> writer,
std::function<void(const VideoFrameQualityInfo&)> frame_info_handler)
: task_queue_("VideoAnalyzer") {
if (!filename_or_empty.empty()) {
output_file_ = fopen(filename_or_empty.c_str(), "w");
RTC_CHECK(output_file_);
: writer_(std::move(writer)), task_queue_("VideoAnalyzer") {
if (writer_) {
PrintHeaders();
frame_info_handlers_.push_back(
[this](const VideoFrameQualityInfo& info) { PrintFrameInfo(info); });
@ -37,8 +35,6 @@ VideoQualityAnalyzer::~VideoQualityAnalyzer() {
rtc::Event event;
task_queue_.PostTask([&event] { event.Set(); });
event.Wait(rtc::Event::kForever);
if (output_file_)
fclose(output_file_);
}
void VideoQualityAnalyzer::OnCapturedFrame(const VideoFrame& frame) {
@ -114,15 +110,15 @@ int64_t VideoQualityAnalyzer::CapturedFrameCaptureTimeOffsetMs(
}
void VideoQualityAnalyzer::PrintHeaders() {
fprintf(output_file_, "capt recv_capt render width height psnr\n");
writer_->Write("capt recv_capt render width height psnr\n");
}
void VideoQualityAnalyzer::PrintFrameInfo(const VideoFrameQualityInfo& sample) {
fprintf(output_file_, "%.3f %.3f %.3f %i %i %.3f\n",
sample.capture_time.seconds<double>(),
sample.received_capture_time.seconds<double>(),
sample.render_time.seconds<double>(), sample.width, sample.height,
sample.psnr);
LogWriteFormat(writer_.get(), "%.3f %.3f %.3f %i %i %.3f\n",
sample.capture_time.seconds<double>(),
sample.received_capture_time.seconds<double>(),
sample.render_time.seconds<double>(), sample.width,
sample.height, sample.psnr);
}
void VideoQualityStats::HandleFrameInfo(VideoFrameQualityInfo sample) {

View file

@ -11,6 +11,7 @@
#define TEST_SCENARIO_QUALITY_STATS_H_
#include <deque>
#include <memory>
#include <string>
#include <vector>
@ -22,6 +23,7 @@
#include "rtc_base/task_queue.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/clock.h"
#include "test/logging/log_writer.h"
#include "test/scenario/quality_info.h"
#include "test/scenario/scenario_config.h"
#include "test/statistics.h"
@ -32,7 +34,7 @@ namespace test {
class VideoQualityAnalyzer {
public:
VideoQualityAnalyzer(
std::string filename_or_empty,
std::unique_ptr<RtcEventLogOutput> writer,
std::function<void(const VideoFrameQualityInfo&)> frame_info_handler);
~VideoQualityAnalyzer();
void OnCapturedFrame(const VideoFrame& frame);
@ -46,12 +48,12 @@ class VideoQualityAnalyzer {
int64_t CapturedFrameCaptureTimeOffsetMs(const VideoFrame& captured) const;
void PrintHeaders();
void PrintFrameInfo(const VideoFrameQualityInfo& sample);
const std::unique_ptr<RtcEventLogOutput> writer_;
std::vector<std::function<void(const VideoFrameQualityInfo&)>>
frame_info_handlers_;
std::deque<VideoFrame> captured_frames_;
absl::optional<int64_t> first_capture_ntp_time_ms_;
absl::optional<uint32_t> first_decode_rtp_timestamp_;
FILE* output_file_ = nullptr;
rtc::TaskQueue task_queue_;
};

View file

@ -11,10 +11,12 @@
#include <algorithm>
#include "absl/memory/memory.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "rtc_base/flags.h"
#include "rtc_base/socket_address.h"
#include "test/logging/file_log_writer.h"
#include "test/scenario/network/network_emulation.h"
#include "test/testsupport/file_utils.h"
@ -27,6 +29,19 @@ namespace webrtc {
namespace test {
namespace {
int64_t kMicrosPerSec = 1000000;
std::unique_ptr<FileLogWriterFactory> GetScenarioLogManager(
std::string file_name) {
if (FLAG_scenario_logs && !file_name.empty()) {
std::string output_root = FLAG_out_root;
if (output_root.empty())
output_root = OutputPath() + "output_data/";
auto base_filename = output_root + file_name + ".";
RTC_LOG(LS_INFO) << "Saving scenario logs to: " << base_filename;
return absl::make_unique<FileLogWriterFactory>(base_filename);
}
return nullptr;
}
}
RepeatedActivity::RepeatedActivity(TimeDelta interval,
@ -54,30 +69,24 @@ Timestamp RepeatedActivity::NextTime() {
return last_update_ + interval_;
}
Scenario::Scenario() : Scenario("", true) {}
Scenario::Scenario()
: Scenario(std::unique_ptr<LogWriterFactoryInterface>(), true) {}
Scenario::Scenario(std::string file_name) : Scenario(file_name, true) {}
Scenario::Scenario(std::string file_name, bool real_time)
: real_time_mode_(real_time),
: Scenario(GetScenarioLogManager(file_name), real_time) {}
Scenario::Scenario(
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
bool real_time)
: log_writer_factory_(std::move(log_writer_factory)),
real_time_mode_(real_time),
sim_clock_(100000 * kMicrosPerSec),
clock_(real_time ? Clock::GetRealTimeClock() : &sim_clock_),
audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()),
audio_encoder_factory_(CreateBuiltinAudioEncoderFactory()) {
if (FLAG_scenario_logs && !file_name.empty()) {
std::string output_root = FLAG_out_root;
if (output_root.empty())
output_root = OutputPath() + "output_data/";
if (output_root.back() == '/')
CreateDir(output_root);
for (size_t i = 0; i < file_name.size(); ++i) {
if (file_name[i] == '/')
CreateDir(output_root + file_name.substr(0, i));
}
base_filename_ = output_root + file_name;
RTC_LOG(LS_INFO) << "Saving scenario logs to: " << base_filename_;
}
if (!real_time_mode_ && !base_filename_.empty()) {
if (!real_time_mode_ && log_writer_factory_) {
rtc::SetClockForTesting(&event_log_fake_clock_);
event_log_fake_clock_.SetTimeNanos(sim_clock_.TimeInMicroseconds() * 1000);
}
@ -105,8 +114,7 @@ StatesPrinter* Scenario::CreatePrinter(std::string name,
std::vector<ColumnPrinter> all_printers{TimePrinter()};
for (auto& printer : printers)
all_printers.push_back(printer);
StatesPrinter* printer =
new StatesPrinter(GetFullPathOrEmpty(name), all_printers);
StatesPrinter* printer = new StatesPrinter(GetLogWriter(name), all_printers);
printers_.emplace_back(printer);
printer->PrintHeaders();
if (interval.IsFinite())
@ -117,7 +125,7 @@ StatesPrinter* Scenario::CreatePrinter(std::string name,
CallClient* Scenario::CreateClient(std::string name, CallClientConfig config) {
RTC_DCHECK(real_time_mode_);
CallClient* client =
new CallClient(clock_, name, GetFullPathOrEmpty(name), config);
new CallClient(clock_, GetLogWriterFactory(name), config);
if (config.transport.state_log_interval.IsFinite()) {
Every(config.transport.state_log_interval, [this, client]() {
client->network_controller_factory_.LogCongestionControllerStats(Now());
@ -182,9 +190,9 @@ SimulatedTimeClient* Scenario::CreateSimulatedTimeClient(
uint64_t send_id = next_route_id_++;
uint64_t return_id = next_route_id_++;
SimulatedTimeClient* client = new SimulatedTimeClient(
GetFullPathOrEmpty(name), config, stream_configs, send_link, return_link,
GetLogWriterFactory(name), config, stream_configs, send_link, return_link,
send_id, return_id, Now());
if (!base_filename_.empty() && !name.empty() &&
if (log_writer_factory_ && !name.empty() &&
config.transport.state_log_interval.IsFinite()) {
Every(config.transport.state_log_interval, [this, client]() {
client->network_controller_factory_.LogCongestionControllerStats(Now());
@ -283,12 +291,11 @@ VideoStreamPair* Scenario::CreateVideoStream(
VideoStreamPair* Scenario::CreateVideoStream(
std::pair<CallClient*, CallClient*> clients,
VideoStreamConfig config) {
std::string quality_log_file_name;
std::unique_ptr<RtcEventLogOutput> quality_logger;
if (config.analyzer.log_to_file)
quality_log_file_name =
GetFullPathOrEmpty(clients.first->name_ + ".video_quality.txt");
quality_logger = clients.first->GetLogWriter(".video_quality.txt");
video_streams_.emplace_back(new VideoStreamPair(
clients.first, clients.second, config, quality_log_file_name));
clients.first, clients.second, config, std::move(quality_logger)));
return video_streams_.back().get();
}
@ -364,7 +371,7 @@ void Scenario::RunUntil(TimeDelta max_duration,
sim_clock_.AdvanceTimeMicroseconds(wait_time.us());
// The fake clock is quite slow to update, we only update it if logging is
// turned on to save time.
if (!base_filename_.empty())
if (!log_writer_factory_)
event_log_fake_clock_.SetTimeNanos(sim_clock_.TimeInMicroseconds() *
1000);
}

View file

@ -14,8 +14,10 @@
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/fake_clock.h"
#include "test/logging/log_writer.h"
#include "test/scenario/audio_stream.h"
#include "test/scenario/call_client.h"
#include "test/scenario/column_printer.h"
@ -63,6 +65,8 @@ class Scenario {
Scenario();
explicit Scenario(std::string file_name);
Scenario(std::string file_name, bool real_time);
Scenario(std::unique_ptr<LogWriterFactoryInterface> log_writer_manager,
bool real_time);
RTC_DISALLOW_COPY_AND_ASSIGN(Scenario);
~Scenario();
@ -163,15 +167,22 @@ class Scenario {
// Return the duration of the current session so far.
TimeDelta Duration();
std::string GetFullPathOrEmpty(std::string name) const {
if (base_filename_.empty() || name.empty())
return std::string();
return base_filename_ + "." + name;
std::unique_ptr<RtcEventLogOutput> GetLogWriter(std::string name) {
if (!log_writer_factory_ || name.empty())
return nullptr;
return log_writer_factory_->Create(name);
}
std::unique_ptr<LogWriterFactoryInterface> GetLogWriterFactory(
std::string name) {
if (!log_writer_factory_ || name.empty())
return nullptr;
return absl::make_unique<LogWriterFactoryAddPrefix>(
log_writer_factory_.get(), name);
}
private:
NullReceiver null_receiver_;
std::string base_filename_;
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory_;
const bool real_time_mode_;
SimulatedClock sim_clock_;
Clock* clock_;

View file

@ -245,7 +245,7 @@ void SimulatedFeedback::OnPacketReceived(EmulatedIpPacket packet) {
}
SimulatedTimeClient::SimulatedTimeClient(
std::string log_filename,
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
SimulatedTimeClientConfig config,
std::vector<PacketStreamConfig> stream_configs,
std::vector<EmulatedNetworkNode*> send_link,
@ -253,7 +253,8 @@ SimulatedTimeClient::SimulatedTimeClient(
uint64_t send_receiver_id,
uint64_t return_receiver_id,
Timestamp at_time)
: network_controller_factory_(log_filename, config.transport),
: log_writer_factory_(std::move(log_writer_factory)),
network_controller_factory_(log_writer_factory_.get(), config.transport),
send_link_(send_link),
return_link_(return_link),
sender_(send_link.front(), send_receiver_id),
@ -274,11 +275,10 @@ SimulatedTimeClient::SimulatedTimeClient(
CongestionProcess(at_time);
network_controller_factory_.LogCongestionControllerStats(at_time);
if (!log_filename.empty()) {
std::string packet_log_name = log_filename + ".packets.txt";
packet_log_ = fopen(packet_log_name.c_str(), "w");
fprintf(packet_log_,
"transport_seq packet_size send_time recv_time feed_time\n");
if (log_writer_factory_) {
packet_log_ = log_writer_factory_->Create(".packets.txt");
packet_log_->Write(
"transport_seq packet_size send_time recv_time feed_time\n");
}
}
@ -289,18 +289,17 @@ void SimulatedTimeClient::OnPacketReceived(EmulatedIpPacket packet) {
packet.arrival_time);
for (PacketResult& feedback : report.packet_feedbacks) {
if (packet_log_)
fprintf(packet_log_, "%" PRId64 " %" PRId64 " %.3lf %.3lf %.3lf\n",
feedback.sent_packet.sequence_number,
feedback.sent_packet.size.bytes(),
feedback.sent_packet.send_time.seconds<double>(),
feedback.receive_time.seconds<double>(),
packet.arrival_time.seconds<double>());
LogWriteFormat(packet_log_.get(),
"%" PRId64 " %" PRId64 " %.3lf %.3lf %.3lf\n",
feedback.sent_packet.sequence_number,
feedback.sent_packet.size.bytes(),
feedback.sent_packet.send_time.seconds<double>(),
feedback.receive_time.seconds<double>(),
packet.arrival_time.seconds<double>());
}
Update(congestion_controller_->OnTransportPacketsFeedback(report));
}
SimulatedTimeClient::~SimulatedTimeClient() {
if (packet_log_)
fclose(packet_log_);
}
void SimulatedTimeClient::Update(NetworkControlUpdate update) {

View file

@ -11,7 +11,6 @@
#define TEST_SCENARIO_SIMULATED_TIME_H_
#include <stdint.h>
#include <stdio.h>
#include <deque>
#include <map>
#include <memory>
@ -25,6 +24,7 @@
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "test/logging/log_writer.h"
#include "test/scenario/call_client.h"
#include "test/scenario/network_node.h"
#include "test/scenario/scenario_config.h"
@ -120,14 +120,15 @@ class SimulatedSender {
// a more accurate simulation, use the real time only CallClient.
class SimulatedTimeClient : EmulatedNetworkReceiverInterface {
public:
SimulatedTimeClient(std::string log_filename,
SimulatedTimeClientConfig config,
std::vector<PacketStreamConfig> stream_configs,
std::vector<EmulatedNetworkNode*> send_link,
std::vector<EmulatedNetworkNode*> return_link,
uint64_t send_receiver_id,
uint64_t return_receiver_id,
Timestamp at_time);
SimulatedTimeClient(
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
SimulatedTimeClientConfig config,
std::vector<PacketStreamConfig> stream_configs,
std::vector<EmulatedNetworkNode*> send_link,
std::vector<EmulatedNetworkNode*> return_link,
uint64_t send_receiver_id,
uint64_t return_receiver_id,
Timestamp at_time);
SimulatedTimeClient(const SimulatedTimeClient&) = delete;
~SimulatedTimeClient();
void Update(NetworkControlUpdate update);
@ -144,6 +145,7 @@ class SimulatedTimeClient : EmulatedNetworkReceiverInterface {
private:
friend class Scenario;
std::unique_ptr<LogWriterFactoryInterface> log_writer_factory_;
LoggingNetworkControllerFactory network_controller_factory_;
std::unique_ptr<NetworkControllerInterface> congestion_controller_;
std::vector<EmulatedNetworkNode*> send_link_;
@ -153,7 +155,7 @@ class SimulatedTimeClient : EmulatedNetworkReceiverInterface {
TargetRateConstraints current_contraints_;
DataRate target_rate_ = DataRate::Infinity();
DataRate link_capacity_ = DataRate::Infinity();
FILE* packet_log_ = nullptr;
std::unique_ptr<RtcEventLogOutput> packet_log_;
std::vector<std::unique_ptr<PacketStream>> packet_streams_;
};

View file

@ -399,12 +399,14 @@ void ReceiveVideoStream::Start() {
VideoStreamPair::~VideoStreamPair() = default;
VideoStreamPair::VideoStreamPair(CallClient* sender,
CallClient* receiver,
VideoStreamConfig config,
std::string quality_log_file_name)
VideoStreamPair::VideoStreamPair(
CallClient* sender,
CallClient* receiver,
VideoStreamConfig config,
std::unique_ptr<RtcEventLogOutput> quality_writer)
: config_(config),
analyzer_(quality_log_file_name, config.analyzer.frame_quality_handler),
analyzer_(std::move(quality_writer),
config.analyzer.frame_quality_handler),
send_stream_(sender, config, &sender->transport_, &analyzer_),
receive_stream_(receiver,
config,

View file

@ -16,6 +16,7 @@
#include "rtc_base/constructor_magic.h"
#include "test/fake_encoder.h"
#include "test/frame_generator_capturer.h"
#include "test/logging/log_writer.h"
#include "test/scenario/call_client.h"
#include "test/scenario/column_printer.h"
#include "test/scenario/network_node.h"
@ -104,7 +105,7 @@ class VideoStreamPair {
VideoStreamPair(CallClient* sender,
CallClient* receiver,
VideoStreamConfig config,
std::string quality_log_file_name);
std::unique_ptr<RtcEventLogOutput> quality_writer);
const VideoStreamConfig config_;