Files
littlefs/scripts/tailpipe.py
Christopher Haster 4fe0738ff4 Added bench.py and bench_runner.c for benchmarking
These are really just different flavors of test.py and test_runner.c
without support for power-loss testing, but with support for measuring
the cumulative number of bytes read, programmed, and erased.

Note that the existing define parameterization should work perfectly
fine for running benchmarks across various dimensions:

./scripts/bench.py \
    runners/bench_runner \
    bench_file_read \
    -gnor \
    -DSIZE='range(0,131072,1024)'

Also added a couple basic benchmarks as a starting point.
2022-11-15 13:33:34 -06:00

122 lines
3.2 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Efficiently displays the last n lines of a file/pipe.
#
# Example:
# ./scripts/tailpipe.py trace -n5
#
# Copyright (c) 2022, The littlefs authors.
# SPDX-License-Identifier: BSD-3-Clause
#
import os
import sys
import threading as th
import time
def openio(path, mode='r'):
if path == '-':
if mode == 'r':
return os.fdopen(os.dup(sys.stdin.fileno()), 'r')
else:
return os.fdopen(os.dup(sys.stdout.fileno()), 'w')
else:
return open(path, mode)
def main(path='-', *, lines=1, sleep=0.01, keep_open=False):
ring = [None] * lines
i = 0
count = 0
lock = th.Lock()
event = th.Event()
done = False
# do the actual reading in a background thread
def read():
nonlocal i
nonlocal count
nonlocal done
while True:
with openio(path) as f:
for line in f:
with lock:
ring[i] = line
i = (i + 1) % lines
count = min(lines, count + 1)
event.set()
if not keep_open:
break
# don't just flood open calls
time.sleep(sleep or 0.1)
done = True
th.Thread(target=read, daemon=True).start()
try:
last_count = 1
while not done:
time.sleep(sleep)
event.wait()
event.clear()
# create a copy to avoid corrupt output
with lock:
ring_ = ring.copy()
i_ = i
count_ = count
# first thing first, give ourself a canvas
while last_count < count_:
sys.stdout.write('\n')
last_count += 1
for j in range(count_):
# move cursor, clear line, disable/reenable line wrapping
sys.stdout.write('\r')
if count_-1-j > 0:
sys.stdout.write('\x1b[%dA' % (count_-1-j))
sys.stdout.write('\x1b[K')
sys.stdout.write('\x1b[?7l')
sys.stdout.write(ring_[(i_-count_+j) % lines][:-1])
sys.stdout.write('\x1b[?7h')
if count_-1-j > 0:
sys.stdout.write('\x1b[%dB' % (count_-1-j))
sys.stdout.flush()
except KeyboardInterrupt:
pass
sys.stdout.write('\n')
if __name__ == "__main__":
import sys
import argparse
parser = argparse.ArgumentParser(
description="Efficiently displays the last n lines of a file/pipe.")
parser.add_argument(
'path',
nargs='?',
help="Path to read from.")
parser.add_argument(
'-n',
'--lines',
type=lambda x: int(x, 0),
help="Number of lines to show, defaults to 1.")
parser.add_argument(
'-s',
'--sleep',
type=float,
help="Seconds to sleep between reads, defaults to 0.01.")
parser.add_argument(
'-k',
'--keep-open',
action='store_true',
help="Reopen the pipe on EOF, useful when multiple "
"processes are writing.")
sys.exit(main(**{k: v
for k, v in vars(parser.parse_intermixed_args()).items()
if v is not None}))