├── chatgdb ├── __init__.py ├── lldb.py ├── gdb.py ├── cli.py └── utils.py ├── .gitignore ├── poetry.lock ├── pyproject.toml ├── LICENSE ├── CONTRIBUTING.md └── README.md /chatgdb/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .secret.txt 2 | dist/ 3 | __pycache__/ 4 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. 2 | package = [] 3 | 4 | [metadata] 5 | lock-version = "2.0" 6 | python-versions = "^3.3" 7 | content-hash = "bf56582923df235ad596b6c20c01bf8553a6e9f3427db1aca1a84ab962b710d8" 8 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "ChatGDB" 3 | version = "1.2.0" 4 | authors = ["Pranay Gosar "] 5 | description = "Harness the power of ChatGPT directly inside the GDB debugger!" 6 | readme = "README.md" 7 | license = "MIT" 8 | repository = "https://github.com/pgosar/chatgdb" 9 | keywords = ["gdb", "debugger", "chatgpt"] 10 | 11 | [tool.poetry.urls] 12 | "Bug Tracker" = "https://github.com/pgosar/chatgdb/issues" 13 | 14 | [tool.poetry.dependencies] 15 | python = "^3.3" 16 | 17 | [build-system] 18 | requires = ["poetry-core"] 19 | build-backend = "poetry.core.masonry.api" 20 | 21 | [tool.poetry.scripts] 22 | chatgdb = "chatgdb.cli:main" 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Pranay Gosar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chatgdb/lldb.py: -------------------------------------------------------------------------------- 1 | import lldb 2 | from chatgdb import utils 3 | 4 | 5 | def __lldb_init_module(debugger, internal_dict): 6 | """This function handles the initialization of the custom commands""" 7 | # lldb doesn't trigger python's main function so we print the help here 8 | print("ChatLLDB loaded successfully. Type 'chat help' for information " 9 | "on how to run the commands.") 10 | debugger.HandleCommand('command script add -f lldb.chat chat') 11 | debugger.HandleCommand('command script add -f lldb.explain explain') 12 | 13 | 14 | prev_command = "" 15 | COMMAND_PROMPT = "Give me a SINGLE LLDB command with no explanation. Do NOT \ 16 | give me a GDB command. DO NOT write any English above or below the command. \ 17 | Only give me the command as text. Here is my question: " 18 | EXPLANATION_PROMPT = "Give me an explanation for this LLDB command: " 19 | 20 | 21 | def chat(debugger, command, result, internal_dict): 22 | """Custom LLDB command - chat 23 | 24 | The chat command is used to generate GDB commands based on plain English 25 | input. 26 | """ 27 | global prev_command 28 | # handle when user types 'chat help' 29 | if command == "help": 30 | utils.chat_help() 31 | return 32 | prev_command, command = utils.chat_helper(command, prompt=COMMAND_PROMPT) 33 | debugger.HandleCommand(command) 34 | 35 | 36 | def explain(debugger, command, result, internal_dict): 37 | """Custom LLDB command - explain 38 | 39 | The explain command is used to generate explanations for either the 40 | previous command or a user query 41 | """ 42 | utils.explain_helper(prev_command, command, prompt=EXPLANATION_PROMPT) 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Reporting Issues 2 | 3 | We'd love to hear ideas for new features or suggestions on improving the tool. Feel free to create an 4 | issue on [GitHub](https://github.com/pgosar/ChatGDB) if you have any in mind! 5 | 6 | Before submitting bug reports, please make sure you've [updated](https://github.com/pgosar/ChatGDB#updating) 7 | ChatGDB. When writing a bug report on [GitHub](https://github.com/pgosar/ChatGDB), please provide 8 | relevant information that can help us figure out what went wrong. This could include but is not limited 9 | to: 10 | 11 | - The output of ```chatgdb -v``` 12 | - Your system information (operating system and version) 13 | - How to reproduce the bug 14 | - Anything else you think is useful 15 | 16 | ### Developing and Making Pull Requests 17 | 18 | We gladly accept pull requests in the [official repository](https://github.com/pgosar/ChatGDB) for 19 | bug fixes, features, etc. 20 | 21 | Working on the project is as simple as cloning the repository and installing [poetry](https://python-poetry.org/docs/). 22 | We use poetry to build the project and generate the ```chatgdb``` executable. Use the command 23 | 24 | ```poetry build``` 25 | 26 | to generate the binaries. Then, run 27 | 28 | ```pip3 install .``` 29 | 30 | in the repository's root directory to simulate a user installation so that you can 31 | easily test your changes. Finally, you can simply uninstall the local version with 32 | 33 | ```pip3 uninstall chatgdb``` 34 | 35 | Please be sure to format your code according to the [pep8 guidelines](https://pep8.org/) 36 | - this is what the repository follows. I recommend installing [autopep8](https://github.com/hhatto/autopep8) to help with this. 37 | 38 | -------------------------------------------------------------------------------- /chatgdb/gdb.py: -------------------------------------------------------------------------------- 1 | import gdb 2 | from chatgdb import utils 3 | 4 | prev_command = "" 5 | COMMAND_PROMPT = "Give me a SINGLE GDB command with no explanation. Do NOT \ 6 | write any English above or below the command. Only give me the command as \ 7 | text. Here is my question: " 8 | EXPLANATION_PROMPT = "Give me an explanation for this GDB command: " 9 | 10 | 11 | class GDBCommand(gdb.Command): 12 | """Custom GDB command - chat 13 | 14 | The chat command is used to generate GDB commands based on plain English 15 | input. 16 | """ 17 | 18 | def __init__(self): 19 | """Initializes custom GDB command""" 20 | super(GDBCommand, self).__init__("chat", gdb.COMMAND_DATA) 21 | 22 | # creates api request on command invocation 23 | def invoke(self, arg, from_tty): 24 | """Invokes custom GDB command and sends API request 25 | 26 | Params: 27 | arg (str): argument passed to command 28 | from_tty (bool): whether command was invoked from TTY 29 | """ 30 | global prev_command 31 | # handling if user is asking for help on how to use the commands 32 | if arg == "help": 33 | utils.chat_help() 34 | return 35 | 36 | prev_command, command = utils.chat_helper(arg, COMMAND_PROMPT) 37 | gdb.execute(command) 38 | 39 | 40 | class ExplainCommand(gdb.Command): 41 | """Custom GDB command - explain 42 | 43 | The explain command is used to generate explanations for either the 44 | previous command or a user query 45 | """ 46 | def __init__(self): 47 | """Initializes custom GDB command""" 48 | super(ExplainCommand, self).__init__("explain", gdb.COMMAND_DATA) 49 | 50 | # creates api request on command invocation 51 | def invoke(self, arg, from_tty): 52 | """Invokes custom GDB command and sends API request 53 | 54 | Params: 55 | arg (str): argument passed to commands 56 | from_tty (bool): whether command was invoked from from_tty 57 | """ 58 | utils.explain_helper(prev_command, arg, EXPLANATION_PROMPT) 59 | 60 | 61 | GDBCommand() 62 | ExplainCommand() 63 | 64 | 65 | def main(): 66 | print("ChatGDB loaded successfully. Type 'chat help' for information " 67 | "on how to run the commands.") 68 | 69 | 70 | if __name__ == "__main__": 71 | main() 72 | -------------------------------------------------------------------------------- /chatgdb/cli.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from os.path import abspath, dirname 3 | from inspect import getfile, currentframe 4 | from urllib.request import Request, urlopen 5 | import json 6 | 7 | PATH = dirname(abspath(getfile(currentframe()))) 8 | 9 | 10 | def set_key(key): 11 | """Set the api key for ChatGDB""" 12 | with open(PATH + "/.secret.txt", "w") as f: 13 | f.write("OPENAI_KEY=\"" + key + "\"") 14 | 15 | 16 | def set_model(model): 17 | """Set the model for ChatGDB""" 18 | with open(PATH + "/.model.txt", "w") as f: 19 | f.write("MODEL=\"" + model + "\"") 20 | 21 | def set_url(url): 22 | """Set the url for ChatGDB""" 23 | with open(PATH + "/.url.txt", "w") as f: 24 | f.write("URL=\"" + url + "\"") 25 | 26 | def version(): 27 | """Return version information""" 28 | with urlopen(Request("https://pypi.org/pypi/chatgdb/json"), timeout=10) as f: 29 | return json.load(f)["info"]["version"] 30 | 31 | 32 | def main(): 33 | parser = argparse.ArgumentParser( 34 | description="Configure ChatGDB, the GDB chatbot", 35 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 36 | parser.add_argument( 37 | '-k', 38 | "--key", 39 | type=str, 40 | help="Provide an api key for ChatGDB") 41 | parser.add_argument( 42 | '-m', 43 | "--model", 44 | type=str, 45 | choices=["gpt-3.5-turbo", "gpt-4", "gpt-4o", "gpt-4o-mini", "o1-preview", "o1-mini"], 46 | help="Provide a model for ChatGDB (gpt-3.5-turbo, gpt-4, gpt-4o, gpt-4o-mini, o1-preview, o1-mini)", 47 | default="gpt-3.5-turbo" 48 | ) 49 | parser.add_argument( 50 | '-u', 51 | "--url", 52 | type=str, 53 | help="Provide a API url for ChatGDB", 54 | default="https://api.openai.com/v1/chat/completions" 55 | ) 56 | parser.add_argument( 57 | '-v', 58 | "--version", 59 | action="version", 60 | version="%(prog)s " + version(), 61 | help="Print the version of ChatGDB") 62 | 63 | args = parser.parse_args() 64 | if args.key: 65 | set_key(args.key) 66 | if args.model: 67 | set_model(args.model) 68 | if args.url: 69 | set_url(args.url) 70 | if not any([args.key, args.model, args.url]): 71 | parser.print_help() 72 | 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatGDB 2 | Harness the power of ChatGPT inside the GDB/LLDB debugger! 3 | 4 | 5 | ChatGDB is a tool designed to superpower your debugging experience with GDB or LLDB, debuggers for compiled languages. Use it to accelerate your debugging workflow by leveraging the power of ChatGPT to assist you while using GDB/LLDB! 6 | 7 | It allows you to explain in natural language what you want to do, and then automatically execute the relevant command. Optionally, you can ask ChatGPT to explain the command it just ran or even pass in any question for it to answer. Focus on what's important - figuring out that nasty bug instead of chasing down GDB/LLDB commands at the tip of your tongue. 8 | 9 | ![Image](https://lh5.googleusercontent.com/xZMLwWWxavqYjC3fyCIZJ0m-s-f-XEoiOeWGbxRrw3dWoukUoWzJJ4iiBkVO2Vtiyr4K6o0WkTs7B40TapeBPIYwgVRVhDXGVjB4tFYoKH3_nK847nYXl3pISB6dEP6Wp_o0uPlfJOjCrLspm0_VNw) 10 | 11 | ## Contents 12 | 13 | 1. [Installation](#installation-intructions) 14 | 2. [Updating](#updating) 15 | 3. [Usage](#usage) 16 | 4. [Contributing](#contributing) 17 | 5. [Getting Updates](#getting-updates) 18 | 19 | ### Installation instructions 20 | First, make sure you install [pip](https://pip.pypa.io/en/stable/installation/). ChatGDB also 21 | requires a python version of 3.3 or above. 22 | 23 | To install, run the command 24 | 25 | ```pip3 install chatgdb```. 26 | 27 | It will create an executable called ```chatgdb``` that you will have to use to set your api key. 28 | To do that, run the command 29 | 30 | ```chatgdb -k ``` 31 | 32 | You can set the model to use. There are two possible options, ```gpt-3.5-turbo``` and ```gpt-4```(defaulting to the former): 33 | 34 | ```chatgdb -m ``` 35 | 36 | If you are using a non-official api provider, you can also set the api url: 37 | 38 | ```chatgdb -u ``` 39 | 40 | This information is stored in text in the same directory as the installed script, which is currently in your python site packages 41 | folder along with the main script. You can easily find this location by running the following in your terminal: 42 | 43 | ``` python -m site --user-site``` 44 | 45 | Optionally, you can also download the compressed files in the releases page to get the scripts directly. 46 | If you do this, navigate to the ```chatgdb``` folder, and you can install with 47 | 48 | ```pip3 install .```. 49 | 50 | ### Updating 51 | 52 | To update ChatGDB, run the following 53 | 54 | ```pip3 install chatgdb --upgrade``` 55 | 56 | 57 | ### Usage 58 | For GDB usage, I first recommend editing your ```$HOME/.gdbinit``` to source the main script automatically on startup. Run the following command: 59 | 60 | ```echo "source $(python -m site --user-site)/chatgdb/gdb.py" > $HOME/.gdbinit``` 61 | 62 | The same applies for LLDB. Edit your ```$HOME/.lldbinit``` and run the following command: 63 | 64 | ```echo "command script import $(python -m site --user-site)/chatgdb/lldb.py" > $HOME/.lldbinit``` 65 | 66 | While inside your debugger, you can run the command chat appended by your query, for example ```chat list all breakpoints that I created```. 67 | There is also a command called ```explain``` that you can use with no arguments to explain the previously run command, 68 | and optionally, with a query to just ask GPT a question. For example, running ```explain``` directly after running 69 | ```break 7``` would prompt the tool to explain how breakpoints work. Running ```explain how input formatting works in gdb``` 70 | would prompt it to explain input formatting (see the image at the top). 71 | 72 | Run chat help to print out a short tutorial on how to use the tool. 73 | 74 | ### Contributing 75 | Thanks for your interest in contributing to ChatGDB! See [CONTRIBUTING.md](CONTRIBUTING.md) on ways to 76 | help the development effort. 77 | 78 | ### Staying Updated 79 | 80 | If you'd like to stay up-to-date on new features/fixes, follow my [twitter](https://twitter.com/pranay__gosar). There's plenty 81 | of exciting features on the horizon such as complete context-awareness that will make it possible 82 | for ChatGDB to not only help you use GDB, but to help you fix the code itself. 83 | -------------------------------------------------------------------------------- /chatgdb/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | from posixpath import dirname 3 | from urllib.error import HTTPError, URLError 4 | from urllib.request import Request, urlopen 5 | from os.path import abspath, dirname 6 | from inspect import getfile, currentframe 7 | 8 | 9 | def get_key(): 10 | """Gets api key from secret file 11 | 12 | Returns: (str) api key 13 | """ 14 | key = [] 15 | secret = "" 16 | # gets path of this script - OS independent 17 | path = dirname(abspath(getfile(currentframe()))) + "/.secret.txt" 18 | try: 19 | # get appropriate api key 20 | with open(path) as f: 21 | key = [line.strip() for line in f] 22 | for k in key: 23 | if k.startswith("OPENAI_KEY"): 24 | secret = k.split('"')[1::2] 25 | except FileNotFoundError: 26 | print("Could not find api key. Please make sure you've run the CLI " 27 | "tool and set up your model") 28 | quit("Exiting...") 29 | 30 | return secret[0] 31 | 32 | 33 | def get_model(): 34 | """Gets model from model file 35 | 36 | Returns: (str) model 37 | """ 38 | model = [] 39 | model_name = "" 40 | # gets path of this script - OS independent 41 | path = dirname(abspath(getfile(currentframe()))) + "/.model.txt" 42 | try: 43 | # get appropriate api key 44 | with open(path) as f: 45 | model = [line.strip() for line in f] 46 | for m in model: 47 | if m.startswith("MODEL"): 48 | model_name = m.split('"')[1::2] 49 | except FileNotFoundError: 50 | print("Could not find model. Please make sure you've run the CLI " 51 | "tool and set up your model") 52 | quit("Exiting...") 53 | 54 | return model_name[0] 55 | 56 | def get_url(): 57 | """Gets api url from url file 58 | 59 | Returns: (str) url 60 | """ 61 | url = [] 62 | url_name = "" 63 | # gets path of this script - OS independent 64 | path = dirname(abspath(getfile(currentframe()))) + "/.url.txt" 65 | try: 66 | # get appropriate api url 67 | with open(path) as f: 68 | url = [line.strip() for line in f] 69 | for u in url: 70 | if u.startswith("URL"): 71 | url_name = u.split('"')[1::2] 72 | except FileNotFoundError: 73 | print("Could not find url. Please make sure you've run the CLI " 74 | "tool and set up your url") 75 | quit("Exiting...") 76 | return url_name[0] 77 | 78 | 79 | 80 | def make_request(url, headers=None, data=None): 81 | """Makes API request 82 | 83 | Params: 84 | url (str): url to make request to 85 | headers (dict, optional): headers to send with request. Defaults to None. 86 | data (bytes, optional): data to send with request. Defaults to None. 87 | """ 88 | request = Request(url, headers=headers or {}, data=data) 89 | try: 90 | with urlopen(request, timeout=10) as response: 91 | return response.read(), response 92 | except HTTPError as error: 93 | print(error.status, error.reason) 94 | quit("Exiting...") 95 | except URLError as error: 96 | print(error.reason) 97 | quit("Exiting...") 98 | except TimeoutError: 99 | print("Request timed out") 100 | quit("Exiting...") 101 | 102 | 103 | def chat_help(): 104 | """Prints help message for all available commands""" 105 | print( 106 | "ChatGDB is a python script that defines some extra helpful GDB and " 107 | "LLDB commands. Before use, be sure to set up your api key using the " 108 | "CLI tool. The commands are as follows:\n\n" 109 | "chat: This command is used to generate GDB/LLDB commands based on plain " 110 | "English input. For example, 'chat stop my code at line 7' will " 111 | "generate the GDB command 'break 7'. Remember that in LLDB, many " 112 | "commands require filename information as well.\n\n" 113 | "explain: This command is used to generate explanations for either " 114 | "the previous command or a user query. 'explain' with " 115 | "no arguments will generate an explanation for the previous command " 116 | "but typing a query after will generate an answer for it.\n\n") 117 | 118 | 119 | HEADERS = { 120 | "Authorization": "Bearer " + get_key(), 121 | "Content-Type": "application/json" 122 | } 123 | URL = get_url() 124 | 125 | def explain_helper(prev_command, command, prompt): 126 | """Generates explanation for either the previous command or a user query 127 | 128 | Params: 129 | prev_command (str): previous command 130 | command (str): user query 131 | prompt (str): prompt to use for explanation 132 | """ 133 | question = prompt + prev_command if command == "" else command 134 | data = {"model": get_model(), 135 | "messages": [{"role": "user", 136 | "content": question}]} 137 | body, response = make_request(URL, HEADERS, data=bytes(json.dumps(data), 138 | encoding="utf-8")) 139 | body = json.loads(body) 140 | explanation = body['choices'][0]['message']['content'] 141 | print(explanation) 142 | 143 | 144 | def chat_helper(command, prompt): 145 | """Generates GDB/LLDB command based on user input 146 | 147 | Params: 148 | command (str): user input 149 | prompt (str): prompt to use for command generation 150 | """ 151 | data = {"model": get_model(), 152 | "messages": [{"role": "user", 153 | "content": prompt + command}]} 154 | 155 | body, response = make_request(URL, HEADERS, data=bytes(json.dumps(data), 156 | encoding="utf-8")) 157 | body = json.loads(body) 158 | command = body['choices'][0]['message']['content'] 159 | print(command) 160 | # the first is technically also the previous command 161 | return command, command 162 | --------------------------------------------------------------------------------