Package: gdbundle
GDBundle - Plugin Manager for GDB and LLDBgdbundle is short for GDB bundle and is a plugin manager for GDB and LLDB.For detailed info about the origin story of gdbundle, read the announcement on Interrupt:gdbundle - GDB's Missing Plugin ManagerBenefitsThere are a handful of indisputable benefits of using gdbundle over manually modifying.gdbinitand.lldbinitfiles.Justpip install gdbundle-<plugin-name>. No more manually editing your~/.gdbinitin specific ways depending on the extension.It enables developers to use virtual environments (and encourages it!).Personal projects and team projects can have project-specificrequirements.txtand.gdbinitfiles. With these two in place, a new
developer would just need topip install -r requirements.txt.Discoverability. Check out thisPyPi filterto find new plugins.Dependency management and version tracking is now done automatically by
Python's packaging infrastructure.Published PluginsPublished ones can be found onPyPiFor now, a list is kept here as well:examplegdb-dashboardPyCortexMDebugvoltronQuickstartNOTE: The Python compiled with GDB should be the same major version as
the Python in the local virtual, Conda, or local environment that
you are installing gdbundle and plugins into.To check both versions of Python, you can run the following:$ gdb
(gdb) pi
>>> import sys; sys.version
'3.6.9 (default, Nov  7 2019, 10:44:02) \n[GCC 8.3.0]'

$ python --version
Python 3.6.9Install gdbundleInstallgdbundlefrom PyPi first. Using avirtual environmentis
recommended.$ pip install gdbundleIf you'd rather not use a virtual environment, it's advised to use--userwhen
installing the package.$ pip install --user gdbundleJust, whatever you do, don't usesudo .... And don't let your friends either.Amend Init ScriptsPlacing the following in the appropriate file will load all installed gdbundle
plugins by default. If you would like to selectively load certain ones, please
refer to theConfigurationsection.GDB's.gdbinitAppend the following to your~/.gdbinitor to a project-specific.gdbinitthat is loaded withgdb --command .gdbinit# -- gdbundle_BEGIN
python
import os,sys,subprocess
# Execute a Python using the user's shell and pull out the sys.path (for site-packages)
paths = subprocess.check_output('python -c "import os,sys;print(os.linesep.join(sys.path).strip())"',shell=True).decode("utf-8").split()
# Extend GDB's Python search path
sys.path.extend(paths)

# Initialize gdbundle
import gdbundle
gdbundle.init()
end
# -- gdbundle_ENDLLDB's.lldbinitCopy thesample_lldbinit.pysomewhere. For now, let's assume it's at/path/to/gdbundle_lldbinit.py# /path/to/gdbundle_lldbinit.py

# -- gdbundle_BEGIN
import os,subprocess,sys
# Execute a Python using the user's shell and pull out the sys.path (for site-packages)
paths = subprocess.check_output('python -c "import os,sys;print(os.linesep.join(sys.path).strip())"',shell=True).decode("utf-8").split()
# Extend LLDB's Python search path
sys.path.extend(paths)

# Initialize gdbundle
import gdbundle
gdbundle.init()
# -- gdbundle_ENDAppend the following to your~/.lldbinitor to project-specific.lldbinitfile that is loaded withlldb -s .lldbinit.# .lldbinit

