5个版本

0.6.4 2024年5月22日
0.6.3 2024年5月12日
0.1.3 2023年10月1日

#110 in 调试

Apache-2.0

17KB

ChatDBG

Emery BergerStephen FreundKyla LevinNicolas van Kempen 贡献(按字母顺序排列)

PyPI Latest Release Downloads Downloads

ChatDBG是一个基于AI的调试助手,用于C/C++/Python/Rust代码,它将大型语言模型集成到标准调试器(pdblldbgdbwindbg)中,以帮助您调试代码。使用ChatDBG,您可以与调试器进行对话,提出关于程序的开放性问题,例如为什么x为null?。ChatDBG将接管方向盘并引导调试器回答您的查询。ChatDBG可以提供错误诊断并建议修复。

据我们所知,ChatDBG是第一个自动执行根本原因分析并提供建议修复的调试器。

观看ChatDBG的实际操作!

test-overflow.cpp上的LLDB test-overflow.cpp上的GDB bootstrap.py上的Pdb

有关技术细节和完整评估,请参阅我们的arXiv论文,ChatDBG:基于AI的调试助手PDF)。

[!NOTE]

ChatDBG针对pdblldbgdb的功能已经完善;我们目前正在将这些调试器的功能移植到其他调试器中。

安装

[!IMPORTANT]

ChatDBG 目前需要连接到 OpenAI 账户您的账户需要保持正余额才能正常工作 (检查您的余额)。如果您从未购买过积分,您需要购买至少 $1 的积分(如果您的 API 账户是在 2023 年 8 月 13 日之前创建的)或 $0.50(如果您有一个较新的 API 账户),以便访问 ChatDBG 所使用的 GPT-4。 在此处获取密钥。

一旦您有了 API 密钥,将其设置为名为 OPENAI_API_KEY 的环境变量。

export OPENAI_API_KEY=<your-api-key>

使用 pip 安装 ChatDBG(无论您是在调试 Python、C 还是 C++ 代码,您都需要这样做)

python3 -m pip install chatdbg

如果您使用 ChatDBG 调试 Python 程序,则已完成。如果您想使用 ChatDBG 通过 gdblldb 调试原生代码,请按照以下安装说明操作。

作为 lldb 扩展安装

lldb安装说明

通过运行以下命令将 ChatDBG 安装到 lldb 调试器中

Linux

python3 -m pip install ChatDBG
python3 -c 'import chatdbg; print(f"command script import {chatdbg.__path__[0]}/chatdbg_lldb.py")' >> ~/.lldbinit

如果您遇到错误,您可能正在使用较旧的 LLVM 版本。按照以下方式更新到最新版本

sudo apt install -y lsb-release wget software-properties-common gnupg
curl -sSf https://apt.llvm.org/llvm.sh | sudo bash -s -- 18 all
# LLDB now available as `lldb-18`.

Mac

xcrun python3 -m pip install ChatDBG
xcrun python3 -c 'import chatdbg; print(f"command script import {chatdbg.__path__[0]}/chatdbg_lldb.py")' >> ~/.lldbinit

这将把 ChatDBG 作为 LLVM 扩展安装。

作为 gdb 扩展安装

gdb安装说明

通过运行以下命令将 ChatDBG 安装到 gdb 调试器中

python3 -m pip install ChatDBG
python3 -c 'import chatdbg; print(f"source {chatdbg.__path__[0]}/chatdbg_gdb.py")' >> ~/.gdbinit

这将把 ChatDBG 作为 GDB 扩展安装。

作为 WinDBG 扩展安装

WinDBG安装说明
  1. 安装 WinDBG:如果尚未安装 WinDBG,请按照 此处 的说明进行操作。
  2. 安装 vcpkg:如果尚未安装 vcpkg,请按照 此处 的说明进行操作。
  3. 安装 Windows 调试工具:从 此处 安装 Windows SDK 并勾选 Debugging Tools for Windows 复选框。
  4. 导航到 src\chatdbg 目录cd src\chatdbg
  5. 安装所需依赖:运行 vcpkg install
  6. 构建 chatdbg.dll 扩展:运行 mkdir build & cd build & cmake .. & cmake --build . & cd ..

使用 ChatDBG:

  • 加载到 WinDBGX
    • 运行 windbgx your_executable_here.exe
    • 点击菜单项 视图 -> 命令浏览器
    • 在命令浏览器中输入 .load debug\chatdbg.dll
  • 在运行代码并遇到异常/信号后
    • 在命令浏览器中输入 !why

用法

调试 Python

要使用 ChatDBG 调试 Python 程序,只需按照以下方式运行您的 Python 脚本

chatdbg -c continue yourscript.py

ChatDBG 是标准 Python 调试器 pdb 的扩展。与 pdb 类似,当你的脚本遇到未捕获的异常时,ChatDBG 将进入死后调试模式。

