#!/usr/bin/env python3
# VICIfast Research, Report 01: The Two Bottlenecks Inside a Busy VICIdial Server
# Load rig (§7.3). FOR A THROWAWAY TEST SERVER, NOT A LIVE FLOOR. Read it before you run it.
# Summarises gdb "thread apply all bt" snapshots of Asterisk: threads by first Asterisk frame, the AMI session
# threads, and any thread inside a Redirect. Taking a snapshot freezes Asterisk for seconds: off-shift only.
# Changes the server? No (it reads snapshot files).
# gdb-an.py <snap files...>: what Asterisk's threads are doing in gdb "thread apply all bt" snapshots.
# Per snapshot: threads by their first Asterisk frame (skipping libc), the AMI session threads by where they
# are, and every thread inside action_redirect / ast_async_goto with its stack.
import collections, re, sys
FRAME = re.compile(r'^#(\d+)\s+(?:0x[0-9a-f]+ in )?(\S+) \(')
SKIP = re.compile(r'^(__|_?pthread|futex|syscall|clock_nanosleep|nanosleep|poll|ppoll|epoll|select|read|recv|write|send|start_thread|clone|__GI_|lll_|_L_lock|_IO_)')
for f in sys.argv[1:]:
    threads, cur = [], None
    for line in open(f, errors='replace'):
        if line.startswith('Thread '):
            cur = []; threads.append(cur); continue
        m = FRAME.match(line.strip())
        if m and cur is not None:
            cur.append(m[2])
    print(f'== {f}: {len(threads)} threads')
    top = collections.Counter()
    sess = collections.Counter()
    for t in threads:
        fn = next((x for x in t if not SKIP.match(x)), t[0] if t else '?')
        top[fn] += 1
        if 'handle_tcptls_connection' in t or 'session_do' in t:
            sess[' < '.join([x for x in t if not SKIP.match(x)][:4])] += 1
    print('  top frames:', ', '.join(f'{k} {v}' for k, v in top.most_common(12)))
    print('  AMI/HTTP session threads, where:')
    for k, v in sess.most_common(8):
        print(f'    {v:4d}  {k}')
    for t in threads:
        if any(x in ('action_redirect', 'ast_async_goto', 'async_goto_with_discard_bridge_after') for x in t):
            print('  IN REDIRECT:', ' < '.join(t[:14]))
