Я провожу бенчмаркинг насимулятор gem5, который продолжает печатать вывод на терминал во время работы. Я уже сохранил пример запуска того же самого бенчмарка втекстовый файл.
Итак, теперь я хочу сравнить выходной поток, выводимый на консоль, с текстовым файлом предыдущего золотого прогона. Если есть разница в выводе по сравнению с текстовым файлом, симуляция должна быть автоматически прекращена.
Тест занимает много времени для выполнения. Меня интересует только первая ошибка в текущем запуске, чтобы я мог сэкономить время ожидания завершения выполнения для сравнения обоих выходов.
решение1
Я не мог удержаться от того, чтобы не поразмыслить еще немного над тем, как правильно сравнить результатызапущенные процессы(в терминале) против файла «золотого прогона», как вы упомянули.
Как перехватить вывод запущенного процесса
Я использовал script
команду с -f
опцией. Это записывает текущее (текстовое) содержимое терминала в файл; опция -f
заключается в обновлении выходного файла при каждом событии записи в терминал. Команда скрипта создана для ведения записи всего, что происходит в окне терминала.
Скрипт ниже периодически импортирует этот вывод.
Что делает этот скрипт
Если вы запустите скрипт в окне терминала, он откроет второе окно терминала, инициированное командой script -f
. В этом (втором) окне терминала вы должны запустить свою команду, чтобы запустить процесс бенчмарка. Пока этот процесс бенчмарка выдает свои результаты, эти результаты периодически (каждые 2 секунды) сравниваются с вашим «золотым прогоном». Если возникло различие, отличающиеся выходные данные отображаются в «главном» (первом) терминале, и скрипт завершается. Появляется строка в формате:
error: ('Solutions: 13.811084', 'Solutions: 13.811084 aap noot mies')
explanation:
error: (<golden_run_result>, <current_differing_output>)
После этого вывода вы можете спокойно закрыть второе окно и запустить тесты.
Как использовать
Скопируйте скрипт ниже в пустой файл.
Когда вы смотрите на свой файл "золотого прогона", первый раздел (до начала фактического теста) не имеет значения и может отличаться в разных системах. Поэтому вам нужно определить строку, где начинается фактический вывод. В вашем случае я установил его следующим образом:first_line = "**** REAL SIMULATION ****"
При необходимости измените его.
- Укажите путь к файлу «golden run».
Сохраните скрипт как
compare.py
, запустите его командой:python3 /path/to/compare.py
`
- открывается второе окно, говорящее
Script started, the file is named </path/to/file>
- во втором окне запустите тест производительности, первый отличающийся результат появится в первом окне:
Как я тестировал
Я создал небольшую программу, которая печатает строки отредактированной версии вашего золотого бега, одну за другой. Я заставил скрипт сравнить его с оригинальным файлом "золотого бега".
Сценарий:
#!/usr/bin/env python3
import subprocess
import os
import time
home = os.environ["HOME"]
# files / first_line; edit if necessaary
golden_run = "/home/jacob/Bureaublad/log_example"
first_line = "**** REAL SIMULATION ****"
# don't change anything below
typescript_outputfile = home+"/"+"scriptlog.txt"
# commands
startup_command = "gnome-terminal -x script -f "+typescript_outputfile
clean_textcommand = "col -bp <"+typescript_outputfile+" | less -R"
# remove old outputfile
try:
os.remove(typescript_outputfile)
except Exception:
pass
# initiate typescript
subprocess.Popen(["/bin/bash", "-c", startup_command])
time.sleep(1)
# read golden run
with open(golden_run) as src:
original = src.read()
orig_section = original[original.find(first_line):]
# read last output of current results so far
def get_last():
read = subprocess.check_output(["/bin/bash", "-c", clean_textcommand]).decode("utf-8")
if not first_line+"\n" in read:
return "Waiting for first line"
else:
return read[read.find(first_line):]
with open(typescript_outputfile, "wt") as clear:
clear.write("\n")
# loop
while True:
current = get_last()
if current == "\n":
pass
else:
if not current in orig_section and current != "Waiting for first line":
orig = orig_section.split("\n")
breakpoint = current.split("\n")
diff = [(orig[i], breakpoint[i]) for i in range(len(breakpoint)) \
if not orig[i] == breakpoint[i]]
print("error: "+str(diff[0]))
break
else:
pass
time.sleep(5)
решение2
Вы можете использовать diff
утилиту.
Предположим, у вас естьтвойзолотой файл, идругойчто я изменился.
У меня ваша программа не запущена, поэтому я написал следующую симуляцию:
#!/bin/bash
while read -r line; do
echo "$line";
sleep 1;
done < bad_file
Он читает издругойфайл (bad_file) и выводить построчно каждую секунду.
Теперь запустим этот скрипт и перенаправим его вывод в log
файл.
$ simulate > log &
Также я написал скрипт проверки:
#!/bin/bash
helper(){
echo "This script takes two file pathes as arguments."
echo "$0 path/to/file1 path/to/file2"
}
validate_input(){
if [[ $# != 2 ]]; then
helper
exit 1
fi
if [[ ! -f "$1" ]]; then
echo "$1" file is not exist.
helper
exit 1
fi
if [[ ! -f "$2" ]]; then
echo "$2" file is not exist.
helper
exit 1
fi
}
diff_files(){
# As input takes two file and check
# difference between files. Only checks
# number of lines you have right now in
# your $2 file, and compare it with exactly
# the same number of lines in $1
diff -q -a -w <(tail -n+"$ULINES" $1 | head -n "$CURR_LINE") <(tail -n+"$ULINES" $2 | head -n "$CURR_LINE")
}
get_curr_lines(){
# count of lines currenly have minus ULINES
echo "$[$(cat $1 | wc -l) - $ULINES]"
}
print_diff_lines(){
diff -a -w --unchanged-line-format="" --new-line-format=":%dn: %L" "$1" "$2" | grep -o ":[0-9]*:" | tr -d ":"
}
ULINES=15 # count of first unused lines. How many first lines to ignore
validate_input "$1" "$2"
CURR_LINE=$(get_curr_lines "$2") # count of lines currenly have minus ULINES
if [[ $CURR_LINE < 0 ]];then
exit 0
fi
IS_DIFF=$(diff_files "$1" "$2")
if [[ -z "$IS_DIFF" ]];then
echo "Do nothing if they are the same"
else
echo "Do something if files already different"
echo "Line number: " `print_diff_lines "$1" "$2"`
fi
Не забудьте сделать его исполняемым chmod +x checker.sh
.
Этот скрипт принимает два аргумента. Первый аргумент — путь к вашему золотому файлу, второй аргумент — путь к вашему файлу журнала.
$ ./checker.sh path_to_golden path_to_log
Эта проверка подсчитывает количество строк, которые в данный момент есть в вашем log
файле, и сравнивает его с точно таким же количеством строк в golden_file
.
Вы запускаете проверку каждую секунду и выполняете команду kill при необходимости
Если хотите, можете написать функцию bash, которая будет запускаться checker.sh
каждую секунду:
$ chk_every() { while true; do ./checker.sh $1 $2; sleep 1; done; }
Часть предыдущего ответа о diff
Вы можете сравнить их построчно в текстовом файле.
Отman diff
NAME
diff - compare files line by line
-a, --text
treat all files as text
-q, --brief
report only when files differ
-y, --side-by-side
output in two columns
Если мы сравним наши файлы:
$ diff -a <(tail -n+15 file1) <(tail -n+15 file2)
Мы увидим такой вывод:
2905c2905
< Solutions: 0.686669
---
> Solutions: 0.686670
2959c2959
< Solutions: 0.279124
---
> Solutions: 0.279125
3030c3030
< Solutions: 0.539016
---
> Solutions: 0.539017
3068c3068
< Solutions: 0.308278
---
> Solutions: 0.308279
Он показывает линию, которая отличается
И вот последняя команда, предполагаю, что вы не хотите проверять первые 15 строк:
$ diff -y -a <(tail -n+15 file1) <(tail -n+15 file2)
Он покажет вам все различия в двух столбцах. Если вы просто хотите узнать, есть ли разница, используйте это:
$ diff -q -a <(tail -n+15 file1) <(tail -n+15 file2)
Если файлы одинаковые, ничего не напечатается.
решение3
Я понятия не имею, насколько сложны ваши входные данные, но вы могли бы использовать что-то вроде awk
считывания каждой поступающей строки и сравнения ее с известным значением.
$ for i in 1 2 3 4 5; do echo $i; sleep 1; done | \
awk '{print "Out:", $0; fflush(); if ($1==2) exit(0)}'
Out: 1
Out: 2
В этом случае я подаю поток чисел с задержкой по времени и awk
работаю до тех пор, пока не будет достигнута первая переменная на входе (толькопеременная здесь) равна 2, затем завершает работу и тем самым останавливает поток.