command script import '/path/to/gdbundle_lldbinit.py'BackgroundGDB hasbuilt-in support for extensionswritten in in Python, Guile, or GDB's command language. However, there is no
convenient way to package, distribute, and install these scripts.GDB does provide a few mechanisms:Place scripts in /usr/local/share/gdb/auto-load or similarUsegdb --command ...to load the scripts on invocationGDB's documentation gives us the following:Python scripts used by GDB should be installed in data-directory/python, where
data-directory is the data directory as determined at GDB startupThis leaves a lot to be desired, because a user would have to copy-paste script
files to this directory. There are many drawbacks to this method, including:Requires manual intervention to install scripts todata-directoryScripts become out of date since they were copy-pasteddata-directoryis usually in a directory next to GDB's installation path,
usually in/usr/local. Everyone should do their best not to manually edit
files there.Therehasto be a better way, and thankfully, there is! It's calledgdbundle.Installing PackagesPackages are prefixed withgdbundle-and are installing throughpipfrom
PyPi.$ pip install gdbundle-exampleConfigurationgdbundletoday is simple and has only a few configuration knobs. Configuration
is passed into thegdbundle.init(...)function call.NOTE: The names passed intoincludeandexcludeshould be the package name
minusgdbundle_, and hyphens are underscores. (e.g.gdbundle-gdb-dashboardbecomesgdb_dashboard)# .gdbinitimportgdbundle# Configure which packages to includeinclude=[# List of packages to load.# e.g. "example"]exclude=[# List of packages to exclude.# Useful if the same virtual environment is used for multiple#  projects and not all packages should be loaded# e.g. "example"]# Load the configured packagesgdbundle.init(include=include,exclude=exclude)If you've created a Python package that has the necessary gdbundle hooks (e.g.gdbundle.gdb_loader.gdbundle_load()), you can manually load it usinggdbundle.load_module().# gdbundle will import this module and attempt to run `my_package.gdb_loader.gdbundle_load()`
gdbundle.load_module("my_package")Creating and Distributing PackagesThe goal was to keepgdbundlepackages as simple as possible. A package only
has a few responsibilities:Define dependencies in thepyproject.tomlso they are automatically
installed.Provide a version so users can easily pin and upgrade to specific versions.Provide a hookgdb_loader.gdbundle_load()and/orlldb_loader.gdbundle_load()which is called by gdbundle, which should
either:Load the script into the debugger context by callinggdb.execute("source <file>")orlldb.debugger.HandleCommand("command script import <file>"Import the Python module which does the sourcing itself:from mypackage import HelloWorld; HelloWorld()To investigate a real package that works with both GDB and LLDB, check out thegdbundle-example plugin.Let's go over each piece quickly.pyproject.tomlA standardsetup.pyfile. The main thing to note here is that our packagenamewill begdbundle-<something>, but our Python package will be calledgdbundle_<something>.[tool.poetry]name="gdbundle-example"version="0.0.1"description=""authors=["Tyler Hoffman <tyler@memfault.com>"]readme="README.md"include=["gdbundle_example/scripts/*"]license="MIT"classifiers=[[...]][tool.poetry.dependencies]python="*"[build-system]requires=["poetry>=0.12"]build-backend="poetry.masonry.api"gdbundle_example/gdb_loader.pyimportgdbimportosPACKAGE_DIR=os.path.dirname(__file__)SCRIPT_PATHS=[[PACKAGE_DIR,'scripts','example_gdb.gdb'],[PACKAGE_DIR,'scripts','example_gdb.py']]def_abs_path(path):returnos.path.abspath(os.path.join(*path))defgdbundle_load():forscript_pathinSCRIPT_PATHS:gdb.execute("source{}".format(_abs_path(script_path)))gdbundle_example/scripts/Place the script files here!How It WorksIf the GDB/LLDB executable you are using was downloaded rather than compiled
from source, it's likely that it is linked against a system Python library andsite-packagesdirectory, rather than against a virtual environment, Conda
environment, or another user installed version of Python.For example, if we startarm-none-eabi-gdb-pydownloaded fromARM,
we'll see that it is searching within the system Python'ssite-packagesfolder
for packages:$ arm-none-eabi-gdb-py
(gdb) python-interactive
>>> import sysconfig
>>> sysconfig.get_paths()['purelib']
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages'Similar with LLDB:$ lldb
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> import sysconfig; sysconfig.get_paths()['purelib']
'/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/site-packages'When a user installs packages with anaptorbrewPython, or one from a
Conda or virtual environment, GDB willnotautomatically find those packages
since Python's configuredsys.pathis only looking in the system'ssite-packagesfolder.There are three recommended (butvery bad) ways to handle this:OverridePYTHONPATHon init or in the users shell.Create sym-links between the virtual environment and the system python
installation.Deal with it, as said in LLDB'sPython Caveatdocs. Install
the packages in the same installation anyways.We want something that editsonlyGDB's Python context, and only appends to
Python'ssys.path, not overrides it.The best solution I've found and have been using (along with 100 other engineers
at my previous employer) is to shell out from within GDB, ask the local shell
environment what the configured Python executable is, get itssys.pathentries, and then append those to our current GDB session's Python environment.
This allows GDB to find and use packages that are installed into a Conda or
virtualenv environment, as well as any other user installed Python environment.Who uses GDB Python ScriptsMany companies and projects use and include GDB scripts with their large
open-source projects. They are usually buried in the repo, difficult to source,
and built and used in various ways. They usually require the developer to
discover that they exist, manually source them, and then look at the source code
to figure out how they work.Check out the gdbundle announcement for a list of ones that are public and
open-source.Neat GDB Script Repositories