Network Latency Monitor (cpp, written by Codex)
envgap__codex__cpp-t1-29
Written by a coding agent; not on GitHubWritten 2026-03-03
01 / FAILURE SIGNATURE
As the study recorded it
No identifying execution failure has been captured.
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
CMakeLists.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
codex/cpp-t1 #29 · read the task the agent was given
Codex wrote this cpp project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: Network Latency Monitor Write a program that continuously monitors network latency to multiple hosts using ICMP-like probes or TCP connection timing, tracking statistics over time and alerting on anomalies. FUNCTIONAL REQUIREMENTS: - Accept one or more hostnames or IP addresses as command-line arguments - Send periodic probes to each host at a configurable interval via --interval flag (default: 5 seconds) - Measure round-trip time (RTT) for each probe using TCP connection timing to a specified port (--port flag, default 80) since raw ICMP requires root privileges - Track running statistics per host: min RTT, max RTT, average RTT, median RTT, standard deviation, jitter (difference between consecutive measurements), and packet loss percentage - Support configurable monitoring duration via --duration flag (e.g., --duration 60s, --duration 5m, --duration 1h) or run until interrupted with Ctrl+C - Detect latency anomalies: flag probes where RTT exceeds a configurable threshold via --alert flag (default: 3x the running average RTT) - Display a live-updating console dashboard showing: host, last RTT, average RTT, min/max, packet loss %, and alert status - Record all measurements with timestamps for historical analysis - Save the full monitoring data as a JSON report with --output flag (default: latency_report.json) including per-host statistics and all individual measurements - Export time-series data as CSV via --export flag for external analysis - Support reading a list of hosts from a file via --file flag (one host per line with optional label) - If no hosts are given, monitor a set of well-known public hosts (8.8.8.8, 1.1.1.1, example.com, google.com) for 30 seconds, display live statistics, then print a final summary report comparing all hosts - Handle errors: unreachable hosts, DNS resolution failures, connection timeouts, and graceful shutdown saving partial data Create a complete C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include: - Source code - CMakeLists.txt with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
CMakeLists.txt
cmake_minimum_required(VERSION 3.22) project(network_latency_monitor LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) include(FetchContent) FetchContent_Declare( nlohmann_json GIT_REPOSITORY https://github.com/nlohmann/json.git GIT_TAG v3.11.3 ) FetchContent_MakeAvailable(nlohmann_json) add_executable(network_latency_monitor src/main.cpp) target_link_libraries(network_latency_monitor PRIVATE nlohmann_json::nlohmann_json)
README.md
# Network Latency Monitor (C++) Continuously probes multiple hosts using TCP connection timing and tracks latency metrics, packet loss, jitter, and anomalies. ## Requirements - Ubuntu 22.04 - G++ 12+ - CMake 3.22+ ## Dependencies (Pinned) - `nlohmann/json` `v3.11.3` for JSON report generation ## Build ```bash cmake -S . -B build cmake --build build -j ``` ## Run ```bash ./build/network_latency_monitor google.com 1.1.1.1 --interval 5 --port 80 --duration 60s ``` With host file and CSV export: ```bash ./build/network_latency_monitor --file hosts.txt --interval 2 --duration 5m --output latency_report.json --export latency_series.csv ``` Alert threshold: ```bash ./build/network_latency_monitor google.com --alert 3x ./build/network_latency_monitor google.com --alert 200 ``` ## Host file format ```text 8.8.8.8 Google-DNS 1.1.1.1,Cloudflare example.com ``` ## Output - Live terminal dashboard - JSON report (`--output`, default `latency_report.json`) - Optional CSV time-series (`--export`)
src/main.cpp
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <csignal>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <future>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <optional>
#include <regex>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <fcntl.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace fs = std::filesystem;
struct HostTarget {
std::string host;
std::string label;
};
struct Measurement {
std::string timestamp;
bool success;
std::optional<double> rtt_ms;
std::string error;
bool alert;
};
struct HostState {
std::string host;
std::string label;
int sent = 0;
int lost = 0;
std::optional<double> last_rtt;
bool last_alert = false;
std::vector<double> rtts;
std::vector<double> jitters;
std::vector<Measurement> measurements;
};
struct AlertConfig {
std::string mode = "multiplier"; // multiplier|absolute
double value = 3.0;
};
struct Config {
double interval_sec = 5.0;
int port = 80;
std::optional<int64_t> duration_ms;
AlertConfig alert;
std::string output = "latency_report.json";
std::optional<std::string> export_csv;
std::optional<std::string> hosts_file;
int timeout_ms = 5000;
std::vector<HostTarget> hosts;
};
static std::atomic<bool> g_stop{false};
static std::string now_iso() {
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::tm tm {};
#if defined(_WIN32)
gmtime_s(&tm, &t);
#else
gmtime_r(&t, &tm);
#endif
char buf[32];
std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &tm);
return std::string(buf);
}
static std::string trim(const std::string& s) {
size_t b = 0;
while (b < s.size() && std::isspace(static_cast<unsigned char>(s[b]))) ++b;
size_t e = s.size();
while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1]))) --e;
return s.substr(b, e - b);
}
static std::optional<int64_t> parse_duration_ms(const std::optional<std::string>& raw) {
if (!raw.has_value()) return std::nullopt;
static const std::regex rx(R"(^(\d+)(ms|s|m|h)$)", std::regex::icase);
std::smatch m;
if (!std::regex_match(*raw, m, rx)) throw std::runtime_error("Invalid --duration value: " + *raw);
int64_t value = std::stoll(m[1].str());
std::string unit = m[2].str();
std::transform(unit.begin(), unit.end(), unit.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
if (unit == "ms") return value;
if (unit == "s") return value * 1000;
if (unit == "m") return value * 60 * 1000;
return value * 60 * 60 * 1000;
}
static AlertConfig parse_alert(const std::string& raw) {
AlertConfig a;
std::string v = raw;
std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
if (!v.empty() && v.back() == 'x') {
a.mode = "multiplier";
a.value = std::stod(v.substr(0, v.size() - 1));
} else {
a.mode = "absolute";
a.value = std::stod(v);
}
if (a.value <= 0.0) throw std::runtime_error("--alert must be positive");
return a;
}
static std::vector<HostTarget> parse_hosts_file(const std::string& path) {
std::ifstream in(path);
if (!in) throw std::runtime_error("Failed to open hosts file: " + path);
std::vector<HostTarget> out;
std::string line;
while (std::getline(in, line)) {
line = trim(line);
if (line.empty() || line[0] == '#') continue;
auto comma = line.find(',');
if (comma != std::string::npos) {
std::string host = trim(line.substr(0, comma));
std::string label = trim(line.substr(comma + 1));
if (!host.empty()) out.push_back({host, label.empty() ? host : label});
continue;
}
std::istringstream iss(line);
std::string host;
iss >> host;
std::string rest;
std::getline(iss, rest);
rest = trim(rest);
if (!host.empty()) out.push_back({host, rest.empty() ? host : rest});
}
return out;
}
static Config parse_args(int argc, char** argv) {
Config cfg;
std::optional<std::string> raw_duration;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg.rfind("--", 0) != 0) {
cfg.hosts.push_back({arg, arg});
continue;
}
if (i + 1 >= argc) throw std::runtime_error("Missing value for " + arg);
std::string val = argv[++i];
if (arg == "--interval") cfg.interval_sec = std::stod(val);
else if (arg == "--port") cfg.port = std::stoi(val);
else if (arg == "--duration") raw_duration = val;
else if (arg == "--alert") cfg.alert = parse_alert(val);
else if (arg == "--output") cfg.output = val;
else if (arg == "--export") cfg.export_csv = val;
else if (arg == "--file") cfg.hosts_file = val;
else if (arg == "--timeout") cfg.timeout_ms = std::stoi(val);
else throw std::runtime_error("Unknown option: " + arg);
}
cfg.duration_ms = parse_duration_ms(raw_duration);
if (cfg.interval_sec <= 0) throw std::runtime_error("--interval must be > 0");
if (cfg.port < 1 || cfg.port > 65535) throw std::runtime_error("--port must be 1..65535");
if (cfg.timeout_ms <= 0) throw std::runtime_error("--timeout must be > 0");
return cfg;
}
static std::optional<double> median(std::vector<double> values) {
if (values.empty()) return std::nullopt;
std::sort(values.begin(), values.end());
size_t mid = values.size() / 2;
if (values.size() % 2 == 0) return (values[mid - 1] + values[mid]) / 2.0;
return values[mid];
}
static std::optional<double> stddev(const std::vector<double>& values, std::optional<double> mean) {
if (values.empty() || !mean.has_value()) return std::nullopt;
if (values.size() < 2) return 0.0;
double sum = 0.0;
for (double v : values) sum += (v - *mean) * (v - *mean);
return std::sqrt(sum / static_cast<double>(values.size()));
}
static std::optional<double> average(const std::vector<double>& values) {
if (values.empty()) return std::nullopt;
return std::accumulate(values.begin(), values.end(), 0.0) / static_cast<double>(values.size());
}
struct ProbeResult {
bool success = false;
std::optional<double> rtt_ms;
std::string error;
};
static ProbeResult probe_tcp(const std::string& host, int port, int timeout_ms) {
addrinfo hints{};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo* result = nullptr;
int gai = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &result);
if (gai != 0) {
return {false, std::nullopt, std::string("dns_error:") + gai_strerror(gai)};
}
ProbeResult out{false, std::nullopt, "unreachable"};
for (addrinfo* rp = result; rp != nullptr; rp = rp->ai_next) {
int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0) continue;
int flags = fcntl(fd, F_GETFL, 0);
if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
auto start = std::chrono::steady_clock::now();
int rc = connect(fd, rp->ai_addr, rp->ai_addrlen);
if (rc == 0) {
auto ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
close(fd);
freeaddrinfo(result);
return {true, ms, ""};
}
if (errno == EINPROGRESS) {
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
timeval tv{};
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
int sel = select(fd + 1, nullptr, &wfds, nullptr, &tv);
if (sel > 0 && FD_ISSET(fd, &wfds)) {
int so_err = 0;
socklen_t len = sizeof(so_err);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &so_err, &len);
if (so_err == 0) {
auto ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
close(fd);
freeaddrinfo(result);
return {true, ms, ""};
}
out.error = std::string("connect_error:") + std::strerror(so_err);
} else if (sel == 0) {
out.error = "timeout";
} else {
out.error = std::string("select_error:") + std::strerror(errno);
}
} else {
out.error = std::string("connect_error:") + std::strerror(errno);
}
close(fd);
}
freeaddrinfo(result);
return out;
}
static std::string fmt_ms(const std::optional<double>& v) {
if (!v.has_value()) return "-";
std::ostringstream oss;
oss << std::fixed << std::setprecision(2) << *v << "ms";
return oss.str();
}
static json stats_json(const HostState& st) {
auto avg = average(st.rtts);
auto med = median(st.rtts);
auto sd = stddev(st.rtts, avg);
auto jitter = average(st.jitters);
json j;
j["sent"] = st.sent;
j["lost"] = st.lost;
j["success_count"] = st.rtts.size();
if (st.rtts.empty()) {
j["min_rtt_ms"] = nullptr;
j["max_rtt_ms"] = nullptr;
} else {
j["min_rtt_ms"] = *std::min_element(st.rtts.begin(), st.rtts.end());
j["max_rtt_ms"] = *std::max_element(st.rtts.begin(), st.rtts.end());
}
j["avg_rtt_ms"] = avg.has_value() ? json(*avg) : json(nullptr);
j["median_rtt_ms"] = med.has_value() ? json(*med) : json(nullptr);
j["stddev_rtt_ms"] = sd.has_value() ? json(*sd) : json(nullptr);
j["jitter_ms"] = jitter.has_value() ? json(*jitter) : json(nullptr);
j["packet_loss_pct"] = st.sent == 0 ? 0.0 : (st.lost * 100.0 / st.sent);
j["last_rtt_ms"] = st.last_rtt.has_value() ? json(*st.last_rtt) : json(nullptr);
return j;
}
static bool should_alert(const HostState& st, const AlertConfig& alert, double rtt) {
if (alert.mode == "absolute") return rtt > alert.value;
auto avg = average(st.rtts);
if (!avg.has_value()) return false;
return rtt > (*avg * alert.value);
}
static void render_dashboard(const std::vector<HostState>& states, int64_t started_ms, std::optional<int64_t> end_ms) {
std::cout << "\033[2J\033[H";
std::cout << "Network Latency Monitor " << now_iso() << "\n";
int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
if (end_ms.has_value()) {
int64_t rem = std::max<int64_t>(0, (*end_ms - now) / 1000);
std::cout << "Remaining: " << rem << "s\n";
} else {
int64_t elapsed = (now - started_ms) / 1000;
std::cout << "Elapsed: " << elapsed << "s\n";
}
std::cout << "--------------------------------------------------------------------------------------\n";
std::cout << "Host Last RTT Avg RTT Min/Max Loss % Alert\n";
std::cout << "--------------------------------------------------------------------------------------\n";
for (const auto& st : states) {
json s = stats_json(st);
std::string host = st.label + " (" + st.host + ")";
if (host.size() > 25) host = host.substr(0, 22) + "...";
if (host.size() < 25) host += std::string(25 - host.size(), ' ');
auto to_opt = [](const json& v) -> std::optional<double> {
if (v.is_null()) return std::nullopt;
return v.get<double>();
};
std::string minmax = fmt_ms(to_opt(s["min_rtt_ms"])) + "/" + fmt_ms(to_opt(s["max_rtt_ms"]));
if (minmax.size() < 18) minmax += std::string(18 - minmax.size(), ' ');
std::cout << host << " " << std::setw(10) << std::left << fmt_ms(to_opt(s["last_rtt_ms"]))
<< std::setw(10) << std::left << fmt_ms(to_opt(s["avg_rtt_ms"]))
<< minmax
<< std::setw(7) << std::right << std::fixed << std::setprecision(2) << s["packet_loss_pct"].get<double>() << "% "
<< (st.last_alert ? "ALERT" : "OK") << "\n";
}
std::cout << "--------------------------------------------------------------------------------------\n";
}
static void print_summary(std::vector<HostState> states) {
std::sort(states.begin(), states.end(), [](const HostState& a, const HostState& b) {
auto av = average(a.rtts).value_or(std::numeric_limits<double>::infinity());
auto bv = average(b.rtts).value_or(std::numeric_limits<double>::infinity());
return av < bv;
});
std::cout << "\nFinal Summary (best average RTT first)\n";
std::cout << "--------------------------------------------------------------------------\n";
for (const auto& st : states) {
json s = stats_json(st);
auto to_opt = [](const json& v) -> std::optional<double> {
if (v.is_null()) return std::nullopt;
return v.get<double>();
};
std::cout << st.label << " (" << st.host << ") avg=" << fmt_ms(to_opt(s["avg_rtt_ms"]))
<< " min=" << fmt_ms(to_opt(s["min_rtt_ms"]))
<< " max=" << fmt_ms(to_opt(s["max_rtt_ms"]))
<< " loss=" << std::fixed << std::setprecision(2) << s["packet_loss_pct"].get<double>() << "%"
<< " jitter=" << fmt_ms(to_opt(s["jitter_ms"])) << "\n";
}
}
static void write_json_report(
const std::vector<HostState>& states,
const Config& cfg,
int64_t started_ms,
int64_t ended_ms,
bool interrupted) {
json root;
root["generated_at"] = now_iso();
root["monitoring"] = {
{"started_at", started_ms},
{"ended_at", ended_ms},
{"interrupted", interrupted},
{"interval_sec", cfg.interval_sec},
{"port", cfg.port},
{"timeout_ms", cfg.timeout_ms},
{"duration_ms", cfg.duration_ms.has_value() ? json(*cfg.duration_ms) : json(nullptr)},
{"alert", {{"mode", cfg.alert.mode}, {"value", cfg.alert.value}}}};
root["hosts"] = json::array();
for (const auto& st : states) {
json h;
h["host"] = st.host;
h["label"] = st.label;
h["stats"] = stats_json(st);
h["measurements"] = json::array();
for (const auto& m : st.measurements) {
h["measurements"].push_back({
{"timestamp", m.timestamp},
{"success", m.success},
{"rtt_ms", m.rtt_ms.has_value() ? json(*m.rtt_ms) : json(nullptr)},
{"error", m.error.empty() ? json(nullptr) : json(m.error)},
{"alert", m.alert},
});
}
root["hosts"].push_back(h);
}
std::ofstream out(cfg.output);
if (!out) throw std::runtime_error("Failed to write JSON output: " + cfg.output);
out << root.dump(2) << "\n";
}
static void write_csv(const std::vector<HostState>& states, const std::string& path) {
std::ofstream out(path);
if (!out) throw std::runtime_error("Failed to write CSV output: " + path);
out << "timestamp,host,label,success,rtt_ms,error,alert\n";
for (const auto& st : states) {
for (const auto& m : st.measurements) {
out << "\"" << m.timestamp << "\","
<< "\"" << st.host << "\","
<< "\"" << st.label << "\","
<< (m.success ? "true" : "false") << ",";
if (m.rtt_ms.has_value()) out << std::fixed << std::setprecision(3) << *m.rtt_ms;
out << ",\"" << m.error << "\","
<< (m.alert ? "true" : "false") << "\n";
}
}
}
int main(int argc, char** argv) {
try {
std::signal(SIGINT, [](int) { g_stop.store(true); });
Config cfg = parse_args(argc, argv);
if (cfg.hosts_file.has_value()) {
auto file_hosts = parse_hosts_file(*cfg.hosts_file);
cfg.hosts.insert(cfg.hosts.end(), file_hosts.begin(), file_hosts.end());
}
if (cfg.hosts.empty()) {
cfg.hosts = {
{"8.8.8.8", "Google DNS"},
{"1.1.1.1", "Cloudflare DNS"},
{"example.com", "Example"},
{"google.com", "Google"},
};
cfg.duration_ms = 30'000;
}
std::vector<HostState> states;
for (const auto& h : cfg.hosts) states.push_back({h.host, h.label});
auto now_ms = []() -> int64_t {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
};
int64_t started_ms = now_ms();
std::optional<int64_t> end_ms = cfg.duration_ms.has_value() ? std::optional<int64_t>(started_ms + *cfg.duration_ms) : std::nullopt;
bool interrupted = false;
while (!g_stop.load() && (!end_ms.has_value() || now_ms() < *end_ms)) {
int64_t tick_start = now_ms();
std::vector<std::future<ProbeResult>> futures;
futures.reserve(states.size());
for (const auto& st : states) {
futures.emplace_back(std::async(std::launch::async, probe_tcp, st.host, cfg.port, cfg.timeout_ms));
}
for (size_t i = 0; i < states.size(); ++i) {
auto& st = states[i];
ProbeResult r = futures[i].get();
st.sent += 1;
Measurement m;
m.timestamp = now_iso();
m.success = r.success;
m.rtt_ms = r.rtt_ms;
m.error = r.error;
m.alert = false;
if (!r.success) {
st.lost += 1;
st.last_alert = false;
st.measurements.push_back(m);
continue;
}
double rtt = *r.rtt_ms;
bool alert = should_alert(st, cfg.alert, rtt);
if (st.last_rtt.has_value()) st.jitters.push_back(std::abs(rtt - *st.last_rtt));
st.last_rtt = rtt;
st.rtts.push_back(rtt);
st.last_alert = alert;
m.alert = alert;
st.measurements.push_back(m);
}
render_dashboard(states, started_ms, end_ms);
int64_t elapsed = now_ms() - tick_start;
int64_t wait_ms = std::max<int64_t>(0, static_cast<int64_t>(cfg.interval_sec * 1000.0) - elapsed);
std::this_thread::sleep_for(std::chrono::milliseconds(wait_ms));
}
if (g_stop.load()) interrupted = true;
int64_t ended_ms = now_ms();
render_dashboard(states, started_ms, end_ms);
print_summary(states);
write_json_report(states, cfg, started_ms, ended_ms, interrupted);
if (cfg.export_csv.has_value()) write_csv(states, *cfg.export_csv);
std::cout << "\nSaved JSON report: " << cfg.output << "\n";
if (cfg.export_csv.has_value()) std::cout << "Saved CSV export: " << *cfg.export_csv << "\n";
return 0;
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
return 1;
}
}