Prime95 workflow mostly done
This commit is contained in:
parent
0eadb784bb
commit
46a6dda0ff
4 changed files with 150 additions and 20 deletions
|
|
@ -15,6 +15,7 @@ ATTRIBUTE_COLORS = (
|
|||
('Maximum', 'PURPLE'),
|
||||
)
|
||||
CPU_FAILURE_TEMP = 90
|
||||
CPU_TEST_MINUTES = 7
|
||||
CPU_THERMAL_LIMIT = 99
|
||||
KEY_NVME = 'nvme_smart_health_information_log'
|
||||
KEY_SMART = 'ata_smart_attributes'
|
||||
|
|
@ -118,8 +119,8 @@ TMUX_LAYOUT = OrderedDict({
|
|||
'Started': {'width': TMUX_SIDE_WIDTH, 'Check': True},
|
||||
'Progress': {'width': TMUX_SIDE_WIDTH, 'Check': True},
|
||||
# Testing panes
|
||||
'Prime95': {'height': 11, 'Check': False},
|
||||
'Temps': {'height': 1000, 'Check': False},
|
||||
'Prime95': {'height': 11, 'Check': False},
|
||||
'SMART': {'height': 3, 'Check': True},
|
||||
'badblocks': {'height': 5, 'Check': True},
|
||||
'I/O Benchmark': {'height': 1000, 'Check': False},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from docopt import docopt
|
|||
|
||||
from wk import cfg, exe, log, net, std, tmux
|
||||
from wk.hw import obj as hw_obj
|
||||
from wk.hw import sensors as hw_sensors
|
||||
|
||||
|
||||
# atexit functions
|
||||
|
|
@ -78,6 +79,7 @@ class State():
|
|||
def __init__(self):
|
||||
self.cpu = None
|
||||
self.disks = []
|
||||
self.layout = cfg.hw.TMUX_LAYOUT.copy()
|
||||
self.log_dir = None
|
||||
self.panes = {}
|
||||
self.tests = OrderedDict({
|
||||
|
|
@ -111,10 +113,12 @@ class State():
|
|||
|
||||
# Init tmux and start a background process to maintain layout
|
||||
self.init_tmux()
|
||||
if hasattr(signal, 'SIGWINCH'):
|
||||
# Use signal handling
|
||||
signal.signal(signal.SIGWINCH, self.fix_tmux_layout)
|
||||
else:
|
||||
#TODO: Fix SIGWINCH?
|
||||
#if hasattr(signal, 'SIGWINCH'):
|
||||
# # Use signal handling
|
||||
# signal.signal(signal.SIGWINCH, self.fix_tmux_layout)
|
||||
#else:
|
||||
# exe.start_thread(self.fix_tmux_layout_loop)
|
||||
exe.start_thread(self.fix_tmux_layout_loop)
|
||||
|
||||
def fix_tmux_layout(self, forced=True, signum=None, frame=None):
|
||||
|
|
@ -125,7 +129,7 @@ class State():
|
|||
signum and frame must be valid aguments.
|
||||
"""
|
||||
try:
|
||||
tmux.fix_layout(self.panes, cfg.hw.TMUX_LAYOUT, forced=forced)
|
||||
tmux.fix_layout(self.panes, self.layout, forced=forced)
|
||||
except RuntimeError:
|
||||
# Assuming self.panes changed while running
|
||||
pass
|
||||
|
|
@ -143,6 +147,8 @@ class State():
|
|||
"""Initialize diagnostic pass."""
|
||||
# Reset objects
|
||||
self.disks.clear()
|
||||
self.layout.clear()
|
||||
self.layout.update(cfg.hw.TMUX_LAYOUT)
|
||||
for test_data in self.tests.values():
|
||||
test_data['Objects'].clear()
|
||||
|
||||
|
|
@ -287,6 +293,81 @@ def build_menu(cli_mode=False, quick_mode=False):
|
|||
def cpu_mprime_test(state, test_objects):
|
||||
"""CPU & cooling check using Prime95."""
|
||||
LOG.info('CPU Test (Prime95)')
|
||||
thermal_abort = False
|
||||
prime_log = pathlib.Path(f'{state.log_dir}/prime.log')
|
||||
test = test_objects[0]
|
||||
|
||||
# Bail early
|
||||
if test.disabled:
|
||||
return
|
||||
|
||||
# Prep
|
||||
dev = test.dev
|
||||
test.set_status('Working')
|
||||
state.update_top_pane(dev.description)
|
||||
|
||||
# Start sensors monitor
|
||||
sensors = hw_sensors.Sensors()
|
||||
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||
sensors_thread = exe.start_thread(
|
||||
sensors.monitor_to_file, args=(sensors_out,))
|
||||
|
||||
# Create monitor and worker panes
|
||||
state.panes['Prime95'] = tmux.split_window(
|
||||
lines=10, vertical=True, watch_file=prime_log)
|
||||
state.panes['Temps'] = tmux.split_window(
|
||||
behind=True, percent=80, vertical=True, watch_file=sensors_out)
|
||||
tmux.resize_pane(height=3)
|
||||
state.panes['Current'] = ''
|
||||
state.layout['Current'] = {'height': 3, 'Check': True}
|
||||
|
||||
# Get idle temps
|
||||
std.clear_screen()
|
||||
std.print_standard('Saving idle temps...')
|
||||
sensors.save_average_temps(temp_label='Idle', seconds=5)
|
||||
|
||||
# Stress CPU
|
||||
std.print_info('Starting stress test')
|
||||
std.print_warning('If running too hot, press CTRL+c to abort the test')
|
||||
set_apple_fan_speed('max')
|
||||
#RUN: mprime -t | grep -iv --line-buffered 'stress.txt' | tee -a "prime.log"
|
||||
try:
|
||||
print_countdown(seconds=cfg.hw.CPU_TEST_MINUTES*60)
|
||||
except KeyboardInterrupt:
|
||||
test.set_status('Aborted')
|
||||
except hw_sensors.ThermalLimitReachedError:
|
||||
test.set_status('Failed')
|
||||
test.failed = True
|
||||
thermal_abort = True
|
||||
|
||||
# Stop Prime95
|
||||
#TODO kill p95
|
||||
tmux.kill_pane(state.panes.pop('Prime95', None))
|
||||
|
||||
# Get cooldown temp
|
||||
set_apple_fan_speed('auto')
|
||||
std.clear_screen()
|
||||
std.print_standard('Letting CPU cooldown...')
|
||||
std.sleep(5)
|
||||
std.print_standard('Saving cooldown temps...')
|
||||
sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
||||
|
||||
# Check results and build report
|
||||
#TODO
|
||||
|
||||
# Stop sensors monitor
|
||||
sensors_out.with_suffix('.stop').touch()
|
||||
sensors_thread.join()
|
||||
|
||||
# Cleanup
|
||||
state.panes.pop('Current', None)
|
||||
tmux.kill_pane(state.panes.pop('Temps', None))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#TODO: p95
|
||||
std.print_warning('TODO: p95')
|
||||
std.pause()
|
||||
|
|
@ -503,6 +584,26 @@ def network_test():
|
|||
std.pause('Press Enter to return to main menu...')
|
||||
|
||||
|
||||
def print_countdown(seconds):
|
||||
"""Print countdown to screen."""
|
||||
time_limit = seconds
|
||||
for i in range(seconds):
|
||||
sec_left = (seconds - i) % 60
|
||||
min_left = int((seconds - i) / 60)
|
||||
|
||||
out_str = '\r'
|
||||
if min_left:
|
||||
out_str += f'{min_left} minute{"s" if min_left != 1 else ""}, '
|
||||
out_str += f'{sec_left} second{"s" if sec_left != 1 else ""}'
|
||||
out_str += ' remaining'
|
||||
|
||||
print(f'{out_str:<40}', end='', flush=True)
|
||||
std.sleep(1)
|
||||
|
||||
# Done
|
||||
print('')
|
||||
|
||||
|
||||
def run_diags(state, menu, quick_mode=False):
|
||||
"""Run selected diagnostics."""
|
||||
aborted = False
|
||||
|
|
@ -569,5 +670,23 @@ def screensaver(name):
|
|||
tmux.zoom_pane()
|
||||
|
||||
|
||||
def set_apple_fan_speed(speed):
|
||||
"""Set Apple fan speed."""
|
||||
cmd = None
|
||||
|
||||
# Check
|
||||
if speed not in ('auto', 'max'):
|
||||
raise RuntimeError(f'Invalid speed {speed}')
|
||||
|
||||
# Set cmd
|
||||
if platform.system() == 'Linux':
|
||||
cmd = ['apple-fans', speed]
|
||||
#TODO: Add method for use under macOS
|
||||
|
||||
# Run cmd
|
||||
if cmd:
|
||||
exe.run_program(cmd, check=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import json
|
||||
import logging
|
||||
import pathlib
|
||||
import platform
|
||||
import re
|
||||
|
||||
|
|
@ -77,14 +78,21 @@ class Sensors():
|
|||
# Done
|
||||
return report
|
||||
|
||||
def monitor_to_file(self, path):
|
||||
def monitor_to_file(self, out_path):
|
||||
"""Write report to path every second until stopped."""
|
||||
stop_path = pathlib.Path(out_path).resolve().with_suffix('.stop')
|
||||
while True:
|
||||
self.update_sensor_data()
|
||||
report = self.generate_report('Current', 'Max')
|
||||
with open(path, 'w') as _f:
|
||||
with open(out_path, 'w') as _f:
|
||||
_f.write('\n'.join(report))
|
||||
sleep(1)
|
||||
|
||||
# Check if we should stop
|
||||
if stop_path.exists():
|
||||
break
|
||||
|
||||
# Sleep before next loop
|
||||
sleep(0.5)
|
||||
|
||||
def save_average_temps(self, temp_label, seconds=10):
|
||||
# pylint: disable=unused-variable
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ def fix_layout(panes, layout, forced=False):
|
|||
if isinstance(pane_list, str):
|
||||
pane_list = [pane_list]
|
||||
for pane_id in pane_list:
|
||||
if name == 'Current':
|
||||
pane_id = None
|
||||
try:
|
||||
resize_pane(pane_id, **data)
|
||||
except RuntimeError:
|
||||
|
|
@ -98,8 +100,11 @@ def layout_needs_fixed(panes, layout):
|
|||
if name not in panes:
|
||||
continue
|
||||
|
||||
# Check pane size
|
||||
pane_id = panes[name]
|
||||
# Check pane size(s)
|
||||
pane_list = panes[name]
|
||||
if isinstance(pane_list, str):
|
||||
pane_list = [pane_list]
|
||||
for pane_id in pane_list:
|
||||
width, height = get_pane_size(pane_id)
|
||||
if data.get('width', False) and data['width'] != width:
|
||||
needs_fixed = True
|
||||
|
|
@ -193,9 +198,6 @@ def resize_pane(pane_id=None, width=None, height=None, **kwargs):
|
|||
cmd = ['tmux', 'resize-pane']
|
||||
|
||||
# Safety checks
|
||||
if not poll_pane(pane_id):
|
||||
LOG.debug('tmux pane %s not found', pane_id)
|
||||
raise RuntimeError(f'tmux pane {pane_id} not found')
|
||||
if not (width or height):
|
||||
LOG.error('Neither width nor height specified')
|
||||
raise RuntimeError('Neither width nor height specified')
|
||||
|
|
|
|||
Loading…
Reference in a new issue