summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xaergia/aergia.py54
-rw-r--r--t/test_functionality.py6
2 files changed, 30 insertions, 30 deletions
diff --git a/aergia/aergia.py b/aergia/aergia.py
index edf82ba..d14520a 100755
--- a/aergia/aergia.py
+++ b/aergia/aergia.py
@@ -26,11 +26,15 @@ Copyright 2025 bdunahu
Commentary:
Aergia is a sampling based profiler based off of SCALENE
- by Emery Berger (https://github.com/plasma-umass/scalene).
+ by Emery Berger and the UMASS Plasma Lab
+ (https://github.com/plasma-umass/scalene).
It is not particularly informative, but unlike SCALENE
or other sampling-based profilers I could find, reports
the wall-time each asyncio await call spends idling.
+ (yappi can profile asyncio, but only report time spent in
+ each function. Instrumentation-profilers cannot do this
+ without adding very large overhead).
The goal behind Aergia is to eventually have these features,
or similar, merged into SCALENE.
@@ -102,12 +106,12 @@ class Aergia(object):
Debug mode must be on by default to avoid losing samples.
Debug mode is required to view the current coroutine being waited on
- in `Aergia.get_idle_task_frames'. The TimerHandler object otherwise
+ in `Aergia._get_idle_task_frames'. The TimerHandler object otherwise
does not keep track of a _source_traceback.
'''
os.environ["PYTHONASYNCIODEBUG"] = "1"
signal.signal(signal.SIGALRM,
- Aergia.idle_signal_handler)
+ Aergia._idle_signal_handler)
signal.setitimer(signal.ITIMER_REAL,
Aergia.signal_interval,
Aergia.signal_interval)
@@ -132,7 +136,7 @@ class Aergia(object):
'''Pretty-print profiling results.'''
if Aergia.total_samples > 0:
print("FILE\tFUNC\tPERC\t(ACTUAL -> SECONDS)")
- for key in Aergia.sort_samples(Aergia.samples):
+ for key in Aergia._sort_samples(Aergia.samples):
Aergia.print_sample(key)
else:
print("No samples were gathered. If you *are* using concurrency, "
@@ -143,21 +147,21 @@ class Aergia(object):
'''Pretty-print a single sample.'''
sig_intv = Aergia.signal_interval
value = Aergia.samples[key]
- print(f"{Aergia.tuple_to_string(key)} :"
+ print(f"{Aergia._tuple_to_string(key)} :"
f"\t\t{value * 100 / Aergia.total_samples:.3f}%"
f"\t({value:.3f} ->"
f" {value*sig_intv:.6f} seconds)")
@staticmethod
- def idle_signal_handler(sig, frame):
+ def _idle_signal_handler(sig, frame):
'''Obtains and records which lines are currently being waited on.'''
- keys = Aergia.compute_frames_to_record()
+ keys = Aergia._compute_frames_to_record()
for key in keys:
- Aergia.samples[Aergia.frame_to_tuple(key)] += 1
+ Aergia.samples[Aergia._frame_to_tuple(key)] += 1
Aergia.total_samples += 1
@staticmethod
- def compute_frames_to_record():
+ def _compute_frames_to_record():
'''Collects all stack frames which are currently being awaited on
during a given timestamp, and
@@ -167,24 +171,24 @@ class Aergia(object):
Luckily, the event loop and asyncio.all_tasks keeps track of
what is running for us.'''
- loops = Aergia.get_event_loops()
- frames = Aergia.get_frames_from_loops(loops)
+ loops = Aergia._get_event_loops()
+ frames = Aergia._get_frames_from_loops(loops)
return frames
@staticmethod
- def get_event_loops():
+ def _get_event_loops():
'''Returns each thread's event loop, if it exists.'''
loops = []
for t in threading.enumerate():
frame = sys._current_frames().get(t.ident)
if frame:
- loop = Aergia.walk_back_until_loop(frame)
+ loop = Aergia._walk_back_until_loop(frame)
if loop and loop not in loops:
loops.append(loop)
return loops
@staticmethod
- def walk_back_until_loop(frame):
+ def _walk_back_until_loop(frame):
'''Walks back the callstack until we are in a method named '_run_once'.
If this is ever true, we assume we are in an Asyncio event loop method,
and check to see if the 'self' variable is indeed and instance of
@@ -200,15 +204,15 @@ class Aergia(object):
return None
@staticmethod
- def get_frames_from_loops(loops):
+ def _get_frames_from_loops(loops):
'''Given LOOPS, returns a flat list of frames.'''
return [
frames for loop in loops
- for frames in Aergia.get_idle_task_frames(loop)
+ for frames in Aergia._get_idle_task_frames(loop)
]
@staticmethod
- def frame_to_tuple(frame):
+ def _frame_to_tuple(frame):
'''Given a frame, constructs a sample key for tallying lines.'''
co = frame.f_code
func_name = co.co_name
@@ -217,20 +221,20 @@ class Aergia(object):
return Sample(filename, line_no, func_name)
@staticmethod
- def tuple_to_string(sample):
+ def _tuple_to_string(sample):
'''Given a namedtuple corresponding to a sample key,
pretty-prints a frame as a function/file name and a line number.'''
return sample.file + ':' + str(sample.line) + '\t' + sample.func
@staticmethod
- def sort_samples(sample_dict):
+ def _sort_samples(sample_dict):
'''Returns SAMPLE_DICT in descending order by number of samples.'''
return {k: v for k, v in sorted(sample_dict.items(),
key=lambda item: item[1],
reverse=True)}
@staticmethod
- def get_idle_task_frames(loop):
+ def _get_idle_task_frames(loop):
'''Given an asyncio event loop, returns the list of idle task frames.
A task is considered 'idle' if it is not currently executing.'''
idle = []
@@ -240,24 +244,24 @@ class Aergia(object):
continue
coro = task.get_coro()
if coro:
- f = Aergia.get_deepest_traceable_frame(coro)
+ f = Aergia._get_deepest_traceable_frame(coro)
if f:
idle.append(f)
return idle
@staticmethod
- def get_deepest_traceable_frame(coro):
+ def _get_deepest_traceable_frame(coro):
curr = coro
ret = None
while curr:
frame = getattr(curr, 'cr_frame', None)
- if frame and Aergia.should_trace(frame.f_code.co_filename):
+ if frame and Aergia._should_trace(frame.f_code.co_filename):
ret = frame
curr = getattr(curr, 'cr_await', None)
return ret
@staticmethod
- def should_trace(filename):
+ def _should_trace(filename):
'''Returns FALSE if filename is uninteresting to the user.
Don't depend on this. It's good enough for testing.'''
# FIXME Assume GuixSD. Makes filtering easy
@@ -272,7 +276,7 @@ class Aergia(object):
return True
@staticmethod
- def gettime():
+ def _gettime():
'''returns the wallclock time'''
return time.process_time()
diff --git a/t/test_functionality.py b/t/test_functionality.py
index 0302a90..2a2bbe3 100644
--- a/t/test_functionality.py
+++ b/t/test_functionality.py
@@ -1,8 +1,4 @@
-try:
- import yappi
-except ImportError:
- print("yappi module not found. Skipping related tests.")
- exit(0)
+import yappi
import utils
import asyncio
import threading