diff --git a/source/device/host.cpp b/source/device/host.cpp index 970b01cd4a..04a2114b7f 100644 --- a/source/device/host.cpp +++ b/source/device/host.cpp @@ -4,6 +4,7 @@ #include "logger/logger.hpp" #include "utils/validation.hpp" +#include "metrics/metrics_collector.hpp" namespace sim { @@ -45,9 +46,11 @@ TimeNs Host::process() { // TODO: add some sender ID for easier packet path tracing LOG_INFO("Processing packet from link on host. Packet: " + packet.to_string()); + packet.network_telemetry_data.push_back(DeviceVisitingTime(get_id(), Scheduler::get_instance().get_current_time())); if (packet.dest_id == get_id()) { packet.flow->update(packet); + MetricsCollector::get_instance().add_arrived_packet(get_id(), packet); } else { LOG_WARN( "Packet arrived to Host that is not its destination; use routing " @@ -100,6 +103,7 @@ TimeNs Host::send_packet() { LOG_INFO(fmt::format("Sent new packet from host. Packet: {}", get_id(), data_packet.to_string())); + data_packet.network_telemetry_data.push_back(DeviceVisitingTime(get_id(), Scheduler::get_instance().get_current_time())); next_link->schedule_arrival(data_packet); if (m_send_data_scheduler.notify_about_finish( diff --git a/source/device/switch.cpp b/source/device/switch.cpp index cd9733ab03..37f7749396 100644 --- a/source/device/switch.cpp +++ b/source/device/switch.cpp @@ -47,6 +47,7 @@ TimeNs Switch::process() { // TODO: add some switch ID for easier packet path tracing LOG_INFO("Processing packet from link on switch. Packet: " + packet.to_string()); + packet.network_telemetry_data.push_back(DeviceVisitingTime(get_id(), Scheduler::get_instance().get_current_time())); // ECN mark for data packets if (packet.ecn_capable_transport) { diff --git a/source/metrics/metrics_collector.cpp b/source/metrics/metrics_collector.cpp index 11b699b815..66f7ad85b3 100644 --- a/source/metrics/metrics_collector.cpp +++ b/source/metrics/metrics_collector.cpp @@ -18,7 +18,8 @@ MetricsCollector::MetricsCollector() m_cwnd_storage("cwnd", m_metrics_filter), m_rate_storage("rate", m_metrics_filter), m_packet_reordering_storage("packet_reordering", m_metrics_filter), - m_links_queue_size_storage(m_metrics_filter) { + m_links_queue_size_storage(m_metrics_filter), + m_arrived_packets("telemetry", m_metrics_filter) { m_is_initialised = true; } @@ -45,6 +46,10 @@ void MetricsCollector::add_queue_size(Id link_id, TimeNs time, SizeByte value, m_links_queue_size_storage.add_record(link_id, type, time, value.value()); } +void MetricsCollector::add_arrived_packet(Id device_id, Packet packet) { + m_arrived_packets.add_record(device_id, packet); +} + void MetricsCollector::add_packet_reordering(Id flow_id, TimeNs time, PacketReordering reordering) { m_packet_reordering_storage.add_record(flow_id, time, reordering); @@ -56,6 +61,7 @@ void MetricsCollector::export_metrics_to_files( m_RTT_storage.export_to_files(metrics_dir); m_rate_storage.export_to_files(metrics_dir); m_packet_reordering_storage.export_to_files(metrics_dir); + m_arrived_packets.export_to_files(metrics_dir); m_links_queue_size_storage.export_to_files(metrics_dir); } diff --git a/source/metrics/metrics_collector.hpp b/source/metrics/metrics_collector.hpp index 11a2069011..fe4789f39c 100644 --- a/source/metrics/metrics_collector.hpp +++ b/source/metrics/metrics_collector.hpp @@ -6,6 +6,7 @@ #include "link/packet_queue/link_queue.hpp" #include "links_queue_size_storage.hpp" #include "multi_id_metrics_storage.hpp" +#include "packet_storage.hpp" #include "packet_reordering/i_packet_reordering.hpp" namespace sim { @@ -23,6 +24,9 @@ class MetricsCollector { void add_queue_size(Id link_id, TimeNs time, SizeByte value, LinkQueueType type = LinkQueueType::FromEgress); + // Device metrics + void add_arrived_packet(Id device_Id, Packet packet); + // Layout void export_metrics_to_files(std::filesystem::path metrics_dir) const; void draw_metric_plots(std::filesystem::path metrics_dir) const; @@ -52,6 +56,9 @@ class MetricsCollector { // link_ID --> vector of values LinksQueueSizeStorage m_links_queue_size_storage; + + // device_ID --> all successfully arrived packets + PacketStorage m_arrived_packets; }; } // namespace sim diff --git a/source/metrics/multi_id_metrics_collector.cpp b/source/metrics/multi_id_metrics_storage.cpp similarity index 100% rename from source/metrics/multi_id_metrics_collector.cpp rename to source/metrics/multi_id_metrics_storage.cpp diff --git a/source/metrics/packet_storage.cpp b/source/metrics/packet_storage.cpp new file mode 100644 index 0000000000..b3d19643ff --- /dev/null +++ b/source/metrics/packet_storage.cpp @@ -0,0 +1,64 @@ +#include + +#include "packet_storage.hpp" +#include "utils/safe_matplot.hpp" + +namespace sim { +PacketStorage::PacketStorage(std::string a_metric_name, + std::string a_filter) + : metric_name(std::move(a_metric_name)), m_filter(a_filter) {} + +void PacketStorage::add_record(Id id, Packet packet) { + auto it = m_storage.find(id); + if (it == m_storage.end()) { + std::string filename = get_metrics_filename(id); + if (!std::regex_match(filename, m_filter)) { + m_storage[id] = std::nullopt; + } else { + std::vector new_storage; + new_storage.push_back(packet); + m_storage.emplace(std::move(id), std::move(new_storage)); + } + } else if (it->second.has_value()) { + it->second->push_back(packet); + } +} + +void PacketStorage::export_to_file(std::filesystem::path path, std::vector device_data) const { + utils::create_all_directories(path); + std::ofstream output_file(path); + if (!output_file) { + throw std::runtime_error("Failed to create file for metric values"); + } + + for (const auto& packet : device_data) { + output_file << packet.to_string() << std::endl; + output_file << packet.get_telemetry_string() << std::endl; + output_file << std::endl; + } + + output_file.close(); +} + +void PacketStorage::export_to_files( + std::filesystem::path output_dir_path) const { + for (auto& [id, values] : data()) { + export_to_file(output_dir_path / get_metrics_filename(id), values); + } +} + +std::unordered_map> PacketStorage::data() const { + std::unordered_map> result; + result.reserve(m_storage.size()); + for (auto [id, maybe_storage] : m_storage) { + if (maybe_storage) { + result.emplace(std::move(id), maybe_storage.value()); + } + } + return result; +} +std::string PacketStorage::get_metrics_filename(Id id) const { + return fmt::format("{}/device_{}.txt", metric_name, id); +} + +} // namespace sim \ No newline at end of file diff --git a/source/metrics/packet_storage.hpp b/source/metrics/packet_storage.hpp new file mode 100644 index 0000000000..9bd41af160 --- /dev/null +++ b/source/metrics/packet_storage.hpp @@ -0,0 +1,34 @@ +#pragma once +#include + +#include +#include + +#include "metrics_storage.hpp" +#include "packet.hpp" + +namespace sim { + +class PacketStorage { +public: + // TODO: think about getting list of metric names for packet and working with all packet metrics from the same storage + PacketStorage(std::string a_metric_name, std::string a_filter); + + void add_record(Id id, Packet packet); + void export_to_file(std::filesystem::path output_dir_path, std::vector) const; + void export_to_files(std::filesystem::path output_dir_path) const; + + std::unordered_map> data() const; + +private: + std::string get_metrics_filename(Id id) const; + + std::string metric_name; + // If m_storage does not contain some id, there was no check is metrics file + // name for id correspond to m_filter + // If m_storage[id] = std::nullopt, this check was failed + // Otherwice, check was succseed + std::unordered_map> > m_storage; + std::regex m_filter; +}; +} // namespace sim \ No newline at end of file diff --git a/source/packet.cpp b/source/packet.cpp index 07227e45c0..f7dd804b2b 100644 --- a/source/packet.cpp +++ b/source/packet.cpp @@ -25,6 +25,17 @@ bool Packet::operator==(const Packet& packet) const { flags == packet.flags; } +std::string Packet::get_telemetry_string() const { + std::ostringstream oss; + oss << "Visited devices: "; + for (auto record: network_telemetry_data) { + oss << "(device id: " << record.device_id; + oss << ", time: " << record.visiting_time << "); "; + } + oss << std::endl; + return oss.str(); +} + // TODO: think about some ID for packet (currently its impossible to distinguish // packets) std::string Packet::to_string() const { diff --git a/source/packet.hpp b/source/packet.hpp index 55549f6d48..cbbb1ae5f8 100644 --- a/source/packet.hpp +++ b/source/packet.hpp @@ -1,12 +1,18 @@ #pragma once #include +#include #include "flow/i_flow.hpp" #include "utils/bitset.hpp" namespace sim { +struct DeviceVisitingTime { + Id device_id; + TimeNs visiting_time; +}; + struct Packet { Packet(SizeByte a_size = SizeByte(0), IFlow* a_flow = nullptr, Id a_source_id = "", Id a_dest_id = "", @@ -17,6 +23,7 @@ struct Packet { bool operator==(const Packet& packet) const; std::string to_string() const; + std::string get_telemetry_string() const; PacketNum packet_num = 0; BitSet flags; @@ -29,6 +36,8 @@ struct Packet { TimeNs sent_time; // Note: ACK's sent time is the data packet sent time SizeByte delivered_data_size_at_origin; // For ACK this is inherited from // data packet + + std::vector network_telemetry_data; TTL ttl = std::numeric_limits::max(); bool ecn_capable_transport; bool congestion_experienced;