core: add debug trace to show plugins loading time

This commit is contained in:
Goffi 2023-03-04 22:21:28 +01:00
commit b5f8914bbc

View file

@ -23,7 +23,7 @@ import mimetypes
from pathlib import Path from pathlib import Path
import sys import sys
import time import time
from typing import Tuple, Optional from typing import Callable, Dict, Tuple, Optional
from urllib.parse import unquote, urlparse from urllib.parse import unquote, urlparse
import uuid import uuid
@ -129,8 +129,7 @@ class SatXMPPEntity(core_types.SatXMPPEntity):
## initialisation ## ## initialisation ##
@defer.inlineCallbacks async def _callConnectionTriggers(self, connection_timer):
def _callConnectionTriggers(self):
"""Call conneting trigger prepare connected trigger """Call conneting trigger prepare connected trigger
@param plugins(iterable): plugins to use @param plugins(iterable): plugins to use
@ -146,17 +145,23 @@ class SatXMPPEntity(core_types.SatXMPPEntity):
# profileConnecting/profileConnected methods handling # profileConnecting/profileConnected methods handling
timer = connection_timer[plugin] = {
"total": 0
}
# profile connecting is called right now (before actually starting client) # profile connecting is called right now (before actually starting client)
connecting_cb = getattr(plugin, "profileConnecting", None) connecting_cb = getattr(plugin, "profileConnecting", None)
if connecting_cb is not None: if connecting_cb is not None:
yield utils.asDeferred(connecting_cb, self) connecting_start = time.time()
await utils.asDeferred(connecting_cb, self)
timer["connecting"] = time.time() - connecting_start
timer["total"] += timer["connecting"]
# profile connected is called after client is ready and roster is got # profile connected is called after client is ready and roster is got
connected_cb = getattr(plugin, "profileConnected", None) connected_cb = getattr(plugin, "profileConnected", None)
if connected_cb is not None: if connected_cb is not None:
plugin_conn_cb.append((plugin, connected_cb)) plugin_conn_cb.append((plugin, connected_cb))
defer.returnValue(plugin_conn_cb) return plugin_conn_cb
def _getPluginsList(self): def _getPluginsList(self):
"""Return list of plugin to use """Return list of plugin to use
@ -177,6 +182,17 @@ class SatXMPPEntity(core_types.SatXMPPEntity):
""" """
return return
@staticmethod
async def _run_profile_connected(
callback: Callable,
entity: "SatXMPPEntity",
timer: Dict[str, float]
) -> None:
connected_start = time.time()
await utils.asDeferred(callback, entity)
timer["connected"] = time.time() - connected_start
timer["total"] += timer["connected"]
@classmethod @classmethod
async def startConnection(cls, host, profile, max_retries): async def startConnection(cls, host, profile, max_retries):
"""instantiate the entity and start the connection""" """instantiate the entity and start the connection"""
@ -258,7 +274,8 @@ class SatXMPPEntity(core_types.SatXMPPEntity):
log.debug(_("setting plugins parents")) log.debug(_("setting plugins parents"))
plugin_conn_cb = await entity._callConnectionTriggers() connection_timer: Dict[str, Dict[str, float]] = {}
plugin_conn_cb = await entity._callConnectionTriggers(connection_timer)
entity.startService() entity.startService()
@ -269,8 +286,14 @@ class SatXMPPEntity(core_types.SatXMPPEntity):
# Call profileConnected callback for all plugins, # Call profileConnected callback for all plugins,
# and print error message if any of them fails # and print error message if any of them fails
conn_cb_list = [] conn_cb_list = []
for __, callback in plugin_conn_cb: for plugin, callback in plugin_conn_cb:
conn_cb_list.append(utils.asDeferred(callback, entity)) conn_cb_list.append(
defer.ensureDeferred(
cls._run_profile_connected(
callback, entity, connection_timer[plugin]
)
)
)
list_d = defer.DeferredList(conn_cb_list) list_d = defer.DeferredList(conn_cb_list)
def logPluginResults(results): def logPluginResults(results):
@ -279,13 +302,49 @@ class SatXMPPEntity(core_types.SatXMPPEntity):
log.error(_("Plugins initialisation error")) log.error(_("Plugins initialisation error"))
for idx, (success, result) in enumerate(results): for idx, (success, result) in enumerate(results):
if not success: if not success:
log.error( plugin_name = plugin_conn_cb[idx][0]._info["import_name"]
"error (plugin %(name)s): %(failure)s" log.error(f"error (plugin {plugin_name}): {result}")
% { plugins_by_timer = sorted(
"name": plugin_conn_cb[idx][0]._info["import_name"], connection_timer,
"failure": result, key=lambda p: connection_timer[p]["total"],
} reverse=True
) )
log.debug(f"Plugin loading time for {profile!r} (longer to shorter):\n")
# total is the addition of all connecting and connected, doesn't really
# reflect the real loading time as connected are launched in a
# DeferredList
total_plugins = 0
# total real sum all connecting (which happen sequentially) and the
# longuest connected (connected happen in parallel, thus the longuest is
# roughly the total time for connected)
total_real = 0
total_real_first_connected = False
for plugin in plugins_by_timer:
name = plugin._info["import_name"]
timer = connection_timer[plugin]
total_plugins += timer["total"]
try:
connecting = f"{timer['connecting']:.2f}s"
except KeyError:
connecting = "n/a"
else:
total_real += timer["connecting"]
try:
connected = f"{timer['connected']:.2f}s"
except KeyError:
connected = "n/a"
else:
if not total_real_first_connected:
total_real += timer['connected']
total_real_first_connected = True
log.debug(
f" - {name}: total={timer['total']:.2f}s "
f"connecting={connecting} connected={connected}"
)
log.debug(
f" Plugins total={total_plugins:.2f}s real={total_real:.2f}s\n"
)
await list_d.addCallback( await list_d.addCallback(
logPluginResults logPluginResults