Skip to content

Commit

Permalink
feat: add tox network profile functionality
Browse files Browse the repository at this point in the history
Profiling can be enabled with the -s run option
  • Loading branch information
JFreegman committed Jan 31, 2025
1 parent ee3d75a commit de99ed2
Show file tree
Hide file tree
Showing 13 changed files with 460 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ LDFLAGS += ${USER_LDFLAGS}

OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o conference.o configdir.o curl_util.o execute.o
OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o groupchats.o groupchat_commands.o help.o
OBJ += init_queue.o input.o line_info.o log.o main.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o
OBJ += init_queue.o input.o line_info.o log.o main.o message_queue.o misc_tools.o name_lookup.o netprof.o notify.o prompt.o qr_code.o
OBJ += settings.o term_mplex.o toxic.o toxic_strings.o windows.o

# Check if debug build is enabled
Expand Down
3 changes: 3 additions & 0 deletions doc/toxic.conf.5.asc
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ OPTIONS
*show_group_connection_msg*;;
Enable group connection change notifications. true or false

*show_network_info*;;
Show network information in the UI home window. true or false

*nodelist_update_freq*;;
How often in days to update the DHT nodes list. (integer; 0 to disable)

Expand Down
3 changes: 3 additions & 0 deletions misc/toxic.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ ui = {
// true to show peer connection change messages in groups
show_group_connection_msg=true;

// true to show network information in the UI home window
show_network_info=true;

// How often in days to update the DHT nodes list. (0 to disable updates)
nodeslist_update_freq=7;

Expand Down
23 changes: 22 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ static void print_usage(void)
fprintf(stderr, " -p, --SOCKS5-proxy Use SOCKS5 proxy: Requires [IP] [port]\n");
fprintf(stderr, " -P, --HTTP-proxy Use HTTP proxy: Requires [IP] [port]\n");
fprintf(stderr, " -r, --namelist Use specified name lookup server list\n");
fprintf(stderr, " -s, --netstats Dump network stats to log on exit: Requires [path]\n");
fprintf(stderr, " -t, --force-tcp Force toxic to use a TCP connection (use with proxies)\n");
fprintf(stderr, " -T, --tcp-server Act as a TCP relay server: Requires [port]\n");
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
Expand Down Expand Up @@ -898,6 +899,7 @@ static void parse_args(Toxic *toxic, Init_Queue *init_q, int argc, char *argv[])
{"help", no_argument, 0, 'h'},
{"noconnect", no_argument, 0, 'o'},
{"namelist", required_argument, 0, 'r'},
{"netstats", required_argument, 0, 's'},
{"force-tcp", no_argument, 0, 't'},
{"tcp-server", required_argument, 0, 'T'},
{"SOCKS5-proxy", required_argument, 0, 'p'},
Expand All @@ -907,7 +909,7 @@ static void parse_args(Toxic *toxic, Init_Queue *init_q, int argc, char *argv[])
{NULL, no_argument, NULL, 0},
};

const char *opts_str = "4bdehLotuxvc:f:l:n:r:p:P:T:";
const char *opts_str = "4bdehLotuxvc:f:l:n:r:s:p:P:T:";
int opt = 0;
int indexptr = 0;

Expand Down Expand Up @@ -1103,6 +1105,25 @@ static void parse_args(Toxic *toxic, Init_Queue *init_q, int argc, char *argv[])
break;
}

case 's': {
if (optarg == NULL) {
init_queue_add(init_q, "Invalid argument for option: %d", opt);
break;
}

run_opts->netprof_fp = fopen(optarg, "w");

if (run_opts->netprof_fp != NULL) {
init_queue_add(init_q, "Network profile logging enabled. Logging to file: '%s'", optarg);
run_opts->netprof_log_dump = true;
run_opts->netprof_start_time = time(NULL);
} else {
init_queue_add(init_q, "Failed to open file '%s' for network profile logging.", optarg);
}

break;
}

