一个特别大的(~10^6 LOC)程序导致我的 stty 设置从 更改为echo ixon icanon
,-echo -ixon -icanon
我想在这个大型程序中找到导致此更改的函数。
我显然不想通过这些混乱的 OOP 意大利面条代码来跟踪执行情况。
如何监视 stty 设置并记录更改它们的内容?我想strace
withawk
可能可以为我提供所需的信息,但我不知道要过滤哪些系统调用。
答案1
如果您认为可以通过特定操作或交互来引发事件,那么到目前为止最简单的方法如下:
watch -d -n1 "stty -F /dev/pts/106 -a | grep -Eo '.(icanon|ixon)'"
在新终端上运行它,选项是-F
您将运行程序的终端(tty
在启动之前运行以查看它是什么)。| grep ..
如果您想观察完整的终端状态,请省略。
如果您使用的是 Linux,下一个选项是使用ltrace
跟踪库调用,这类似于strace
(并且它包含一些strace
功能),但它适用于用户空间库,而不仅仅是内核系统调用:
ltrace -tt -e tcgetattr+tcsetattr myprogram ...
这将显示对libc 函数 和 的调用tcgetattr()
并添加时间戳,以获取和设置终端属性。tcsetattr()
最终,这些 libc 调用将使用ioctl()
系统调用,您可以使用strace
或进行跟踪,以下是在 Linux 上truss
使用的方法:strace
strace -tt -e trace=ioctl myprogram [...]
这里的一个很大的优点是,它strace
会很乐意为您将各种参数解码为系统调用。
上面的内容都不会从逻辑上告诉您问题可能发生在程序的哪个位置,为此您有两个选择:调试器或 DLL 注入。
您可以轻松地在上gdb
设置断点tcsetattr()
,然后检查调用堆栈,但是如果有很多调用,这可能会很乏味(并且需要调试 libc 的构建或符号以获得最佳结果)。
最全面的选项(假设目标程序是动态链接的)是注入您自己的 DLL,它拦截或包装您需要跟踪的函数,在本例中为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;
}
按照注释中的指示编译和调用。此代码找到了真正的 libctcsetattr()
函数,并包含一个替代版本。此代码backtrace()
在 FD 0 上看到可能有趣的活动时调用,然后调用真正的 libc 版本。可能需要进行细微调整。