Skip to content

Commit 14deb31

Browse files
committed
feat: add tox network profile functionality
Profiling can be enabled with the -s run option
1 parent cb4e3a7 commit 14deb31

File tree

13 files changed

+460
-5
lines changed

13 files changed

+460
-5
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LDFLAGS += ${USER_LDFLAGS}
1414

1515
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o conference.o configdir.o curl_util.o execute.o
1616
OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o groupchats.o groupchat_commands.o help.o
17-
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
17+
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
1818
OBJ += settings.o term_mplex.o toxic.o toxic_strings.o windows.o
1919

2020
# Check if debug build is enabled

doc/toxic.conf.5.asc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ OPTIONS
9393
*show_group_connection_msg*;;
9494
Enable group connection change notifications. true or false
9595

96+
*show_network_info*;;
97+
Show network information in the UI home window. true or false
98+
9699
*nodelist_update_freq*;;
97100
How often in days to update the DHT nodes list. (integer; 0 to disable)
98101

misc/toxic.conf.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ ui = {
7070
// true to show peer connection change messages in groups
7171
show_group_connection_msg=true;
7272

73+
// true to show network information in the UI home window
74+
show_network_info=true;
75+
7376
// How often in days to update the DHT nodes list. (0 to disable updates)
7477
nodeslist_update_freq=7;
7578

src/main.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,7 @@ static void print_usage(void)
864864
fprintf(stderr, " -p, --SOCKS5-proxy Use SOCKS5 proxy: Requires [IP] [port]\n");
865865
fprintf(stderr, " -P, --HTTP-proxy Use HTTP proxy: Requires [IP] [port]\n");
866866
fprintf(stderr, " -r, --namelist Use specified name lookup server list\n");
867+
fprintf(stderr, " -s, --netstats Dump network stats to log on exit: Requires [path]\n");
867868
fprintf(stderr, " -t, --force-tcp Force toxic to use a TCP connection (use with proxies)\n");
868869
fprintf(stderr, " -T, --tcp-server Act as a TCP relay server: Requires [port]\n");
869870
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
@@ -898,6 +899,7 @@ static void parse_args(Toxic *toxic, Init_Queue *init_q, int argc, char *argv[])
898899
{"help", no_argument, 0, 'h'},
899900
{"noconnect", no_argument, 0, 'o'},
900901
{"namelist", required_argument, 0, 'r'},
902+
{"netstats", required_argument, 0, 's'},
901903
{"force-tcp", no_argument, 0, 't'},
902904
{"tcp-server", required_argument, 0, 'T'},
903905
{"SOCKS5-proxy", required_argument, 0, 'p'},
@@ -907,7 +909,7 @@ static void parse_args(Toxic *toxic, Init_Queue *init_q, int argc, char *argv[])
907909
{NULL, no_argument, NULL, 0},
908910
};
909911

910-
const char *opts_str = "4bdehLotuxvc:f:l:n:r:p:P:T:";
912+
const char *opts_str = "4bdehLotuxvc:f:l:n:r:s:p:P:T:";
911913
int opt = 0;
912914
int indexptr = 0;
913915

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