case 'u': {
run_opts->unencrypt_data = true;
break;
Expand Down
302 changes: 302 additions & 0 deletions src/netprof.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
/* netprof.c
*
* Copyright (C) 2025 Toxic All Rights Reserved.
*
* This file is part of Toxic. Toxic is free software licensed
* under the GNU General Public License 3.0.
*/

#include <stdint.h>
#include <stdio.h>

#include "netprof.h"

#ifdef TOX_EXPERIMENTAL
#include <tox/tox_private.h>
#endif // TOX_EXPERIMENTAL

static void log_tcp_packet_id(FILE *fp, unsigned int id, uint64_t total, uint64_t TCP_id_sent, uint64_t TCP_id_recv)
{
switch (id) {
case TOX_NETPROF_PACKET_ID_ZERO:
case TOX_NETPROF_PACKET_ID_ONE:
case TOX_NETPROF_PACKET_ID_TWO:
case TOX_NETPROF_PACKET_ID_TCP_DISCONNECT:
case TOX_NETPROF_PACKET_ID_FOUR:
case TOX_NETPROF_PACKET_ID_TCP_PONG:
case TOX_NETPROF_PACKET_ID_TCP_OOB_SEND:
case TOX_NETPROF_PACKET_ID_TCP_OOB_RECV:
case TOX_NETPROF_PACKET_ID_TCP_ONION_REQUEST:
case TOX_NETPROF_PACKET_ID_TCP_ONION_RESPONSE:
case TOX_NETPROF_PACKET_ID_TCP_DATA: {
if (TCP_id_recv || TCP_id_sent) {
fprintf(fp, "0x%02x (total): %lu (%.2f%%)\n", id, TCP_id_sent + TCP_id_recv,
((float)TCP_id_recv + TCP_id_sent) / total * 100.0);
}

if (TCP_id_sent) {
fprintf(fp, "0x%02x (sent): %lu (%.2f%%)\n", id, TCP_id_sent, (float)TCP_id_sent / total * 100.0);
}

if (TCP_id_recv) {
fprintf(fp, "0x%02x (recv): %lu (%.2f%%)\n", id, TCP_id_recv, (float)TCP_id_recv / total * 100.0);
}

break;
}

default:
return;
}
}

static void log_udp_packet_id(FILE *fp, unsigned int id, uint64_t total, uint64_t UDP_id_sent, uint64_t UDP_id_recv)
{
switch (id) {
case TOX_NETPROF_PACKET_ID_ZERO:
case TOX_NETPROF_PACKET_ID_ONE:
case TOX_NETPROF_PACKET_ID_TWO:
case TOX_NETPROF_PACKET_ID_FOUR:
case TOX_NETPROF_PACKET_ID_COOKIE_REQUEST:
case TOX_NETPROF_PACKET_ID_COOKIE_RESPONSE:
case TOX_NETPROF_PACKET_ID_CRYPTO_HS:
case TOX_NETPROF_PACKET_ID_CRYPTO_DATA:
case TOX_NETPROF_PACKET_ID_CRYPTO:
case TOX_NETPROF_PACKET_ID_LAN_DISCOVERY:
case TOX_NETPROF_PACKET_ID_GC_HANDSHAKE:
case TOX_NETPROF_PACKET_ID_GC_LOSSLESS:
case TOX_NETPROF_PACKET_ID_GC_LOSSY:
case TOX_NETPROF_PACKET_ID_ONION_SEND_INITIAL:
case TOX_NETPROF_PACKET_ID_ONION_SEND_1:
case TOX_NETPROF_PACKET_ID_ONION_SEND_2:
case TOX_NETPROF_PACKET_ID_ANNOUNCE_REQUEST:
case TOX_NETPROF_PACKET_ID_ANNOUNCE_REQUEST_OLD:
case TOX_NETPROF_PACKET_ID_ANNOUNCE_RESPONSE:
case TOX_NETPROF_PACKET_ID_ANNOUNCE_RESPONSE_OLD:
case TOX_NETPROF_PACKET_ID_ONION_DATA_REQUEST:
case TOX_NETPROF_PACKET_ID_ONION_DATA_RESPONSE:
case TOX_NETPROF_PACKET_ID_ONION_RECV_3:
case TOX_NETPROF_PACKET_ID_ONION_RECV_2:
case TOX_NETPROF_PACKET_ID_ONION_RECV_1:
case TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO:
case TOX_NETPROF_PACKET_ID_FORWARD_REQUEST:
case TOX_NETPROF_PACKET_ID_FORWARDING:
case TOX_NETPROF_PACKET_ID_FORWARD_REPLY:
case TOX_NETPROF_PACKET_ID_DATA_SEARCH_REQUEST:
case TOX_NETPROF_PACKET_ID_DATA_SEARCH_RESPONSE:
case TOX_NETPROF_PACKET_ID_DATA_RETRIEVE_REQUEST:
case TOX_NETPROF_PACKET_ID_DATA_RETRIEVE_RESPONSE:
case TOX_NETPROF_PACKET_ID_STORE_ANNOUNCE_REQUEST:
case TOX_NETPROF_PACKET_ID_STORE_ANNOUNCE_RESPONSE: {
if (UDP_id_recv || UDP_id_sent) {
fprintf(fp, "0x%02x (total): %lu (%.2f%%)\n", id, UDP_id_sent + UDP_id_recv,
((float)UDP_id_recv + UDP_id_sent) / total * 100.0);
}

if (UDP_id_sent) {
fprintf(fp, "0x%02x (sent): %lu (%.2f%%)\n", id, UDP_id_sent, (float)UDP_id_sent / total * 100.0);
}

if (UDP_id_recv) {
fprintf(fp, "0x%02x (recv): %lu (%.2f%%)\n", id, UDP_id_recv, (float)UDP_id_recv / total * 100.0);
}

break;
}

default:
return;
}
}

static void dump_packet_id_counts(const Tox *tox, FILE *fp, uint64_t total_count, Tox_Netprof_Packet_Type packet_type)
{
if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
fprintf(fp, "--- TCP packet counts by packet ID --- \n");
} else {
fprintf(fp, "--- UDP packet counts by packet ID --- \n");
}

for (unsigned long i = TOX_NETPROF_PACKET_ID_ZERO; i <= TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO; ++i) {
const uint64_t id_count_sent = tox_netprof_get_packet_id_count(tox, packet_type, i, TOX_NETPROF_DIRECTION_SENT);
const uint64_t id_count_recv = tox_netprof_get_packet_id_count(tox, packet_type, i, TOX_NETPROF_DIRECTION_RECV);

if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
log_tcp_packet_id(fp, i, total_count, id_count_sent, id_count_recv);
} else {
log_udp_packet_id(fp, i, total_count, id_count_sent, id_count_recv);
}
}

