Estou executando um benchmark emsimulador gem5, que continua imprimindo a saída no terminal enquanto está em execução. Já armazenei uma amostra do mesmo benchmark em umarquivo de texto.
Agora quero comparar o fluxo de saída que está sendo impresso no console com o arquivo de texto da execução dourada anterior. Se houver uma diferença na saída em relação ao arquivo de texto, a simulação deverá ser encerrada automaticamente.
O benchmark leva muito tempo para ser executado. Estou interessado apenas no primeiro erro da execução atual, para poder economizar tempo de espera até a conclusão da execução para comparar as duas saídas.
Responder1
Não pude resistir a pensar um pouco mais sobre como encontrar uma maneira adequada de comparar a saída de umprocessos em execução(no terminal) contra um arquivo "golden run", como você mencionou.
Como capturar a saída do processo em execução
Usei o script
comando com a -f
opção. Isso grava o conteúdo atual (textual) do terminal em um arquivo; a -f
opção é atualizar o arquivo de saída em cada evento de gravação no terminal. O comando script é feito para manter registro de tudo o que acontece em uma janela de terminal.
O script abaixo importa esta saída periodicamente.
O que este script faz
Se você executar o script em uma janela de terminal, ele abrirá uma segunda janela de terminal, iniciada com o script -f
comando. Nesta (segunda) janela do terminal, você deve executar seu comando para iniciar o processo de benchmark. Embora esse processo de benchmark produza seus resultados, esses resultados são comparados periodicamente (a cada 2 segundos) com sua "corrida de ouro". Se ocorrer uma diferença, a saída diferente será exibida no terminal "principal" (primeiro) e o script será encerrado. Uma linha aparece, no formato:
error: ('Solutions: 13.811084', 'Solutions: 13.811084 aap noot mies')
explanation:
error: (<golden_run_result>, <current_differing_output>)
Após esta saída, você pode fechar com segurança a segunda janela, executando seus testes.
Como usar
Copie o script abaixo em um arquivo vazio.
Quando você olha para o arquivo "golden run", a primeira seção (antes do início do teste real) é irrelevante e pode diferir em sistemas diferentes. Portanto, você precisa definir a linha onde a saída real começa. No seu caso eu configurei para:first_line = "**** REAL SIMULATION ****"
altere-o se necessário.
- Defina o caminho para o seu arquivo "golden run".
Salve o script como
compare.py
e execute-o pelo comando:python3 /path/to/compare.py
`
- uma segunda janela se abre, dizendo
Script started, the file is named </path/to/file>
- nesta segunda janela, execute seu teste de benchmark, o primeiro resultado diferente aparece na primeira janela:
Como eu testei
Criei um pequeno programa que imprime as linhas de uma versão editada do seu golden run, uma por uma. Fiz o script compará-lo com o arquivo original da "corrida dourada".
O roteiro:
#!/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)
Responder2
Você pode usar diff
utilitário.
Suponha que você tenhaseuarquivo dourado, eoutroque eu mudei.
Não tenho seu programa em execução, então escrevi esta simulação:
#!/bin/bash
while read -r line; do
echo "$line";
sleep 1;
done < bad_file
Ele lê deoutroarquivo (bad_file) e saída linha por linha a cada segundo.
Agora execute este script e redirecione a saída para log
o arquivo.
$ simulate > log &
Também escrevi o script verificador:
#!/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
Não se esqueça de torná-lo executável chmod +x checker.sh
.
Este script leva dois argumentos. O primeiro argumento é o caminho para o seu arquivo dourado, o segundo argumento é o caminho para o seu arquivo de log.
$ ./checker.sh path_to_golden path_to_log
Este verificador conta o número de linhas que você tem agora em seu log
arquivo e compara-o com exatamente o mesmo número de linhas em golden_file
.
Você executa o verificador a cada segundo e executa o comando kill, se necessário
Se quiser, você pode escrever uma função bash para ser executada checker.sh
a cada segundo:
$ chk_every() { while true; do ./checker.sh $1 $2; sleep 1; done; }
Parte da resposta anterior sobre diff
Você pode compará-los linha por linha como arquivo de texto
Deman 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
Se compararmos nossos arquivos:
$ diff -a <(tail -n+15 file1) <(tail -n+15 file2)
Veremos esta saída:
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
Mostra a linha que difere
E aqui está o comando final, presumo que você não queira verificar as primeiras 15 linhas:
$ diff -y -a <(tail -n+15 file1) <(tail -n+15 file2)
Ele mostrará todas as diferenças em duas colunas. Se você quer apenas saber se há alguma diferença, use isto:
$ diff -q -a <(tail -n+15 file1) <(tail -n+15 file2)
Não imprimirá nada se os arquivos forem iguais
Responder3
Não tenho ideia de quão complicados são seus dados de entrada, mas você pode usar algo como awk
ler cada linha à medida que ela chega e compará-la com um valor conhecido.
$ 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
Neste caso, estou alimentando um fluxo de números com atraso de tempo e awk
executando até a primeira variável na entrada (oapenasvariável aqui) é igual a 2, então sai e, ao fazer isso, interrompe o fluxo.