1108+
case 's': {
1109+
if (optarg == NULL) {
1110+
init_queue_add(init_q, "Invalid argument for option: %d", opt);
1111+
break;
1112+
}
1113+
1114+
run_opts->netprof_fp = fopen(optarg, "w");
1115+
1116+
if (run_opts->netprof_fp != NULL) {
1117+
init_queue_add(init_q, "Network profile logging enabled. Logging to file: '%s'", optarg);
1118+
run_opts->netprof_log_dump = true;
1119+
run_opts->netprof_start_time = time(NULL);
1120+
} else {
1121+
init_queue_add(init_q, "Failed to open file '%s' for network profile logging.", optarg);
1122+
}
1123+
1124+
break;
1125+
}
1126+
11061127
case 'u': {
11071128
run_opts->unencrypt_data = true;
11081129
break;

src/netprof.c

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
/* netprof.c
2+
*
3+
* Copyright (C) 2025 Toxic All Rights Reserved.
4+
*
5+
* This file is part of Toxic. Toxic is free software licensed
6+
* under the GNU General Public License 3.0.
7+
*/
8+
9+
#include <stdint.h>
10+
#include <stdio.h>
11+
12+
#include "netprof.h"
13+
14+
#ifdef TOX_EXPERIMENTAL
15+
#include <tox/tox_private.h>
16+
#endif // TOX_EXPERIMENTAL
17+
18+
static void log_tcp_packet_id(FILE *fp, unsigned int id, uint64_t total, uint64_t TCP_id_sent, uint64_t TCP_id_recv)
19+
{
20+
switch (id) {
21+
case TOX_NETPROF_PACKET_ID_ZERO:
22+
case TOX_NETPROF_PACKET_ID_ONE:
23+
case TOX_NETPROF_PACKET_ID_TWO:
24+
case TOX_NETPROF_PACKET_ID_TCP_DISCONNECT:
25+
case TOX_NETPROF_PACKET_ID_FOUR:
26+
case TOX_NETPROF_PACKET_ID_TCP_PONG:
27+
case TOX_NETPROF_PACKET_ID_TCP_OOB_SEND:
28+
case TOX_NETPROF_PACKET_ID_TCP_OOB_RECV:
29+
case TOX_NETPROF_PACKET_ID_TCP_ONION_REQUEST:
30+
case TOX_NETPROF_PACKET_ID_TCP_ONION_RESPONSE:
31+
case TOX_NETPROF_PACKET_ID_TCP_DATA: {
32+
if (TCP_id_recv || TCP_id_sent) {
33+
fprintf(fp, "0x%02x (total): %lu (%.2f%%)\n", id, TCP_id_sent + TCP_id_recv,
34+
((float)TCP_id_recv + TCP_id_sent) / total * 100.0);
35+
}
36+
37+
if (TCP_id_sent) {
38+
fprintf(fp, "0x%02x (sent): %lu (%.2f%%)\n", id, TCP_id_sent, (float)TCP_id_sent / total * 100.0);
39+
}
40+
41+
if (TCP_id_recv) {
42+
fprintf(fp, "0x%02x (recv): %lu (%.2f%%)\n", id, TCP_id_recv, (float)TCP_id_recv / total * 100.0);
43+
}
44+
45+
break;
46+
}
47+
48+
default:
49+
return;
50+
}
51+
}
52+
53+
static void log_udp_packet_id(FILE *fp, unsigned int id, uint64_t total, uint64_t UDP_id_sent, uint64_t UDP_id_recv)
54+
{
55+
switch (id) {
56+
case TOX_NETPROF_PACKET_ID_ZERO:
57+
case TOX_NETPROF_PACKET_ID_ONE:
58+
case TOX_NETPROF_PACKET_ID_TWO:
59+
case TOX_NETPROF_PACKET_ID_FOUR:
60+
case TOX_NETPROF_PACKET_ID_COOKIE_REQUEST:
61+
case TOX_NETPROF_PACKET_ID_COOKIE_RESPONSE:
62+
case TOX_NETPROF_PACKET_ID_CRYPTO_HS:
63+
case TOX_NETPROF_PACKET_ID_CRYPTO_DATA:
64+
case TOX_NETPROF_PACKET_ID_CRYPTO:
65+
case TOX_NETPROF_PACKET_ID_LAN_DISCOVERY:
66+
case TOX_NETPROF_PACKET_ID_GC_HANDSHAKE:
67+
case TOX_NETPROF_PACKET_ID_GC_LOSSLESS:
68+
case TOX_NETPROF_PACKET_ID_GC_LOSSY:
69+
case TOX_NETPROF_PACKET_ID_ONION_SEND_INITIAL:
70+
case TOX_NETPROF_PACKET_ID_ONION_SEND_1:
71+
case TOX_NETPROF_PACKET_ID_ONION_SEND_2:
72+
case TOX_NETPROF_PACKET_ID_ANNOUNCE_REQUEST:
73+
case TOX_NETPROF_PACKET_ID_ANNOUNCE_REQUEST_OLD:
74+
case TOX_NETPROF_PACKET_ID_ANNOUNCE_RESPONSE:
75+
case TOX_NETPROF_PACKET_ID_ANNOUNCE_RESPONSE_OLD:
76+
case TOX_NETPROF_PACKET_ID_ONION_DATA_REQUEST:
77+
case TOX_NETPROF_PACKET_ID_ONION_DATA_RESPONSE:
78+
case TOX_NETPROF_PACKET_ID_ONION_RECV_3:
79+
case TOX_NETPROF_PACKET_ID_ONION_RECV_2:
80+
case TOX_NETPROF_PACKET_ID_ONION_RECV_1:
81+
case TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO:
82+
case TOX_NETPROF_PACKET_ID_FORWARD_REQUEST:
83+
case TOX_NETPROF_PACKET_ID_FORWARDING:
84+
case TOX_NETPROF_PACKET_ID_FORWARD_REPLY:
85+
case TOX_NETPROF_PACKET_ID_DATA_SEARCH_REQUEST:
86+
case TOX_NETPROF_PACKET_ID_DATA_SEARCH_RESPONSE:
87+
case TOX_NETPROF_PACKET_ID_DATA_RETRIEVE_REQUEST:
88+
case TOX_NETPROF_PACKET_ID_DATA_RETRIEVE_RESPONSE:
89+
case TOX_NETPROF_PACKET_ID_STORE_ANNOUNCE_REQUEST:
90+
case TOX_NETPROF_PACKET_ID_STORE_ANNOUNCE_RESPONSE: {
91+
if (UDP_id_recv || UDP_id_sent) {
92+
fprintf(fp, "0x%02x (total): %lu (%.2f%%)\n", id, UDP_id_sent + UDP_id_recv,
93+
((float)UDP_id_recv + UDP_id_sent) / total * 100.0);
94+
}
95+
96+
if (UDP_id_sent) {
97+
fprintf(fp, "0x%02x (sent): %lu (%.2f%%)\n", id, UDP_id_sent, (float)UDP_id_sent / total * 100.0);
98+
}
99+
100+
if (UDP_id_recv) {
101+
fprintf(fp, "0x%02x (recv): %lu (%.2f%%)\n", id, UDP_id_recv, (float)UDP_id_recv / total * 100.0);
102+
}
103+
104+
break;
105+
}
106+
107+
default:
108+
return;
109+
}
110+
}
111+
112+
static void dump_packet_id_counts(const Tox *tox, FILE *fp, uint64_t total_count, Tox_Netprof_Packet_Type packet_type)
113+
{
114+
if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
115+
fprintf(fp, "--- TCP packet counts by packet ID --- \n");
116+
} else {
117+
fprintf(fp, "--- UDP packet counts by packet ID --- \n");
118+
}
119+
120+
for (unsigned long i = TOX_NETPROF_PACKET_ID_ZERO; i <= TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO; ++i) {
121+
const uint64_t id_count_sent = tox_netprof_get_packet_id_count(tox, packet_type, i, TOX_NETPROF_DIRECTION_SENT);
122+
const uint64_t id_count_recv = tox_netprof_get_packet_id_count(tox, packet_type, i, TOX_NETPROF_DIRECTION_RECV);
123+
124+
if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
125+
log_tcp_packet_id(fp, i, total_count, id_count_sent, id_count_recv);
126+
} else {
127+
log_udp_packet_id(fp, i, total_count, id_count_sent, id_count_recv);
128+
}
129+
}
130+
131+
fprintf(fp, "\n\n");
132+
}
133+
134+
static void dump_packet_id_bytes(const Tox *tox, FILE *fp, uint64_t total_bytes, Tox_Netprof_Packet_Type packet_type)
135+
{
136+
if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
137+
fprintf(fp, "--- TCP byte counts by packet ID --- \n");
138+
} else {
139+
fprintf(fp, "--- UDP byte counts by packet ID --- \n");
140+
}
141+
142+
for (unsigned long i = TOX_NETPROF_PACKET_ID_ZERO; i <= TOX_NETPROF_PACKET_ID_BOOTSTRAP_INFO; ++i) {
143+
const uint64_t id_bytes_sent = tox_netprof_get_packet_id_bytes(tox, packet_type, i, TOX_NETPROF_DIRECTION_SENT);
144+
const uint64_t id_bytes_recv = tox_netprof_get_packet_id_bytes(tox, packet_type, i, TOX_NETPROF_DIRECTION_RECV);
145+
146+
if (packet_type == TOX_NETPROF_PACKET_TYPE_TCP) {
147+
log_tcp_packet_id(fp, i, total_bytes, id_bytes_sent, id_bytes_recv);
148+
} else {
149+
log_udp_packet_id(fp, i, total_bytes, id_bytes_sent, id_bytes_recv);
150+
}
151+
}
152+
153+
fprintf(fp, "\n\n");
154+
}
155+
156+
static void dump_packet_count_totals(const Tox *tox, FILE *fp, uint64_t total_packet_count,
157+
uint64_t UDP_count_sent, uint64_t UDP_count_recv,
158+
uint64_t TCP_count_sent, uint64_t TCP_count_recv)
159+
{
160+
const uint64_t total_UDP_count = UDP_count_sent + UDP_count_recv;
161+
const uint64_t total_TCP_count = TCP_count_sent + TCP_count_recv;
162+
const uint64_t total_packet_count_sent = UDP_count_sent + TCP_count_sent;
163+
const uint64_t total_packet_count_recv = UDP_count_recv + TCP_count_recv;
164+
165+
fprintf(fp, "--- Total packet counts --- \n");
166+
167+
fprintf(fp, "Total packets: %lu\n", total_packet_count);
168+
169+
fprintf(fp, "Total packets sent: %lu (%.2f%%)\n", total_packet_count_sent,
170+
(float)total_packet_count_sent / total_packet_count * 100.0);
171+
172+
fprintf(fp, "Total packets recv: %lu (%.2f%%)\n", total_packet_count_recv,
173+
(float)total_packet_count_recv / total_packet_count * 100.0);
174+
175+
fprintf(fp, "total UDP packets: %lu (%.2f%%)\n", total_UDP_count,
176+
(float)total_UDP_count / total_packet_count * 100.0);
177+
178+
fprintf(fp, "UDP packets sent: %lu (%.2f%%)\n", UDP_count_sent,
179+
(float)UDP_count_sent / total_packet_count * 100.0);
180+
181+
fprintf(fp, "UDP packets recv: %lu (%.2f%%)\n", UDP_count_recv,
182+
(float)UDP_count_recv / total_packet_count * 100.0);
183+
184+
fprintf(fp, "Total TCP packets: %lu (%.2f%%)\n", total_TCP_count,
185+
(float)total_TCP_count / total_packet_count * 100.0);
186+
187+
fprintf(fp, "TCP packets sent: %lu (%.2f%%)\n", TCP_count_sent,
188+
(float)TCP_count_sent / total_packet_count * 100.0);
189+
190+
fprintf(fp, "TCP packets recv: %lu (%.2f%%)\n", TCP_count_recv,
191+
(float)TCP_count_recv / total_packet_count * 100.0);
192+
193+
fprintf(fp, "\n\n");
194+
}
195+
196+
static void dump_packet_bytes_totals(const Tox *tox, FILE *fp, const uint64_t total_bytes,
197+
const uint64_t UDP_bytes_sent, const uint64_t UDP_bytes_recv,
198+
const uint64_t TCP_bytes_sent, const uint64_t TCP_bytes_recv)
199+
{
200+
const uint64_t total_UDP_bytes = UDP_bytes_sent + UDP_bytes_recv;
201+
const uint64_t total_TCP_bytes = TCP_bytes_sent + TCP_bytes_recv;
202+
const uint64_t total_bytes_sent = UDP_bytes_sent + TCP_bytes_sent;
203+
const uint64_t total_bytes_recv = UDP_bytes_recv + TCP_bytes_recv;
204+
205+
fprintf(fp, "--- Total byte counts --- \n");
206+
207+
fprintf(fp, "Total bytes: %lu\n", total_bytes);
208+
209+
fprintf(fp, "Total bytes sent: %lu (%.2f%%)\n", total_bytes_sent,
210+
(float)total_bytes_sent / total_bytes * 100.0);
211+
212+
fprintf(fp, "Total bytes recv: %lu (%.2f%%)\n", total_bytes_recv,
213+
(float)total_bytes_recv / total_bytes * 100.0);
214+
215+
fprintf(fp, "Total UDP bytes: %lu (%.2f%%)\n", total_UDP_bytes,
216+
(float)total_UDP_bytes / total_bytes * 100.0);
217+
218+
fprintf(fp, "UDP bytes sent: %lu (%.2f%%)\n", UDP_bytes_sent,
219+
(float)UDP_bytes_sent / total_bytes * 100.0);
220+
221+
fprintf(fp, "UDP bytes recv: %lu (%.2f%%)\n", UDP_bytes_recv,
222+
(float)UDP_bytes_recv / total_bytes * 100.0);
223+
224+
fprintf(fp, "Total TCP bytes: %lu (%.2f%%)\n", total_TCP_bytes,
225+
(float)total_TCP_bytes / total_bytes * 100.0);
226+
227+
fprintf(fp, "TCP bytes sent: %lu (%.2f%%)\n", TCP_bytes_sent,
228+
(float)TCP_bytes_sent / total_bytes * 100.0);
229+
230+
fprintf(fp, "TCP bytes recv: %lu (%.2f%%)\n", TCP_bytes_recv,
231+
(float)TCP_bytes_recv / total_bytes * 100.0);
232+
233+
fprintf(fp, "\n\n");
234+
}
235+
236+
void netprof_log_dump(const Tox *tox, FILE *fp, time_t run_time)
237+
{
238+
if (fp == NULL) {
239+
fprintf(stderr, "Failed to dump network statistics: null file pointer\n");
240+
return;
241+
}
242+
243+
const uint64_t UDP_count_sent = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_UDP,
244+
TOX_NETPROF_DIRECTION_SENT);
245+
const uint64_t UDP_count_recv = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_UDP,
246+
TOX_NETPROF_DIRECTION_RECV);
247+
const uint64_t TCP_count_sent = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_TCP,
248+
TOX_NETPROF_DIRECTION_SENT);
249+
const uint64_t TCP_count_recv = tox_netprof_get_packet_total_count(tox, TOX_NETPROF_PACKET_TYPE_TCP,
250+
TOX_NETPROF_DIRECTION_RECV);
251+
const uint64_t UDP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
252+
TOX_NETPROF_DIRECTION_SENT);
253+
const uint64_t UDP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
254+
TOX_NETPROF_DIRECTION_RECV);
255+
const uint64_t TCP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
256+
TOX_NETPROF_DIRECTION_SENT);
257+
const uint64_t TCP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
258+
TOX_NETPROF_DIRECTION_RECV);
259+
260+
const uint64_t total_count = UDP_count_sent + UDP_count_recv + TCP_count_sent + TCP_count_recv;
261+
const uint64_t total_bytes = UDP_bytes_sent + UDP_bytes_recv + TCP_bytes_sent + TCP_bytes_recv;
262+
263+
fprintf(fp, "--- Tox network profile log dump ---\n");
264+
fprintf(fp, "Run time: %lu seconds\n", run_time);
265+
266+
if (run_time && total_count && total_bytes) {
267+
fprintf(fp, "Average kilobytes per second: %.2f\n", ((float)total_bytes / run_time) / 1000.0);
268+
fprintf(fp, "Average packets per second: %lu\n", total_count / run_time);
269+
fprintf(fp, "Average packet size: %lu bytes\n", total_bytes / total_count);
270+
fprintf(fp, "\n");
271+
}
272+
273+
dump_packet_count_totals(tox, fp, total_count, UDP_count_sent, UDP_count_recv, TCP_count_sent, TCP_count_recv);
274+
dump_packet_bytes_totals(tox, fp, total_bytes, UDP_bytes_sent, UDP_bytes_recv, TCP_bytes_sent, TCP_bytes_recv);
275+
dump_packet_id_counts(tox, fp, total_count, TOX_NETPROF_PACKET_TYPE_TCP);
276+
dump_packet_id_counts(tox, fp, total_count, TOX_NETPROF_PACKET_TYPE_UDP);
277+
dump_packet_id_bytes(tox, fp, total_bytes, TOX_NETPROF_PACKET_TYPE_TCP);
278+
dump_packet_id_bytes(tox, fp, total_bytes, TOX_NETPROF_PACKET_TYPE_UDP);
279+
280+
fflush(fp);
281+
}
282+
283+
uint64_t netprof_get_bytes_up(const Tox *tox)
284+
{
285+
const uint64_t UDP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
286+
TOX_NETPROF_DIRECTION_SENT);
287+
const uint64_t TCP_bytes_sent = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
288+
TOX_NETPROF_DIRECTION_SENT);
289+
290+
return UDP_bytes_sent + TCP_bytes_sent;
291+
292+
}
293+
294+
uint64_t netprof_get_bytes_down(const Tox *tox)
295+
{
296+
const uint64_t UDP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_UDP,
297+
TOX_NETPROF_DIRECTION_RECV);
298+
const uint64_t TCP_bytes_recv = tox_netprof_get_packet_total_bytes(tox, TOX_NETPROF_PACKET_TYPE_TCP,
299+
TOX_NETPROF_DIRECTION_RECV);
300+
301+
return UDP_bytes_recv + TCP_bytes_recv;
302+
}

0 commit comments

Comments
 (0)