Visualização de resultados do SS (socket statistics)¶
PLOT de dados originados de arquivo .txt
contendo estatísticas de redes extraídos do
comando unix SS
O comando executado foi o seguinte:
while true; do ss --no-header -temni dst 172.20.5.193 | ts '%.s' | sed -e ':a; /<->$/ { N; s/<->\n//; ba; }' >> /home/ramdisk/sender-ss.txt; done
O comando é executado após o início de uma transferencia feita através de uma ferramenta como Iperf3 ou ETHR
Exemplo de parte do conteúdo de sender-ss.txt:
1726770768.426399 ESTAB 0 3518640 172.20.5.192:44278 172.20.5.193:5201 timer:(on,003ms,0) uid:1000 ino:32098 sk:1 cgroup:/user.slice/user-1000.slice/session-3.scope 1726770768.426770 skmem:(r0,rb131072,t321896,tb4194304,f1872,w3590320,o0,bl0,d0) ts sack cubic wscale:7,7 rto:201 rtt:0.587/0.147 mss:1448 pmtu:1500 rcvmss:536 advmss:1448 cwnd:352 ssthresh:273 bytes_sent:1886485020205 bytes_retrans:480855632 bytes_acked:1886003720038 segs_out:1302877622 segs_in:10509446 data_segs_out:1302877620 send 6946453152bps lastrcv:2583755 pacing_rate 8028025592bps delivery_rate 5912438560bps delivered:1302545227 busy:2582964ms rwnd_limited:68316ms(2.6%) unacked:307 retrans:0/332131 dsack_dups:44 rcv_space:14480 rcv_ssthresh:31832 notsent:2487664 minrtt:0.098 snd_wnd:4427904
1726770768.426851 ESTAB 0 2215440 172.20.5.192:44284 172.20.5.193:5201 timer:(on,003ms,0) uid:1000 ino:32099 sk:2 cgroup:/user.slice/user-1000.slice/session-3.scope 1726770768.426980 skmem:(r0,rb131072,t89440,tb4082176,f752,w2260240,o0,bl0,d0) ts sack cubic wscale:7,7 rto:201 rtt:0.342/0.043 mss:1448 pmtu:1500 rcvmss:536 advmss:1448 cwnd:105 ssthresh:104 bytes_sent:1075548512165 bytes_retrans:2787717112 bytes_acked:1072760664734 segs_out:742782161 segs_in:15415723 data_segs_out:742782159 send 3556491228bps lastrcv:2583754 pacing_rate 4266230176bps delivery_rate 2250777200bps delivered:740856851 busy:2583597ms unacked:90 retrans:0/1925219 rcv_space:14480 rcv_ssthresh:31832 notsent:2085120 minrtt:0.091 snd_wnd:4741504
1726770768.427031 ESTAB 0 0 172.20.5.192:44246 172.20.5.193:5201 uid:1000 ino:32096 sk:3 cgroup:/user.slice/user-1000.slice/session-3.scope 1726770768.427123 skmem:(r0,rb131072,t0,tb87040,f0,w0,o0,bl0,d0) ts sack cubic wscale:7,7 rto:201 rtt:0.635/0.397 ato:40 mss:1448 pmtu:1500 rcvmss:536 advmss:1448 cwnd:10 bytes_sent:167 bytes_acked:168 bytes_received:4 segs_out:7 segs_in:7 data_segs_out:3 data_segs_in:4 send 182425197bps lastsnd:2583756 lastrcv:2583752 lastack:2583752 pacing_rate 364419976bps delivery_rate 100294368bps delivered:4 app_limited rcv_space:14480 rcv_ssthresh:31832 minrtt:0.13 snd_wnd:31872
Caminho para o arquivo de texto
file_path = 'sender-ss_2024-10_rj.sc_4-P.txt'
ip_dst = 'IP-DESTINO'
Processar o arquivo e obter o resultado:
result = process_file(file_path, ip_dst)
import re
from collections import defaultdict
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
Funções¶
Extracao dos valores de cada linha do arquivo .TXT:
# Função para extrair cwnd e ssthresh de uma linha
def extract_values(line):
timestamp_match = re.search(r"(\S+)", line)
cwnd_match = re.search(r'cwnd:(\d+)', line)
ssthresh_match = re.search(r'ssthresh:(\d+)', line)
retrans_match = re.search(r'retrans:(\d+)/\d+', line)
pacing_rate_match = re.search(r'pacing_rate (\d+\S+)', line)
timestamp = float(timestamp_match.group(1)) if timestamp_match else 0
#cwnd = int(cwnd_match.group(1)) if cwnd_match else None
cwnd = int(cwnd_match.group(1)) if cwnd_match else 0
ssthresh = int(ssthresh_match.group(1)) if ssthresh_match else 0
retrans = int(retrans_match.group(1)) if retrans_match else 0
if pacing_rate_match:
pacing_rate = float(str(pacing_rate_match.group(1)).removesuffix('Gbps')) if 'Gbps' in pacing_rate_match.group(1) else pacing_rate_match
if pacing_rate_match:
pacing_rate = float(str(pacing_rate_match.group(1)).removesuffix('bps')) * 1e-9 if 'bps' in pacing_rate_match.group(1) else 0
else:
pacing_rate = 0
# Tratamento Timestamp
dt = datetime.fromtimestamp(timestamp)
miliseg = int((timestamp - int(timestamp)) * 1000)
segundos_miliseg = float(dt.strftime('%S') + f'.{miliseg:03d}')
return segundos_miliseg, cwnd, ssthresh, retrans, pacing_rate
# TODO reordenacao
def reordenacao(cwnd_total, ssthresh_total):
# CWND
lista_cwnd_segundos = cwnd_total['cwnd']['segundos_miliseg']
lista_cwnd_valores = cwnd_total['cwnd']['valor']
lista_tuplas = list(zip(cwnd_total['cwnd']['segundos_miliseg'],
cwnd_total['cwnd']['valor']))
lista_tuplas.sort(key=lambda x:[0])
lista_cwnd_segundos_reordenado, lista_cwnd_valores_reordenado = zip(*lista_tuplas)
# SSTHRESH
#lista_tuplas = 0
lista_tuplas = list(zip(ssthresh_total['ssthresh']['segundos_miliseg'],
ssthresh_total['ssthresh']['valor']))
lista_tuplas.sort(key=lambda x:[0])
lista_ssthesh_segundos_reordenado, lista_ssthresh_valores_reordenado = zip(*lista_tuplas)
cwnd_total['cwnd']['segundos_miliseg'] = list(lista_cwnd_segundos_reordenado)
cwnd_total['cwnd']['valor'] = list(lista_cwnd_valores_reordenado)
ssthresh_total['ssthresh']['segundos_miliseg'] = list(lista_ssthesh_segundos_reordenado)
ssthresh_total['ssthresh']['valor'] = list(lista_ssthresh_valores_reordenado)
return cwnd_total, ssthresh_total
Função principal de processamento do arquivo
contendo a análise linha a linha.
Main¶
Inicialização das variáveis e seu processamento:
file_path: Caminho do arquivo .txt
ip_dst: ip de destino
# Função para processar o arquivo e construir o dicionário
def process_file(file_path, ip_dst):
fluxo_controle = ""
port_dict = defaultdict(lambda: {'segundos_miliseg': [], 'cwnd': [], 'ssthresh': [], 'retrans': [], 'pacing_rate': []})
with open(file_path, 'r') as file:
# Parametros a serem analisados
lista_itens = ['cwnd', 'ssthresh', 'retrans', 'pacing_rate']
for line in file:
# Extrair a porta de origem
match = re.search(r'(\d+\.\d+\.\d+\.\d+:\d+)\s+(\d+\.\d+\.\d+\.\d+:\d+)', line)
if match:
src_port = match.group(1)
# Excluir portas de destino e processar os valores de cwnd e ssthresh
# Muito provavelmente as conexoes abertas com porta 443 no destino sao de CONTROLE,
# portanto nao devem ser contabilizadas
if ip_dst not in src_port and f'{ip_dst}:443' not in line:
segundos_miliseg, cwnd, ssthresh, retrans, pacing_rate = extract_values(line)
if cwnd is not None:
port_dict[src_port]['segundos_miliseg'].append(segundos_miliseg)
if cwnd is not None:
port_dict[src_port]['cwnd'].append(cwnd)
if ssthresh is not None:
port_dict[src_port]['ssthresh'].append(ssthresh)
# Retransmission
if retrans is not None:
port_dict[src_port]['retrans'].append(retrans)
# Pacing rate
if pacing_rate is not None:
port_dict[src_port]['pacing_rate'].append(pacing_rate)
for item in lista_itens:
plt.figure(figsize=(32, 12))
contador = 1
for key, value in port_dict.items():
lista_tempo_atualizada = []
segundo_inicial = int(value['segundos_miliseg'][0])
'''
Devido ao uso do comando ts (timestamp) para registrar o tempo da captura, isto é,
se baseando no tempo atual para host, o inicio da captura não necessariamente vai iniciar
no 0. Podendo, por exemplo, começar 13h40:55s e terminar em 13h41:08s. Desta forma, para melhorar
a visualização do gráfico, há o tratamento para o eixo x do gráfico iniciar no 0.
'''
for elemento in value['segundos_miliseg']:
if segundo_inicial <= int(elemento):
if float(f"{elemento:.3f}") - segundo_inicial == 0:
lista_tempo_atualizada.append(0)
else:
lista_tempo_atualizada.append( float(str(int(elemento) - segundo_inicial) + '.' + str(elemento).split(".")[1]) )
elif segundo_inicial > int(elemento):
#apresentacao_segundos_eixoX = 1 + float(str(segundo_inicial - int( 58 - elemento )) + '.' + str(elemento).split(".")[1])
'''
Atencao na linha seguinte!
'''
apresentacao_segundos_eixoX = (60 - segundo_inicial) + elemento
lista_tempo_atualizada.append(float(f"{apresentacao_segundos_eixoX:.3f}"))
else:
lista_tempo_atualizada.append(elemento)
if not all(x == value[item][0] for x in value[item]) and (fluxo_controle is not None or fluxo_controle == ''):
try:
#plt.plot(value['segundos_miliseg'], value[item], label=f'{key} - {item}')
plt.plot(lista_tempo_atualizada, value[item], label=f'Fluxo {contador}')
except ValueError as e:
print(f"Algum erro nas dimensoes do fluxo: {key}. Erro: {e}")
else:
fluxo_controle = key
if contador <= len(port_dict.items()):
contador += 1
plt.title(item.upper(), fontsize=32)
plt.legend(fontsize=20)
# LABEL
plt.xlabel('Segundos', fontsize=24)
# Garantindo que o eixo X exiba valores float com 2 casas decimais
plt.gca().xaxis.set_major_formatter(ticker.FormatStrFormatter('%.2f'))
# Definindo intervalo dos ticks principais no eixo X para ajustar a exibição
if item.upper() == 'PACING_RATE':
plt.ylabel('Gbps', fontsize=24)
# TICKS
plt.xticks(fontsize=24)
plt.yticks(fontsize=24)
plt.grid(True)
plt.show()
Troubleshooting¶
Análise de um servidor que em dado momento estava com pci x4 ao invés de x16
# Caminho para o arquivo de texto
file_path = 'ss-sender2.txt'
ip_dst = '172.20.5.193'
# Processar o arquivo e obter o resultado
result = process_file(file_path, ip_dst)
C:\Users\vmedeiros.LARC2\AppData\Local\Temp\ipykernel_17156\1588345192.py:87: UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. plt.legend(fontsize=20)
Análise Rede: 'Start' com Default x 2G¶
# Caminho para o arquivo de texto
file_path = 'ss-sender_rj-sc_P4-default.txt'
ip_dst = '<IP-DESTINO>'
# Processar o arquivo e obter o resultado
result = process_file(file_path, ip_dst)
C:\Users\vmedeiros.LARC2\AppData\Local\Temp\ipykernel_17156\1588345192.py:87: UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. plt.legend(fontsize=20)
O Fluxo 1 refere-se a conexão de controle aberta da ferramenta de teste, por isso os 5 fluxos totais e o valor constante por parte do Fluxo 1
# Caminho para o arquivo de texto
file_path = 'ss-sender_rj-sc_P4-2G.txt'
ip_dst = '<IP-DESTINO>'
# Processar o arquivo e obter o resultado
result = process_file(file_path, ip_dst)
C:\Users\vmedeiros.LARC2\AppData\Local\Temp\ipykernel_17924\2134817016.py:93: UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. plt.legend(fontsize=20)