summaryrefslogtreecommitdiff
path: root/ot_stats.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_stats.c')
-rw-r--r--ot_stats.c828
1 files changed, 0 insertions, 828 deletions
diff --git a/ot_stats.c b/ot_stats.c
deleted file mode 100644
index d38355a..0000000
--- a/ot_stats.c
+++ /dev/null
@@ -1,828 +0,0 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <inttypes.h>
8#include <pthread.h>
9#include <stdatomic.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/types.h>
14#include <sys/uio.h>
15#include <unistd.h>
16#ifdef WANT_SYSLOGS
17#include <syslog.h>
18#endif
19
20/* Opentracker */
21#include "ot_accesslist.h"
22#include "ot_hex.h"
23#include "ot_iovec.h"
24#include "ot_ip.h"
25#include "ot_mutex.h"
26#include "ot_workqueue.h"
27#include "ot_fmt.h"
28#include "ot_stats.h"
29#include "trackerlogic.h"
30
31#ifndef NO_FULLSCRAPE_LOGGING
32#define LOG_TO_STDERR(...) fprintf(stderr, __VA_ARGS__)
33#else
34#define LOG_TO_STDERR(...)
35#endif
36
37/* Forward declaration */
38static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode);
39#define OT_STATS_TMPSIZE 8192
40
41/* Each serving thread keeps its own statistics tally. The list is linked
42 through intrusive next pointers within the workstruct.
43
44 Registration happens once per thread and the adding up once per statistics
45 request, so this lock is never on anybody's hot path. */
46static struct ot_workstruct *g_counter_contexts;
47static ot_counter g_retired[OT_COUNTER_COUNT];
48static pthread_mutex_t g_counter_mutex = PTHREAD_MUTEX_INITIALIZER;
49
50void stats_counters_register(struct ot_workstruct *ws) {
51 pthread_mutex_lock(&g_counter_mutex);
52 ws->counters_next = g_counter_contexts;
53 g_counter_contexts = ws;
54 pthread_mutex_unlock(&g_counter_mutex);
55}
56
57void stats_counters_retire(struct ot_workstruct *ws) {
58 struct ot_workstruct **link;
59 size_t i;
60
61 pthread_mutex_lock(&g_counter_mutex);
62 for (link = &g_counter_contexts; *link; link = &(*link)->counters_next)
63 if (*link == ws) {
64 *link = ws->counters_next;
65 break;
66 }
67
68 /* When retiring a worker thread, collect the counter tally in the global */
69 for (i = 0; i < OT_COUNTER_COUNT; ++i)
70 atomic_fetch_add_explicit(g_retired + i, atomic_load_explicit(ws->counters + i, memory_order_relaxed), memory_order_relaxed);
71 pthread_mutex_unlock(&g_counter_mutex);
72}
73
74/* What every thread has counted so far, added together */
75static void counters_total(unsigned long long *total) {
76 struct ot_workstruct *ws;
77 size_t i;
78
79 pthread_mutex_lock(&g_counter_mutex);
80 for (i = 0; i < OT_COUNTER_COUNT; ++i)
81 total[i] = atomic_load_explicit(g_retired + i, memory_order_relaxed);
82 for (ws = g_counter_contexts; ws; ws = ws->counters_next)
83 for (i = 0; i < OT_COUNTER_COUNT; ++i)
84 total[i] += atomic_load_explicit(ws->counters + i, memory_order_relaxed);
85 pthread_mutex_unlock(&g_counter_mutex);
86}
87
88#define COUNT(index, by) atomic_fetch_add_explicit(ws->counters + (index), (unsigned long long)(by), memory_order_relaxed)
89
90static const char *ot_failed_request_names[] = {
91 "302 Redirect", "400 Parse Error", "400 Invalid Parameter", "400 Invalid Parameter (compact=0)", "400 Not Modest",
92 "402 Payment Required", "403 Access Denied", "404 Not found", "500 Internal Server Error"};
93
94static time_t ot_start_time;
95
96#define STATS_NETWORK_NODE_BITWIDTH 4
97#define STATS_NETWORK_NODE_COUNT (1 << STATS_NETWORK_NODE_BITWIDTH)
98
99#define __BYTE(P, D) (((uint8_t *)P)[D / 8])
100#define __MSK (STATS_NETWORK_NODE_COUNT - 1)
101#define __SHFT(D) ((D ^ STATS_NETWORK_NODE_BITWIDTH) & STATS_NETWORK_NODE_BITWIDTH)
102
103#define __LDR(P, D) ((__BYTE((P), (D)) >> __SHFT((D))) & __MSK)
104#define __STR(P, D, V) __BYTE((P), (D)) = (__BYTE((P), (D)) & ~(__MSK << __SHFT((D)))) | ((V) << __SHFT((D)))
105
106#if 0
107// XXX
108#define STATS_NETWORK_NODE_MAXDEPTH (68 - STATS_NETWORK_NODE_BITWIDTH)
109#define STATS_NETWORK_NODE_LIMIT (48 - STATS_NETWORK_NODE_BITWIDTH)
110#endif
111#define STATS_NETWORK_NODE_MAXDEPTH (28 - STATS_NETWORK_NODE_BITWIDTH)
112#define STATS_NETWORK_NODE_LIMIT (24 - STATS_NETWORK_NODE_BITWIDTH)
113
114typedef union stats_network_node stats_network_node;
115union stats_network_node {
116 size_t counters[STATS_NETWORK_NODE_COUNT];
117 stats_network_node *children[STATS_NETWORK_NODE_COUNT];
118};
119
120#ifdef WANT_LOG_NETWORKS
121static stats_network_node *stats_network_counters_root;
122#endif
123
124static int stat_increase_network_count(stats_network_node **pnode, int depth, uintptr_t ip) {
125 int foo = __LDR(ip, depth);
126 stats_network_node *node;
127
128 if (!*pnode) {
129 *pnode = malloc(sizeof(stats_network_node));
130 if (!*pnode)
131 return -1;
132 memset(*pnode, 0, sizeof(stats_network_node));
133 }
134 node = *pnode;
135
136 if (depth < STATS_NETWORK_NODE_MAXDEPTH)
137 return stat_increase_network_count(node->children + foo, depth + STATS_NETWORK_NODE_BITWIDTH, ip);
138
139 node->counters[foo]++;
140 return 0;
141}
142
143static int stats_shift_down_network_count(stats_network_node **node, int depth, int shift) {
144 size_t i, rest = 0;
145
146 if (!*node)
147 return 0;
148
149 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
150 if (depth < STATS_NETWORK_NODE_MAXDEPTH)
151 rest += stats_shift_down_network_count((*node)->children + i, depth + STATS_NETWORK_NODE_BITWIDTH, shift);
152 else
153 rest += (*node)->counters[i] >>= shift;
154
155 if (!rest) {
156 free(*node);
157 *node = NULL;
158 }
159
160 return rest;
161}
162
163static size_t stats_get_highscore_networks(stats_network_node *node, int depth, ot_ip6 node_value, size_t *scores, ot_ip6 *networks, int network_count,
164 int limit) {
165 size_t i, score = 0;
166
167 if (!node)
168 return 0;
169
170 if (depth < limit) {
171 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
172 if (node->children[i]) {
173 __STR(node_value, depth, i);
174 score += stats_get_highscore_networks(node->children[i], depth + STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit);
175 }
176 return score;
177 }
178
179 if (depth > limit && depth < STATS_NETWORK_NODE_MAXDEPTH) {
180 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
181 if (node->children[i])
182 score += stats_get_highscore_networks(node->children[i], depth + STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit);
183 return score;
184 }
185
186 if (depth > limit && depth == STATS_NETWORK_NODE_MAXDEPTH) {
187 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i)
188 score += node->counters[i];
189 return score;
190 }
191
192 /* if( depth == limit ) */
193 for (i = 0; i < STATS_NETWORK_NODE_COUNT; ++i) {
194 int j = 1;
195 size_t node_score;
196
197 if (depth == STATS_NETWORK_NODE_MAXDEPTH)
198 node_score = node->counters[i];
199 else
200 node_score = stats_get_highscore_networks(node->children[i], depth + STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit);
201
202 score += node_score;
203
204 if (node_score <= scores[0])
205 continue;
206
207 __STR(node_value, depth, i);
208 while (j < network_count && node_score > scores[j])
209 ++j;
210 --j;
211
212 memmove(scores, scores + 1, j * sizeof(*scores));
213 memmove(networks, networks + 1, j * sizeof(*networks));
214 scores[j] = node_score;
215 memcpy(networks + j, node_value, sizeof(*networks));
216 }
217
218 return score;
219}
220
221static void stats_return_busy_networks(char **reply, const char *reply_end, stats_network_node *tree, size_t amount, int limit) {
222 ot_ip6 networks[amount];
223 ot_ip6 node_value;
224 size_t scores[amount];
225 size_t i;
226
227 memset(scores, 0, sizeof(scores));
228 memset(networks, 0, sizeof(networks));
229 memset(node_value, 0, sizeof(node_value));
230
231 stats_get_highscore_networks(tree, 0, node_value, scores, networks, amount, limit);
232
233 fmt_append(reply, reply_end, "Networks, limit /%d:\n", limit + STATS_NETWORK_NODE_BITWIDTH);
234 for (i = amount; i > 0; --i) {
235 if (scores[i - 1]) {
236 char ip_readable[64];
237
238 ip_readable[ip_format(ip_readable, networks[i - 1])] = 0;
239 fmt_append(reply, reply_end, "%08zd: %s\n", scores[i - 1], ip_readable);
240 }
241 }
242 fmt_append(reply, reply_end, "\n");
243}
244
245static void stats_slash24s_txt(char **reply, const char *reply_end, size_t amount) {
246 stats_network_node *slash24s_network_counters_root = NULL;
247 size_t i, bucket, peer_size = OT_PEER_SIZE4;
248
249 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
250 ot_vector *torrents_list = mutex_bucket_lock(bucket);
251 for (i = 0; i < torrents_list->size; ++i) {
252 ot_peerlist *peer_list = (((ot_torrent *)(torrents_list->data))[i]).peer_list4;
253 ot_vector *bucket_list = &peer_list->peers;
254 int num_buckets = 1;
255
256 if (OT_PEERLIST_HASBUCKETS(peer_list)) {
257 num_buckets = bucket_list->size;
258 bucket_list = (ot_vector *)bucket_list->data;
259 }
260
261 while (num_buckets--) {
262 ot_peer *peers = (ot_peer *)bucket_list->data;
263 size_t numpeers = bucket_list->size;
264 while (numpeers--) {
265 if (stat_increase_network_count(&slash24s_network_counters_root, 0, (uintptr_t)(peers)))
266 goto bailout_unlock;
267 peers += peer_size;
268 }
269 ++bucket_list;
270 }
271 }
272 mutex_bucket_unlock(bucket, 0);
273 if (!g_opentracker_running)
274 goto done;
275 }
276
277 /* The tree is built. Now analyze */
278 stats_return_busy_networks(reply, reply_end, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_MAXDEPTH);
279 stats_return_busy_networks(reply, reply_end, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_LIMIT);
280 goto done;
281
282bailout_unlock:
283 mutex_bucket_unlock(bucket, 0);
284done:
285 stats_shift_down_network_count(&slash24s_network_counters_root, 0, sizeof(int) * 8 - 1);
286}
287
288#ifdef WANT_SPOT_WOODPECKER
289static stats_network_node *stats_woodpeckers_tree;
290static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER;
291
292static void stats_return_woodpeckers(char **reply, const char *reply_end, int amount) {
293 pthread_mutex_lock(&g_woodpeckers_mutex);
294 stats_return_busy_networks(reply, reply_end, stats_woodpeckers_tree, amount, STATS_NETWORK_NODE_MAXDEPTH);
295 pthread_mutex_unlock(&g_woodpeckers_mutex);
296}
297#endif
298
299typedef struct {
300 unsigned long long torrent_count;
301 unsigned long long peer_count;
302 unsigned long long seed_count;
303} torrent_stats;
304
305static int torrent_statter(ot_torrent *torrent, uintptr_t data) {
306 torrent_stats *stats = (torrent_stats *)data;
307 stats->torrent_count++;
308 stats->peer_count += torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
309 stats->seed_count += torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
310 return 0;
311}
312
313/* Converter function from memory to human readable hex strings */
314typedef struct {
315 size_t val;
316 ot_hash hash;
317} ot_record;
318
319static inline void stats_insert_into_highscore(size_t val, ot_record* list, size_t amount, ot_hash* hash) {
320 size_t i;
321 for (i = 0; (i < amount) && (list[i].val > val); ++i)
322 {}
323 /* if we score better than a record in the list, we need to make room in the high
324 score array by moving lower scoring entries up */
325 if (i < amount) {
326 memmove(list + i + 1, list + i, (amount - i - 1) * sizeof(ot_record));
327
328 memcpy(&list[i].hash, hash, sizeof(ot_hash));
329 list[i].val = val;
330 }
331}
332
333
334/* Fetches stats from tracker */
335void stats_top_txt(char **reply, const char *reply_end, size_t amount) {
336 size_t j, idx, bucket;
337 ot_record top100s[100], top100c[100], top100l[100];
338 char hex_out[42];
339
340 if (amount > 100)
341 amount = 100;
342
343 memset(top100s, 0, sizeof(top100s));
344 memset(top100c, 0, sizeof(top100c));
345 memset(top100l, 0, sizeof(top100l));
346
347 /* Iterate over complete torrent list */
348 for (bucket = 0; bucket < OT_BUCKET_COUNT; ++bucket) {
349 ot_vector *torrents_list = mutex_bucket_lock(bucket);
350 for (j = 0; j < torrents_list->size; ++j) {
351 ot_torrent *torrent = (ot_torrent *)(torrents_list->data) + j;
352 size_t peer_count = torrent->peer_list6->peer_count + torrent->peer_list4->peer_count;
353 size_t seed_count = torrent->peer_list6->seed_count + torrent->peer_list4->seed_count;
354 size_t leech_count = peer_count - seed_count;
355
356 stats_insert_into_highscore(peer_count, top100c, amount, &torrent->hash);
357 stats_insert_into_highscore(seed_count, top100s, amount, &torrent->hash);
358 stats_insert_into_highscore(leech_count, top100l, amount, &torrent->hash);
359 }
360 mutex_bucket_unlock(bucket, 0);
361 if (!g_opentracker_running)
362 return;
363 }
364
365 fmt_append(reply, reply_end, "Top %zd torrents by peers:\n", amount);
366 for (idx = 0; idx < amount; ++idx)
367 if (top100c[idx].val)
368 fmt_append(reply, reply_end, "\t%zd\t%s\n", top100c[idx].val, hex_from_hash(hex_out, top100c[idx].hash));
369 fmt_append(reply, reply_end, "Top %zd torrents by seeds:\n", amount);
370 for (idx = 0; idx < amount; ++idx)
371 if (top100s[idx].val)
372 fmt_append(reply, reply_end, "\t%zd\t%s\n", top100s[idx].val, hex_from_hash(hex_out, top100s[idx].hash));
373 fmt_append(reply, reply_end, "Top %zd torrents by leechers:\n", amount);
374 for (idx = 0; idx < amount; ++idx)
375 if (top100l[idx].val)
376 fmt_append(reply, reply_end, "\t%zd\t%s\n", top100l[idx].val, hex_from_hash(hex_out, top100l[idx].hash));
377}
378
379static unsigned long events_per_time(unsigned long long events, time_t t) { return events / ((unsigned int)t ? (unsigned int)t : 1); }
380
381static void stats_connections_mrtg(char **reply, const char *reply_end) {
382 ot_time t = time(NULL) - ot_start_time;
383 unsigned long long total[OT_COUNTER_COUNT];
384
385 counters_total(total);
386
387 fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.",
388 total[OT_COUNTER_TCP_CONNECTIONS] + total[OT_COUNTER_UDP_CONNECTIONS],
389 total[OT_COUNTER_TCP_ANNOUNCES] + total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], (int)t, (int)(t / 3600),
390 events_per_time(total[OT_COUNTER_TCP_CONNECTIONS] + total[OT_COUNTER_UDP_CONNECTIONS], t),
391 events_per_time(total[OT_COUNTER_TCP_ANNOUNCES] + total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], t));
392}
393
394static void stats_udpconnections_mrtg(char **reply, const char *reply_end) {
395 ot_time t = time(NULL) - ot_start_time;
396 unsigned long long total[OT_COUNTER_COUNT];
397
398 counters_total(total);
399
400 fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.", total[OT_COUNTER_UDP_CONNECTIONS],
401 total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], (int)t, (int)(t / 3600),
402 events_per_time(total[OT_COUNTER_UDP_CONNECTIONS], t),
403 events_per_time(total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], t));
404}
405
406static void stats_tcpconnections_mrtg(char **reply, const char *reply_end) {
407 time_t t = time(NULL) - ot_start_time;
408 unsigned long long total[OT_COUNTER_COUNT];
409
410 counters_total(total);
411
412 fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.", total[OT_COUNTER_TCP_CONNECTIONS],
413 total[OT_COUNTER_TCP_ANNOUNCES], (int)t, (int)(t / 3600), events_per_time(total[OT_COUNTER_TCP_CONNECTIONS], t),
414 events_per_time(total[OT_COUNTER_TCP_ANNOUNCES], t));
415}
416
417static void stats_scrape_mrtg(char **reply, const char *reply_end) {
418 time_t t = time(NULL) - ot_start_time;
419 unsigned long long total[OT_COUNTER_COUNT];
420
421 counters_total(total);
422
423 fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)",
424 total[OT_COUNTER_TCP_SCRAPES], total[OT_COUNTER_UDP_SCRAPES], (int)t, (int)(t / 3600),
425 events_per_time((total[OT_COUNTER_TCP_SCRAPES] + total[OT_COUNTER_UDP_SCRAPES]), t));
426}
427
428static void stats_fullscrapes_mrtg(char **reply, const char *reply_end) {
429 ot_time t = time(NULL) - ot_start_time;
430 unsigned long long total[OT_COUNTER_COUNT];
431
432 counters_total(total);
433
434 fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.",
435 total[OT_COUNTER_FULLSCRAPES] * 1000, total[OT_COUNTER_FULLSCRAPE_BYTES], (int)t, (int)(t / 3600),
436 events_per_time(total[OT_COUNTER_FULLSCRAPES], t),
437 events_per_time(total[OT_COUNTER_FULLSCRAPE_BYTES], t));
438}
439
440static void stats_peers_mrtg(char **reply, const char *reply_end) {
441 torrent_stats stats = {0, 0, 0};
442
443 iterate_all_torrents(torrent_statter, (uintptr_t)&stats);
444
445 fmt_append(reply, reply_end, "%llu\n%llu\nopentracker serving %llu torrents\nopentracker", stats.peer_count, stats.seed_count,
446 stats.torrent_count);
447}
448
449static void stats_torrents_mrtg(char **reply, const char *reply_end) {
450 size_t torrent_count = mutex_get_torrent_count();
451
452 fmt_append(reply, reply_end, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker", torrent_count, (size_t)0, torrent_count);
453}
454
455static void stats_httperrors_txt(char **reply, const char *reply_end) {
456 unsigned long long total[OT_COUNTER_COUNT];
457
458 counters_total(total);
459
460 fmt_append(reply, reply_end, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n",
461 total[OT_COUNTER_FAILED + 0], total[OT_COUNTER_FAILED + 1], total[OT_COUNTER_FAILED + 2], total[OT_COUNTER_FAILED + 3],
462 total[OT_COUNTER_FAILED + 4], total[OT_COUNTER_FAILED + 5], total[OT_COUNTER_FAILED + 6]);
463}
464
465static void stats_return_renew_bucket(char **reply, const char *reply_end) {
466 unsigned long long total[OT_COUNTER_COUNT];
467
468 counters_total(total);
469
470 int i;
471
472 for (i = 0; i < OT_PEER_TIMEOUT; ++i)
473 fmt_append(reply, reply_end, "%02i %llu\n", i, total[OT_COUNTER_RENEWED + i]);
474}
475
476static void stats_return_sync_mrtg(char **reply, const char *reply_end) {
477 ot_time t = time(NULL) - ot_start_time;
478 unsigned long long total[OT_COUNTER_COUNT];
479
480 counters_total(total);
481
482 fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.", total[OT_COUNTER_SYNCS], 0LL,
483 (int)t, (int)(t / 3600), events_per_time(total[OT_COUNTER_TCP_CONNECTIONS] + total[OT_COUNTER_UDP_CONNECTIONS], t),
484 events_per_time(total[OT_COUNTER_TCP_ANNOUNCES] + total[OT_COUNTER_UDP_ANNOUNCES] + total[OT_COUNTER_UDP_CONNECTS], t));
485}
486
487static void stats_return_completed_mrtg(char **reply, const char *reply_end) {
488 ot_time t = time(NULL) - ot_start_time;
489 unsigned long long total[OT_COUNTER_COUNT];
490
491 counters_total(total);
492
493
494 fmt_append(reply, reply_end, "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.", total[OT_COUNTER_COMPLETED], 0LL, (int)t, (int)(t / 3600),
495 events_per_time(total[OT_COUNTER_COMPLETED], t / 3600));
496}
497
498#ifdef WANT_LOG_NUMWANT
499extern unsigned long long numwants[201];
500static void stats_return_numwants(char **reply, const char *reply_end) {
501 int i;
502 for (i = 0; i <= 200; ++i)
503 fmt_append(reply, reply_end, "%03d => %lld\n", i, numwants[i]);
504}
505#endif
506
507#ifdef WANT_FULLLOG_NETWORKS
508static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, char *r) {
509 ot_log *loglist = g_logchain_first, *llnext;
510 char *re = r + OT_STATS_TMPSIZE;
511
512 g_logchain_first = g_logchain_last = 0;
513
514 while (loglist) {
515 if (r + (loglist->size + 64) >= re) {
516 r = iovec_next_chunk(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE);
517 if (!r)
518 return;
519 re = r + 32 * OT_STATS_TMPSIZE;
520 }
521 char ip_readable[64];
522
523 ip_readable[ip_format(ip_readable, loglist->ip)] = 0;
524 fmt_append(&r, re, "%08ld: %s\n", loglist->time, ip_readable);
525 fmt_bytes(&r, re, loglist->data, loglist->size);
526 fmt_append(&r, re, "\n*\n\n");
527
528 llnext = loglist->next;
529 free(loglist->data);
530 free(loglist);
531 loglist = llnext;
532 }
533 iovec_trim_last(iovec_entries, iovector, r);
534}
535#endif
536
537static void stats_return_everything(char **reply, const char *reply_end) {
538 unsigned long long total[OT_COUNTER_COUNT];
539
540 counters_total(total);
541
542 torrent_stats stats = {0, 0, 0};
543 int i;
544
545 iterate_all_torrents(torrent_statter, (uintptr_t)&stats);
546
547 fmt_append(reply, reply_end, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
548 fmt_append(reply, reply_end, "<stats>\n");
549 fmt_append(reply, reply_end, " <tracker_id>%" PRIu32 "</tracker_id>\n", g_tracker_id);
550 fmt_append(reply, reply_end, " <version>\n");
551 stats_return_tracker_version(reply, reply_end);
552 fmt_append(reply, reply_end, " </version>\n");
553 fmt_append(reply, reply_end, " <uptime>%llu</uptime>\n", (unsigned long long)(time(NULL) - ot_start_time));
554 fmt_append(reply, reply_end, " <torrents>\n");
555 fmt_append(reply, reply_end, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count());
556 fmt_append(reply, reply_end, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count);
557 fmt_append(reply, reply_end, " </torrents>\n");
558 fmt_append(reply, reply_end, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count);
559 fmt_append(reply, reply_end, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count);
560 fmt_append(reply, reply_end, " <completed>\n <count>%llu</count>\n </completed>\n", total[OT_COUNTER_COMPLETED]);
561 fmt_append(reply, reply_end, " <connections>\n");
562 fmt_append(reply, reply_end, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n",
563 total[OT_COUNTER_TCP_CONNECTIONS], total[OT_COUNTER_TCP_ANNOUNCES], total[OT_COUNTER_TCP_SCRAPES]);
564 fmt_append(reply, reply_end, " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n",
565 total[OT_COUNTER_UDP_CONNECTIONS], total[OT_COUNTER_UDP_CONNECTS], total[OT_COUNTER_UDP_ANNOUNCES], total[OT_COUNTER_UDP_SCRAPES],
566 total[OT_COUNTER_UDP_CONNID_MISSMATCH]);
567 fmt_append(reply, reply_end, " <livesync>\n <count>%llu</count>\n </livesync>\n", total[OT_COUNTER_SYNCS]);
568 fmt_append(reply, reply_end, " </connections>\n");
569 fmt_append(reply, reply_end, " <debug>\n");
570 fmt_append(reply, reply_end, " <renew>\n");
571 for (i = 0; i < OT_PEER_TIMEOUT; ++i)
572 fmt_append(reply, reply_end, " <count interval=\"%02i\">%llu</count>\n", i, total[OT_COUNTER_RENEWED + i]);
573 fmt_append(reply, reply_end, " </renew>\n");
574 fmt_append(reply, reply_end, " <http_error>\n");
575 for (i = 0; i < CODE_HTTPERROR_COUNT; ++i)
576 fmt_append(reply, reply_end, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], total[OT_COUNTER_FAILED + i]);
577 fmt_append(reply, reply_end, " </http_error>\n");
578 fmt_append(reply, reply_end, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", total[OT_COUNTER_STALLS]);
579 fmt_append(reply, reply_end, " </debug>\n");
580 fmt_append(reply, reply_end, "</stats>");
581}
582
583void stats_return_tracker_version(char **reply, const char *reply_end) {
584#define QUOTE(name) #name
585#define SQUOTE(name) QUOTE(name)
586 fmt_append(reply, reply_end, "https://erdgeist.org/gitweb/opentracker/commit/?id=" SQUOTE(GIT_VERSION) "\n");
587}
588
589void return_stats_for_tracker(char **reply, const char *reply_end, int mode, int format) {
590 (void)format;
591 switch (mode & TASK_TASK_MASK) {
592 case TASK_STATS_CONNS:
593 stats_connections_mrtg(reply, reply_end);
594 break;
595 case TASK_STATS_SCRAPE:
596 stats_scrape_mrtg(reply, reply_end);
597 break;
598 case TASK_STATS_UDP:
599 stats_udpconnections_mrtg(reply, reply_end);
600 break;
601 case TASK_STATS_TCP:
602 stats_tcpconnections_mrtg(reply, reply_end);
603 break;
604 case TASK_STATS_FULLSCRAPE:
605 stats_fullscrapes_mrtg(reply, reply_end);
606 break;
607 case TASK_STATS_COMPLETED:
608 stats_return_completed_mrtg(reply, reply_end);
609 break;
610 case TASK_STATS_HTTPERRORS:
611 stats_httperrors_txt(reply, reply_end);
612 break;
613 case TASK_STATS_VERSION:
614 stats_return_tracker_version(reply, reply_end);
615 break;
616 case TASK_STATS_RENEW:
617 stats_return_renew_bucket(reply, reply_end);
618 break;
619 case TASK_STATS_SYNCS:
620 stats_return_sync_mrtg(reply, reply_end);
621 break;
622#ifdef WANT_LOG_NUMWANT
623 case TASK_STATS_NUMWANTS:
624 stats_return_numwants(reply, reply_end);
625 break;
626#endif
627 default:
628 break;
629 }
630}
631
632static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_tasktype mode) {
633 char *r;
634 const char *r_end;
635
636 *iovec_entries = 0;
637 *iovector = NULL;
638 if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_STATS_TMPSIZE)))
639 return;
640 r_end = r + OT_STATS_TMPSIZE;
641
642 switch (mode & TASK_TASK_MASK) {
643 case TASK_STATS_TORRENTS:
644 stats_torrents_mrtg(&r, r_end);
645 break;
646 case TASK_STATS_PEERS:
647 stats_peers_mrtg(&r, r_end);
648 break;
649 case TASK_STATS_SLASH24S:
650 stats_slash24s_txt(&r, r_end, 128);
651 break;
652 case TASK_STATS_TOP10:
653 stats_top_txt(&r, r_end, 10);
654 break;
655 case TASK_STATS_TOP100:
656 r = iovec_next_chunk(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE);
657 if (!r)
658 return;
659 r_end = r + 4 * OT_STATS_TMPSIZE;
660 stats_top_txt(&r, r_end, 100);
661 break;
662 case TASK_STATS_EVERYTHING:
663 r = iovec_next_chunk(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT);
664 if (!r)
665 return;
666 r_end = r + OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT;
667 stats_return_everything(&r, r_end);
668 break;
669#ifdef WANT_SPOT_WOODPECKER
670 case TASK_STATS_WOODPECKERS:
671 stats_return_woodpeckers(&r, r_end, 128);
672 break;
673#endif
674#ifdef WANT_FULLLOG_NETWORKS
675 case TASK_STATS_FULLLOG:
676 stats_return_fulllog(iovec_entries, iovector, r);
677 return;
678#endif
679 default:
680 iovec_free(iovec_entries, iovector);
681 return;
682 }
683 iovec_trim_last(iovec_entries, iovector, r);
684}
685
686void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) {
687 if (!ws)
688 return;
689
690 switch (event) {
691 case EVENT_ACCEPT:
692 if (proto == FLAG_TCP)
693 COUNT(OT_COUNTER_TCP_CONNECTIONS, 1);
694 else
695 COUNT(OT_COUNTER_UDP_CONNECTIONS, 1);
696#ifdef WANT_LOG_NETWORKS
697 stat_increase_network_count(&stats_network_counters_root, 0, event_data);
698#endif
699 break;
700 case EVENT_ANNOUNCE:
701 if (proto == FLAG_TCP)
702 COUNT(OT_COUNTER_TCP_ANNOUNCES, 1);
703 else
704 COUNT(OT_COUNTER_UDP_ANNOUNCES, 1);
705 break;
706 case EVENT_CONNECT:
707 if (proto == FLAG_TCP)
708 COUNT(OT_COUNTER_TCP_CONNECTS, 1);
709 else
710 COUNT(OT_COUNTER_UDP_CONNECTS, 1);
711 break;
712 case EVENT_COMPLETED:
713#ifdef WANT_SYSLOGS
714 {
715 char timestring[64];
716 char hash_hex[42], peerid_hex[42], ip_readable[64];
717 struct tm time_now;
718 time_t ttt;
719
720 time(&ttt);
721 localtime_r(&ttt, &time_now);
722 strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now);
723
724 hex_from_hash(hash_hex, *ws->hash);
725 if (ws->peer_id)
726 hex_from_hash(peerid_hex, (uint8_t *)ws->peer_id);
727 else {
728 *peerid_hex = 0;
729 }
730
731 ip_readable[ip_format(ip_readable, (char *)&ws->peer)] = 0;
732 syslog(LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable);
733 }
734#endif
735 COUNT(OT_COUNTER_COMPLETED, 1);
736 break;
737 case EVENT_SCRAPE:
738 if (proto == FLAG_TCP)
739 COUNT(OT_COUNTER_TCP_SCRAPES, 1);
740 else
741 COUNT(OT_COUNTER_UDP_SCRAPES, 1);
742 break;
743 case EVENT_FULLSCRAPE:
744 COUNT(OT_COUNTER_FULLSCRAPES, 1);
745 COUNT(OT_COUNTER_FULLSCRAPE_BYTES, event_data);
746 break;
747 case EVENT_FULLSCRAPE_REQUEST: {
748 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
749 char _debug[512];
750 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(time_now_seconds() - ot_start_time) / 60);
751 off += ip_format(_debug + off, *ip);
752 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
753 (void)write(2, _debug, off);
754 COUNT(OT_COUNTER_FULLSCRAPE_REQUESTS, 1);
755 } break;
756 case EVENT_FULLSCRAPE_REQUEST_GZIP: {
757 ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */
758 char _debug[512];
759 int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(time_now_seconds() - ot_start_time) / 60);
760 off += ip_format(_debug + off, *ip);
761 off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n");
762 (void)write(2, _debug, off);
763 COUNT(OT_COUNTER_FULLSCRAPE_REQUESTS, 1);
764 } break;
765 case EVENT_FAILED:
766 COUNT(OT_COUNTER_FAILED + event_data, 1);
767 break;
768 case EVENT_RENEW:
769 COUNT(OT_COUNTER_RENEWED + event_data, 1);
770 break;
771 case EVENT_SYNC:
772 COUNT(OT_COUNTER_SYNCS, event_data);
773 break;
774 case EVENT_BUCKET_LOCKED:
775 COUNT(OT_COUNTER_STALLS, 1);
776 break;
777#ifdef WANT_SPOT_WOODPECKER
778 case EVENT_WOODPECKER:
779 pthread_mutex_lock(&g_woodpeckers_mutex);
780 stat_increase_network_count(&stats_woodpeckers_tree, 0, event_data);
781 pthread_mutex_unlock(&g_woodpeckers_mutex);
782 break;
783#endif
784 case EVENT_CONNID_MISSMATCH:
785 COUNT(OT_COUNTER_UDP_CONNID_MISSMATCH, 1);
786 default:
787 break;
788 }
789}
790
791void stats_cleanup(void) {
792#ifdef WANT_SPOT_WOODPECKER
793 pthread_mutex_lock(&g_woodpeckers_mutex);
794 stats_shift_down_network_count(&stats_woodpeckers_tree, 0, 1);
795 pthread_mutex_unlock(&g_woodpeckers_mutex);
796#endif
797}
798
799static void *stats_worker(void *args) {
800 size_t iovec_entries;
801 struct iovec *iovector;
802
803 (void)args;
804
805 while (g_opentracker_running) {
806 ot_tasktype tasktype = TASK_STATS;
807 ot_taskid taskid = workqueue_poptask(&tasktype);
808 if (!taskid)
809 break;
810 stats_make(&iovec_entries, &iovector, tasktype);
811 if (workqueue_pushresult(taskid, iovec_entries, iovector))
812 iovec_free(&iovec_entries, &iovector);
813 }
814 return NULL;
815}
816
817void stats_deliver(int64_t sock, int tasktype) { workqueue_pushtask(sock, tasktype); }
818
819static pthread_t thread_id;
820void stats_init(void) {
821 ot_start_time = time_now_seconds();
822 pthread_create(&thread_id, NULL, stats_worker, NULL);
823}
824
825void stats_deinit(void) {
826 workqueue_wakeall();
827 pthread_join(thread_id, NULL);
828}