fprintf(fp, "\n\n");
}

static void dump_packet_id_bytes(const Tox *tox, FILE *fp, uint64_t total_bytes, Tox_Netprof_Packet_Type packet_type)
{
if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
fprintf(fp, "--- TCP byte counts by packet ID --- \n");
} else {
fprintf(fp, "--- UDP byte counts by packet ID --- \n");
}

for (unsigned long i = TOX_NETPROF_PACKET_ID_ZERO; i <= TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO; ++i) {
const uint64_t id_bytes_sent = tox_netprof_get_packet_id_bytes(tox, packet_type, i, TOX_NETPROF_DIRECTION_SENT);
const uint64_t id_bytes_recv = tox_netprof_get_packet_id_bytes(tox, packet_type, i, TOX_NETPROF_DIRECTION_RECV);

if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
log_tcp_packet_id(fp, i, total_bytes, id_bytes_sent, id_bytes_recv);
} else {
log_udp_packet_id(fp, i, total_bytes, id_bytes_sent, id_bytes_recv);
}
}

fprintf(fp, "\n\n");
}

static void dump_packet_count_totals(const Tox *tox, FILE *fp, uint64_t total_packet_count,
uint64_t UDP_count_sent, uint64_t UDP_count_recv,
uint64_t TCP_count_sent, uint64_t TCP_count_recv)
{
const uint64_t total_UDP_count = UDP_count_sent + UDP_count_recv;
const uint64_t total_TCP_count = TCP_count_sent + TCP_count_recv;
const uint64_t total_packet_count_sent = UDP_count_sent + TCP_count_sent;
const uint64_t total_packet_count_recv = UDP_count_recv + TCP_count_recv;

fprintf(fp, "--- Total packet counts --- \n");

fprintf(fp, "Total packets: %lu\n", total_packet_count);

fprintf(fp, "Total packets sent: %lu (%.2f%%)\n", total_packet_count_sent,
(float)total_packet_count_sent / total_packet_count * 100.0);

fprintf(fp, "Total packets recv: %lu (%.2f%%)\n", total_packet_count_recv,
(float)total_packet_count_recv / total_packet_count * 100.0);

fprintf(fp, "total UDP packets: %lu (%.2f%%)\n", total_UDP_count,
(float)total_UDP_count / total_packet_count * 100.0);

fprintf(fp, "UDP packets sent: %lu (%.2f%%)\n", UDP_count_sent,
(float)UDP_count_sent / total_packet_count * 100.0);

fprintf(fp, "UDP packets recv: %lu (%.2f%%)\n", UDP_count_recv,
(float)UDP_count_recv / total_packet_count * 100.0);

fprintf(fp, "Total TCP packets: %lu (%.2f%%)\n", total_TCP_count,
(float)total_TCP_count / total_packet_count * 100.0);

fprintf(fp, "TCP packets sent: %lu (%.2f%%)\n", TCP_count_sent,
(float)TCP_count_sent / total_packet_count * 100.0);

fprintf(fp, "TCP packets recv: %lu (%.2f%%)\n", TCP_count_recv,
(float)TCP_count_recv / total_packet_count * 100.0);

fprintf(fp, "\n\n");
}

