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.