Add live profiling results output to rnstatus.

master
K8 2026-08-19 19:57:42 -06:00
parent 9da6664976
commit 9302415f9e
No known key found for this signature in database
4 changed files with 73 additions and 37 deletions

View File

@ -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

View File

@ -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:

View File

@ -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("")

View File

@ -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