与其他调试器不同,你可以使用 why 命令询问 ChatDBG 为什么你的程序失败,并得到一个建议的修复方案。在大型语言模型(LLM)响应后,你可以发出额外的调试命令,或者通过输入任何其他文本继续对话。

IPython 和 Jupyter 支持

要将 ChatDBG 设置为 IPython 或 Jupyter 笔记本中的默认调试器,请创建一个 IPython 配置文件,然后在启动时添加必要的扩展。如果已经有了自定义的配置文件,请根据需要修改这些行。

ipython profile create
echo "c.InteractiveShellApp.extensions = ['chatdbg.chatdbg_pdb', 'ipyflow']" >> ~/.ipython/profile_default/ipython_config.py

在命令行上,你可以运行

ipython --pdb yourscript.py

在 Jupyter 中,使用 ipyflow 内核 运行你的笔记本,并在文件的顶部包含此行魔术命令。

%pdb

调试原生代码(C、C++ 或 Rust)lldb / gdb)

要使用 ChatDBG 与 lldbgdb 一起工作,只需使用你的选择调试器运行原生代码(使用 -g 编译调试符号),当它崩溃时,询问 why。这也适用于死后调试(当使用 -c 选项加载核心时)。

原生调试器的工作方式略不同于 Pdb。在调试器对你的问题做出响应后,你将进入 ChatDBG 的命令循环,如 (ChatDBG 聊天) 提示所示。你可以继续发出调试命令,并且可以通过以 "chat" 开头的消息向大型语言模型(LLM)发送额外的消息。当你完成时,键入 quit 返回调试器的主命令循环。

调试 Rust 程序

要使用 ChatDBG 与 Rust 一起工作,你需要进行两个步骤:修改你的 Cargo.toml 文件并在源程序中添加一行。

  1. 将以下内容添加到你的 Cargo.toml 文件中
[dependencies]
chatdbg = "0.6.2"

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"
  1. 在你的程序中,将 #[chatdbg::main] 属性应用于 main 函数
#[chatdbg::main]
fn main() {

现在你可以使用 gdblldb 调试你的 Rust 代码。

示例

ChatDBG 示例在lldb
(ChatDBG lldb) run
Process 85494 launched: '/Users/emery/git/ChatDBG/test/a.out' (arm64)
TEST 1
TEST -422761288
TEST 0
TEST 0
TEST 0
TEST 0
TEST 0
TEST 0
Process 85494 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x100056200)
    frame #0: 0x0000000100002f64 a.out`foo(n=8, b=1) at test.cpp:7:22
   4     int x[] = { 1, 2, 3, 4, 5 };
   5     
   6     void foo(int n, float b) {
-> 7       cout << "TEST " << x[n * 10000] << endl;
   8     }
   9     
   10    int main()
Target 0: (a.out) stopped.

询问 why 以让 ChatDBG 提供一个有用的解释为什么这个程序失败,并提出一个修复方案

(ChatDBG lldb) why
The root cause of this error is accessing an index of the array `x`
that is out of bounds. In `foo()`, the index is calculated as `n *
10000`, which can be much larger than the size of the array `x` (which
is only 5 elements). In the given trace, the program is trying to
access the memory address `0x100056200`, which is outside of the range
of allocated memory for the array `x`.

To fix this error, we need to ensure that the index is within the
bounds of the array. One way to do this is to check the value of `n`
before calculating the index, and ensure that it is less than the size
of the array divided by the size of the element. For example, we can
modify `foo()` as follows:

    ```
    void foo(int n, float b) {
      if (n < 0 || n >= sizeof(x)/sizeof(x[0])) {
        cout << "ERROR: Invalid index" << endl;
        return;
      }
      cout << "TEST " << x[n] << endl;
    }
    ```

This code checks if `n` is within the valid range, and prints an error
message if it is not. If `n` is within the range, the function prints
the value of the element at index `n` of `x`. With this modification,
the program will avoid accessing memory outside the bounds of the
array, and will print the expected output for valid indices.
ChatDBG 示例在 Python(pdb)
Traceback (most recent call last):
  File "yourscript.py", line 9, in <module>
    print(tryme(100))
  File "yourscript.py", line 4, in tryme
    if x / i > 2:
ZeroDivisionError: division by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> yourscript.py(4)tryme()
-> if x / i > 2:

询问 why 以让 ChatDBG 提供一个有用的解释为什么这个程序失败,并提出一个修复方案

(ChatDBG Pdb) why
The root cause of the error is that the code is attempting to
divide by zero in the line "if x / i > 2". As i ranges from 0 to 99,
it will eventually reach the value of 0, causing a ZeroDivisionError.

A possible fix for this would be to add a check for i being equal to
zero before performing the division. This could be done by adding an
additional conditional statement, such as "if i == 0: continue", to
skip over the iteration when i is zero. The updated code would look
like this:

def tryme(x):
    count = 0
    for i in range(100):
        if i == 0:
            continue
        if x / i > 2:
            count += 1
    return count

if __name__ == '__main__':
    print(tryme(100))

依赖项

~245–690KB
~16K SLoC