static void dump_packet_bytes_totals(const Tox *tox, FILE *fp, const uint64_t total_bytes,
const uint64_t UDP_bytes_sent, const uint64_t UDP_bytes_recv,
const uint64_t TCP_bytes_sent, const uint64_t TCP_bytes_recv)
{
const uint64_t total_UDP_bytes = UDP_bytes_sent + UDP_bytes_recv;
const uint64_t total_TCP_bytes = TCP_bytes_sent + TCP_bytes_recv;
const uint64_t total_bytes_sent = UDP_bytes_sent + TCP_bytes_sent;
const uint64_t total_bytes_recv = UDP_bytes_recv + TCP_bytes_recv;

fprintf(fp, "--- Total byte counts --- \n");

fprintf(fp, "Total bytes: %lu\n", total_bytes);

fprintf(fp, "Total bytes sent: %lu (%.2f%%)\n", total_bytes_sent,
(float)total_bytes_sent / total_bytes * 100.0);

fprintf(fp, "Total bytes recv: %lu (%.2f%%)\n", total_bytes_recv,
(float)total_bytes_recv / total_bytes * 100.0);

fprintf(fp, "Total UDP bytes: %lu (%.2f%%)\n", total_UDP_bytes,
(float)total_UDP_bytes / total_bytes * 100.0);

fprintf(fp, "UDP bytes sent: %lu (%.2f%%)\n", UDP_bytes_sent,
(float)UDP_bytes_sent / total_bytes * 100.0);

fprintf(fp, "UDP bytes recv: %lu (%.2f%%)\n", UDP_bytes_recv,
(float)UDP_bytes_recv / total_bytes * 100.0);

fprintf(fp, "Total TCP bytes: %lu (%.2f%%)\n", total_TCP_bytes,
(float)total_TCP_bytes / total_bytes * 100.0);

fprintf(fp, "TCP bytes sent: %lu (%.2f%%)\n", TCP_bytes_sent,
(float)TCP_bytes_sent / total_bytes * 100.0);

fprintf(fp, "TCP bytes recv: %lu (%.2f%%)\n", TCP_bytes_recv,
(float)TCP_bytes_recv / total_bytes * 100.0);

fprintf(fp, "\n\n");
}

