diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index 4618e456..94924ee9 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -190,8 +190,6 @@ class Reticulum: RNS.Transport.exit_handler() RNS.Identity.exit_handler() - if RNS.Profiler.ran(): RNS.Profiler.results() - RNS.loglevel = RNS.LOG_NONE RNS._detach_stdout() @@ -1293,6 +1291,7 @@ class Reticulum: if path == "packet_rssi": self.rpc_return(conn, self.get_packet_rssi(call["packet_hash"])) if path == "packet_snr": self.rpc_return(conn, self.get_packet_snr(call["packet_hash"])) if path == "packet_q": self.rpc_return(conn, self.get_packet_q(call["packet_hash"])) + if path == "profiling_results": self.rpc_return(conn, self.get_profiling_results()) if path == "blackholed_identities": self.rpc_return(conn, self.get_blackholed_identities()) if path == "is_blackholed": self.rpc_return(conn, self.is_blackholed(call["identity_hash"])) @@ -1848,6 +1847,17 @@ class Reticulum: return None + def get_profiling_results(self): + if self.is_connected_to_shared_instance: + rpc_connection = self.get_rpc_client() + rpc_connection.send_bytes(mp.packb({"get": "profiling_results"})) + response = mp.unpackb(rpc_connection.recv_bytes()) + return response + + else: + if RNS.Profiler.ran(): return RNS.Profiler.results() + else: return None + def halt_interface(self, interface): pass diff --git a/RNS/Transport.py b/RNS/Transport.py index 279814f7..10fc6d1e 100755 --- a/RNS/Transport.py +++ b/RNS/Transport.py @@ -3339,6 +3339,9 @@ class Transport: response.append(Transport.owner.get_interface_stats()) if data[0] == True: response.append(Transport.owner.get_link_count()) + if len(data) >= 2: + if data[1] == True: response.append(Transport.owner.get_profiling_results()) + return response except Exception as e: diff --git a/RNS/Utilities/rnstatus.py b/RNS/Utilities/rnstatus.py index 87be681b..f489d891 100644 --- a/RNS/Utilities/rnstatus.py +++ b/RNS/Utilities/rnstatus.py @@ -63,9 +63,10 @@ request_concluded = False first_remote_req = True remote_destination = None remote_link = None -def get_remote_status(destination_hash, include_lstats, identity, no_output=False, timeout=RNS.Transport.PATH_REQUEST_TIMEOUT): +def get_remote_status(destination_hash, include_lstats, include_profiling, identity, no_output=False, timeout=RNS.Transport.PATH_REQUEST_TIMEOUT): global request_result, request_concluded, first_remote_req, remote_destination, remote_link link_count = None + profiling_results = None if not RNS.Transport.has_path(destination_hash): if not no_output: @@ -114,7 +115,10 @@ def get_remote_status(destination_hash, include_lstats, identity, no_output=Fals if len(response) > 1: link_count = response[1] else: link_count = None - request_result = (status, link_count) + if len(response) > 2: profiling_results = response[2] + else: profiling_results = None + + request_result = (status, link_count, profiling_results) request_concluded = True @@ -125,7 +129,7 @@ def get_remote_status(destination_hash, include_lstats, identity, no_output=Fals print("Sending request...", end=" ") sys.stdout.flush() link.identify(identity) - link.request("/status", data = [include_lstats], response_callback = got_response, failed_callback = request_failed) + link.request("/status", data = [include_lstats, include_profiling], response_callback = got_response, failed_callback = request_failed) first_remote_req = False if not remote_link and not no_output: @@ -155,7 +159,7 @@ def get_remote_status(destination_hash, include_lstats, identity, no_output=Fals def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=False, astats=False, pstats=False, lstats=False, sorting=None, sort_reverse=False, remote=None, management_identity=None, must_exit=True, rns_instance=None, traffic_totals=False, discovered_interfaces=False, config_entries=False, burst_filter=False, blocked_ips=False, - queue_stats=False, pps=False, remote_timeout=RNS.Transport.PATH_REQUEST_TIMEOUT): + queue_stats=False, pps=False, profiling=False, remote_timeout=RNS.Transport.PATH_REQUEST_TIMEOUT): if remote: require_shared = False else: require_shared = True @@ -175,6 +179,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= link_count = None active_link_count = None stats = None + profiling_results = None details = False if config_entries: @@ -327,8 +332,8 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= if identity == None: raise ValueError("Could not load management identity from "+str(management_identity)) try: - remote_status = get_remote_status(destination_hash, lstats, identity, no_output=json, timeout=remote_timeout) - if remote_status != None: stats, link_count = remote_status + remote_status = get_remote_status(destination_hash, lstats, profiling, identity, no_output=json, timeout=remote_timeout) + if remote_status != None: stats, link_count, profiling_results = remote_status except Exception as e: raise e except Exception as e: @@ -343,6 +348,10 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= try: active_link_count = reticulum.get_active_link_count() except Exception as e: pass + if profiling: + try: profiling_results = reticulum.get_profiling_results() + except Exception as e: pass + try: stats = reticulum.get_interface_stats() except Exception as e: pass @@ -799,6 +808,9 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json= print(f" {pqpress}") print(f" {ilpress}") + if profiling_results: + print(f"\n Profiling :\n{RNS.Profiler.format_results(profiling_results)}") + if "transport_id" in stats and stats["transport_id"] != None: print("\n Transport Instance "+RNS.prettyhexrep(stats["transport_id"])+" running") if "network_id" in stats and stats["network_id"] != None: @@ -838,6 +850,7 @@ def main(must_exit=True, rns_instance=None): parser.add_argument("-t", "--totals", action="store_true", help="display traffic totals", default=False) parser.add_argument("-p", "--pps", action="store_true", help="display packets per second in totals", default=False) parser.add_argument("-q", "--queues", action="store_true", help="display queue stats", default=False) + parser.add_argument("-z", "--profiling", action="store_true", help="display live profiling results", default=False) parser.add_argument("-s", "--sort", action="store", help="sort interfaces by [rate, traffic, rx, tx, rxs, txs, announces, arx, atx, arxc, atxc, held, prx, ptx, prxc, ptxc, pvs, ivs, flt]", default=None, type=str) parser.add_argument("-r", "--reverse", action="store_true", help="reverse sorting", default=False) parser.add_argument("-j", "--json", action="store_true", help="output in JSON format", default=False) @@ -876,7 +889,7 @@ def main(must_exit=True, rns_instance=None): astats=args.announce_stats, pstats=args.pr_stats, lstats=args.link_stats, sorting=args.sort, sort_reverse=args.reverse, remote=args.R, management_identity=args.i, remote_timeout=args.w, must_exit=False, rns_instance=reticulum, traffic_totals=args.totals, discovered_interfaces=args.discovered, config_entries=args.D, burst_filter=args.burst, - blocked_ips=args.blocked_ips, queue_stats=args.queues, pps=args.pps) + blocked_ips=args.blocked_ips, queue_stats=args.queues, pps=args.pps, profiling=args.profiling) finally: sys.stdout = old_stdout @@ -894,7 +907,7 @@ def main(must_exit=True, rns_instance=None): astats=args.announce_stats, pstats=args.pr_stats, lstats=args.link_stats, sorting=args.sort, sort_reverse=args.reverse, remote=args.R, management_identity=args.i, remote_timeout=args.w, must_exit=must_exit, rns_instance=rns_instance, traffic_totals=args.totals, discovered_interfaces=args.discovered, config_entries=args.D, burst_filter=args.burst, - blocked_ips=args.blocked_ips, queue_stats=args.queues, pps=args.pps) + blocked_ips=args.blocked_ips, queue_stats=args.queues, pps=args.pps, profiling=args.profiling) except KeyboardInterrupt: print("") diff --git a/RNS/__init__.py b/RNS/__init__.py index eb26bb9d..e4356502 100755 --- a/RNS/__init__.py +++ b/RNS/__init__.py @@ -41,21 +41,6 @@ from threading import Lock, Condition from ._version import __version__ -from .Reticulum import Reticulum -from .Identity import Identity -from .Link import Link, RequestReceipt -from .Channel import MessageBase -from .Buffer import Buffer, RawChannelReader, RawChannelWriter -from .Transport import Transport -from .Discovery import InterfaceAnnouncer -from .Destination import Destination -from .Packet import Packet -from .Packet import PacketReceipt -from .Resolver import Resolver -from .Resource import Resource, ResourceAdvertisement -from .Cryptography import HKDF -from .Cryptography import Hashes - py_modules = glob.glob(os.path.dirname(__file__)+"/*.py") pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc") modules = py_modules+pyc_modules @@ -492,7 +477,7 @@ class Profiler: from statistics import mean, median, stdev results = {} - for tag in Profiler.tags: + for tag in sorted(Profiler.tags): tag_captures = [] tag_entry = Profiler.tags[tag] @@ -534,33 +519,41 @@ class Profiler: results[tag] = tag_results + return results + + @staticmethod + def format_results(results): def print_results_recursive(tag, results, level=0): - print_tag_results(tag, level+1) + results_str = print_tag_results(tag, level+1) + "\n" for tag_name in results: sub_tag = results[tag_name] if sub_tag["super"] == tag["name"]: - print_results_recursive(sub_tag, results, level=level+1) + results_str += print_results_recursive(sub_tag, results, level=level+1) + + return results_str def print_tag_results(tag, level): ind = " "*level name = tag["name"]; count = tag["count"] mean = tag["mean"]; median = tag["median"]; stdev = tag["stdev"] - print( f"{ind}{name}") - print( f"{ind} Samples : {count}") + results_str = f" {ind}{name}\n" + results_str += f" {ind} Samples : {count}\n" if stdev != None: - print(f"{ind} Mean : {prettyshorttime(mean)}") - print(f"{ind} Median : {prettyshorttime(median)}") - print(f"{ind} St.dev. : {prettyshorttime(stdev)}") - print( f"{ind} Total : {prettyshorttime(mean*count)}") - print("") + results_str += f" {ind} Mean : {prettyshorttime(mean)}\n" + results_str += f" {ind} Median : {prettyshorttime(median)}\n" + results_str += f" {ind} St.dev. : {prettyshorttime(stdev)}\n" + results_str += f" {ind} Total : {prettyshorttime(mean*count)}\n" + return results_str - print("\nProfiler results:\n") + results_str = "" for tag_name in results: tag = results[tag_name] if tag["super"] == None: - print_results_recursive(tag, results) + results_str += print_results_recursive(tag, results) + + return results_str profile = Profiler.get_profiler @@ -610,3 +603,20 @@ def bytes_to_b256(data): if not type(data) == bytes: raise TypeError("Invalid input data for base256 encode") try: return [byte_to_b256(c) for c in data] except Exception as e: raise TypeError(f"Could not encode to base256: {e}") + + +from .Reticulum import Reticulum +from .Identity import Identity +from .Link import Link, RequestReceipt +from .Channel import MessageBase +from .Buffer import Buffer, RawChannelReader, RawChannelWriter +from .Transport import Transport +from .Discovery import InterfaceAnnouncer +from .Destination import Destination +from .Packet import Packet +from .Packet import PacketReceipt +from .Resolver import Resolver +from .Resource import Resource, ResourceAdvertisement +from .Cryptography import HKDF +from .Cryptography import Hashes +