Como posso determinar se uma latência é devida a um driver ou ao agendador?

3

Usando um osciloscópio e alternando alguns pinos, às vezes vejo latências de 1 a 2 segundos de quando um pacote UART de 8 bytes é transmitido para quando uma leitura de bloqueio retorna. Os pacotes são 1 segunda parte com alguns milissegundos de jitter. Eu também medi o tempo para as chamadas do sistema (veja abaixo) usando strace e os resultados acordados com minhas medições de I / O.

Estou tentando determinar se essa latência está no driver UART ou se outras tarefas estão sobrecarregando minha tarefa, o que tem um valor de niceness de -20. A razão pela qual estou me perguntando sobre o driver é que uma versão anterior desse código usou de forma confiável o UART para transmitir pacotes que eram ~ 26 kb por segundo (o buffer do driver é de 4 kB).

O processo é um script Python que está usando pyserial . Nesse caso com falha, strace informa o tempo entre epoll_wait e clock_gettime como mais de 3 segundos.

0.000883 epoll_ctl(3, EPOLL_CTL_ADD, 7, {EPOLLIN, {u32=7, u64=8589934599}}) = -1 EEXIST (File exists)
0.000681 clock_gettime(CLOCK_MONOTONIC, {92406, 209555006}) = 0
0.000655 epoll_wait(3, {}, 64, 98) = 0
3.004082 clock_gettime(CLOCK_MONOTONIC, {92409, 214251206}) = 0

As ações repetidas são: Receber pacote de 8 bytes para solicitar que o Linux leia N número de bytes via SPI. Execute o SPI. Leia pacote de 8 bytes para ver se a solicitação SPI foi concluída com êxito. A transferência do SPI leva cerca de 40 ms. O padrão saudável é ~ 40 ms entre o pacote de pedidos e o pacote de resultados. E ~ 960 ms até receber o próximo pedido.

Duchess: strace -r -e read -p 7564
Process 7564 attached
     0.000000 read(7, "5656d
0.000883 epoll_ctl(3, EPOLL_CTL_ADD, 7, {EPOLLIN, {u32=7, u64=8589934599}}) = -1 EEXIST (File exists)
0.000681 clock_gettime(CLOCK_MONOTONIC, {92406, 209555006}) = 0
0.000655 epoll_wait(3, {}, 64, 98) = 0
3.004082 clock_gettime(CLOCK_MONOTONIC, {92409, 214251206}) = 0
Duchess: strace -r -e read -p 7564
Process 7564 attached
     0.000000 read(7, "5656d%pre%%pre%", 8) = 8
     0.049142 read(7, "5656%pre%%pre%%pre%", 8) = 8
     0.950381 read(7, "5656d%pre%%pre%", 8) = 8
     0.050035 read(7, "5656%pre%%pre%%pre%", 8) = 8
     0.949962 read(7, "5656d%pre%%pre%", 8) = 8
     0.049601 read(7, "5656%pre%%pre%%pre%", 8) = 8
     0.950417 read(7, "5656d%pre%%pre%", 8) = 8
     0.049654 read(7, "5656%pre%%pre%%pre%", 8) = 8
     0.950507 read(7, "5656d%pre%%pre%", 8) = 80.950516 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request]
     0.049944 read(7, "5656%pre%%pre%%pre%", 8) = 8 [Success]
     2.196903 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request]
     0.048876 read(7, "5656%pre%%pre%%pre%%pre%", 8) = 8 [Failure]
     0.015570 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request]
     0.053889 read(7, "5656%pre%%pre%%pre%%pre%", 8) = 8 [Failure]
     0.634720 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request]
     0.050070 read(7, "5656%pre%%pre%%pre%", 8) = 8 [Success]
", 8) = 8 0.049142 read(7, "5656%pre%%pre%%pre%", 8) = 8 0.950381 read(7, "5656d%pre%%pre%", 8) = 8 0.050035 read(7, "5656%pre%%pre%%pre%", 8) = 8 0.949962 read(7, "5656d%pre%%pre%", 8) = 8 0.049601 read(7, "5656%pre%%pre%%pre%", 8) = 8 0.950417 read(7, "5656d%pre%%pre%", 8) = 8 0.049654 read(7, "5656%pre%%pre%%pre%", 8) = 8 0.950507 read(7, "5656d%pre%%pre%", 8) = 80.950516 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request] 0.049944 read(7, "5656%pre%%pre%%pre%", 8) = 8 [Success] 2.196903 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request] 0.048876 read(7, "5656%pre%%pre%%pre%%pre%", 8) = 8 [Failure] 0.015570 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request] 0.053889 read(7, "5656%pre%%pre%%pre%%pre%", 8) = 8 [Failure] 0.634720 read(7, "5656d%pre%%pre%", 8) = 8 [SPI Request] 0.050070 read(7, "5656%pre%%pre%%pre%", 8) = 8 [Success]
    
por Kenny 24.01.2018 / 21:29

1 resposta

2

strace provavelmente não fornecerá o nível necessário de detalhes; provavelmente você precisará do SystemTap, do sysdig ou de depuração semelhante no nível do kernel para mostrar melhor o que está acontecendo. Por exemplo, com o SystemTap instalado e todos os detalhes debuginfo e de configuração necessários, um poderia começar com:

probe begin
{
        printf("%-16s %s\n", "TIME", "WHAT");
}

probe tty.{ioctl,receive}
{
        if (pid() != target()) next;
        printf("%d ttyx    %s\n", gettimeofday_us(), name);
}

probe tty.poll
{
        if (pid() != target()) next;
        printf("%d ttypoll %s\n", gettimeofday_us(), file_name);
}

probe tty.{read,write}
{
        if (pid() != target()) next;
        printf("%d ttyio   %s %d\n", gettimeofday_us(), file_name, nr);
}

probe syscall.{read,write,epoll_*}
{
        if (pid() != target()) next;
        printf("%d syscall %s\tenter\n", gettimeofday_us(), name);
}

probe syscall.{read,write,epoll_*}.return
{
        if (pid() != target()) next;
        printf("%d syscall %s\treturn\n", gettimeofday_us(), name);
}

e execute isso via

$ sudo stap -x "$(pidof ...)" filecontainingtheabovecode.stp

que para um programa de teste em série e um anexo do Arduino para mim mostra:

TIME             WHAT
1516997916648359 syscall read   enter
1516997916648366 ttyio   ttyACM0 4096
1516997916652456 syscall read   return
1516997916652465 syscall read   enter
1516997916652470 ttyio   ttyACM0 4096
1516997916656459 syscall read   return
1516997916656497 syscall write  enter
1516997916656503 ttyio   4 21
1516997916656511 syscall write  return
...

Você também pode precisar consultar a documentação de tetsets do SystemTap para outros pontos de interesse do probe (planejador, planejador de E / S, IRQs?) se o acima não mostrar onde está o atraso. E possivelmente para comparar os tempos com a versão antiga do código / driver / sistema ...

    
por 26.01.2018 / 21:31