Monitorar e alertar o usuário quando as configurações stty são alteradas?

3

Um programa particularmente grande (~ 10 ^ 6 LOC) faz com que minhas configurações stty mudem de echo ixon icanon para -echo -ixon -icanon e gostaria de encontrar a função nesse programa massivo que causa essa alteração.

Eu obviamente não gostaria de rastrear a execução através desta confusão de código espaguete OOP.

Como posso monitorar as configurações stty e registrar o que as altera? Eu estou pensando strace com awk provavelmente pode me obter as informações que eu preciso, mas eu não sei o que o sistema chama para filtrar.

    
por nathanvy 17.01.2015 / 00:16

1 resposta

4

Se você acha que pode causar o evento por uma ação ou interação específica, de longe o método mais simples é algo como:

 watch -d -n1 "stty -F /dev/pts/106 -a | grep -Eo '.(icanon|ixon)'"

Execute isto em um novo terminal, a opção para -F é o terminal no qual você executará o programa (execute tty para ver o que é antes de iniciá-lo). Omit | grep .. se você quiser observar o estado completo do terminal.

A próxima opção, se você estiver usando Linux, é usar ltrace para rastrear chamadas de bibliotecas, semelhante a strace (e incorpora um recurso strace ), mas funciona com bibliotecas de espaço do usuário, não apenas com as chamadas do sistema kernel:

ltrace -tt -e tcgetattr+tcsetattr myprogram ...

Isso mostrará e marcará as chamadas de timestamp para tcgetattr() e tcsetattr() , as funções libc para obter e definir os atributos do terminal.

Por fim, essas chamadas libc usarão a chamada de sistema ioctl() , isso você pode rastrear com strace ou truss , veja como usar strace no linux:

strace -tt -e trace=ioctl myprogram [...]

Uma grande vantagem aqui é que strace decodificará vários parâmetros para syscalls para você.

Nenhuma das opções acima informará muito logicamente onde, no programa, o problema pode ocorrer, mas para isso você tem duas opções: um depurador ou uma injeção de DLL.

Dentro de gdb , você pode facilmente definir um ponto de interrupção em tcsetattr() e, em seguida, verificar a pilha de chamadas, mas isso pode ser entediante se houver muitas chamadas (e exigirá um depura a compilação ou símbolos para libc para obter os melhores resultados).

A opção mais abrangente (supondo que o programa alvo esteja dinamicamente vinculado) é injetar sua própria DLL que intercepta ou encapsula as funções que você precisa rastrear, neste caso tcsetattr() .

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>
#include <termios.h>

/* 
 * gcc -nostartfiles -shared -ldl -Wl,-soname,tcsetattr -o tc.so wraptc.c
 * LD_PRELOAD=./tc.so stty -icanon ixon
 *
 */

#define DEBUG 1
#define dfprintf(fmt, ...) \
    do { if (DEBUG) fprintf(stderr, "[%14s#%04d:%8s()] " fmt, \
          __FILE__, __LINE__, __func__, __VA_ARGS__); } while (0)


typedef int tcsetattr_fp(int fd, int optional_actions, 
                     const struct termios *termios_p);
static tcsetattr_fp    *real_tcsetattr;

void _init()
{
    dfprintf("It's alive!\n","");
    real_tcsetattr = dlsym(RTLD_NEXT, "tcsetattr");
    dfprintf("Hooked %p tcsetattr()\n",(void *)real_tcsetattr);
}

int tcsetattr(int fd, int optional_actions, const struct termios *termios_p)
{
     void *bt[20];
     size_t btsz;

     int rc,stacktr=0;
     dfprintf("Caught tcsetattr(%i,%04x,...)\n",fd,optional_actions);

     if ( (fd==0) && !((termios_p->c_lflag) & ICANON)) {
         dfprintf("ICANON off!\n","");
         stacktr=1;
     }
     if ( (fd==0) && !((termios_p->c_iflag) & IXON)) {
         dfprintf("IXON off!\n","");
         stacktr=1;
     }
     if (stacktr) {
         btsz=backtrace(bt,sizeof(bt));
         backtrace_symbols_fd(bt,btsz,STDERR_FILENO);
     }

     rc=real_tcsetattr(fd,optional_actions, termios_p);
     return rc;
}

Compile e invoque como indicado nos comentários. Este código localiza a função real libc tcsetattr() e contém uma versão alternativa que é usada em seu lugar. Este código chama backtrace() quando ele vê uma atividade possivelmente interessante no FD 0 e, em seguida, chama a versão libc real. Pode exigir um ajuste menor.

    
por 27.01.2015 / 13:17