1import asyncio
2import gc
3import linecache
4import os
5import platform
6import signal
7import sys
8import threading
9import traceback
10from contextlib import redirect_stdout
11
12from OpenSSL import SSL
13from mitmproxy import version
14from mitmproxy.utils import asyncio_utils
15
16
17def dump_system_info():
18    mitmproxy_version = version.get_dev_version()
19
20    data = [
21        f"Mitmproxy: {mitmproxy_version}",
22        f"Python:    {platform.python_version()}",
23        "OpenSSL:   {}".format(SSL.SSLeay_version(SSL.SSLEAY_VERSION).decode()),
24        f"Platform:  {platform.platform()}",
25    ]
26    return "\n".join(data)
27
28
29def dump_info(signal=None, frame=None, file=sys.stdout, testing=False):  # pragma: no cover
30    with redirect_stdout(file):
31        print("****************************************************")
32        print("Summary")
33        print("=======")
34
35        try:
36            import psutil
37        except:
38            print("(psutil not installed, skipping some debug info)")
39        else:
40            p = psutil.Process()
41            print("num threads: ", p.num_threads())
42            if hasattr(p, "num_fds"):
43                print("num fds: ", p.num_fds())
44            print("memory: ", p.memory_info())
45
46            print()
47            print("Files")
48            print("=====")
49            for i in p.open_files():
50                print(i)
51
52            print()
53            print("Connections")
54            print("===========")
55            for i in p.connections():
56                print(i)
57
58        print()
59        print("Threads")
60        print("=======")
61        bthreads = []
62        for i in threading.enumerate():
63            if hasattr(i, "_threadinfo"):
64                bthreads.append(i)
65            else:
66                print(i.name)
67        bthreads.sort(key=lambda x: x._thread_started)
68        for i in bthreads:
69            print(i._threadinfo())
70
71        print()
72        print("Memory")
73        print("=======")
74        gc.collect()
75        d = {}
76        for i in gc.get_objects():
77            t = str(type(i))
78            if "mitmproxy" in t:
79                d[t] = d.setdefault(t, 0) + 1
80        itms = list(d.items())
81        itms.sort(key=lambda x: x[1])
82        for i in itms[-20:]:
83            print(i[1], i[0])
84
85        try:
86            asyncio.get_running_loop()
87        except RuntimeError:
88            pass
89        else:
90            print()
91            print("Tasks")
92            print("=======")
93            for task in asyncio.all_tasks():
94                f = task.get_stack(limit=1)[0]
95                line = linecache.getline(f.f_code.co_filename, f.f_lineno, f.f_globals).strip()
96                line = f"{line}  # at {os.path.basename(f.f_code.co_filename)}:{f.f_lineno}"
97                print(f"{asyncio_utils.task_repr(task)}\n"
98                      f"    {line}")
99
100        print("****************************************************")
101
102    if not testing:
103        sys.exit(1)
104
105
106def dump_stacks(signal=None, frame=None, file=sys.stdout, testing=False):
107    id2name = {th.ident: th.name for th in threading.enumerate()}
108    code = []
109    for threadId, stack in sys._current_frames().items():
110        code.append(
111            "\n# Thread: %s(%d)" % (
112                id2name.get(threadId, ""), threadId
113            )
114        )
115        for filename, lineno, name, line in traceback.extract_stack(stack):
116            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
117            if line:
118                code.append("  %s" % (line.strip()))
119    print("\n".join(code), file=file)
120    if not testing:  # pragma: no cover
121        sys.exit(1)
122
123
124def register_info_dumpers():
125    if os.name != "nt":  # pragma: windows no cover
126        signal.signal(signal.SIGUSR1, dump_info)
127        signal.signal(signal.SIGUSR2, dump_stacks)
128