Skip to content

Commit 79644ad

Browse files
authored
IVS-681-Observability (#536)
* IVS-681-Observability * Cleanup --------- Co-authored-by: Geert Hesselink <Ghesselink@users.noreply.github.com>
1 parent c5a8fac commit 79644ad

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

features/environment.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,40 @@ def set_logger(context):
5252

5353
return logger
5454

55+
56+
57+
def _read_proc_kb(field, path='/proc/self/status'):
58+
"""Read a '<field>: <n> kB' line from /proc. Returns None when unavailable."""
59+
try:
60+
with open(path) as f:
61+
for line in f:
62+
if line.startswith(field + ':'):
63+
return int(line.split()[1])
64+
except (OSError, ValueError, IndexError):
65+
pass
66+
return None
67+
68+
69+
def _reset_peak_rss():
70+
"""Reset the kernel peak-memory counter (VmHWM) to the current RSS. Linux-only."""
71+
try:
72+
with open('/proc/self/clear_refs', 'w') as f:
73+
f.write('5')
74+
return True
75+
except OSError:
76+
return False
77+
78+
79+
def _feature_mem_txt(context):
80+
"""Peak memory for this feature: the high-water mark since before_feature,
81+
and how much that is above the memory at feature start. Empty if /proc is unusable."""
82+
peak_kb = _read_proc_kb('VmHWM')
83+
start_kb = getattr(context, 'feature_rss_start_kb', None)
84+
if peak_kb and start_kb and getattr(context, 'feature_peak_reset', False):
85+
delta_mb = (peak_kb - start_kb) / 1024
86+
return f" Peak RSS: {peak_kb/1024:.0f} MB (delta {delta_mb:+.0f} MB)."
87+
return ""
88+
5589
def before_feature(context, feature):
5690
#@todo incorporate into gherkin error handling
5791
# assert protocol.enforce(context, feature), 'failed'
@@ -62,7 +96,12 @@ def before_feature(context, feature):
6296
except KeyError: # run via console, task_id not provided
6397
context.validation_task_id = None
6498
Scenario.continue_after_failed_step = False
65-
99+
100+
# note current RSS and reset the kernel peak counter so that VmHWM in
101+
# after_feature reflects the peak of *this* feature only.
102+
context.feature_rss_start_kb = _read_proc_kb('VmRSS')
103+
context.feature_peak_reset = _reset_peak_rss()
104+
66105
execution_mode = context.config.userdata.get('execution_mode')
67106
if execution_mode and execution_mode == 'ExecutionMode.PRODUCTION':
68107
context.feature_start_time = time.process_time()
@@ -177,7 +216,7 @@ def reduce_db_outcomes(feature_outcomes):
177216
end_time = time.process_time()
178217
elapsed_time = end_time - context.feature_start_time
179218
logger = set_logger(context)
180-
logger.info(f"Feature '{feature.name}' completed. Elapsed process time: {elapsed_time:.2f} seconds.")
219+
logger.info(f"Feature '{feature.name}' completed. Elapsed process time: {elapsed_time:.2f} seconds.{_feature_mem_txt(context)}")
181220

182221
else: # invoked via console or CI/CD pipeline
183222
outcomes = [outcome.to_dict() for outcome in context.gherkin_outcomes]

0 commit comments

Comments
 (0)