void netprof_log_dump(const Tox *tox, FILE *fp, time_t run_time)
{
if (fp == NULL) {
fprintf(stderr, "Failed to dump network statistics: null file pointer\n");
return;
}

const uint64_t UDP_count_sent = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_UDP,
TOX_NETPROF_DIRECTION_SENT);
const uint64_t UDP_count_recv = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_UDP,
TOX_NETPROF_DIRECTION_RECV);
const uint64_t TCP_count_sent = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_TCP,
TOX_NETPROF_DIRECTION_SENT);
const uint64_t TCP_count_recv = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_TCP,
TOX_NETPROF_DIRECTION_RECV);
const uint64_t UDP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
TOX_NETPROF_DIRECTION_SENT);
const uint64_t UDP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
TOX_NETPROF_DIRECTION_RECV);
const uint64_t TCP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
TOX_NETPROF_DIRECTION_SENT);
const uint64_t TCP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
TOX_NETPROF_DIRECTION_RECV);

const uint64_t total_count = UDP_count_sent + UDP_count_recv + TCP_count_sent + TCP_count_recv;
const uint64_t total_bytes = UDP_bytes_sent + UDP_bytes_recv + TCP_bytes_sent + TCP_bytes_recv;

fprintf(fp, "--- Tox network profile log dump ---\n");
fprintf(fp, "Run time: %lu seconds\n", run_time);

if (run_time && total_count && total_bytes) {
fprintf(fp, "Average kilobytes per second: %.2f\n", ((float)total_bytes / run_time) / 1000.0);
fprintf(fp, "Average packets per second: %lu\n", total_count / run_time);
fprintf(fp, "Average packet size: %lu bytes\n", total_bytes / total_count);
fprintf(fp, "\n");
}

dump_packet_count_totals(tox, fp, total_count, UDP_count_sent, UDP_count_recv, TCP_count_sent, TCP_count_recv);
dump_packet_bytes_totals(tox, fp, total_bytes, UDP_bytes_sent, UDP_bytes_recv, TCP_bytes_sent, TCP_bytes_recv);
dump_packet_id_counts(tox, fp, total_count, TOX_NETPROF_PACKET_TYPE_TCP);
dump_packet_id_counts(tox, fp, total_count, TOX_NETPROF_PACKET_TYPE_UDP);
dump_packet_id_bytes(tox, fp, total_bytes, TOX_NETPROF_PACKET_TYPE_TCP);
dump_packet_id_bytes(tox, fp, total_bytes, TOX_NETPROF_PACKET_TYPE_UDP);

fflush(fp);
}

uint64_t netprof_get_bytes_up(const Tox *tox)
{
const uint64_t UDP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
TOX_NETPROF_DIRECTION_SENT);
const uint64_t TCP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
TOX_NETPROF_DIRECTION_SENT);

return UDP_bytes_sent + TCP_bytes_sent;

}

uint64_t netprof_get_bytes_down(const Tox *tox)
{
const uint64_t UDP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
TOX_NETPROF_DIRECTION_RECV);
const uint64_t TCP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
TOX_NETPROF_DIRECTION_RECV);

return UDP_bytes_recv + TCP_bytes_recv;
}
Loading

0 comments on commit de99ed2

Please sign in to comment.