├── .coveragerc ├── .eslintrc.json ├── .flake8 ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build_executable.yml │ └── tests.yml ├── .gitignore ├── .prettierrc.js ├── .vscode └── settings.json ├── .vulture_whitelist.py ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── docs ├── CNAME ├── api.md ├── changelog.md ├── contact.md ├── contributing.md ├── examples.md ├── faq.md ├── gettingstarted.md ├── guides.md ├── howitworks.md ├── index.md ├── installation.md └── screenshots.md ├── examples ├── .gitignore ├── README.md ├── c │ ├── debug_segfault.c │ ├── hello.c │ ├── input.c │ ├── makefile │ ├── sleeper.c │ ├── threads.c │ └── tree.c ├── cpp │ ├── hello.cpp │ ├── linked_list.cpp │ ├── makefile │ ├── sin.cpp │ └── smart_ptr_demo.cpp ├── fortran │ ├── fortran_array.f90 │ └── makefile ├── golang │ ├── hello.go │ └── makefile └── rust │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── compile_and_debug.sh │ └── src │ └── main.rs ├── gdbgui ├── SSLify.py ├── VERSION.txt ├── __init__.py ├── __main__.py ├── cli.py ├── htmllistformatter.py ├── py.typed ├── server │ ├── __init__.py │ ├── app.py │ ├── constants.py │ ├── http_routes.py │ ├── http_util.py │ ├── ptylib.py │ ├── server.py │ └── sessionmanager.py ├── src │ └── js │ │ ├── Actions.ts │ │ ├── BinaryLoader.tsx │ │ ├── Breakpoints.tsx │ │ ├── ControlButtons.tsx │ │ ├── CopyToClipboard.tsx │ │ ├── Expressions.tsx │ │ ├── FileOps.tsx │ │ ├── FileSystem.tsx │ │ ├── FoldersView.tsx │ │ ├── GdbApi.tsx │ │ ├── GdbMiOutput.tsx │ │ ├── GdbVariable.tsx │ │ ├── GdbguiModal.tsx │ │ ├── GlobalEvents.ts │ │ ├── HoverVar.tsx │ │ ├── InferiorProgramInfo.tsx │ │ ├── InitialStoreData.ts │ │ ├── Links.tsx │ │ ├── Locals.tsx │ │ ├── Memory.tsx │ │ ├── MemoryLink.tsx │ │ ├── MiddleLeft.tsx │ │ ├── ReactTable.tsx │ │ ├── Registers.tsx │ │ ├── RightSidebar.tsx │ │ ├── Settings.tsx │ │ ├── SourceCode.tsx │ │ ├── SourceCodeHeading.tsx │ │ ├── SourceFileAutocomplete.tsx │ │ ├── StatusBar.tsx │ │ ├── Terminals.tsx │ │ ├── Threads.tsx │ │ ├── ToolTip.tsx │ │ ├── ToolTipTourguide.tsx │ │ ├── TopBar.tsx │ │ ├── Tree.ts │ │ ├── Util.ts │ │ ├── constants.ts │ │ ├── dashboard.tsx │ │ ├── gdbgui.tsx │ │ ├── processFeatures.ts │ │ ├── process_gdb_response.tsx │ │ ├── register_descriptions.ts │ │ ├── tests │ │ └── Util.jest.ts │ │ └── types.d.ts ├── static │ ├── css │ │ ├── gdbgui.css │ │ ├── splitjs-gdbgui.css │ │ └── tailwind.css │ ├── favicon.ico │ ├── images │ │ ├── github.jpg │ │ ├── paypal.svg │ │ ├── ploticon.png │ │ ├── ploticon_orig.png │ │ └── twitter.png │ └── vendor │ │ ├── css │ │ ├── animate.css │ │ ├── bootstrap.min.css │ │ ├── gdbgui_awesomeplete.css │ │ └── pygments │ │ │ ├── emacs.css │ │ │ ├── light.css │ │ │ ├── monokai.css │ │ │ └── vim.css │ │ ├── fonts │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── images │ │ └── splitjs │ │ │ └── grips │ │ │ └── horizontal.png │ │ └── js │ │ ├── awesomeplete.min.js │ │ ├── bootstrap.min.js │ │ ├── jquery.flot-0.8.3.min.js │ │ ├── jquery.min.js │ │ ├── lodash.min.js │ │ ├── moment.min.js │ │ ├── socket.io-2.0.3.min.js │ │ ├── socket.io.js.map │ │ ├── splitjs.min-1.2.0.js │ │ └── vis-4.20.1.min.js └── templates │ ├── dashboard.html │ └── gdbgui.html ├── images ├── favicon.ico ├── gdbgui.xcf ├── gdbgui_banner.png ├── gdbgui_small.xcf └── gdbgui_square.xcf ├── jest.config.js ├── make_executable.py ├── mkdocs.yml ├── noxfile.py ├── package.json ├── postcss.config.js ├── requirements.in ├── requirements.txt ├── screenshots ├── assembly.png ├── authentication.png ├── console.png ├── controls.png ├── dashboard.png ├── expressions.png ├── gdbgui.png ├── gdbgui2.png ├── gdbgui_animation.gif ├── hover.png ├── load_binary_and_args.png ├── locals.png ├── memory.png ├── plots.png ├── radix.gif ├── ready.png ├── registers.png ├── reverse_debugging.png ├── rust_main.png ├── send_signal.png ├── source.png ├── source_with_assembly.png ├── stack_and_threads.png └── tree_explorer.png ├── setup.py ├── tailwind.config.js ├── tests ├── __init__.py ├── test_backend.py ├── test_cli.py ├── test_ptylib.py └── test_sessionmanager.py ├── tsconfig.json ├── tslint.json ├── webpack.config.js └── yarn.lock /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | exclude_lines = 3 | pragma: no cover 4 | def __repr__ 5 | if self.debug: 6 | if settings.DEBUG 7 | raise AssertionError 8 | raise NotImplementedError 9 | if __name__ == .__main__.: 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jquery": true 6 | }, 7 | "globals": { 8 | "initial_data": true, 9 | "module": true, 10 | "_": true, 11 | "moment": true 12 | }, 13 | "extends": "eslint:recommended", 14 | "parserOptions": { 15 | "ecmaFeatures": { 16 | "experimentalObjectRestSpread": true, 17 | "jsx": true 18 | }, 19 | "sourceType": "module" 20 | }, 21 | "plugins": ["react"], 22 | "rules": { 23 | "no-console": [0], 24 | "react/jsx-uses-vars": 1 25 | }, 26 | "parser": "babel-eslint" 27 | } 28 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | ignore = E501, E203, W503, E402, E231 4 | # line length, whitespace before ':', line break before binary operator, module level import not at top of file 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Please complete the following information:** 24 | * OS: 25 | * gdbgui version (`gdbgui -v`): 26 | * gdb version (`gdb -v`): 27 | * browser [e.g. chrome, safari]: 28 | * python packages (`pip freeze`): 29 | 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | - [] I have added an entry to `CHANGELOG.md`, or an entry is not needed for this change 3 | 4 | ## Summary of changes 5 | 9 | 10 | ## Test plan 11 | 12 | Tested by running 13 | ``` 14 | # command(s) to exercise these changes 15 | ``` 16 | -------------------------------------------------------------------------------- /.github/workflows/build_executable.yml: -------------------------------------------------------------------------------- 1 | name: Build native gdbgui executables with pyinstaller and pex 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | release: 9 | 10 | jobs: 11 | build: 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest, macos-latest] 16 | python-version: ["3.12"] 17 | include: 18 | - os: ubuntu-latest 19 | buildname: linux 20 | # - os: windows-latest 21 | # buildname: windows 22 | - os: macos-latest 23 | buildname: mac 24 | steps: 25 | - uses: actions/checkout@v1 26 | - name: Set up Python ${{ matrix.python-version }} 27 | uses: actions/setup-python@v2 28 | with: 29 | python-version: ${{ matrix.python-version }} 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | python -m pip install nox 34 | - name: Compile ${{ matrix.buildname }} gdbgui executable 35 | run: | 36 | nox --non-interactive --session build_executables_${{ matrix.buildname }} 37 | - name: Upload ${{ matrix.buildname }} executable 38 | # if: github.ref == 'refs/heads/master' 39 | uses: actions/upload-artifact@v2 40 | with: 41 | name: gdbgui_${{ matrix.buildname }} 42 | path: | 43 | ./build/executable 44 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions 2 | 3 | name: Tests 4 | 5 | on: 6 | pull_request: 7 | push: 8 | branches: 9 | - master 10 | release: 11 | 12 | jobs: 13 | run_tests: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest] 18 | python-version: ["3.12"] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install nox 30 | - name: Install gdb ubuntu 31 | run: | 32 | sudo apt update 33 | sudo apt upgrade 34 | sudo apt install gdb 35 | - name: Execute Tests 36 | run: | 37 | nox --non-interactive --session tests-${{ matrix.python-version }} 38 | 39 | build: 40 | runs-on: ${{ matrix.os }} 41 | strategy: 42 | matrix: 43 | os: [ubuntu-latest] 44 | python-version: ["3.12"] 45 | 46 | steps: 47 | - uses: actions/checkout@v2 48 | - name: Set up Python ${{ matrix.python-version }} 49 | uses: actions/setup-python@v2 50 | with: 51 | python-version: ${{ matrix.python-version }} 52 | - name: Install dependencies 53 | run: | 54 | python -m pip install --upgrade pip 55 | pip install nox 56 | - name: Execute Tests 57 | run: | 58 | nox --non-interactive --session build 59 | 60 | lint: 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v2 64 | - name: Set up Python 65 | uses: actions/setup-python@v2 66 | with: 67 | python-version: 3.9 68 | - name: Install dependencies 69 | run: | 70 | python -m pip install --upgrade pip 71 | pip install nox 72 | - name: Lint 73 | run: | 74 | nox --non-interactive --session lint 75 | 76 | docs: 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v2 80 | - name: Set up Python 81 | uses: actions/setup-python@v2 82 | with: 83 | python-version: "3.12" 84 | - name: Install dependencies 85 | run: | 86 | python -m pip install --upgrade pip 87 | pip install nox 88 | - name: Verify Docs 89 | run: | 90 | nox --non-interactive --session docs 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | *egg* 3 | node_modules 4 | build 5 | executable 6 | .DS_Store 7 | *.a.dSYM 8 | gdbgui_pyinstaller.spec 9 | *-link 10 | *-link.c 11 | *-link.dSYM 12 | *.pyc 13 | yarn-error.log 14 | venv 15 | site 16 | gdbgui/static/js/* 17 | __pycache__ 18 | .coverage* 19 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 90, 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.formatting.provider": "none", 3 | "[python]": { 4 | "editor.formatOnSave": true, 5 | "editor.defaultFormatter": "ms-python.black-formatter" 6 | }, 7 | "[json]": { 8 | "editor.formatOnSave": true 9 | }, 10 | "files.associations": { 11 | "*.spec": "python" 12 | } 13 | } -------------------------------------------------------------------------------- /.vulture_whitelist.py: -------------------------------------------------------------------------------- 1 | _.sessions # unused attribute (noxfile.py:7) 2 | _.reuse_existing_virtualenvs # unused attribute (noxfile.py:6) 3 | _.secret_key # unused attribute (gdbgui/backend.py:104) 4 | _.reuse_existing_virtualenvs # unused attribute (noxfile.py:6) 5 | _.sessions # unused attribute (noxfile.py:7) 6 | cover # unused function (noxfile.py:50) 7 | lint # unused function (noxfile.py:78) 8 | autoformat # unused function (noxfile.py:94) 9 | docs # unused function (noxfile.py:103) 10 | develop # unused function (noxfile.py:109) 11 | serve # unused function (noxfile.py:118) 12 | publish # unused function (noxfile.py:133) 13 | watch_docs # unused function (noxfile.py:142) 14 | build_executable_current_platform # unused function (noxfile.py:154) 15 | build_executable_mac # unused function (noxfile.py:162) 16 | build_executable_linux # unused function (noxfile.py:169) 17 | build_executable_windows # unused function (noxfile.py:176) 18 | on_connect # unused function (tests/test_backend.py:14) 19 | monkeypatch # unused variable (tests/test_cli.py:23) 20 | monkeypatch # unused variable (tests/test_cli.py:33) 21 | monkeypatch # unused variable (tests/test_cli.py:43) 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thanks for your interest in contributing to gdbgui! 2 | 3 | If your change is small, go ahead and submit a pull request. If it is substantial, create a GitHub issue to discuss it before making the change. 4 | 5 | ## Dependencies 6 | 7 | 1.) [nox](https://github.com/theacodes/nox) is used to automate various tasks. You will need it installed on your system before continuing. 8 | 9 | You can install it with pipx (recommended): 10 | ``` 11 | > pipx install nox 12 | ``` 13 | or pip: 14 | ``` 15 | > pip install --user nox 16 | ``` 17 | 18 | 2.) [yarn](https://yarnpkg.com/) is used for managing JavaScript files 19 | 20 | ## Developing 21 | Development can be done with one simple step: 22 | ``` 23 | > nox -s develop 24 | ``` 25 | This will install all Python and JavaScript dependencies, and build and watch Python and JavaScript files for changes, automatically reloading as things are changed. 26 | 27 | Make sure you [turn your cache off](https://www.technipages.com/google-chrome-how-to-completely-disable-cache) so that changes made locally are reflected in the page. 28 | 29 | ## Running and Adding tests 30 | ```bash 31 | > nox 32 | ``` 33 | 34 | runs all applicable tests and linting. 35 | 36 | Python tests are in `gdbgui/tests`. They are run as part of the above command, but can be run with 37 | ``` 38 | > nox -s python_tests 39 | ``` 40 | 41 | JavaScript tests are in `gdbgui/src/js/tests`. They are run as part of the above command, but can be run with 42 | ``` 43 | > nox -s js_tests 44 | ``` 45 | 46 | ## Documentation 47 | 48 | ### Modifying Documentation 49 | Documentation is made with `mkdocs`. Then make changes to `mkdocs.yml` or md files in the `docs` directory. 50 | 51 | To build docs, run 52 | ``` 53 | nox -s docs 54 | ``` 55 | 56 | To see a live preview of current documentation, run 57 | ``` 58 | nox -s watch_docs 59 | ``` 60 | 61 | ### Publishing Documentation 62 | The generated documentation is published to the `gh-pages` branch. 63 | ``` 64 | nox -s publish_docs 65 | ``` 66 | 67 | ### Building Binary Executables 68 | 69 | These are automatically built on CI, but can be built locally with corresponding `nox` commands, such as: 70 | 71 | ``` 72 | nox -s build_executables_current_platform 73 | ``` 74 | 75 | ## Publishing a New Version 76 | 1. Make sure the version number is incremented in `VERSION.txt`. 77 | 1. The version to release must be on the master branch and have all CI tests pass and new binary executable artifacts attached to the GitHub action results 78 | 1. Publish the package to PyPI and update documentation. Both are done with this `nox -s publish`. 79 | 1. Create a "release" in GitHub and attach the gdbgui binary executable artifacts to it. 80 | 81 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE 3 | include requirements.txt 4 | 5 | graft gdbgui 6 | # these files are built and must be included in distribution 7 | # but shouldn't be included in git repository since they 8 | # are generated 9 | graft gdbgui/static/js 10 | 11 | prune examples 12 | prune .vscode 13 | prune downloads 14 | prune screenshots 15 | prune tests 16 | prune docs 17 | prune docker 18 | prune images 19 | prune gdbgui/__pycache__ 20 | prune gdbgui/server/__pycache__ 21 | prune gdbgui/src 22 | 23 | exclude mypy.ini 24 | exclude .eslintrc.json 25 | exclude .coveragerc 26 | exclude .flake8 27 | exclude .vulture_whitelist.py 28 | exclude .prettierrc.js 29 | exclude jest.config.js 30 | exclude make_executable.py 31 | exclude mkdocs.yml 32 | exclude package.json 33 | exclude requirements.in 34 | exclude tsconfig.json 35 | exclude tslint.json 36 | exclude webpack.config.js 37 | exclude yarn.lock 38 | exclude noxfile.py 39 | exclude CHANGELOG.md 40 | exclude CONTRIBUTING.md 41 | exclude postcss.config.js 42 | exclude tailwind.config.js 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | A browser-based frontend to gdb (gnu debugger) 7 |

8 | 9 |

10 | 11 | 12 | image 13 | 14 | 15 | PyPI version 16 | 17 | 18 | image 19 | 20 |

21 | 22 | --- 23 | 24 | **Documentation**: https://gdbgui.com 25 | 26 | **Source Code**: https://github.com/cs01/gdbgui/ 27 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | www.gdbgui.com -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | This is the command line help output of gdbgui. 2 | 3 | ``` 4 | usage: gdbgui [-h] [-g GDB_CMD] [-p PORT] [--host HOST] [-r] 5 | [--auth-file AUTH_FILE] [--user USER] [--password PASSWORD] 6 | [--key KEY] [--cert CERT] [--remap-sources REMAP_SOURCES] 7 | [--project PROJECT] [-v] [-n] [-b BROWSER] [--debug] 8 | [--args ...] 9 | [debug_program] 10 | 11 | A server that provides a graphical user interface to the gnu debugger (gdb). 12 | https://github.com/cs01/gdbgui 13 | 14 | positional arguments: 15 | debug_program The executable file you wish to debug, and any 16 | arguments to pass to it. To pass flags to the 17 | binary, wrap in quotes, or use --args instead. 18 | Example: gdbgui ./mybinary [other-gdbgui-args...] 19 | Example: gdbgui './mybinary myarg -flag1 -flag2' 20 | [other gdbgui args...] (default: None) 21 | 22 | optional arguments: 23 | -h, --help show this help message and exit 24 | --args ... Specify the executable file you wish to debug and 25 | any arguments to pass to it. All arguments are taken 26 | literally, so if used, this must be the last 27 | argument. This can also be specified later in the 28 | frontend. passed to gdbgui. Example: gdbgui [...] 29 | --args ./mybinary myarg -flag1 -flag2 (default: []) 30 | 31 | gdb settings: 32 | -g GDB_CMD, --gdb-cmd GDB_CMD 33 | gdb binary and arguments to run. If passing 34 | arguments, enclose in quotes. If using rr, it should 35 | be specified here with 'rr replay'. Examples: gdb, 36 | /path/to/gdb, 'gdb --command=FILE -ix', 'rr replay' 37 | (default: gdb) 38 | 39 | gdbgui network settings: 40 | -p PORT, --port PORT The port on which gdbgui will be hosted (default: 41 | 5000) 42 | --host HOST The host ip address on which gdbgui serve (default: 43 | 127.0.0.1) 44 | -r, --remote Shortcut to set host to 0.0.0.0 and suppress browser 45 | from opening. This allows remote access to gdbgui 46 | and is useful when running on a remote machine that 47 | you want to view/debug from your local browser, or 48 | let someone else debug your application remotely. 49 | (default: False) 50 | 51 | security settings: 52 | --auth-file AUTH_FILE 53 | Require authentication before accessing gdbgui in 54 | the browser. Specify a file that contains the HTTP 55 | Basic auth username and password separate by 56 | newline. (default: None) 57 | --user USER Username when authenticating (default: None) 58 | --password PASSWORD Password when authenticating (default: None) 59 | --key KEY SSL private key. Generate with:openssl req -newkey 60 | rsa:2048 -nodes -keyout host.key -x509 -days 365 61 | -out host.cert (default: None) 62 | --cert CERT SSL certificate. Generate with:openssl req -newkey 63 | rsa:2048 -nodes -keyout host.key -x509 -days 365 64 | -out host.cert (default: None) 65 | 66 | other settings: 67 | --remap-sources REMAP_SOURCES, -m REMAP_SOURCES 68 | Replace compile-time source paths to local source 69 | paths. Pass valid JSON key/value pairs.i.e. --remap- 70 | sources='{"/buildmachine": "/current/machine"}' 71 | (default: None) 72 | --project PROJECT Set the project directory. When viewing the 73 | "folders" pane, paths are shown relative to this 74 | directory. (default: None) 75 | -v, --version Print version (default: False) 76 | -n, --no-browser By default, the browser will open with gdbgui. Pass 77 | this flag so the browser does not open. (default: 78 | False) 79 | -b BROWSER, --browser BROWSER 80 | Use the given browser executable instead of the 81 | system default. (default: None) 82 | --debug The debug flag of this Flask application. Pass this 83 | flag when debugging gdbgui itself to automatically 84 | reload the server when changes are detected 85 | (default: False) 86 | ``` 87 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | ../CHANGELOG.md -------------------------------------------------------------------------------- /docs/contact.md: -------------------------------------------------------------------------------- 1 | * Email: chadsmith.software@gmail.com -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | ../CONTRIBUTING.md -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | 2 | # Examples 3 | ## Code Examples 4 | View code examples on [GitHub](https://github.com/cs01/gdbgui/tree/master/examples). 5 | 6 | ## gdbgui Invocation Examples 7 | 8 | launch gdbgui 9 | 10 | ``` 11 | gdbgui 12 | ``` 13 | 14 | set the inferior program, pass argument, set a breakpoint at main 15 | 16 | ``` 17 | gdbgui --args ./myprogram myarg -myflag 18 | ``` 19 | 20 | 21 | ``` 22 | gdbgui "./myprogram myarg -myflag" 23 | ``` 24 | 25 | use gdb binary not on your $PATH 26 | 27 | ``` 28 | gdbgui --gdb-cmd build/mygdb 29 | ``` 30 | 31 | Pass arbitrary arguments directly to gdb when it is launched 32 | 33 | ``` 34 | gdbgui --gdb-cmd="gdb -x gdbcmds.txt" 35 | ``` 36 | 37 | run on port 8080 instead of the default port 38 | 39 | ``` 40 | gdbgui --port 8080 41 | ``` 42 | 43 | 44 | 45 | run on a server and host on 0.0.0.0. Accessible to the outside world as long as port 80 is not blocked. 46 | 47 | ``` 48 | gdbgui -r 49 | ``` 50 | 51 | Same as previous but will prompt for a username and password 52 | 53 | ``` 54 | gdbgui -r --auth 55 | ``` 56 | 57 | Same as previous but with encrypted https connection. 58 | ``` 59 | openssl req -newkey rsa:2048 -nodes -keyout private.key -x509 -days 365 -out host.cert 60 | ``` 61 | ``` 62 | gdbgui -r --auth --key private.key --cert host.cert 63 | ``` 64 | 65 | Use Mozilla's [record and replay](https://rr-project.org) (rr) debugging supplement to gdb. rr lets your record a program (usually with a hard-to-reproduce bug in it), then deterministically replay it as many times as you want. You can even step forwards and backwards. 66 | ``` 67 | gdbgui --gdb-cmd "rr replay --" 68 | ``` 69 | 70 | Use recording other than the most recent one 71 | 72 | ``` 73 | gdbgui --gdb-cmd "rr replay RECORDED_DIRECTORY --" 74 | ``` 75 | 76 | Don't automatically open the browser when launching 77 | 78 | ``` 79 | gdbgui -n 80 | ``` 81 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | ## How can I see what commands are being sent to gdb? 2 | Go to Settings and check the box that says `Print all sent commands in console, including those sent automatically by gdbgui` 3 | 4 | ## How can I see gdb's raw output? 5 | Launch gdbgui with the debug flag, `gdbgui --debug`, then a new component will appear on the bottom right side of UI. 6 | 7 | ## Can I use a different gdb executable? 8 | Yes, use `gdbgui -g ` 9 | 10 | ## Does this work with LLDB? 11 | No, only gdb. 12 | 13 | ## Can this debug Python? 14 | No. It uses gdb on the backend which does not debug Python code. 15 | 16 | ## How do I make program output appear in a different terminal? 17 | On linux terminals are named. You can get a terminal's name by running `tty` which will print something like `/dev/ttys3`. Tell gdb to use the terminal gdbgui was launched from with 18 | 19 | ```bash 20 | gdbgui --gdb-args="--tty=$(tty)" 21 | ``` 22 | 23 | or if you want to set it from the UI after gdbgui has been opened, run 24 | 25 | ```bash 26 | set inferior-tty /dev/ttys3 # replace /dev/ttys3 with desired tty name 27 | ``` 28 | 29 | ## Help! There isn't a button for something I want to do. What should I do? 30 | The vast majority of common use cases are handled in the UI, and to keep the UI somewhat simple I do not intend on making UI support for every single gdb command. You can search gdb documentation and use any gdb command you want in the console at the bottom of the window. If you think there should be a UI element for a command or function, create an issue on GitHub and I will consider it. 31 | -------------------------------------------------------------------------------- /docs/gettingstarted.md: -------------------------------------------------------------------------------- 1 | Before running `gdbgui`, you should compile your program with debug symbols and a lower level of optimization, so code isn't optimized out before runtime. To include debug symbols with `gcc` use `-ggdb`, with `rustc` use `-g`. To disable most optimizations in `gcc` use the `-O0` flag, with `rustc` use `-O`. 2 | 3 | For more details, consult your compiler's documentation or a search engine. 4 | 5 | Now that you have `gdbgui` installed and your program compiled with debug symbols, all you need to do is run 6 | ``` 7 | gdbgui 8 | ``` 9 | 10 | This will start gdbgui's server and open a new tab in your browser. That tab contains a fully functional frontend running `gdb`! 11 | 12 | You can see gdbgui in action on [YouTube](https://www.youtube.com/channel/UCUCOSclB97r9nd54NpXMV5A). 13 | 14 | To see the full list of options gdbgui offers, you can view command line options by running 15 | ``` 16 | gdbgui --help 17 | ``` 18 | 19 | If you have a question about something 20 | 21 | * Read documentation on the [homepage](https://github.com/cs01/gdbgui/) 22 | * [Ask question in an issue on github](https://github.com/cs01/gdbgui/issues) 23 | 24 | 25 | ## Settings 26 | `gdbgui` settings can be accessed by clicking the gear icon in the top right of the frontend. Most of these settings persist between sessions for a given url and port. 27 | 28 | 29 | ## Keyboard Shortcuts 30 | The following keyboard shortcuts are available when the focus is not in an input field. They have the same effect as when the button is pressed. 31 | 32 | * Run: r 33 | * Continue: c 34 | * Next: n or right arrow 35 | * Step: s or down arrow 36 | * Up: u or up arrow 37 | * Next Instruction: m 38 | * Step Instruction: , 39 | -------------------------------------------------------------------------------- /docs/howitworks.md: -------------------------------------------------------------------------------- 1 | gdbgui consists of two main parts: the frontend and the backend 2 | 3 | ## Backend 4 | 5 | The backend is written in Python and consists of a Flask server with websocket capability thanks to the `python-socketio` package. 6 | 7 | When a new websocket connection from a browser is established, the server starts a new gdb subprocess and associates it with this websocket. This gdb process is told to use gdb's [machine interface](https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html) interpreter, which enables gdb's input and output to be programatically parsed so you can write code to do further processing with it, such as build a user interface. 8 | 9 | The [pygdbmi library](https://github.com/cs01/pygdbmi) is used to manage the gdb subprocess and parse its output. It returns key/value pairs (dictionaries) that can be used to create a frontend. I wrote pygdbmi as a building block for gdbgui, but it is useful for any type of programmatic control over gdb. 10 | 11 | In summary, the backend is used to: 12 | 13 | - create endpoints for the browser, including http and websocket. 14 | - The server can access the operating system and do things like read source files or send signals to processes. 15 | - create a managed gdb subprocess and parse output with pygdbmi 16 | - spawn a separate thread to constantly check for output from the gdb subprocess 17 | - forward output to the client through a websocket as it is parsed in the reader thread 18 | 19 | ## Frontend 20 | 21 | The frontend is written in JavaScript and uses React. It establishes a websocket connection to the server, at which time the server starts a new gdb subprocess for that particular websocket connection as mentioned above. Commands can be sent from the browser through the websocket to the server which writes to gdb, and output from gdb is forwarded from the server through the websocket to the browser. 22 | 23 | As the browser receives websocket messages from the server, it maintains the state of gdb, such as whether it's running, paused, or exited, where breakpoints are, what the stack is, etc. As this state changes, React performs the necessary DOM updates. 24 | 25 | In summary, the frontend is used to: 26 | 27 | * Convert key/value pairs of gdb's machine interface output into a user interface 28 | * Maintain the state of gdb 29 | * Provide UI elements that can send gdb machine interface commands to gdb -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | A browser-based frontend to gdb (gnu debugger) 7 |

8 | 9 |

10 | 11 | 12 | CI Tests 13 | 14 | 15 | PyPI version 16 | 17 | 18 | Download Count 19 | 20 |

21 | 22 | --- 23 | 24 |

25 | 26 | 27 | 28 | 29 |

30 | 31 | `gdbgui` is a browser-based frontend to `gdb`, the [gnu debugger](https://www.gnu.org/software/gdb/). You can add breakpoints, view stack traces, and more in C, C++, Go, and Rust! 32 | 33 | It's perfect for beginners and experts. Simply run `gdbgui` from the terminal to start the gdbgui server, and a new tab will open in your browser. 34 | 35 | **Sound Good? Get started with [installation](installation)**. 36 | 37 | ## Testimonials 38 | 39 | "*Definitely worth checking out.*" 40 | 41 |
42 | —Jason Turner, host of C++ weekly on YouTube 43 |
44 | 45 | "_Seriously, great front-end to gdb for those of us who are not always using a full IDE. Great project._" 46 | 47 |
48 | —Jefferson on Twitter 49 |
50 | 51 | "_Where were you all my life? And why did I use DDD?_" 52 | 53 |
54 | —Mario Zechner, author, game engine developer on Twitter 55 |
56 | 57 | gdbgui is used by thousands of developers around the world including engineers at Google and college computer science course instructions. It even made its way into the Rust programming language's [source code](https://github.com/rust-lang/rust/blob/master/src/etc/rust-gdbgui) and appeared on episode [110 of C++ Weekly](https://youtu.be/em842geJhfk). 58 | 59 | 60 | 61 | ## License 62 | 63 | gdbgui's license is GNU GPLv3. To summarize it, you 64 | 65 | - can use it for free at work or for personal use 66 | - can modify its source code 67 | - must disclose your source code if you redistribute any part of gdbgui 68 | 69 | ## Distribution 70 | 71 | gdbgui is distributed through 72 | 73 | - github ([https://github.com/cs01/gdbgui](https://github.com/cs01/gdbgui)) 74 | - [PyPI](https://pypi.python.org/pypi/gdbgui/) 75 | 76 | ## Authors 77 | 78 | - Chad Smith, creator/maintainer 79 | - @bobthekingofegypt, contibutor 80 | - [Community contributions](https://github.com/cs01/gdbgui/graphs/contributors) 81 | 82 | ## Donate 83 | 84 | [Paypal](https://www.paypal.me/grassfedcode/20) 85 | 86 | ## Contact 87 | 88 | https://chadsmith.dev 89 | chadsmith.software@gmail.com 90 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # gdbgui installation 2 | 3 | There are a few ways to install gdbgui on your machine. There is even a way to run gdbgui without installing it. Read on to to find the one that's right for you. 4 | 5 | ## Method 1: Using `pipx` (recommended) 6 | 7 | gdbgui recommends using [pipx](https://github.com/pipxproject/pipx), a program to run Python CLI binaries in isolated environments. 8 | 9 | You can install pipx like this: 10 | 11 | ``` 12 | python3 -m pip install --user pipx 13 | python3 -m userpath append ~/.local/bin 14 | ``` 15 | 16 | Restart/re-source your console to make sure the userpath is up to date. 17 | 18 | Then, install gdbgui with pipx: 19 | 20 | ``` 21 | pipx install gdbgui 22 | ``` 23 | 24 | To upgrade run 25 | 26 | ``` 27 | pipx upgrade gdbgui 28 | ``` 29 | 30 | When installation is finished, type `gdbgui` from the command line to run it, or `gdbgui -h` for help. 31 | 32 | To uninstall, run 33 | 34 | ``` 35 | pipx uninstall gdbgui 36 | ``` 37 | 38 | ### Try Without Installing 39 | 40 | By using [pipx](https://github.com/pipxproject/pipx), you can run Python CLI programs in ephemeral one-time virtual environments. 41 | 42 | ``` 43 | pipx run gdbgui 44 | ``` 45 | 46 | A new tab running the latest version of gdbgui will open in your browser. Press CTRL+C to end the process, and your system will remain untouched. 47 | 48 | ## Method 2: Using `pip` 49 | 50 | `pip` is a popular installer for Python packages. gdbgui is a Python package and as such can be installed with pip, though we recommend using `pipx` rather than `pip` if possible. 51 | 52 | If you prefer to use Virtual Environments, you can activate one and then run 53 | 54 | ``` 55 | pip install gdbgui 56 | ``` 57 | 58 | You can get upgrades with 59 | 60 | ``` 61 | pip install --upgrade gdbgui 62 | ``` 63 | 64 | To uninstall, run 65 | 66 | ``` 67 | pip uninstall gdbgui 68 | ``` 69 | 70 | ## Method 3: Download and Run Binary Executable 71 | 72 | Download and run the binary executable for your system from [GitHub Releases](https://github.com/cs01/gdbgui/releases). 73 | 74 | ## System Dependencies for Python Package 75 | 76 | Note that this only applies if you are installing the Python package, and not using the binary executable. 77 | 78 | - gdb (gnu debugger) 79 | - Python 3.4+ (recommended) or 2.7 80 | - pip version 8 or higher 81 | 82 | ### Linux Dependencies 83 | 84 | sudo apt install gdb python3 85 | 86 | ### macOS Dependencies 87 | 88 | brew install python3 89 | brew install gdb --with-python --with-all-targets 90 | 91 | macOS users must also codesign gdb: follow [these 92 | instructions](http://andresabino.com/2015/04/14/codesign-gdb-on-mac-os-x-yosemite-10-10-2/). This will fix the error 93 | `please check gdb is codesigned - see taskgated(8)`. 94 | 95 | ### Windows Dependencies 96 | 97 | Note that windows is only supported for gdbgui versions less than 0.14. 98 | 99 | - [Python 3](https://www.python.org/downloads/windows/) 100 | - gdb, make, gcc 101 | 102 | If you do not have already have gdb/make/gcc installed, there are two options to install them on Windows: `MinGW` and `cygwin`. 103 | 104 | ##### MinGW (recommended) 105 | 106 | Minimal GNU for Windows ([`MinGW`]([http://mingw.org/)) is the recommended Windows option. [Install MinGW](https://sourceforge.net/projects/mingw/files/Installer/mingw-get-setup.exe/download) with the "MinGW Base System" package. This is the default package which contains `make`, `gcc`, and `gdb`. 107 | 108 | It will install to somewhere like `C:\MinGW\bin\...`. For example `C:\MinGW\bin\gdb.exe`, `C:\MinGW\bin\mingw32-make.exe`, etc. 109 | 110 | Ensure this MinGW binary directory (i.e. `C:\MinGW\bin\`) is on your "Path" environment variable: Go to `Control Panel > System Properties > Environment Variables > System Variables > Path` and make sure `C:\MinGW\bin\` is added to that list. If it is not added to your "Path", you will have to run gdbgui with the path explicitly called out, such as `gdbgui -g C:\MinGW\bin\gdb.exe`. 111 | 112 | ##### Cygwin 113 | 114 | Cygwin is a more UNIX-like compatibility layer on Windows, and `gdbgui` works with it as well. 115 | 116 | - Install [cygwin](https://cygwin.com/install.html) 117 | 118 | When installing cygwin packages, add the following: 119 | 120 | - python3 121 | - python3-pip 122 | - python3-devel 123 | - gdb 124 | - gcc-core 125 | - gcc-g++ 126 | 127 | ### Running from Source 128 | 129 | See the [contributing](/contributing) section. 130 | -------------------------------------------------------------------------------- /docs/screenshots.md: -------------------------------------------------------------------------------- 1 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/gdbgui.png) 2 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/gdbgui2.png) 3 | 4 | Enter the binary and args just as you'd call them on the command line. 5 | The binary is restored when gdbgui is opened at a later time. 6 | 7 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/load_binary_and_args.png) 8 | 9 | Intuitive control of your program. From left to right: Run, Continue, 10 | Next, Step, Return, Next Instruction, Step Instruction. 11 | 12 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/controls.png) 13 | 14 | If the environment supports reverse debugging, such as when using an Intel CPU and running Linux and debugging with [rr](http://rr-project.org/), gdbgui allows you to debug in reverse. 15 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/reverse_debugging.png) 16 | 17 | ## Stack/Threads 18 | 19 | View all threads, the full stack on the active thread, the current frame 20 | on inactive threads. Switch between frames on the stack, or threads by 21 | pointing and clicking. 22 | 23 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/stack_and_threads.png) 24 | 25 | ## Send Signal to Inferior (debugged) Process 26 | Choose from any signal your OS supports to send to the inferior. For example, to mock `CTRL+C` in plain gdb, you can send `SIGINT` to interrupt the inferior process. If the inferior process is hung for some reason, you can send `SIGKILL`, etc. 27 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/send_signal.png) 28 | 29 | 30 | ## Source Code 31 | View source, assembly, add breakpoints. All symbols used to compile the 32 | target are listed in a dropdown above the source code viewer, and have 33 | autocompletion capabilities. There are two different color schemes: dark (monokai), and a light theme (default). 34 | 35 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/source.png) 36 | 37 | With assembly. Note the bold line is the current instruction that gdb is 38 | stopped on. 39 | 40 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/source_with_assembly.png) 41 | 42 | If the source file is not found, it will display assembly, and allow you to step through it as desired. 43 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/assembly.png) 44 | 45 | 46 | ## Variables and Expressions 47 | 48 | All local variables are automatically displayed, and are clickable to 49 | explore their fields. 50 | 51 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/locals.png) 52 | 53 | Hover over a variable and explore it, just like in the Chrome debugger. 54 | 55 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/hover.png) 56 | 57 | Arbitrary expressions can be evaluated as well. These expressions persist as the program is stepped through. The base/radix can be modified as desired. 58 | 59 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/radix.gif) 60 | 61 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/expressions.png) 62 | 63 | Expressions record their previous values, and can be displayed in an x/y 64 | plot. 65 | 66 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/plots.png) 67 | 68 | Expressions can be interactively explored in a tree view. 69 | 70 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/tree_explorer.png) 71 | 72 | 73 | ## Memory Viewer 74 | 75 | All hex addresses are automatically converted to clickable links to 76 | explore memory. Length of memory is configurable. In this case 10 bytes 77 | are displayed per row. 78 | 79 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/memory.png) 80 | 81 | ## Registers 82 | 83 | View all registers. If a register was updated it is highlighted in 84 | yellow. 85 | 86 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/registers.png) 87 | 88 | ## gdb console 89 | 90 | * Prints gdb output 91 | * Allows you to write directly to the underlying gdb subprocess as if you were using it in the terminal 92 | * Tab completion works, and displays a button to view help on gdb commands 93 | * Can be used to ease into learning gdb 94 | * Can be used as a fallback for commands that don't have a UI widget 95 | * History can be accessed using up/down arrows 96 | 97 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/console.png) 98 | 99 | ## authentication 100 | Authentication can be enabled when serving on a publicly accessible IP address. See `gdbgui --help` for instructions on how to enable authentication. 101 | 102 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/authentication.png) 103 | 104 | 105 | ## Dashboard 106 | A dashboard is available to let you look at all gdb instances managed by gdbgui. You can kill them, or attach to them. More than one person can attach to a managed gdb subprocess and participate in the debugging session simultaneously. i.e. if one person steps forward, all connected users see the program step forward in real time. 107 | 108 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/dashboard.png) 109 | 110 | ## gdbgui at launch 111 | 112 | ![image](https://github.com/cs01/gdbgui/raw/master/screenshots/ready.png) 113 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Overview 4 | `gdbgui` can debug executables generated from various languages. This folder contains example source code and makefiles to build and automatically launch `gdbgui`. 5 | 6 | ## Clone 7 | To get started, first clone this repository: 8 | ``` 9 | git clone https://github.com/cs01/gdbgui.git 10 | ``` 11 | 12 | ## Install Dependencies 13 | If you already installed `gdbgui` with `pip`, you have all dependencies installed. If not, you need to install them manually: 14 | ```bash 15 | pip install -r gdbgui/requirements.txt # run as sudo if this fails 16 | ``` 17 | 18 | ## Build Executables and Debug with gdbgui 19 | Enter the directory with the language of your choice in `gdbgui/examples/*` (`c`, `cpp`, `rust`, `golang`, `fortran`), then type `make` and hit the `tab` to see the make targets. 20 | 21 | For example, in `gdbgui/examples/c`, running `make hello` will: 22 | 23 | * build the binary (assuming you have the right compilers and libraries installed) 24 | * open a new tab in your browser 25 | * load the executable for the make target you just built 26 | * insert a breakpoint at main (Rust and Go users may see machine code displayed rather than source code. This is a `gdb` limitation.) 27 | * **Note: Although the program has loaded, you still must click the run icon to actually begin running the program.** 28 | -------------------------------------------------------------------------------- /examples/c/debug_segfault.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | char* badstring = 0; 8 | const char* s = "gdbgui"; 9 | 10 | int myvar = 100; 11 | unsigned int myvar2 = 200; 12 | 13 | printf("The next function call will cause a segfault. With gdbgui, the state of the program\n" 14 | "can be debugged at the time the program exited by running the command\n" 15 | "\"backtrace\" or \"bt\" in the gdb console.\n\n" 16 | "It will re-enter the state the\n" 17 | "program was in, including the stack trace: you'll end up in `main -> _IO_puts -> strlen`.\n" 18 | "If you click on the `main` function in the call stack, gdbgui will put you in the main function\n" 19 | "where you can inspect your local\n" 20 | "variables and determine how the segfault occured.\n\n" 21 | ); 22 | 23 | 24 | printf("%s\n", badstring); 25 | 26 | printf("This line is never reached because the above line causes a segfault\n"); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /examples/c/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | void say_something(const char *str) 4 | { 5 | printf("%s\n", str); 6 | } 7 | 8 | struct mystruct_t 9 | { 10 | int value; 11 | char letter; 12 | char *string; 13 | 14 | struct 15 | { 16 | double dbl; 17 | } substruct; /* named sub-struct */ 18 | 19 | struct 20 | { 21 | float fp; 22 | }; /* anonymous struct */ 23 | 24 | void *ptr; 25 | size_t struct_size; 26 | union { 27 | int unionint; 28 | double uniondouble; 29 | }; 30 | }; 31 | 32 | int main(int argc, char **argv) 33 | { 34 | printf("Hello World\n"); 35 | 36 | int retval = 1; 37 | 38 | /* bytes are allocated for s, 39 | but still contain garbage */ 40 | struct mystruct_t s; 41 | s.value = 100; 42 | s.string = "pass"; 43 | s.substruct.dbl = 567.8; 44 | s.letter = 'P'; 45 | s.fp = 123.4; 46 | s.ptr = say_something; /* address of function */ 47 | s.ptr = &say_something; /* also address of function */ 48 | s.unionint = 0; 49 | s.uniondouble = 1.0; 50 | 51 | for (int i = 0; i < 2; i++) 52 | { 53 | printf("i is %d\n", i); 54 | } 55 | 56 | if (!strcmp(s.string, "pass")) 57 | { 58 | retval = 0; 59 | } 60 | 61 | printf("returning %d\n", retval); 62 | say_something("Goodbye"); 63 | return retval; 64 | } 65 | -------------------------------------------------------------------------------- /examples/c/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) 5 | { 6 | char name[20]; 7 | printf("Hello. What's your name?\n"); 8 | fgets(name, 20, stdin); 9 | printf("Hi there, %s", name); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /examples/c/makefile: -------------------------------------------------------------------------------- 1 | ROOT:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | 3 | hello: hello.c 4 | gcc hello.c -o hello_c.a -std=c99 -g 5 | @echo Run with gdbgui: gdbgui --args $(ROOT)/hello_c.a 6 | 7 | input: input.c 8 | gcc input.c -o input.a -std=c99 -g 9 | @echo Run with gdbgui: gdbgui --args $(ROOT)/input.a 10 | 11 | debug_segfault: debug_segfault.c 12 | gcc debug_segfault.c -g -o debug_segfault.a -std=c99 13 | @echo Run with gdbgui: gdbgui --args $(ROOT)/debug_segfault.a 14 | 15 | threads: threads.c 16 | gcc threads.c -o threads.a -std=c99 -lpthread -g 17 | @echo Run with gdbgui: gdbgui --args $(ROOT)/threads.a 18 | 19 | sleeper: sleeper.c 20 | gcc sleeper.c -o sleeper.a -std=c99 -g 21 | @echo Run with gdbgui: gdbgui --args $(ROOT)/sleeper.a 22 | -------------------------------------------------------------------------------- /examples/c/sleeper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | printf("entering\n"); 7 | while(1){ 8 | // while this loop is running, you cannot interact with 9 | // gdb until you interrupt (send signal SIGINT) to gdb 10 | // or the inferior process 11 | printf("sleeping...\n"); 12 | sleep(2); 13 | printf("Finished sleeping. Repeating.\n"); 14 | } 15 | printf("exiting\n"); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /examples/c/threads.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static const int num_increments = 2; 5 | 6 | /* this function is run by the second thread */ 7 | void *thread_callback(void *arg) 8 | { 9 | int *val = (int*)arg; 10 | while((*val) < num_increments){ 11 | printf("incrementing\n"); 12 | (*val)++; 13 | } 14 | printf("increment finished\n"); 15 | } 16 | 17 | int main() 18 | { 19 | int x = 0, y = 0; 20 | printf("x: %d, y: %d\n", x, y); 21 | pthread_t thread_to_increment_x, thread_to_increment_y; 22 | 23 | /* create and run threads */ 24 | if(pthread_create(&thread_to_increment_x, NULL, thread_callback, &x)) { 25 | printf("error: pthread_create returned non-zero value\n"); 26 | return 1; 27 | } 28 | if(pthread_create(&thread_to_increment_y, NULL, thread_callback, &y)) { 29 | printf("error: pthread_create returned non-zero value\n"); 30 | return 1; 31 | } 32 | 33 | /* wait for threads to finish */ 34 | if(pthread_join(thread_to_increment_x, NULL)) { 35 | printf("error: pthread_join returned non-zero value\n"); 36 | return 1; 37 | } 38 | if(pthread_join(thread_to_increment_y, NULL)) { 39 | printf("error: pthread_join returned non-zero value\n"); 40 | return 1; 41 | } 42 | printf("x: %d, y: %d\n", x, y); 43 | 44 | return 0; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /examples/c/tree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Node 6 | { 7 | struct Node* left; 8 | struct Node* right; 9 | char* name; 10 | }; 11 | 12 | void visit(struct Node* node) 13 | { 14 | printf("visiting node '%s'\n", node->name); 15 | } 16 | 17 | void dfs(struct Node *node) 18 | { 19 | if (node == NULL) 20 | { 21 | return; 22 | } 23 | 24 | visit(node); 25 | dfs(node->left); 26 | dfs(node->right); 27 | } 28 | 29 | int main(void) 30 | { 31 | printf("gdbgui has a widget that allows interactive tree exploration. " 32 | "Enter 'root' in 'Expressions' widget, then hover over root and click the tree icon next to 'root' to draw the tree. " 33 | "The tree is automatically updated as the program's state changes changed.\n\n"); 34 | /* initialize nodes so that left/right are NULL and each 35 | node has a name */ 36 | struct Node 37 | root = {.name = "root"}, 38 | a = {.name = "a"}, 39 | b = {.name = "b"}, 40 | c = {.name = "c"}, 41 | d = {.name = "d"}, 42 | e = {.name = "e"}, 43 | f = {.name = "f"}; 44 | 45 | /* connect nodes */ 46 | printf("As you step through the following code, you can see the graph grow and change as assignments are made\n"); 47 | root.left = &a; 48 | root.right = &b; 49 | a.left = &c; 50 | a.right = &d; 51 | d.left = &e; 52 | b.right = &f; 53 | 54 | printf("beginning depth first search. We can verify the dfs algorithm is accurate by comparing it to gdbgui's graph view.\n"); 55 | dfs(&root); 56 | printf("finished depth first search\n"); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /examples/cpp/hello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) 6 | { 7 | std::cout << "Hello World" << std::endl; 8 | 9 | std::cout << "Example vector" << std::endl; 10 | std::vector myvector {}; 11 | myvector.push_back(1.1); 12 | myvector.push_back(2.2); 13 | myvector.push_back(3.3); 14 | myvector.push_back(4.4); 15 | for (auto i : myvector){ 16 | std::cout << i << " is an element in a vector" << std::endl; 17 | } 18 | 19 | std::cout << "Example map" << std::endl; 20 | std::map mymap; 21 | mymap['a'] = 10; 22 | mymap['b'] = 30; 23 | mymap['c'] = 50; 24 | mymap['d'] = 70; 25 | for (auto i : mymap){ 26 | std::cout << i.first << " is a key in a map with a value of " << i.second << std::endl; 27 | } 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /examples/cpp/linked_list.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Node{ 4 | 5 | private: 6 | Node* next = 0; 7 | Node* prev = 0; 8 | int value; 9 | 10 | public: 11 | Node(int v){ 12 | value = v; 13 | } 14 | 15 | int get_value() const{ 16 | return value; 17 | } 18 | 19 | void print_values() const{ 20 | std::cout << this->get_value() << std::endl; 21 | if(this->next){ 22 | this->next->print_values(); 23 | } 24 | } 25 | 26 | void append(int v){ 27 | Node* new_node = new Node(v); 28 | Node* iter = this; 29 | while(iter->next){ 30 | iter = iter->next; 31 | } 32 | iter->next = new_node; 33 | new_node->prev = iter; 34 | } 35 | }; 36 | 37 | int main(){ 38 | Node* linked_list = new Node(0); 39 | linked_list->print_values(); 40 | linked_list->append(1); 41 | linked_list->append(2); 42 | linked_list->append(3); 43 | linked_list->append(4); 44 | linked_list->print_values(); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /examples/cpp/makefile: -------------------------------------------------------------------------------- 1 | GDBGUI=../../gdbgui/backend.py 2 | 3 | hello: hello.cpp 4 | g++ hello.cpp -o hello_cpp.a -std=c++11 -g 5 | @echo Run with gdbgui: gdbgui --args hello_cpp.a 6 | 7 | linked_list: linked_list.cpp 8 | g++ linked_list.cpp -o linked_list_cpp.a -std=c++11 -g 9 | @echo Run with gdbgui: gdbgui --args linked_list_cpp.a 10 | 11 | smart_ptr_demo: smart_ptr_demo.cpp 12 | g++ smart_ptr_demo.cpp -o smart_ptr_demo_cpp.a -std=c++11 -g 13 | @echo Run with gdbgui: gdbgui --args smart_ptr_demo_cpp.a 14 | 15 | sin: sin.cpp 16 | g++ sin.cpp -o sin_cpp.a -std=c++11 -g 17 | @echo Run with gdbgui: gdbgui --args sin_cpp.a 18 | -------------------------------------------------------------------------------- /examples/cpp/sin.cpp: -------------------------------------------------------------------------------- 1 | #include /* sin */ 2 | 3 | int main () 4 | { 5 | double angle = 0, result = 0; 6 | static const double RAD_TO_DEG = 3.14159265 / 180; 7 | while (angle <= 360){ 8 | result = sin(angle * RAD_TO_DEG); 9 | angle += 20; 10 | } 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /examples/cpp/smart_ptr_demo.cpp: -------------------------------------------------------------------------------- 1 | // A demonstration of unique, smart, weak, and raw pointers in C++11. 2 | // compile with: 3 | // g++ smart_ptr_demo.cpp -std=c++11 -o smart_ptr_cpp_demo.a -g 4 | // 5 | // running yields: 6 | // >> ./smart_ptr_demo_cpp.a 7 | // constructed raw pointer, at address 0x169fc20 8 | // entering local scope 9 | // entered local scope 10 | // constructed unique (only one reference) pointer, at address 0x16a0090 11 | // constructed shared (local and global reference) pointer, at address 0x16a00f0 12 | // constructed shared (one shared reference, two weak references) pointer, at address 0x16a0060 13 | // local weak pointer has valid reference 14 | // global weak pointer has valid reference 15 | // smart pointers can be accessed like regular pointers 16 | // This is my type: unique (only one reference) 17 | // This is my type: shared (local and global reference) 18 | // leaving local scope 19 | // destroyed shared (one shared reference, two weak references) pointer, at address 0x16a0060 20 | // destroyed unique (only one reference) pointer, at address 0x16a0090 21 | // left local scope 22 | // global weak pointer has no reference 23 | // destroyed raw pointer, at address 0x169fc20 24 | // leaving main 25 | // destroyed shared (local and global reference) pointer, at address 0x16a00f0 26 | 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | // A class that prints metadata when constructed and destroyed 33 | class SimpleType 34 | { 35 | std::string m_ptr_type; 36 | 37 | public: 38 | SimpleType(const std::string& ptr_type){ 39 | m_ptr_type = ptr_type; 40 | std::cout << "constructed " << m_ptr_type << " pointer, at address " << this << std::endl; 41 | } 42 | ~SimpleType(){ 43 | std::cout << "destroyed " << m_ptr_type << " pointer, at address " << this << std::endl; 44 | } 45 | void identify(){ 46 | std::cout << "This is my type: " << m_ptr_type << std::endl; 47 | } 48 | }; 49 | 50 | int main() 51 | { 52 | std::unique_ptr globalunique; 53 | std::shared_ptr globalshared; 54 | std::weak_ptr globalweak; 55 | SimpleType* raw_ptr = new SimpleType("raw"); 56 | 57 | // locally scoped operations will cause smart pointers to automatically 58 | // be deleted (garbage collected) if no owners remain at the end of the scope 59 | std::cout << "entering local scope" << std::endl; 60 | { 61 | std::cout << "entered local scope" << std::endl; 62 | // unique (deleted upon exit of this local scope) 63 | std::unique_ptr localunique = std::unique_ptr(new SimpleType("unique (only one reference)")); 64 | 65 | // shared with > 1 owner (not deleted upon exit of this local scope) 66 | std::shared_ptr localshared = std::shared_ptr(new SimpleType("shared (local and global reference)")); 67 | globalshared = localshared; // assign global reference 68 | 69 | // shared with exactly 1 owner (deleted upon exit of this local scope) 70 | std::shared_ptr localshared2 = std::shared_ptr(new SimpleType("shared (one shared reference, two weak references)")); 71 | std::weak_ptr localweak = localshared2; // shared_ptr reference count does not increment here, because weak pointers don't "own" the pointer 72 | globalweak = localweak; // again, the shared_ptr reference count does not increment 73 | // prove that the weak pointer references the shared pointer (but it does not own it!) 74 | std::cout << (localweak.lock() ? "local weak pointer has valid reference" : "local weak pointer has no reference") << std::endl; 75 | std::cout << (globalweak.lock() ? "global weak pointer has valid reference" : "global weak pointer has no reference") << std::endl; 76 | 77 | std::cout << "smart pointers can be accessed like regular pointers" << std::endl; 78 | localunique->identify(); 79 | (*globalshared).identify(); 80 | 81 | std::cout << "leaving local scope" << std::endl; 82 | } // localshared is not deleted here because the globalshared reference still exists and shares ownership of it. 83 | // localshared2/globalweak's object is deleted here. Even though globalweak still references it, it doesn't own it, so it's deleted. 84 | 85 | std::cout << "left local scope" << std::endl; 86 | std::cout << (globalweak.lock() ? "global weak pointer has valid reference" : "global weak pointer has no reference") << std::endl; 87 | delete raw_ptr; // this needs to be done manually 88 | 89 | std::cout << "leaving main" << std::endl; 90 | } 91 | -------------------------------------------------------------------------------- /examples/fortran/fortran_array.f90: -------------------------------------------------------------------------------- 1 | program array 2 | integer, parameter :: n=3 3 | integer :: ii 4 | real, dimension(n) :: a, b 5 | real, dimension(n,n) :: c 6 | 7 | a = [( real(ii), ii=1, n )] 8 | 9 | do ii = 1,n 10 | print *, a(ii) 11 | enddo 12 | 13 | b = 0. 14 | do ii = 1, n 15 | ! You could just write b = a ** 2., but I want to see the loop progress 16 | b(ii) = a(ii) ** 2. 17 | enddo 18 | 19 | do ii = 1, n 20 | print *, b(ii) 21 | enddo 22 | 23 | c = reshape( [( real(ii), ii=1,n**2 )], [n,n] ) 24 | do ii = 1, n 25 | print *, c(ii,:) 26 | enddo 27 | 28 | end program 29 | -------------------------------------------------------------------------------- /examples/fortran/makefile: -------------------------------------------------------------------------------- 1 | GDBGUI=../../gdbgui/backend.py 2 | 3 | array_demo: fortran_array.f90 4 | gfortran fortran_array.f90 -o array_f90.a -g 5 | @echo Run with gdbgui: gdbgui --args array_f90.a 6 | -------------------------------------------------------------------------------- /examples/golang/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | import "fmt" 3 | 4 | func main() { 5 | fmt.Println("hello world") 6 | // Create an array of three ints. 7 | array := [...]int{10, 20, 30} 8 | 9 | // Loop over three ints and print them. 10 | for i := 0; i < len(array); i++ { 11 | fmt.Println(array[i]) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/golang/makefile: -------------------------------------------------------------------------------- 1 | GDBGUI=../../gdbgui/backend.py 2 | 3 | hello: hello.go 4 | go build -o hello_go.a -gccgoflags "-w" hello.go 5 | @echo Run with gdbgui: gdbgui --args hello_go.a 6 | -------------------------------------------------------------------------------- /examples/rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | 4 | # User-specific stuff: 5 | .idea/**/workspace.xml 6 | .idea/**/tasks.xml 7 | .idea/dictionaries 8 | -------------------------------------------------------------------------------- /examples/rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "hello" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /examples/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 3 | version = "0.1.0" 4 | authors = ["Your Name "] 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /examples/rust/README.md: -------------------------------------------------------------------------------- 1 | This directory contains a very simple Rust program to demonstrate 2 | how to run gdbgui. 3 | 4 | Run the shell script `compile_and_debug.sh` which will use Cargo 5 | to build the program in debug mode and then launch `gdbgui` to 6 | debug the program. 7 | -------------------------------------------------------------------------------- /examples/rust/compile_and_debug.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GDBGUI=../../gdbgui/backend.py 4 | 5 | cargo build && $GDBGUI ./target/debug/hello 6 | -------------------------------------------------------------------------------- /examples/rust/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | // This function borrows a slice 4 | fn analyze_slice(slice: &[i32]) { 5 | println!("first element of the slice: {}", slice[0]); 6 | println!("the slice has {} elements", slice.len()); 7 | } 8 | 9 | fn main() { 10 | println!("Hello World!"); 11 | // Fixed-size array (type signature is superfluous) 12 | let xs: [i32; 5] = [1, 2, 3, 4, 5]; 13 | 14 | // All elements can be initialized to the same value 15 | let ys: [i32; 9] = [0; 9]; 16 | 17 | // Indexing starts at 0 18 | println!("first element of the array: {}", xs[0]); 19 | println!("second element of the array: {}", xs[1]); 20 | 21 | // `len` returns the size of the array 22 | println!("array size: {}", xs.len()); 23 | 24 | // Arrays are stack allocated 25 | println!("array occupies {} bytes", mem::size_of_val(&xs)); 26 | 27 | // Arrays can be automatically borrowed as slices 28 | println!("borrow the whole array as a slice"); 29 | analyze_slice(&xs); 30 | 31 | // Slices can point to a section of an array 32 | println!("borrow a section of the array as a slice"); 33 | analyze_slice(&ys[1 .. 4]); 34 | } 35 | -------------------------------------------------------------------------------- /gdbgui/SSLify.py: -------------------------------------------------------------------------------- 1 | """Module to enable SSL for Flask application. Adapted from 2 | https://github.com/kennethreitz/flask-sslify 3 | """ 4 | 5 | import os 6 | import ssl 7 | 8 | from flask import redirect, request 9 | 10 | YEAR_IN_SECS = 31536000 11 | 12 | 13 | class SSLify(object): 14 | """Secures your Flask App.""" 15 | 16 | def __init__( 17 | self, app=None, age=YEAR_IN_SECS, subdomains=False, permanent=False, skips=None 18 | ): 19 | self.app = app 20 | self.hsts_age = age 21 | 22 | self.hsts_include_subdomains = subdomains 23 | self.permanent = permanent 24 | self.skip_list = skips 25 | 26 | if app is not None: 27 | self.init_app(app) 28 | 29 | def init_app(self, app): 30 | """Configures the specified Flask app to enforce SSL.""" 31 | app.config.setdefault("SSLIFY_SUBDOMAINS", False) 32 | app.config.setdefault("SSLIFY_PERMANENT", False) 33 | app.config.setdefault("SSLIFY_SKIPS", None) 34 | 35 | self.hsts_include_subdomains = ( 36 | self.hsts_include_subdomains or app.config["SSLIFY_SUBDOMAINS"] 37 | ) 38 | self.permanent = self.permanent or self.app.config["SSLIFY_PERMANENT"] 39 | self.skip_list = self.skip_list or self.app.config["SSLIFY_SKIPS"] 40 | 41 | app.before_request(self.redirect_to_ssl) 42 | app.after_request(self.set_hsts_header) 43 | 44 | @property 45 | def hsts_header(self): 46 | """Returns the proper HSTS policy.""" 47 | hsts_policy = "max-age={0}".format(self.hsts_age) 48 | 49 | if self.hsts_include_subdomains: 50 | hsts_policy += "; includeSubDomains" 51 | 52 | return hsts_policy 53 | 54 | @property 55 | def skip(self): 56 | """Checks the skip list.""" 57 | # Should we skip? 58 | if self.skip_list and isinstance(self.skip_list, list): 59 | for skip in self.skip_list: 60 | if request.path.startswith("/{0}".format(skip)): 61 | return True 62 | return False 63 | 64 | def redirect_to_ssl(self): 65 | """Redirect incoming requests to HTTPS.""" 66 | # Should we redirect? 67 | criteria = [ 68 | request.is_secure, 69 | self.app.debug, 70 | self.app.testing, 71 | request.headers.get("X-Forwarded-Proto", "http") == "https", 72 | ] 73 | 74 | if not any(criteria) and not self.skip: 75 | if request.url.startswith("http://"): 76 | url = request.url.replace("http://", "https://", 1) 77 | code = 302 78 | if self.permanent: 79 | code = 301 80 | r = redirect(url, code=code) 81 | return r 82 | 83 | def set_hsts_header(self, response): 84 | """Adds HSTS header to each response.""" 85 | # Should we add STS header? 86 | if request.is_secure and not self.skip: 87 | response.headers.setdefault("Strict-Transport-Security", self.hsts_header) 88 | return response 89 | 90 | 91 | def get_ssl_context(private_key, certificate): 92 | """Get ssl context from private key and certificate paths. 93 | The return value is used when calling Flask. 94 | i.e. app.run(ssl_context=get_ssl_context(,,,)) 95 | """ 96 | if ( 97 | certificate 98 | and os.path.isfile(certificate) 99 | and private_key 100 | and os.path.isfile(private_key) 101 | ): 102 | context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) 103 | context.load_cert_chain(certificate, private_key) 104 | return context 105 | return None 106 | -------------------------------------------------------------------------------- /gdbgui/VERSION.txt: -------------------------------------------------------------------------------- 1 | 0.15.2.0 2 | -------------------------------------------------------------------------------- /gdbgui/__init__.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import sys 4 | 5 | _base_dir = getattr(sys, "_MEIPASS", os.path.dirname(os.path.realpath(__file__))) 6 | _version = ( 7 | io.open(os.path.join(_base_dir, "VERSION.txt"), "r", encoding="utf-8") 8 | .read() 9 | .strip() 10 | ) 11 | 12 | __title__ = "gdbgui" 13 | __version__ = _version 14 | __author__ = "Chad Smith" 15 | __copyright__ = "Copyright Chad Smith" 16 | -------------------------------------------------------------------------------- /gdbgui/__main__.py: -------------------------------------------------------------------------------- 1 | from . import cli 2 | 3 | cli.main() 4 | -------------------------------------------------------------------------------- /gdbgui/htmllistformatter.py: -------------------------------------------------------------------------------- 1 | from pygments.formatters import HtmlFormatter # type: ignore 2 | 3 | 4 | class HtmlListFormatter(HtmlFormatter): 5 | """A custom pygments class to format html. Returns a list of source code. 6 | Each element of the list corresponds to a line of (marked up) source code. 7 | """ 8 | 9 | def get_marked_up_list(self, tokensource): 10 | """an updated version of pygments.formatter.format_unencoded""" 11 | source = self._format_lines(tokensource) 12 | if self.hl_lines: 13 | source = self._highlight_lines(source) 14 | if not self.nowrap: 15 | if self.linenos == 2: 16 | source = self._wrap_inlinelinenos(source) 17 | if self.lineanchors: 18 | source = self._wrap_lineanchors(source) 19 | if self.linespans: 20 | source = self._wrap_linespans(source) 21 | if self.linenos == 1: 22 | source = self._wrap_tablelinenos(source) 23 | # instead of this: 24 | # for t, piece in source: 25 | # outfile.write(piece) 26 | # evaluate the generator to a list of just source code: 27 | IS_CODE_INDEX = 0 28 | HTML_VALUE_INDEX = 1 29 | IS_CODE_VAL = 1 30 | source_list = [ 31 | html_line[HTML_VALUE_INDEX] 32 | for html_line in self._wrap_div(self._wrap_pre(source)) 33 | if html_line[IS_CODE_INDEX] == IS_CODE_VAL 34 | ] 35 | return source_list 36 | -------------------------------------------------------------------------------- /gdbgui/py.typed: -------------------------------------------------------------------------------- 1 | # Marker file for PEP 561. This package uses inline types. 2 | # https://mypy.readthedocs.io/en/latest/installed_packages.html#making-pep-561-compatible-packages -------------------------------------------------------------------------------- /gdbgui/server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/server/__init__.py -------------------------------------------------------------------------------- /gdbgui/server/constants.py: -------------------------------------------------------------------------------- 1 | import os 2 | import signal 3 | import sys 4 | from pathlib import Path 5 | 6 | DEFAULT_GDB_EXECUTABLE = "gdb" 7 | DEFAULT_HOST = "127.0.0.1" 8 | DEFAULT_PORT = 5000 9 | USING_WINDOWS = os.name == "nt" 10 | IS_A_TTY = sys.stdout.isatty() 11 | pyinstaller_base_dir = getattr(sys, "_MEIPASS", None) 12 | using_pyinstaller = pyinstaller_base_dir is not None 13 | if using_pyinstaller: 14 | BASE_PATH = Path(pyinstaller_base_dir or "") 15 | else: 16 | BASE_PATH = Path(os.path.realpath(__file__)).parent.parent 17 | PARENTDIR = BASE_PATH.parent 18 | sys.path.append(str(PARENTDIR)) 19 | 20 | TEMPLATE_DIR = BASE_PATH / "templates" 21 | STATIC_DIR = BASE_PATH / "static" 22 | 23 | 24 | def colorize(text): 25 | if IS_A_TTY and not USING_WINDOWS: 26 | return "\033[1;32m" + text + "\x1b[0m" 27 | 28 | else: 29 | return text 30 | 31 | 32 | # create dictionary of signal names 33 | SIGNAL_NAME_TO_OBJ = {} 34 | for n in dir(signal): 35 | if n.startswith("SIG") and "_" not in n: 36 | SIGNAL_NAME_TO_OBJ[n.upper()] = getattr(signal, n) 37 | -------------------------------------------------------------------------------- /gdbgui/server/http_util.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import logging 3 | import os 4 | from functools import wraps 5 | 6 | from flask import Response, abort, current_app, jsonify, request, session 7 | 8 | logger = logging.getLogger(__file__) 9 | 10 | 11 | def add_csrf_token_to_session(): 12 | if "csrf_token" not in session: 13 | session["csrf_token"] = binascii.hexlify(os.urandom(20)).decode("utf-8") 14 | 15 | 16 | def is_cross_origin(request): 17 | """Compare headers HOST and ORIGIN. Remove protocol prefix from ORIGIN, then 18 | compare. Return true if they are not equal 19 | example HTTP_HOST: '127.0.0.1:5000' 20 | example HTTP_ORIGIN: 'http://127.0.0.1:5000' 21 | """ 22 | origin = request.environ.get("HTTP_ORIGIN") 23 | host = request.environ.get("HTTP_HOST") 24 | if origin is None: 25 | # origin is sometimes omitted by the browser when origin and host are equal 26 | return False 27 | 28 | if origin.startswith("http://"): 29 | origin = origin.replace("http://", "") 30 | elif origin.startswith("https://"): 31 | origin = origin.replace("https://", "") 32 | return host != origin 33 | 34 | 35 | def csrf_protect(f): 36 | """A decorator to add csrf protection by validing the X_CSRFTOKEN 37 | field in request header""" 38 | 39 | @wraps(f) 40 | def wrapper(*args, **kwargs): 41 | token = session.get("csrf_token", None) 42 | if token is None or token != request.environ.get("HTTP_X_CSRFTOKEN"): 43 | logger.warning("Received invalid csrf token. Aborting") 44 | abort(403) 45 | # call original request handler 46 | return f(*args, **kwargs) 47 | 48 | return wrapper 49 | 50 | 51 | def client_error(obj): 52 | return jsonify(obj), 400 53 | 54 | 55 | def authenticate(f): 56 | @wraps(f) 57 | def wrapper(*args, **kwargs): 58 | if current_app.config.get("gdbgui_auth_user_credentials") is not None: 59 | auth = request.authorization 60 | if ( 61 | not auth 62 | or not auth.username 63 | or not auth.password 64 | or not credentials_are_valid(auth.username, auth.password) 65 | ): 66 | return Response( 67 | "You must log in to continue.", 68 | 401, 69 | {"WWW-Authenticate": 'Basic realm="gdbgui_login"'}, 70 | ) 71 | 72 | return f(*args, **kwargs) 73 | 74 | return wrapper 75 | 76 | 77 | def credentials_are_valid(username, password): 78 | user_credentials = current_app.config.get("gdbgui_auth_user_credentials") 79 | if user_credentials is None: 80 | return False 81 | 82 | elif len(user_credentials) < 2: 83 | return False 84 | 85 | return user_credentials[0] == username and user_credentials[1] == password 86 | -------------------------------------------------------------------------------- /gdbgui/server/ptylib.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | USING_WINDOWS = os.name == "nt" 4 | if USING_WINDOWS: 5 | raise RuntimeError( 6 | "Windows is not supported at this time. " 7 | + "Versions lower than 0.14.x. are Windows compatible." 8 | ) 9 | import fcntl 10 | import pty 11 | import select 12 | import shlex 13 | import signal 14 | import struct 15 | import termios 16 | from typing import Optional 17 | 18 | 19 | class Pty: 20 | max_read_bytes = 1024 * 20 21 | 22 | def __init__(self, *, cmd: Optional[str] = None, echo: bool = True): 23 | if cmd: 24 | (child_pid, fd) = pty.fork() 25 | if child_pid == 0: 26 | # this is the child process fork. 27 | # anything printed here will show up in the pty, including the output 28 | # of this subprocess 29 | def sigint_handler(_sig, _frame): 30 | # prevent SIGINT (ctrl+c) from exiting 31 | # the whole program 32 | pass 33 | 34 | signal.signal(signal.SIGINT, sigint_handler) 35 | args = shlex.split(cmd) 36 | os.execvp(args[0], args) 37 | 38 | else: 39 | # this is the parent process fork. 40 | # store child fd and pid 41 | self.stdin = fd 42 | self.stdout = fd 43 | self.pid = child_pid 44 | else: 45 | (master, slave) = pty.openpty() 46 | self.stdin = master 47 | self.stdout = master 48 | self.name = os.ttyname(slave) 49 | self.set_echo(echo) 50 | 51 | def set_echo(self, echo_on: bool) -> None: 52 | (iflag, oflag, cflag, lflag, ispeed, ospeed, cc) = termios.tcgetattr(self.stdin) 53 | if echo_on: 54 | lflag = lflag & termios.ECHO # type: ignore 55 | else: 56 | lflag = lflag & ~termios.ECHO # type: ignore 57 | termios.tcsetattr( 58 | self.stdin, 59 | termios.TCSANOW, 60 | [iflag, oflag, cflag, lflag, ispeed, ospeed, cc], 61 | ) 62 | 63 | def set_winsize(self, rows: int, cols: int): 64 | xpix = 0 65 | ypix = 0 66 | winsize = struct.pack("HHHH", rows, cols, xpix, ypix) 67 | if self.stdin is None: 68 | raise RuntimeError("fd stdin not assigned") 69 | fcntl.ioctl(self.stdin, termios.TIOCSWINSZ, winsize) 70 | 71 | def read(self) -> Optional[str]: 72 | if self.stdout is None: 73 | return "done" 74 | timeout_sec = 0 75 | (data_to_read, _, _) = select.select([self.stdout], [], [], timeout_sec) 76 | if data_to_read: 77 | try: 78 | response = os.read(self.stdout, self.max_read_bytes).decode() 79 | except (OSError, UnicodeDecodeError): 80 | return None 81 | return response 82 | return None 83 | 84 | def write(self, data: str): 85 | edata = data.encode() 86 | os.write(self.stdin, edata) 87 | -------------------------------------------------------------------------------- /gdbgui/server/server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import webbrowser 4 | 5 | from .constants import DEFAULT_HOST, DEFAULT_PORT, colorize 6 | 7 | try: 8 | from gdbgui.SSLify import SSLify, get_ssl_context # noqa 9 | except ImportError: 10 | print("Warning: Optional SSL support is not available") 11 | 12 | def get_ssl_context(private_key, certificate): # noqa 13 | return None 14 | 15 | 16 | def get_extra_files(): 17 | """returns a list of files that should be watched by the Flask server 18 | when in debug mode to trigger a reload of the server 19 | """ 20 | FILES_TO_SKIP = ["src/gdbgui.js"] 21 | THIS_DIR = os.path.dirname(os.path.abspath(__file__)) 22 | extra_dirs = [THIS_DIR] 23 | extra_files = [] 24 | for extra_dir in extra_dirs: 25 | for dirname, _, files in os.walk(extra_dir): 26 | for filename in files: 27 | filepath = os.path.join(dirname, filename) 28 | if os.path.isfile(filepath) and filepath not in extra_files: 29 | for skipfile in FILES_TO_SKIP: 30 | if skipfile not in filepath: 31 | extra_files.append(filepath) 32 | return extra_files 33 | 34 | 35 | def run_server( 36 | *, 37 | app=None, 38 | socketio=None, 39 | host=DEFAULT_HOST, 40 | port=DEFAULT_PORT, 41 | debug=False, 42 | open_browser=True, 43 | browsername=None, 44 | testing=False, 45 | private_key=None, 46 | certificate=None, 47 | ): 48 | """Run the server of the gdb gui""" 49 | 50 | kwargs = {} 51 | ssl_context = get_ssl_context(private_key, certificate) 52 | if ssl_context: 53 | # got valid ssl context 54 | # force everything through https 55 | SSLify(app) 56 | # pass ssl_context to flask 57 | kwargs["ssl_context"] = ssl_context 58 | 59 | url = "%s:%s" % (host, port) 60 | if kwargs.get("ssl_context"): 61 | protocol = "https://" 62 | url_with_prefix = "https://" + url 63 | else: 64 | protocol = "http://" 65 | url_with_prefix = "http://" + url 66 | 67 | socketio.server_options["allow_upgrades"] = False 68 | socketio.init_app(app) 69 | 70 | if testing is False: 71 | if host == DEFAULT_HOST: 72 | url = (DEFAULT_HOST, port) 73 | else: 74 | try: 75 | url = (socket.gethostbyname(socket.gethostname()), port) 76 | except Exception: 77 | url = (host, port) 78 | 79 | if open_browser is True and debug is False: 80 | browsertext = repr(browsername) if browsername else "default browser" 81 | args = (browsertext,) + url 82 | text = ("Opening gdbgui with %s at " + protocol + "%s:%d") % args 83 | print(colorize(text)) 84 | b = webbrowser.get(browsername) if browsername else webbrowser 85 | b.open(url_with_prefix) 86 | else: 87 | print(colorize(f"View gdbgui at {protocol}{url[0]}:{url[1]}")) 88 | print( 89 | colorize(f"View gdbgui dashboard at {protocol}{url[0]}:{url[1]}/dashboard") 90 | ) 91 | 92 | print("exit gdbgui by pressing CTRL+C") 93 | try: 94 | socketio.run( 95 | app, 96 | debug=debug, 97 | port=int(port), 98 | host=host, 99 | extra_files=get_extra_files(), 100 | **kwargs, 101 | ) 102 | except KeyboardInterrupt: 103 | # Process was interrupted by ctrl+c on keyboard, show message 104 | pass 105 | -------------------------------------------------------------------------------- /gdbgui/src/js/ControlButtons.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Actions from "./Actions"; 4 | import GdbApi from "./GdbApi"; 5 | import { store } from "statorgfc"; 6 | 7 | type State = any; 8 | 9 | class ControlButtons extends React.Component<{}, State> { 10 | constructor() { 11 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 12 | super(); 13 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 14 | store.connectComponentState(this, ["gdb_pid", "reverse_supported"]); 15 | } 16 | render() { 17 | let btn_class = "btn btn-default btn-sm"; 18 | 19 | return ( 20 | 21 | 30 | 31 | 43 | 44 | 52 | 53 | 65 | 66 | 78 | 79 | 88 |
89 | 101 | 113 |
114 |
115 | ); 116 | } 117 | } 118 | 119 | export default ControlButtons; 120 | -------------------------------------------------------------------------------- /gdbgui/src/js/CopyToClipboard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import ToolTip from "./ToolTip"; 3 | import { store } from "statorgfc"; 4 | 5 | type Props = { 6 | content: string | null; 7 | }; 8 | 9 | class CopyToClipboard extends React.Component { 10 | node: HTMLSpanElement | null = null; 11 | render() { 12 | if (!this.props.content) { 13 | return null; 14 | } 15 | return ( 16 | (this.node = node)} 20 | onMouseOver={() => { 21 | ToolTip.show_tooltip_on_node("copy to clipboard", this.node); 22 | }} 23 | onMouseLeave={ToolTip.hide_tooltip} 24 | onClick={() => { 25 | try { 26 | let textarea = store.get("textarea_to_copy_to_clipboard"); 27 | textarea.value = this.props.content; 28 | textarea.select(); 29 | if (document.execCommand("copy") === true) { 30 | ToolTip.show_copied_tooltip_on_node(this.node); 31 | } else { 32 | ToolTip.show_tooltip_on_node("unable to copy", this.node); 33 | } 34 | } catch (err) { 35 | ToolTip.show_tooltip_on_node("unable to copy", this.node); 36 | } 37 | }} 38 | /> 39 | ); 40 | } 41 | } 42 | 43 | export default CopyToClipboard; 44 | -------------------------------------------------------------------------------- /gdbgui/src/js/Expressions.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { store } from "statorgfc"; 3 | import GdbVariable from "./GdbVariable"; 4 | import constants from "./constants"; 5 | 6 | class Expressions extends React.Component { 7 | objs_to_delete: any; 8 | objs_to_render: any; 9 | constructor() { 10 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 11 | super(); 12 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 13 | store.connectComponentState(this, ["expressions"]); 14 | } 15 | 16 | render() { 17 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name '_'. 18 | let sorted_expression_objs = _.sortBy( 19 | store.get("expressions"), 20 | (unsorted_obj: any) => unsorted_obj.expression 21 | ); 22 | // only render variables in scope that were not created for the Locals component 23 | this.objs_to_render = sorted_expression_objs.filter( 24 | (obj: any) => obj.in_scope === "true" && obj.expr_type === "expr" 25 | ); 26 | this.objs_to_delete = sorted_expression_objs.filter( 27 | (obj: any) => obj.in_scope === "invalid" 28 | ); 29 | 30 | // delete invalid objects 31 | this.objs_to_delete.map((obj: any) => GdbVariable.delete_gdb_variable(obj.name)); 32 | 33 | let content = this.objs_to_render.map((obj: any) => ( 34 | 41 | )); 42 | if (content.length === 0) { 43 | content.push( 44 | 45 | no expressions in this context 46 | 47 | ); 48 | } 49 | content.push( 50 |
51 | ); 52 | 53 | return ( 54 |
55 | 68 | 69 |

70 | 71 | {content} 72 |

73 | ); 74 | } 75 | componentDidUpdate() { 76 | for (let obj of this.objs_to_render) { 77 | GdbVariable.plot_var_and_children(obj); 78 | } 79 | } 80 | 81 | static keydown_on_input(e: any) { 82 | if (e.keyCode === constants.ENTER_BUTTON_NUM) { 83 | let expr = e.currentTarget.value, 84 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name '_'. 85 | trimmed_expr = _.trim(expr); 86 | 87 | if (trimmed_expr !== "") { 88 | GdbVariable.create_variable(trimmed_expr, "expr"); 89 | } 90 | } 91 | } 92 | } 93 | 94 | export default Expressions; 95 | -------------------------------------------------------------------------------- /gdbgui/src/js/FileSystem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class FileSystem extends React.Component { 4 | nodecount: any; 5 | get_node_jsx(node: any, depth = 0) { 6 | if (!node) { 7 | return null; 8 | } 9 | this.nodecount++; 10 | 11 | let get_child_jsx_for_node = (node: any) => { 12 | if (!(node.children && node.toggled)) { 13 | return null; 14 | } 15 | return ( 16 |
    {node.children.map((child: any) => this.get_node_jsx(child, depth + 1))}
17 | ); 18 | }; 19 | let indent = "\u00A0\u00A0\u00A0".repeat(depth), 20 | glyph = null; 21 | let is_file = !node.children, 22 | is_dir = !is_file; 23 | if (is_dir) { 24 | glyph = node.toggled ? "glyphicon-chevron-down" : "glyphicon-chevron-right"; 25 | } 26 | 27 | let onClickName = null; 28 | if (is_file) { 29 | onClickName = () => { 30 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'onClickName' does not exist on type 'Rea... Remove this comment to see the full error message 31 | this.props.onClickName(node); 32 | }; 33 | } 34 | 35 | return ( 36 | 37 |
  • 38 | {indent} 39 | { 42 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'onToggle' does not exist on type 'Readon... Remove this comment to see the full error message 43 | this.props.onToggle(node); 44 | }} 45 | /> 46 | {/* @ts-expect-error ts-migrate(2322) FIXME: Type 'null' is not assignable to type '((event: Mo... Remove this comment to see the full error message */} 47 | {node.name} 48 |
  • 49 | {get_child_jsx_for_node(node)} 50 |
    51 | ); 52 | } 53 | 54 | render() { 55 | this.nodecount = -1; 56 | return ( 57 |
    58 | {/* @ts-expect-error ts-migrate(2339) FIXME: Property 'rootnode' does not exist on type 'Readon... Remove this comment to see the full error message */} 59 |
      {this.get_node_jsx(this.props.rootnode)}
    60 |
    61 | ); 62 | } 63 | } 64 | 65 | export default FileSystem; 66 | -------------------------------------------------------------------------------- /gdbgui/src/js/GdbMiOutput.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * A component to display, in gory detail, what is 3 | * returned from gdb's machine interface. This displays the 4 | * data source that is fed to all components and UI elements 5 | * in gdb gui, and is useful when debugging gdbgui, or 6 | * a command that failed but didn't have a useful failure 7 | * message in gdbgui. 8 | */ 9 | import React from "react"; 10 | import { store } from "statorgfc"; 11 | 12 | type State = any; 13 | 14 | class GdbMiOutput extends React.Component<{}, State> { 15 | static MAX_OUTPUT_ENTRIES = 500; 16 | _debounced_scroll_to_bottom: any; 17 | el: any; 18 | constructor() { 19 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 20 | super(); 21 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 22 | store.connectComponentState(this, ["gdb_mi_output"]); 23 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name '_'. 24 | this._debounced_scroll_to_bottom = _.debounce( 25 | this._scroll_to_bottom.bind(this), 26 | 300, 27 | { 28 | leading: true 29 | } 30 | ); 31 | } 32 | render() { 33 | return ( 34 |
    35 | 43 |
    44 | {this.state.gdb_mi_output} 45 |
    46 |
    47 | ); 48 | } 49 | componentDidMount() { 50 | this.el = document.getElementById("gdb_mi_output"); 51 | } 52 | componentDidUpdate() { 53 | this._debounced_scroll_to_bottom(); 54 | } 55 | _scroll_to_bottom() { 56 | this.el.scrollTop = this.el.scrollHeight; 57 | } 58 | static add_mi_output(mi_obj: any) { 59 | let new_str = JSON.stringify(mi_obj, null, 4) 60 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. 61 | .replace(/[^(\\)]\\n/g) 62 | .replace("<", "<") 63 | .replace(">", ">"), 64 | gdb_mi_output = store.get("gdb_mi_output"); 65 | 66 | while (gdb_mi_output.length > GdbMiOutput.MAX_OUTPUT_ENTRIES) { 67 | gdb_mi_output.shift(); 68 | } 69 | gdb_mi_output.push(new_str); 70 | 71 | store.set("gdb_mi_output", gdb_mi_output); 72 | } 73 | } 74 | 75 | export default GdbMiOutput; 76 | -------------------------------------------------------------------------------- /gdbgui/src/js/GdbguiModal.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Actions from "./Actions"; 3 | import { store } from "statorgfc"; 4 | 5 | type State = any; 6 | 7 | class Modal extends React.Component<{}, State> { 8 | fullscreen_node: any; 9 | constructor() { 10 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 11 | super(); 12 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 13 | store.connectComponentState(this, ["show_modal", "modal_body", "modal_header"]); 14 | } 15 | render() { 16 | return ( 17 |
    (this.fullscreen_node = el)} 20 | onClick={e => { 21 | if (e.target === this.fullscreen_node) { 22 | Actions.toggle_modal_visibility(); 23 | } 24 | }} 25 | > 26 |
    27 |
    28 | 35 |
    36 | 37 |

    {this.state.modal_header}

    38 | 39 |
    {this.state.modal_body}
    40 | 41 | 49 |
    50 |
    51 |
    52 | ); 53 | } 54 | } 55 | 56 | export default Modal; 57 | -------------------------------------------------------------------------------- /gdbgui/src/js/GlobalEvents.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Setup global DOM events 3 | */ 4 | 5 | import constants from "./constants"; 6 | import GdbApi from "./GdbApi"; 7 | import { store } from "statorgfc"; 8 | 9 | const GlobalEvents = { 10 | init: function() { 11 | window.onkeydown = function(e: any) { 12 | if (e.keyCode === constants.ENTER_BUTTON_NUM) { 13 | // when pressing enter in an input, don't redirect entire page! 14 | e.preventDefault(); 15 | } 16 | }; 17 | $("body").on("keydown", GlobalEvents.body_keydown); 18 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'tooltip' does not exist on type 'JQuery<... Remove this comment to see the full error message 19 | $('[data-toggle="tooltip"]').tooltip(); 20 | 21 | window.onbeforeunload = () => 22 | "text here makes dialog appear when exiting. Set function to back to null for nomal behavior."; 23 | }, 24 | /** 25 | * keyboard shortcuts to interact with gdb. 26 | * enabled only when key is depressed on a target that is NOT an input. 27 | */ 28 | body_keydown: function(e: any) { 29 | let modifier = e.altKey || e.ctrlKey || e.metaKey; 30 | 31 | if (e.target.nodeName !== "INPUT" && !modifier) { 32 | let char = String.fromCharCode(e.keyCode).toLowerCase(); 33 | if (e.keyCode === constants.DOWN_BUTTON_NUM || char === "s") { 34 | GdbApi.click_step_button(); 35 | } else if (e.keyCode === constants.RIGHT_BUTTON_NUM) { 36 | GdbApi.click_next_button(); 37 | } else if (char === "n") { 38 | GdbApi.click_next_button(e.shiftKey); 39 | } else if (char === "c") { 40 | GdbApi.click_continue_button(e.shiftKey); 41 | } else if (e.keyCode === constants.UP_BUTTON_NUM || char === "u") { 42 | GdbApi.click_return_button(); 43 | } else if (char === "r") { 44 | GdbApi.click_run_button(); 45 | } else if (char === "m") { 46 | GdbApi.click_next_instruction_button(e.shiftKey); 47 | } else if (e.keyCode === constants.COMMA_BUTTON_NUM) { 48 | GdbApi.click_step_instruction_button(e.shiftKey); 49 | } else if ( 50 | e.keyCode === constants.LEFT_BUTTON_NUM && 51 | store.get("reverse_supported") 52 | ) { 53 | GdbApi.click_next_button(true); 54 | } 55 | } 56 | } 57 | }; 58 | 59 | export default GlobalEvents; 60 | -------------------------------------------------------------------------------- /gdbgui/src/js/HoverVar.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * A component to show/hide variable exploration when hovering over a variable 3 | * in the source code 4 | */ 5 | 6 | import React from "react"; 7 | import { store } from "statorgfc"; 8 | import constants from "./constants"; 9 | import GdbVariable from "./GdbVariable"; 10 | 11 | class HoverVar extends React.Component { 12 | static enter_timeout = undefined; // debounce fetching the expression 13 | static exit_timeout = undefined; // debounce removing the box 14 | static left = 0; 15 | static top = 0; 16 | 17 | obj: any; 18 | 19 | constructor() { 20 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 21 | super(); 22 | 23 | // when hovering over a potential variable 24 | $("body").on("mouseover", "#code_table span.n", HoverVar.mouseover_variable); 25 | $("body").on("mouseleave", "#code_table span.n", HoverVar.mouseout_variable); 26 | 27 | $("body").on("mouseover", "#code_table span.nx", HoverVar.mouseover_variable); 28 | $("body").on("mouseleave", "#code_table span.nx", HoverVar.mouseout_variable); 29 | 30 | // when hovering over the hover var "tooltip"-like window 31 | $("body").on("mouseenter", "#hovervar", HoverVar.mouseover_hover_window); 32 | $("body").on("mouseleave", "#hovervar", HoverVar.mouseout_hover_window); 33 | 34 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 35 | store.connectComponentState(this, ["expressions"]); 36 | } 37 | render() { 38 | let hover_objs = store.get("expressions").filter((o: any) => o.expr_type === "hover"), 39 | obj; 40 | if (Array.isArray(hover_objs) && hover_objs.length === 1) { 41 | obj = hover_objs[0]; 42 | } 43 | this.obj = obj; 44 | if (obj) { 45 | let style = { 46 | position: "absolute", 47 | left: HoverVar.left + "px", 48 | top: HoverVar.top + "px", 49 | backgroundColor: "white" 50 | }; 51 | return ( 52 | // @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type '"absolute... Remove this comment to see the full error message 53 |
    54 | 61 |
    62 | ); 63 | } else { 64 | return
    no variable hovered
    ; 65 | } 66 | } 67 | static mouseover_variable(e: any) { 68 | HoverVar.clear_hover_state(); 69 | 70 | let rect = e.target.getBoundingClientRect(), 71 | var_name = e.target.textContent; 72 | 73 | // store coordinates of where the box should be displayed 74 | HoverVar.left = rect.left; 75 | HoverVar.top = rect.bottom; 76 | 77 | const WAIT_TIME_SEC = 0.5; 78 | // @ts-expect-error ts-migrate(2322) FIXME: Type 'Timeout' is not assignable to type 'undefine... Remove this comment to see the full error message 79 | HoverVar.enter_timeout = setTimeout(() => { 80 | if (store.get("inferior_program") === constants.inferior_states.paused) { 81 | let ignore_errors = true; 82 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 3. 83 | GdbVariable.create_variable(var_name, "hover", ignore_errors); 84 | } 85 | }, WAIT_TIME_SEC * 1000); 86 | } 87 | static mouseout_variable(e: any) { 88 | void e; 89 | const WAIT_TIME_SEC = 0.1; 90 | // @ts-expect-error ts-migrate(2322) FIXME: Type 'Timeout' is not assignable to type 'undefine... Remove this comment to see the full error message 91 | HoverVar.exit_timeout = setTimeout(() => { 92 | HoverVar.clear_hover_state(); 93 | }, WAIT_TIME_SEC * 1000); 94 | } 95 | static mouseover_hover_window(e: any) { 96 | void e; 97 | // Mouse went from hovering over variable name in source code to 98 | // hovering over the window showing the contents of the variable. 99 | // Don't remove the window in this case. 100 | clearTimeout(HoverVar.exit_timeout); 101 | } 102 | static mouseout_hover_window(e: any) { 103 | void e; 104 | HoverVar.clear_hover_state(); 105 | } 106 | static clear_hover_state() { 107 | clearTimeout(HoverVar.enter_timeout); 108 | clearTimeout(HoverVar.exit_timeout); 109 | let exprs_objs_to_remove = store 110 | .get("expressions") 111 | .filter((obj: any) => obj.expr_type === "hover"); 112 | exprs_objs_to_remove.map((obj: any) => GdbVariable.delete_gdb_variable(obj.name)); 113 | } 114 | } 115 | 116 | export default HoverVar; 117 | -------------------------------------------------------------------------------- /gdbgui/src/js/InferiorProgramInfo.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Actions from "./Actions"; 4 | import { store } from "statorgfc"; 5 | 6 | type State = any; 7 | 8 | class InferiorProgramInfo extends React.Component<{}, State> { 9 | constructor() { 10 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 11 | super(); 12 | this.get_li_for_signal = this.get_li_for_signal.bind(this); 13 | this.get_dropdown = this.get_dropdown.bind(this); 14 | this.state = { 15 | selected_signal: "SIGINT", 16 | other_pid: "" 17 | }; 18 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 19 | store.connectComponentState(this, ["inferior_pid", "gdb_pid"]); 20 | } 21 | get_li_for_signal(s: any, signal_key: any) { 22 | let onclick = function() { 23 | let obj = {}; 24 | // @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message 25 | obj[signal_key] = s; 26 | // @ts-expect-error ts-migrate(2683) FIXME: 'this' implicitly has type 'any' because it does n... Remove this comment to see the full error message 27 | this.setState(obj); 28 | }.bind(this); 29 | 30 | return ( 31 |
  • 32 | {/* @ts-expect-error ts-migrate(2339) FIXME: Property 'signals' does not exist on type 'Readonl... Remove this comment to see the full error message */} 33 | {`${s} (${this.props.signals[s]})`} 34 |
  • 35 | ); 36 | } 37 | get_signal_choices(signal_key: any) { 38 | let signals = []; 39 | // push SIGINT and SIGKILL to top 40 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'signals' does not exist on type 'Readonl... Remove this comment to see the full error message 41 | for (let s in this.props.signals) { 42 | if (s === "SIGKILL" || s === "SIGINT") { 43 | signals.push(this.get_li_for_signal(s, signal_key)); 44 | } 45 | } 46 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'signals' does not exist on type 'Readonl... Remove this comment to see the full error message 47 | for (let s in this.props.signals) { 48 | if (s !== "SIGKILL" && s !== "SIGINT") { 49 | signals.push(this.get_li_for_signal(s, signal_key)); 50 | } 51 | } 52 | return signals; 53 | } 54 | get_dropdown() { 55 | return ( 56 |
    57 | 67 |
      68 | {this.get_signal_choices("selected_signal")} 69 |
    70 |
    71 | ); 72 | } 73 | render() { 74 | let gdb_button = ( 75 | 87 | ); 88 | 89 | let inferior_button = null; 90 | if (this.state.inferior_pid) { 91 | inferior_button = ( 92 | 102 | ); 103 | } 104 | 105 | let other_input_and_button = ( 106 | 117 | ); 118 | return ( 119 |
    120 | send  121 | {this.get_dropdown()} 122 |  to  123 |
    124 | {gdb_button} 125 | {inferior_button} 126 |
    127 |

    128 | {other_input_and_button} 129 | { 139 | this.setState({ other_pid: e.currentTarget.value }); 140 | }} 141 | value={this.state.other_pid} 142 | /> 143 |

    144 |
    145 | ); // return 146 | } // render 147 | } // component 148 | 149 | export default InferiorProgramInfo; 150 | -------------------------------------------------------------------------------- /gdbgui/src/js/Links.tsx: -------------------------------------------------------------------------------- 1 | import Actions from "./Actions"; 2 | import * as React from "react"; 3 | import CopyToClipboard from "./CopyToClipboard"; 4 | import MemoryLink from "./MemoryLink"; 5 | 6 | type Props = { 7 | file?: string; 8 | fullname?: string; 9 | line: string; 10 | num_lines?: number; 11 | }; 12 | 13 | export class FileLink extends React.Component { 14 | render() { 15 | let line = parseInt(this.props.line); 16 | let onclick = () => {}, 17 | cls = ""; 18 | if (!this.props.file || !line) { 19 | line = 0; 20 | } 21 | let sep = ""; 22 | if (line && line !== 0) { 23 | sep = ":"; 24 | } 25 | if (this.props.fullname) { 26 | onclick = () => Actions.view_file(this.props.fullname, line); 27 | cls = "pointer"; 28 | } 29 | 30 | let clipboard_content = null; 31 | if (this.props.fullname || this.props.file) { 32 | clipboard_content = (this.props.fullname || this.props.file) + sep + line; 33 | } 34 | return ( 35 |
    36 | 42 | {this.props.file} 43 | {sep} 44 | {line > 0 ? line : ""} 45 | 46 | 47 | 48 | {this.props.num_lines ? `(${this.props.num_lines} lines total)` : ""} 49 |
    50 | ); 51 | } 52 | } 53 | 54 | type FrameLinkProps = { 55 | addr: string; 56 | file?: string; 57 | fullname?: string; 58 | line: string; 59 | }; 60 | 61 | export class FrameLink extends React.Component { 62 | render() { 63 | return ( 64 |
    65 | 70 | 71 | 72 |
    73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /gdbgui/src/js/Locals.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * A component to render "local" variables, as well as a few static methods to 3 | * assist in their creation and deletion. 4 | */ 5 | 6 | import React from "react"; 7 | import { store } from "statorgfc"; 8 | import GdbVariable from "./GdbVariable"; 9 | 10 | class Locals extends React.Component { 11 | constructor() { 12 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 13 | super(); 14 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 15 | store.connectComponentState(this, ["expressions", "locals"]); 16 | } 17 | render() { 18 | let content = []; 19 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name '_'. 20 | let sorted_local_objs = _.sortBy( 21 | store.get("locals"), 22 | (unsorted_obj: any) => unsorted_obj.name 23 | ); 24 | 25 | for (let local of sorted_local_objs) { 26 | let obj = this.get_autocreated_obj_from_expr(local.name); 27 | if (obj) { 28 | content.push( 29 | 36 | ); 37 | } else { 38 | content.push( 39 | 46 | ); 47 | } 48 | } 49 | 50 | if (content.length === 0) { 51 | return ( 52 | 53 | no locals in this context 54 | 55 | ); 56 | } else { 57 | return content; 58 | } 59 | } 60 | get_autocreated_obj_from_expr(expr: any) { 61 | for (let obj of store.get("expressions")) { 62 | if (obj.expression === expr && obj.expr_type === "local") { 63 | return obj; 64 | } 65 | } 66 | return null; 67 | } 68 | static clear_autocreated_exprs() { 69 | let exprs_objs_to_remove = store 70 | .get("expressions") 71 | .filter((obj: any) => obj.expr_type === "local"); 72 | exprs_objs_to_remove.map((obj: any) => GdbVariable.delete_gdb_variable(obj.name)); 73 | } 74 | static clear() { 75 | store.set("locals", []); 76 | Locals.clear_autocreated_exprs(); 77 | } 78 | static save_locals(locals: any) { 79 | let locals_with_meta = locals.map((local: any) => { 80 | // add field to local 81 | local.can_be_expanded = Locals.can_local_be_expanded(local) ? true : false; 82 | return local; 83 | }); 84 | store.set("locals", locals_with_meta); 85 | } 86 | static can_local_be_expanded(local: any) { 87 | // gdb returns list of locals. We may want to turn that local into a GdbVariable 88 | // to explore its children 89 | if ("value" in local) { 90 | // local has a value associated with it. It's either a native 91 | // type or a pointer. It's not a complex type like a struct. 92 | if (local.type.indexOf("*") !== -1) { 93 | // make plus if value is a pointer (has asterisk) 94 | // and can therefore be evaluated further by gdb 95 | return true; 96 | } else { 97 | return false; 98 | } 99 | } else { 100 | // is a struct or object that can be evaluated further by gdb 101 | return true; 102 | } 103 | } 104 | } 105 | 106 | export default Locals; 107 | -------------------------------------------------------------------------------- /gdbgui/src/js/MemoryLink.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Memory from "./Memory"; 3 | 4 | type OwnProps = { 5 | addr: string; 6 | style?: React.CSSProperties; 7 | }; 8 | 9 | type Props = OwnProps & typeof MemoryLink.defaultProps; 10 | 11 | class MemoryLink extends React.Component { 12 | render() { 13 | // turn 0x00000000000000 into 0x0 14 | const address_no_leading_zeros = "0x" + parseInt(this.props.addr, 16).toString(16); 15 | return ( 16 | Memory.set_inputs_from_address(address_no_leading_zeros)} 19 | title={`click to explore memory at ${address_no_leading_zeros}`} 20 | style={this.props.style} 21 | > 22 | {address_no_leading_zeros} 23 | 24 | ); 25 | } 26 | static defaultProps = { style: { fontFamily: "monospace" } }; 27 | } 28 | 29 | export default MemoryLink; 30 | -------------------------------------------------------------------------------- /gdbgui/src/js/MiddleLeft.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * The middle left div will be rendered with this content 3 | */ 4 | 5 | import React from "react"; 6 | import SourceCode from "./SourceCode"; 7 | import FileOps from "./FileOps"; 8 | 9 | class MiddleLeft extends React.Component { 10 | fetch_more_at_top_timeout: any; 11 | onscroll_timeout: any; 12 | source_code_container_node: any; 13 | constructor() { 14 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 15 | super(); 16 | this.onscroll_container = this.onscroll_container.bind(this); 17 | this.onscroll_timeout = null; 18 | this.fetch_more_at_top_timeout = null; 19 | } 20 | render() { 21 | return ( 22 |
    (this.source_code_container_node = el)} 26 | > 27 | 28 |
    29 | ); 30 | } 31 | componentDidMount() { 32 | // @ts-expect-error ts-migrate(2322) FIXME: Type 'JQuery' is not assignable to ty... Remove this comment to see the full error message 33 | SourceCode.el_code_container = $("#code_container"); // todo: no jquery 34 | 35 | if (this.source_code_container_node) { 36 | this.source_code_container_node.onscroll = this.onscroll_container.bind(this); 37 | } 38 | } 39 | 40 | onscroll_container() { 41 | clearTimeout(this.onscroll_timeout); 42 | this.onscroll_timeout = setTimeout(this.check_to_autofetch_more_source, 100); 43 | } 44 | 45 | check_to_autofetch_more_source() { 46 | // test if "view more" buttons are visible, and if so, fetch more source 47 | 48 | let fetching_for_top = false; // don't fetch for more at bottom and top at same time 49 | if (SourceCode.view_more_top_node) { 50 | let { is_visible } = SourceCode.is_source_line_visible( 51 | // @ts-expect-error ts-migrate(2769) FIXME: Argument of type 'null' is not assignable to param... Remove this comment to see the full error message 52 | $(SourceCode.view_more_top_node) 53 | ); 54 | if (is_visible) { 55 | fetching_for_top = true; 56 | FileOps.fetch_more_source_at_beginning(); 57 | } 58 | } 59 | 60 | if (!fetching_for_top && SourceCode.view_more_bottom_node) { 61 | let { is_visible } = SourceCode.is_source_line_visible( 62 | // @ts-expect-error ts-migrate(2769) FIXME: Argument of type 'null' is not assignable to param... Remove this comment to see the full error message 63 | $(SourceCode.view_more_bottom_node) 64 | ); 65 | if (is_visible) { 66 | FileOps.fetch_more_source_at_end(); 67 | } 68 | } 69 | } 70 | } 71 | 72 | export default MiddleLeft; 73 | -------------------------------------------------------------------------------- /gdbgui/src/js/ReactTable.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TableRow extends React.Component { 4 | className: any; 5 | get_tds() { 6 | let tds = []; 7 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'data' does not exist on type 'Readonly<{... Remove this comment to see the full error message 8 | for (let i in this.props.data) { 9 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'data' does not exist on type 'Readonly<{... Remove this comment to see the full error message 10 | tds.push({this.props.data[i]}); 11 | } 12 | return tds; 13 | } 14 | 15 | render() { 16 | return {this.get_tds()}; 17 | } 18 | } 19 | 20 | class ReactTable extends React.Component { 21 | static defaultProps = { header: [] }; 22 | render_row(row_data: any, i: any) { 23 | // @ts-expect-error ts-migrate(2769) FIXME: Property 'data' does not exist on type 'IntrinsicA... Remove this comment to see the full error message 24 | return ; 25 | } 26 | 27 | render_head() { 28 | let ths = [], 29 | i = 0; 30 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'header' does not exist on type 'Readonly... Remove this comment to see the full error message 31 | for (let th_data of this.props.header) { 32 | ths.push({th_data}); 33 | i++; 34 | } 35 | return ths; 36 | } 37 | 38 | render() { 39 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'classes' does not exist on type 'Readonl... Remove this comment to see the full error message 40 | let classes = ["table", "table-condensed"].concat(this.props.classes); 41 | return ( 42 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'style' does not exist on type 'Readonly<... Remove this comment to see the full error message 43 | 44 | 45 | {this.render_head()} 46 | 47 | {/* @ts-expect-error ts-migrate(2339) FIXME: Property 'data' does not exist on type 'Readonly<{... Remove this comment to see the full error message */} 48 | {this.props.data.map(this.render_row)} 49 |
    50 | ); 51 | } 52 | } 53 | 54 | export default ReactTable; 55 | -------------------------------------------------------------------------------- /gdbgui/src/js/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { store } from "statorgfc"; 2 | import Actions from "./Actions"; 3 | import ToolTip from "./ToolTip"; 4 | import React from "react"; 5 | 6 | /** 7 | * Settings modal when clicking the gear icon 8 | */ 9 | class Settings extends React.Component { 10 | max_source_file_lines_input: any; 11 | save_button: any; 12 | settings_node: any; 13 | constructor() { 14 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 15 | super(); 16 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 17 | store.connectComponentState(this, [ 18 | "debug", 19 | "current_theme", 20 | "themes", 21 | "gdb_version", 22 | "gdb_pid", 23 | "show_settings", 24 | "auto_add_breakpoint_to_main", 25 | "pretty_print", 26 | "refresh_state_after_sending_console_command", 27 | "show_all_sent_commands_in_console", 28 | "highlight_source_code" 29 | ]); 30 | this.get_update_max_lines_of_code_to_fetch = this.get_update_max_lines_of_code_to_fetch.bind( 31 | this 32 | ); 33 | } 34 | static toggle_key(key: any) { 35 | store.set(key, !store.get(key)); 36 | localStorage.setItem(key, JSON.stringify(store.get(key))); 37 | } 38 | static get_checkbox_row(store_key: any, text: any) { 39 | return ( 40 | 41 | 42 |
    43 | 51 |
    52 | 53 | 54 | ); 55 | } 56 | get_update_max_lines_of_code_to_fetch() { 57 | return ( 58 | 59 | 60 | Maximum number of source file lines to display: 61 | (this.max_source_file_lines_input = el)} 65 | /> 66 | 77 | 78 | 79 | ); 80 | } 81 | get_table() { 82 | return ( 83 | 84 | 85 | {Settings.get_checkbox_row( 86 | "auto_add_breakpoint_to_main", 87 | "Add breakpoint to main after loading executable" 88 | )} 89 | {this.get_update_max_lines_of_code_to_fetch()} 90 | {Settings.get_checkbox_row( 91 | "pretty_print", 92 | "Pretty print dynamic variables (requires restart)" 93 | )} 94 | {Settings.get_checkbox_row( 95 | "refresh_state_after_sending_console_command", 96 | "Refresh all components when a command is sent from the console" 97 | )} 98 | {Settings.get_checkbox_row( 99 | "show_all_sent_commands_in_console", 100 | "Print all sent commands in console, including those sent automatically by gdbgui" 101 | )} 102 | {Settings.get_checkbox_row( 103 | "highlight_source_code", 104 | "Add syntax highlighting to source files" 105 | )} 106 | 107 | 108 | 122 | 123 | 124 |
    109 | Theme:{" "} 110 | 121 |
    125 | ); 126 | } 127 | 128 | render() { 129 | return ( 130 |
    (this.settings_node = el)} 133 | onClick={e => { 134 | if (e.target === this.settings_node) { 135 | Settings.toggle_key("show_settings"); 136 | } 137 | }} 138 | > 139 |
    140 | 143 |

    Settings

    144 | {this.get_table()} 145 |
    146 | 153 |
    154 |
    155 |
    156 | ); 157 | } 158 | } 159 | 160 | export default Settings; 161 | -------------------------------------------------------------------------------- /gdbgui/src/js/SourceCodeHeading.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import constants from "./constants"; 3 | import { store } from "statorgfc"; 4 | import { FileLink } from "./Links"; 5 | import FileOps from "./FileOps"; 6 | 7 | type State = any; 8 | 9 | class SourceCodeHeading extends React.Component<{}, State> { 10 | constructor() { 11 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 12 | super(); 13 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 14 | store.connectComponentState(this, [ 15 | "fullname_to_render", 16 | "paused_on_frame", 17 | "line_of_source_to_flash", 18 | "source_code_selection_state" 19 | ]); 20 | } 21 | render() { 22 | let line; 23 | if ( 24 | this.state.source_code_selection_state === 25 | constants.source_code_selection_states.PAUSED_FRAME && 26 | this.state.paused_on_frame 27 | ) { 28 | line = this.state.paused_on_frame.line; 29 | } else { 30 | line = this.state.line_of_source_to_flash; 31 | } 32 | 33 | let num_lines = 0; 34 | if ( 35 | this.state.fullname_to_render && 36 | FileOps.get_source_file_obj_from_cache(this.state.fullname_to_render) 37 | ) { 38 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1. 39 | num_lines = FileOps.get_num_lines_in_file(this.state.fullname_to_render); 40 | } 41 | return ( 42 | 48 | ); 49 | } 50 | } 51 | 52 | export default SourceCodeHeading; 53 | -------------------------------------------------------------------------------- /gdbgui/src/js/SourceFileAutocomplete.tsx: -------------------------------------------------------------------------------- 1 | import { store } from "statorgfc"; 2 | import constants from "./constants"; 3 | import Actions from "./Actions"; 4 | import Util from "./Util"; 5 | import FileOps from "./FileOps"; 6 | import React from "react"; 7 | 8 | /** 9 | * The autocomplete dropdown of source files is complicated enough 10 | * to have its own component. It uses the awesomeplete library, 11 | * which is really nice: https://leaverou.github.io/awesomplete/ 12 | */ 13 | 14 | const help_text = "Enter file path to view, press enter"; 15 | /* global Awesomplete */ 16 | class SourceFileAutocomplete extends React.Component { 17 | awesomeplete_input: any; 18 | html_input: any; 19 | constructor() { 20 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 21 | super(); 22 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'subscribeToKeys' does not exist on type ... Remove this comment to see the full error message 23 | store.subscribeToKeys(["source_file_paths"], this.store_change_callback.bind(this)); 24 | } 25 | store_change_callback() { 26 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name '_'. 27 | if (!_.isEqual(this.awesomeplete_input._list, store.get("source_file_paths"))) { 28 | this.awesomeplete_input.list = store.get("source_file_paths"); 29 | } 30 | } 31 | render() { 32 | return ( 33 |
    34 | (this.html_input = el)} 43 | style={{ width: "100%" }} 44 | /> 45 | 54 |
    55 | ); 56 | } 57 | keyup_source_file_input(e: any) { 58 | if (e.keyCode === constants.ENTER_BUTTON_NUM) { 59 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name '_'. 60 | let user_input = _.trim(e.currentTarget.value); 61 | 62 | if (user_input.length === 0) { 63 | return; 64 | } 65 | 66 | let fullname, 67 | default_line = 0, 68 | line; 69 | // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number' is not assignable to par... Remove this comment to see the full error message 70 | [fullname, line] = Util.parse_fullname_and_line(user_input, default_line); 71 | FileOps.user_select_file_to_view(fullname, line); 72 | } else if (store.get("source_file_paths").length === 0) { 73 | // source file list has not been fetched yet, so fetch it 74 | Actions.fetch_source_files(); 75 | } 76 | } 77 | onclick_dropdown() { 78 | if (store.get("source_file_paths").length === 0) { 79 | // we have not asked gdb to get the list of source paths yet, or it just doesn't have any. 80 | // request that gdb populate this list. 81 | Actions.fetch_source_files(); 82 | return; 83 | } 84 | 85 | if (this.awesomeplete_input.ul.childNodes.length === 0) { 86 | this.awesomeplete_input.evaluate(); 87 | } else if (this.awesomeplete_input.ul.hasAttribute("hidden")) { 88 | this.awesomeplete_input.open(); 89 | } else { 90 | this.awesomeplete_input.close(); 91 | } 92 | } 93 | componentDidMount() { 94 | // initialize list of source files 95 | // TODO maybe use a pre-built React component for this 96 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name 'Awesomplete'. 97 | this.awesomeplete_input = new Awesomplete("#source_file_input", { 98 | minChars: 0, 99 | maxItems: 10000, 100 | list: [], 101 | // standard sort algorithm (the default Awesomeplete sort is weird) 102 | sort: (a: any, b: any) => { 103 | return a < b ? -1 : 1; 104 | } 105 | }); 106 | 107 | // perform action when an item is selected 108 | this.html_input.addEventListener("awesomplete-selectcomplete", function(e: any) { 109 | let fullname = e.currentTarget.value; 110 | FileOps.user_select_file_to_view(fullname, 1); 111 | }); 112 | } 113 | } 114 | 115 | export default SourceFileAutocomplete; 116 | -------------------------------------------------------------------------------- /gdbgui/src/js/StatusBar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Util from "./Util"; 3 | import { store } from "statorgfc"; 4 | 5 | type State = any; 6 | 7 | /** 8 | * Component to render a status message with optional error/warning label 9 | */ 10 | class StatusBar extends React.Component<{}, State> { 11 | render() { 12 | if (this.state.waiting_for_response) { 13 | return ; 14 | } else { 15 | return ""; 16 | } 17 | } 18 | } 19 | 20 | export default StatusBar; 21 | -------------------------------------------------------------------------------- /gdbgui/src/js/ToolTip.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { store } from "statorgfc"; 3 | 4 | class ToolTip extends React.Component { 5 | timeout: any; 6 | constructor() { 7 | // @ts-expect-error ts-migrate(2554) FIXME: Expected 1-2 arguments, but got 0. 8 | super(); 9 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 10 | store.connectComponentState(this, ["tooltip"]); 11 | this.timeout = null; 12 | } 13 | static hide_tooltip() { 14 | store.set("tooltip", { 15 | hidden: true, 16 | show_for_n_sec: null, 17 | node: null, 18 | content: null 19 | }); 20 | } 21 | static show_tooltip_on_node(content: any, node: any, show_for_n_sec = null) { 22 | store.set("tooltip", { 23 | hidden: false, 24 | show_for_n_sec: show_for_n_sec, 25 | node: node, 26 | content: content 27 | }); 28 | } 29 | static show_copied_tooltip_on_node(node: any) { 30 | // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '1' is not assignable to paramete... Remove this comment to see the full error message 31 | ToolTip.show_tooltip_on_node("copied!", node, 1); 32 | } 33 | render() { 34 | clearTimeout(this.timeout); 35 | const tooltip = store.get("tooltip"); 36 | if (!tooltip.node || tooltip.hidden) { 37 | return null; 38 | } 39 | let rect = tooltip.node.getBoundingClientRect(), 40 | assumed_width_px = 200, 41 | distance_to_right_edge = window.innerWidth - rect.x, 42 | horizontal_buffer = 43 | distance_to_right_edge < assumed_width_px 44 | ? assumed_width_px - distance_to_right_edge 45 | : 0, 46 | left = rect.x - horizontal_buffer + "px", 47 | top = rect.y + tooltip.node.offsetHeight + "px"; 48 | // @ts-expect-error ts-migrate(2304) FIXME: Cannot find name '_'. 49 | if (_.isInteger(tooltip.show_for_n_sec)) { 50 | this.timeout = setTimeout(ToolTip.hide_tooltip, tooltip.show_for_n_sec * 1000); 51 | } 52 | return ( 53 |
    65 | {tooltip.content} 66 |
    67 | ); 68 | } 69 | } 70 | 71 | export default ToolTip; 72 | -------------------------------------------------------------------------------- /gdbgui/src/js/ToolTipTourguide.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Util from "./Util"; 3 | import { store } from "statorgfc"; 4 | 5 | type State = any; 6 | 7 | class ToolTipTourguide extends React.Component<{}, State> { 8 | constructor(props: {}) { 9 | super(props); 10 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'position' does not exist on type '{}'. 11 | if (!props.position && !(props.top && props.left)) { 12 | console.warn("did not receive position"); 13 | } 14 | // @ts-expect-error ts-migrate(2551) FIXME: Property 'ref' does not exist on type 'ToolTipTour... Remove this comment to see the full error message 15 | this.ref = React.createRef(); 16 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'connectComponentState' does not exist on... Remove this comment to see the full error message 17 | store.connectComponentState(this, [ 18 | "tour_guide_step", 19 | "num_tour_guide_steps", 20 | "show_tour_guide" 21 | ]); 22 | } 23 | componentWillMount() { 24 | store.set("num_tour_guide_steps", store.get("num_tour_guide_steps") + 1); 25 | } 26 | static dismiss() { 27 | store.set("show_tour_guide", false); 28 | store.set("tour_guide_step", 0); 29 | Util.persist_value_for_key("show_tour_guide"); 30 | } 31 | static next() { 32 | store.set("tour_guide_step", store.get("tour_guide_step") + 1); 33 | } 34 | guide_finshed() { 35 | store.set("tour_guide_step", 0); 36 | } 37 | static start_guide() { 38 | store.set("tour_guide_step", 0); 39 | store.set("show_tour_guide", true); 40 | Util.persist_value_for_key("show_tour_guide"); 41 | } 42 | componentDidUpdate() { 43 | // @ts-expect-error ts-migrate(2551) FIXME: Property 'ref' does not exist on type 'ToolTipTour... Remove this comment to see the full error message 44 | if (this.state.show_tour_guide && this.ref.current) { 45 | // need to ensure absolute position is respected by setting parent to 46 | // relative 47 | // @ts-expect-error ts-migrate(2551) FIXME: Property 'ref' does not exist on type 'ToolTipTour... Remove this comment to see the full error message 48 | this.ref.current.parentNode.style.position = "relative"; 49 | } 50 | } 51 | get_position(position_name: any) { 52 | let top, left; 53 | switch (position_name) { 54 | case "left": 55 | top = "100%"; 56 | left = "-50%"; 57 | break; 58 | case "right": 59 | top = "50%"; 60 | left = "0px"; 61 | break; 62 | case "bottom": 63 | case "bottomcenter": 64 | top = "100%"; 65 | left = "50%"; 66 | break; 67 | case "bottomleft": 68 | top = "100%"; 69 | left = "0"; 70 | break; 71 | case "topleft": 72 | top = "0"; 73 | left = "0"; 74 | break; 75 | case "overlay": 76 | top = "50%"; 77 | left = "50%"; 78 | break; 79 | default: 80 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'position' does not exist on type 'Readon... Remove this comment to see the full error message 81 | console.warn("invalid position " + this.props.position); 82 | top = "100%"; 83 | left = "50%"; 84 | break; 85 | } 86 | return [top, left]; 87 | } 88 | render() { 89 | if (!this.state.show_tour_guide) { 90 | return null; 91 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'step_num' does not exist on type 'Readon... Remove this comment to see the full error message 92 | } else if (this.props.step_num !== this.state.tour_guide_step) { 93 | return null; 94 | } 95 | 96 | let top, left; 97 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'top' does not exist on type 'Readonly<{}... Remove this comment to see the full error message 98 | if (this.props.top && this.props.left) { 99 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'top' does not exist on type 'Readonly<{}... Remove this comment to see the full error message 100 | top = this.props.top; 101 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'left' does not exist on type 'Readonly<{... Remove this comment to see the full error message 102 | left = this.props.left; 103 | } else { 104 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'position' does not exist on type 'Readon... Remove this comment to see the full error message 105 | [top, left] = this.get_position(this.props.position); 106 | } 107 | 108 | // @ts-expect-error ts-migrate(2339) FIXME: Property 'step_num' does not exist on type 'Readon... Remove this comment to see the full error message 109 | let is_last_step = this.props.step_num + 1 === this.state.num_tour_guide_steps, 110 | dismiss = is_last_step ? null : ( 111 | 112 | Dismiss 113 | 114 | ); 115 | return ( 116 |
    134 | {/* @ts-expect-error ts-migrate(2339) FIXME: Property 'content' does not exist on type 'Readonl... Remove this comment to see the full error message */} 135 | {this.props.content} 136 |

    137 | {/* @ts-expect-error ts-migrate(2339) FIXME: Property 'step_num' does not exist on type 'Readon... Remove this comment to see the full error message */} 138 | {this.props.step_num + 1} of {this.state.num_tour_guide_steps} 139 |

    140 | {dismiss} 141 | 142 | {is_last_step ? "Finish" : "Next"} 143 | 144 |

    145 | ); 146 | } 147 | } 148 | 149 | export default ToolTipTourguide; 150 | -------------------------------------------------------------------------------- /gdbgui/src/js/Util.ts: -------------------------------------------------------------------------------- 1 | import { store } from "statorgfc"; 2 | 3 | /** 4 | * Some general utility methods 5 | */ 6 | const Util = { 7 | persist_value_for_key: function(key: any) { 8 | try { 9 | let value = store.get(key); 10 | localStorage.setItem(key, JSON.stringify(value)); 11 | } catch (err) { 12 | console.error(err); 13 | } 14 | }, 15 | /** 16 | * Get html table 17 | * @param columns: array of strings 18 | * @param data: array of arrays of data 19 | */ 20 | get_table: function(columns: any, data: any, style = "") { 21 | var result = [ 22 | `` 23 | ]; 24 | if (columns) { 25 | result.push(""); 26 | result.push(""); 27 | for (let h of columns) { 28 | result.push(``); 29 | } 30 | result.push(""); 31 | result.push(""); 32 | } 33 | 34 | if (data) { 35 | result.push(""); 36 | for (let row of data) { 37 | result.push(""); 38 | for (let cell of row) { 39 | result.push(``); 40 | } 41 | result.push(""); 42 | } 43 | } 44 | result.push(""); 45 | result.push("
    ${h}
    ${cell}
    "); 46 | return result.join("\n"); 47 | }, 48 | /** 49 | * Escape gdb's output to be browser compatible 50 | * @param s: string to mutate 51 | */ 52 | escape: function(s: any) { 53 | return s 54 | .replace(/>/g, ">") 55 | .replace(/") 57 | .replace(/\\r/g, "") 58 | .replace(/\\"/g, '"') 59 | .replace(/\\t/g, " "); 60 | }, 61 | /** 62 | * take a string of html in JavaScript and strip out the html 63 | * http://stackoverflow.com/a/822486/2893090 64 | */ 65 | get_text_from_html: function(html: any) { 66 | var tmp = document.createElement("DIV"); 67 | tmp.innerHTML = html; 68 | return tmp.textContent || tmp.innerText || ""; 69 | }, 70 | /** 71 | * @param fullname_and_line: i.e. /path/to/file.c:78 72 | * @param default_line_if_not_found: i.e. 0 73 | * @return: Array, with 0'th element == path, 1st element == line 74 | */ 75 | parse_fullname_and_line: function( 76 | fullname_and_line: any, 77 | default_line_if_not_found = undefined 78 | ) { 79 | let user_input_array = fullname_and_line.split(":"), 80 | fullname = user_input_array[0], 81 | line = default_line_if_not_found; 82 | if (user_input_array.length === 2) { 83 | line = user_input_array[1]; 84 | } 85 | // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'undefined' is not assignable to ... Remove this comment to see the full error message 86 | return [fullname, parseInt(line)]; 87 | }, 88 | string_to_array_safe_quotes(str: any) { 89 | let output = [], 90 | cur_str = "", 91 | in_quotes = false; 92 | 93 | for (let i = 0; i < str.length; i++) { 94 | let char = str[i]; 95 | 96 | if (char === '"') { 97 | in_quotes = !in_quotes; 98 | cur_str += char; 99 | } else if (char !== " " || (char === " " && in_quotes)) { 100 | cur_str += char; 101 | } else if (char === " ") { 102 | // got a space outside of quotes 103 | if (cur_str === "") { 104 | // a consecutive space. do nothing. 105 | } else { 106 | // save this argument, and reset cur_str 107 | output.push(cur_str); 108 | cur_str = ""; 109 | } 110 | } 111 | } 112 | if (cur_str !== "") { 113 | output.push(cur_str); 114 | } 115 | return output; 116 | }, 117 | /* Return true is latest is > current 118 | 1.0.0, 0.9.9 -> true 119 | 0.1.0, 0.0.9 -> true 120 | 0.0.9, 0.0.8 -> false 121 | */ 122 | is_newer(latest: any, current: any) { 123 | latest = latest.split("."); 124 | current = current.split("."); 125 | if (latest.length !== current.length) { 126 | return true; 127 | } 128 | for (let i in latest) { 129 | if (latest[i] > current[i]) { 130 | return true; 131 | } 132 | } 133 | return false; 134 | } 135 | }; 136 | 137 | export default Util; 138 | -------------------------------------------------------------------------------- /gdbgui/src/js/constants.ts: -------------------------------------------------------------------------------- 1 | let constants = { 2 | ENTER_BUTTON_NUM: 13, 3 | TAB_BUTTON_NUM: 9, 4 | LEFT_BUTTON_NUM: 37, 5 | UP_BUTTON_NUM: 38, 6 | RIGHT_BUTTON_NUM: 39, 7 | DOWN_BUTTON_NUM: 40, 8 | Y_BUTTON_NUM: 89, 9 | N_BUTTON_NUM: 78, 10 | COMMA_BUTTON_NUM: 188, 11 | DATE_FORMAT: "dddd, MMMM Do YYYY, h:mm:ss a", 12 | IGNORE_ERRORS_TOKEN_STR: "1", 13 | DISASSEMBLY_FOR_MISSING_FILE_STR: "2", 14 | CREATE_VAR_STR: "3", 15 | INLINE_DISASSEMBLY_STR: "4", 16 | 17 | console_entry_type: { 18 | SENT_COMMAND: "SENT_COMMAND", 19 | STD_ERR: "STD_ERR", 20 | STD_OUT: "STD_OUT", 21 | GDBGUI_OUTPUT: "GDBGUI_OUTPUT", 22 | GDBGUI_OUTPUT_RAW: "GDBGUI_OUTPUT_RAW", 23 | AUTOCOMPLETE_OPTION: "AUTOCOMPLETE_OPTION" 24 | }, 25 | 26 | source_code_selection_states: { 27 | USER_SELECTION: "USER_SELECTION", 28 | PAUSED_FRAME: "PAUSED_FRAME" 29 | }, 30 | 31 | source_code_states: { 32 | ASSM_AND_SOURCE_CACHED: "ASSM_AND_SOURCE_CACHED", 33 | SOURCE_CACHED: "SOURCE_CACHED", 34 | FETCHING_SOURCE: "FETCHING_SOURCE", 35 | ASSM_CACHED: "ASSM_CACHED", 36 | FETCHING_ASSM: "FETCHING_ASSM", 37 | ASSM_UNAVAILABLE: "ASSM_UNAVAILABLE", 38 | FILE_MISSING: "FILE_MISSING", 39 | NONE_AVAILABLE: "NONE_AVAILABLE" 40 | }, 41 | 42 | inferior_states: { 43 | unknown: "unknown", 44 | running: "running", 45 | paused: "paused", 46 | exited: "exited" 47 | }, 48 | 49 | tree_component_id: "tree", 50 | 51 | default_max_lines_of_code_to_fetch: 500, 52 | 53 | keys_to_not_log_changes_in_console: ["gdb_mi_output"], 54 | xtermColors: { 55 | reset: "\x1B[0m", 56 | red: "\x1B[31m", 57 | grey: "\x1b[1;30m", 58 | green: "\x1B[0;32m", 59 | lgreen: "\x1B[1;32m", 60 | blue: "\x1B[0;34m", 61 | lblue: "\x1B[1;34m", 62 | yellow: "\x1B[0;33m" 63 | } 64 | }; 65 | 66 | const colorTypeMap = {}; 67 | // @ts-expect-error ts-migrate(7053) FIXME: No index signature with a parameter of type 'strin... Remove this comment to see the full error message 68 | colorTypeMap[constants.console_entry_type.STD_OUT] = constants.xtermColors["reset"]; 69 | // @ts-expect-error ts-migrate(7053) FIXME: No index signature with a parameter of type 'strin... Remove this comment to see the full error message 70 | colorTypeMap[constants.console_entry_type.STD_ERR] = constants.xtermColors["red"]; 71 | // @ts-expect-error ts-migrate(7053) FIXME: No index signature with a parameter of type 'strin... Remove this comment to see the full error message 72 | colorTypeMap[constants.console_entry_type.SENT_COMMAND] = constants.xtermColors["lblue"]; 73 | // @ts-expect-error ts-migrate(7053) FIXME: No index signature with a parameter of type 'strin... Remove this comment to see the full error message 74 | colorTypeMap[constants.console_entry_type.GDBGUI_OUTPUT] = 75 | constants.xtermColors["yellow"]; 76 | // @ts-expect-error ts-migrate(7053) FIXME: No index signature with a parameter of type 'strin... Remove this comment to see the full error message 77 | colorTypeMap[constants.console_entry_type.GDBGUI_OUTPUT_RAW] = 78 | constants.xtermColors["green"]; 79 | 80 | // @ts-expect-error ts-migrate(7053) FIXME: Property 'colorTypeMap' does not exist on type '{ ... Remove this comment to see the full error message 81 | constants["colorTypeMap"] = colorTypeMap; 82 | 83 | // @ts-expect-error ts-migrate(2551) FIXME: Property 'IGNORE_ERRORS_TOKEN_INT' does not exist ... Remove this comment to see the full error message 84 | constants["IGNORE_ERRORS_TOKEN_INT"] = parseInt(constants.IGNORE_ERRORS_TOKEN_STR); 85 | // @ts-expect-error ts-migrate(2551) FIXME: Property 'DISASSEMBLY_FOR_MISSING_FILE_INT' does n... Remove this comment to see the full error message 86 | constants["DISASSEMBLY_FOR_MISSING_FILE_INT"] = parseInt( 87 | constants.DISASSEMBLY_FOR_MISSING_FILE_STR 88 | ); 89 | // @ts-expect-error ts-migrate(2551) FIXME: Property 'CREATE_VAR_INT' does not exist on type '... Remove this comment to see the full error message 90 | constants["CREATE_VAR_INT"] = parseInt(constants.CREATE_VAR_STR); 91 | // @ts-expect-error ts-migrate(2551) FIXME: Property 'INLINE_DISASSEMBLY_INT' does not exist o... Remove this comment to see the full error message 92 | constants["INLINE_DISASSEMBLY_INT"] = parseInt(constants.INLINE_DISASSEMBLY_STR); 93 | 94 | export default Object.freeze(constants); 95 | -------------------------------------------------------------------------------- /gdbgui/src/js/processFeatures.ts: -------------------------------------------------------------------------------- 1 | // https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Support-Commands.html#GDB_002fMI-Support-Commands 2 | 3 | import { store } from "statorgfc"; 4 | type Feature = 5 | | "thread-info" 6 | | "reverse" 7 | | "async" 8 | | "frozen-varobjs" 9 | | "pending-breakpoints" 10 | | "data-read-memory-bytes" 11 | | "python" 12 | | "ada-task-info" 13 | | "language-option" 14 | | "info-gdb-mi-command" 15 | | "undefined-command-error-code" 16 | | "exec-run-start-option" 17 | | "data-disassemble-a-option" 18 | | "breakpoint-notification"; 19 | 20 | export function processFeatures(features: Array) { 21 | if (features.indexOf("reverse") !== -1) { 22 | store.set("reverse_supported", true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gdbgui/src/js/tests/Util.jest.ts: -------------------------------------------------------------------------------- 1 | import Util from "../Util"; 2 | 3 | /* eslint-env jest */ 4 | 5 | test("parses spaces", () => { 6 | const fn = Util.string_to_array_safe_quotes; 7 | expect(fn("hi")).toEqual(["hi"]); 8 | expect(fn('"hi bye"')).toEqual(['"hi bye"']); 9 | expect(fn("hi bye")).toEqual(["hi", "bye"]); 10 | expect(fn('hi bye "1 2, 3" asdf\n\t')).toEqual(["hi", "bye", '"1 2, 3"', "asdf\n\t"]); 11 | expect(fn('"hi bye" "1 2, 3" asdf\n\t')).toEqual(['"hi bye"', '"1 2, 3"', "asdf\n\t"]); 12 | }); 13 | 14 | test("dot version comparison", () => { 15 | expect(Util.is_newer("1.0.0", "1.0.0")).toEqual(false); 16 | expect(Util.is_newer("1.0.0", "0.9.9")).toEqual(true); 17 | expect(Util.is_newer("0.1.0", "0.0.9")).toEqual(true); 18 | expect(Util.is_newer("0.0.8", "0.0.9")).toEqual(false); 19 | expect(Util.is_newer("0.11.3.1", "0.11.3.0")).toEqual(true); 20 | expect(Util.is_newer("0.11.4.0", "0.11.3.0")).toEqual(true); 21 | expect(Util.is_newer("0.11.4.0\n", "0.11.3.0")).toEqual(true); 22 | expect(Util.is_newer("0.11.3.0", "0.11.3.0\n")).toEqual(false); 23 | expect(Util.is_newer("0.11.3.0", "0.11.4.0\n")).toEqual(false); 24 | expect(Util.is_newer("0.12.0.0", "0.11.4.0\n")).toEqual(true); 25 | expect(Util.is_newer("1.0.0", "0.11.4.0\n")).toEqual(true); 26 | expect(Util.is_newer("1.0.1", "1.0.0")).toEqual(true); 27 | }); 28 | -------------------------------------------------------------------------------- /gdbgui/src/js/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module "statorgfc" { 2 | export let store: { 3 | get(key: string): any; 4 | set(key: string, value: any): any; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /gdbgui/static/css/splitjs-gdbgui.css: -------------------------------------------------------------------------------- 1 | /* splitjs is so unopinionated, it doesn't work without specific css classes (float: left) when splitting into two side-by-side 2 | panes ("horizonal" */ 3 | /* splitjs classes for splitting the body of the webpage (https://github.com/nathancahill/Split.js) */ 4 | html, 5 | body, 6 | .splitjs_container { 7 | /* for splitjs, it's important that html also has these properties, not just body */ 8 | height: 100%; 9 | padding: 0; 10 | margin: 0; 11 | background-color: #333; 12 | box-sizing: border-box; 13 | overflow: hidden; 14 | position: relative; 15 | } 16 | 17 | .split { 18 | -webkit-box-sizing: border-box; 19 | -moz-box-sizing: border-box; 20 | box-sizing: border-box; 21 | 22 | overflow-y: hidden; 23 | overflow-x: hidden; 24 | } 25 | 26 | .content { 27 | border: 0px solid #c0c0c0; 28 | box-shadow: inset 0 1px 2px #e4e4e4; 29 | background-color: #fff; 30 | overflow: auto; 31 | float: left; 32 | height: 100%; 33 | width: 100%; 34 | position: relative; 35 | } 36 | 37 | .gutter { 38 | background: lightgrey; 39 | } 40 | .gutter.gutter-horizontal { 41 | cursor: col-resize; 42 | } 43 | 44 | .gutter.gutter-vertical { 45 | cursor: row-resize; 46 | } 47 | 48 | .split.split-horizontal, 49 | .gutter.gutter-horizontal { 50 | height: 100%; 51 | float: left; 52 | } 53 | -------------------------------------------------------------------------------- /gdbgui/static/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | @import "tailwindcss/components"; 3 | @import "tailwindcss/utilities"; 4 | 5 | button:focus { 6 | /* For some reason normalize.css is getting injected into the html page, and it adds a 5px outline 7 | on focused buttons */ 8 | outline-width: 0; 9 | } 10 | -------------------------------------------------------------------------------- /gdbgui/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/static/favicon.ico -------------------------------------------------------------------------------- /gdbgui/static/images/github.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/static/images/github.jpg -------------------------------------------------------------------------------- /gdbgui/static/images/paypal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 14 | 19 | 25 | 31 | 35 | 37 | 38 | 39 | 40 | 45 | 48 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /gdbgui/static/images/ploticon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/static/images/ploticon.png -------------------------------------------------------------------------------- /gdbgui/static/images/ploticon_orig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/static/images/ploticon_orig.png -------------------------------------------------------------------------------- /gdbgui/static/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/static/images/twitter.png -------------------------------------------------------------------------------- /gdbgui/static/vendor/css/gdbgui_awesomeplete.css: -------------------------------------------------------------------------------- 1 | /* This is mostly from the vendor, with a few overrides for gdbgui */ 2 | 3 | .awesomplete [hidden] { 4 | display: none; 5 | } 6 | 7 | .awesomplete .visually-hidden { 8 | position: absolute; 9 | clip: rect(0, 0, 0, 0); 10 | } 11 | 12 | .awesomplete { 13 | display: inline-block; 14 | position: relative; 15 | width: 100%; 16 | } 17 | 18 | .awesomplete > input { 19 | display: block; 20 | height: 2em; 21 | } 22 | #source_file_dropdown_button{ 23 | height: 2em; 24 | } 25 | .awesomplete > ul { 26 | position: absolute; 27 | left: 0; 28 | z-index: 11; /* make dropdown stay above gdbgui's controls */ 29 | min-width: 100%; 30 | box-sizing: border-box; 31 | list-style: none; 32 | padding: 0; 33 | margin: 0; 34 | background: #fff; 35 | 36 | /* Limit to 200px high so it doesn't take over the page */ 37 | max-height: 200px; 38 | /* and add scrollbar */ 39 | overflow: auto; 40 | } 41 | 42 | .awesomplete > ul:empty { 43 | display: none; 44 | } 45 | 46 | .awesomplete > ul { 47 | border-radius: .3em; 48 | margin: .2em 0 0; 49 | background: white; 50 | border: 1px solid rgba(0,0,0,.3); 51 | box-shadow: .05em .2em .6em rgba(0,0,0,.2); 52 | text-shadow: none; 53 | } 54 | 55 | @supports (transform: scale(0)) { 56 | .awesomplete > ul { 57 | transition: .3s cubic-bezier(.4,.2,.5,1.4); 58 | transform-origin: 1.43em -.43em; 59 | } 60 | 61 | .awesomplete > ul[hidden], 62 | .awesomplete > ul:empty { 63 | opacity: 0; 64 | transform: scale(0); 65 | display: block; 66 | transition-timing-function: ease; 67 | } 68 | } 69 | 70 | /* Pointer */ 71 | .awesomplete > ul:before { 72 | content: ""; 73 | position: absolute; 74 | top: -.43em; 75 | left: 1em; 76 | width: 0; height: 0; 77 | padding: .4em; 78 | background: white; 79 | border: inherit; 80 | border-right: 0; 81 | border-bottom: 0; 82 | -webkit-transform: rotate(45deg); 83 | transform: rotate(45deg); 84 | } 85 | 86 | .awesomplete > ul > li { 87 | position: relative; 88 | padding: .2em .5em; 89 | cursor: pointer; 90 | } 91 | 92 | .awesomplete > ul > li:hover { 93 | background: hsl(200, 40%, 80%); 94 | color: black; 95 | } 96 | 97 | .awesomplete > ul > li[aria-selected="true"] { 98 | background: hsl(205, 40%, 40%); 99 | color: white; 100 | } 101 | 102 | .awesomplete mark { 103 | background: hsl(65, 100%, 50%); 104 | } 105 | 106 | .awesomplete li:hover mark { 107 | background: hsl(68, 100%, 41%); 108 | } 109 | 110 | .awesomplete li[aria-selected="true"] mark { 111 | background: hsl(86, 100%, 21%); 112 | color: inherit; 113 | } 114 | /*# sourceMappingURL=awesomplete.css.map */ 115 | -------------------------------------------------------------------------------- /gdbgui/static/vendor/css/pygments/emacs.css: -------------------------------------------------------------------------------- 1 | .emacs .hll { background-color: #ffffcc } 2 | .emacs .c { color: #008800; font-style: italic } /* Comment */ 3 | .emacs .err { border: 1px solid #FF0000 } /* Error */ 4 | .emacs .k { color: #AA22FF; font-weight: bold } /* Keyword */ 5 | .emacs .o { color: #666666 } /* Operator */ 6 | .emacs .ch { color: #008800; font-style: italic } /* Comment.Hashbang */ 7 | .emacs .cm { color: #008800; font-style: italic } /* Comment.Multiline */ 8 | .emacs .cp { color: #008800 } /* Comment.Preproc */ 9 | .emacs .cpf { color: #008800; font-style: italic } /* Comment.PreprocFile */ 10 | .emacs .c1 { color: #008800; font-style: italic } /* Comment.Single */ 11 | .emacs .cs { color: #008800; font-weight: bold } /* Comment.Special */ 12 | .emacs .gd { color: #A00000 } /* Generic.Deleted */ 13 | .emacs .ge { font-style: italic } /* Generic.Emph */ 14 | .emacs .gr { color: #FF0000 } /* Generic.Error */ 15 | .emacs .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 16 | .emacs .gi { color: #00A000 } /* Generic.Inserted */ 17 | .emacs .go { color: #888888 } /* Generic.Output */ 18 | .emacs .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 19 | .emacs .gs { font-weight: bold } /* Generic.Strong */ 20 | .emacs .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 21 | .emacs .gt { color: #0044DD } /* Generic.Traceback */ 22 | .emacs .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */ 23 | .emacs .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */ 24 | .emacs .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */ 25 | .emacs .kp { color: #AA22FF } /* Keyword.Pseudo */ 26 | .emacs .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */ 27 | .emacs .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */ 28 | .emacs .m { color: #666666 } /* Literal.Number */ 29 | .emacs .s { color: #BB4444 } /* Literal.String */ 30 | .emacs .na { color: #BB4444 } /* Name.Attribute */ 31 | .emacs .nb { color: #AA22FF } /* Name.Builtin */ 32 | .emacs .nc { color: #0000FF } /* Name.Class */ 33 | .emacs .no { color: #880000 } /* Name.Constant */ 34 | .emacs .nd { color: #AA22FF } /* Name.Decorator */ 35 | .emacs .ni { color: #999999; font-weight: bold } /* Name.Entity */ 36 | .emacs .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 37 | .emacs .nf { color: #00A000 } /* Name.Function */ 38 | .emacs .nl { color: #A0A000 } /* Name.Label */ 39 | .emacs .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 40 | .emacs .nt { color: #008000; font-weight: bold } /* Name.Tag */ 41 | .emacs .nv { color: #B8860B } /* Name.Variable */ 42 | .emacs .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 43 | .emacs .w { color: #bbbbbb } /* Text.Whitespace */ 44 | .emacs .mb { color: #666666 } /* Literal.Number.Bin */ 45 | .emacs .mf { color: #666666 } /* Literal.Number.Float */ 46 | .emacs .mh { color: #666666 } /* Literal.Number.Hex */ 47 | .emacs .mi { color: #666666 } /* Literal.Number.Integer */ 48 | .emacs .mo { color: #666666 } /* Literal.Number.Oct */ 49 | .emacs .sa { color: #BB4444 } /* Literal.String.Affix */ 50 | .emacs .sb { color: #BB4444 } /* Literal.String.Backtick */ 51 | .emacs .sc { color: #BB4444 } /* Literal.String.Char */ 52 | .emacs .dl { color: #BB4444 } /* Literal.String.Delimiter */ 53 | .emacs .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */ 54 | .emacs .s2 { color: #BB4444 } /* Literal.String.Double */ 55 | .emacs .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 56 | .emacs .sh { color: #BB4444 } /* Literal.String.Heredoc */ 57 | .emacs .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 58 | .emacs .sx { color: #008000 } /* Literal.String.Other */ 59 | .emacs .sr { color: #BB6688 } /* Literal.String.Regex */ 60 | .emacs .s1 { color: #BB4444 } /* Literal.String.Single */ 61 | .emacs .ss { color: #B8860B } /* Literal.String.Symbol */ 62 | .emacs .bp { color: #AA22FF } /* Name.Builtin.Pseudo */ 63 | .emacs .fm { color: #00A000 } /* Name.Function.Magic */ 64 | .emacs .vc { color: #B8860B } /* Name.Variable.Class */ 65 | .emacs .vg { color: #B8860B } /* Name.Variable.Global */ 66 | .emacs .vi { color: #B8860B } /* Name.Variable.Instance */ 67 | .emacs .vm { color: #B8860B } /* Name.Variable.Magic */ 68 | .emacs .il { color: #666666 } /* Literal.Number.Integer.Long */ 69 | -------------------------------------------------------------------------------- /gdbgui/static/vendor/css/pygments/light.css: -------------------------------------------------------------------------------- 1 | /* gdbgui-specific stuff */ 2 | .light { 3 | background-color: white; 4 | color: black; 5 | } 6 | 7 | /* when hovering, set background color of entire row */ 8 | .light .paused_on_line { 9 | background-color: #bbefff; 10 | } 11 | 12 | .light .assembly { color: black; } 13 | 14 | /* show a flash of color */ 15 | .light .flash { 16 | -webkit-animation-name: flash-animation-default; 17 | -webkit-animation-duration: 3.0s; 18 | 19 | animation-name: flash-animation-default; 20 | animation-duration: 3.0s; 21 | } 22 | 23 | @-webkit-keyframes flash-animation-default { 24 | from { background: white; } 25 | from { background: #fdfd54; } 26 | to { background: default; } 27 | } 28 | 29 | @keyframes flash-animation-default { 30 | from { background: white; } 31 | from { background: #fdfd54; } 32 | to { background: default; } 33 | } 34 | 35 | /* generated by pygments */ 36 | .light .hll { background-color: #ffffcc } 37 | .light .c { color: #408080; font-style: italic } /* Comment */ 38 | .light .err { border: 1px solid #FF0000 } /* Error */ 39 | .light .k { color: #008000; font-weight: bold } /* Keyword */ 40 | .light .o { color: #666666 } /* Operator */ 41 | .light .ch { color: #408080; font-style: italic } /* Comment.Hashbang */ 42 | .light .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 43 | .light .cp { color: #BC7A00 } /* Comment.Preproc */ 44 | .light .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */ 45 | .light .c1 { color: #408080; font-style: italic } /* Comment.Single */ 46 | .light .cs { color: #408080; font-style: italic } /* Comment.Special */ 47 | .light .gd { color: #A00000 } /* Generic.Deleted */ 48 | .light .ge { font-style: italic } /* Generic.Emph */ 49 | .light .gr { color: #FF0000 } /* Generic.Error */ 50 | .light .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 51 | .light .gi { color: #00A000 } /* Generic.Inserted */ 52 | .light .go { color: #888888 } /* Generic.Output */ 53 | .light .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 54 | .light .gs { font-weight: bold } /* Generic.Strong */ 55 | .light .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 56 | .light .gt { color: #0044DD } /* Generic.Traceback */ 57 | .light .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 58 | .light .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 59 | .light .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 60 | .light .kp { color: #008000 } /* Keyword.Pseudo */ 61 | .light .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 62 | .light .kt { color: #B00040 } /* Keyword.Type */ 63 | .light .m { color: #666666 } /* Literal.Number */ 64 | .light .s { color: #BA2121 } /* Literal.String */ 65 | .light .na { color: #7D9029 } /* Name.Attribute */ 66 | .light .nb { color: #008000 } /* Name.Builtin */ 67 | .light .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 68 | .light .no { color: #880000 } /* Name.Constant */ 69 | .light .nd { color: #AA22FF } /* Name.Decorator */ 70 | .light .ni { color: #999999; font-weight: bold } /* Name.Entity */ 71 | .light .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 72 | .light .nf { color: #0000FF } /* Name.Function */ 73 | .light .nl { color: #A0A000 } /* Name.Label */ 74 | .light .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 75 | .light .nt { color: #008000; font-weight: bold } /* Name.Tag */ 76 | .light .nv { color: #19177C } /* Name.Variable */ 77 | .light .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 78 | .light .w { color: #bbbbbb } /* Text.Whitespace */ 79 | .light .mb { color: #666666 } /* Literal.Number.Bin */ 80 | .light .mf { color: #666666 } /* Literal.Number.Float */ 81 | .light .mh { color: #666666 } /* Literal.Number.Hex */ 82 | .light .mi { color: #666666 } /* Literal.Number.Integer */ 83 | .light .mo { color: #666666 } /* Literal.Number.Oct */ 84 | .light .sa { color: #BA2121 } /* Literal.String.Affix */ 85 | .light .sb { color: #BA2121 } /* Literal.String.Backtick */ 86 | .light .sc { color: #BA2121 } /* Literal.String.Char */ 87 | .light .dl { color: #BA2121 } /* Literal.String.Delimiter */ 88 | .light .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 89 | .light .s2 { color: #BA2121 } /* Literal.String.Double */ 90 | .light .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 91 | .light .sh { color: #BA2121 } /* Literal.String.Heredoc */ 92 | .light .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 93 | .light .sx { color: #008000 } /* Literal.String.Other */ 94 | .light .sr { color: #BB6688 } /* Literal.String.Regex */ 95 | .light .s1 { color: #BA2121 } /* Literal.String.Single */ 96 | .light .ss { color: #19177C } /* Literal.String.Symbol */ 97 | .light .bp { color: #008000 } /* Name.Builtin.Pseudo */ 98 | .light .fm { color: #0000FF } /* Name.Function.Magic */ 99 | .light .vc { color: #19177C } /* Name.Variable.Class */ 100 | .light .vg { color: #19177C } /* Name.Variable.Global */ 101 | .light .vi { color: #19177C } /* Name.Variable.Instance */ 102 | .light .vm { color: #19177C } /* Name.Variable.Magic */ 103 | .light .il { color: #666666 } /* Literal.Number.Integer.Long */ 104 | -------------------------------------------------------------------------------- /gdbgui/static/vendor/css/pygments/monokai.css: -------------------------------------------------------------------------------- 1 | /* gdbgui-specific stuff*/ 2 | .monokai { 3 | background-color: #333; 4 | color: grey; 5 | } 6 | 7 | /* when hovering, set background color of entire row */ 8 | .monokai .paused_on_line { 9 | background-color: #444; 10 | } 11 | 12 | .monokai .assembly { color: #d8d8d8; } 13 | 14 | /* show a flash of color */ 15 | .monokai .flash { 16 | -webkit-animation-name: flash-animation-monokai; 17 | -webkit-animation-duration: 3.0s; 18 | 19 | animation-name: flash-animation-monokai; 20 | animation-duration: 3.0s; 21 | } 22 | 23 | @-webkit-keyframes flash-animation-monokai { 24 | from { background: white; } 25 | from { background: #444; } 26 | to { background: default; } 27 | } 28 | 29 | @keyframes flash-animation-monokai { 30 | from { background: white; } 31 | from { background: #444; } 32 | to { background: default; } 33 | } 34 | 35 | 36 | /* generated by pygments */ 37 | .monokai .hll { background-color: #49483e } 38 | .monokai .c { color: #75715e } /* Comment */ 39 | .monokai .err { color: #960050; background-color: #1e0010 } /* Error */ 40 | .monokai .k { color: #66d9ef } /* Keyword */ 41 | .monokai .l { color: #ae81ff } /* Literal */ 42 | .monokai .n { color: #f8f8f2 } /* Name */ 43 | .monokai .o { color: #f92672 } /* Operator */ 44 | .monokai .p { color: #f8f8f2 } /* Punctuation */ 45 | .monokai .ch { color: #75715e } /* Comment.Hashbang */ 46 | .monokai .cm { color: #75715e } /* Comment.Multiline */ 47 | /*.monokai .cp { color: #75715e } /* Comment.Preproc THIS IS PROVIDED BY PYGMENTS BUT LOOKS WRONG*/ 48 | .monokai .cp { color: #e8636f } /* Comment.Preproc UPDATED FOR GDBGUI HERE (pink, not grey) */ 49 | 50 | /*.monokai .cpf { color: #75715e } /* Comment.PreprocFile THIS IS PROVIDED BY PYGMENTS BUT LOOKS WRONG */ 51 | .monokai .cpf { color: #fbff00 } /* Comment.PreprocFile UPDATED FOR GDBGUI HERE (yellow, not grey) */ 52 | 53 | .monokai .c1 { color: #75715e } /* Comment.Single */ 54 | .monokai .cs { color: #75715e } /* Comment.Special */ 55 | .monokai .gd { color: #f92672 } /* Generic.Deleted */ 56 | .monokai .ge { font-style: italic } /* Generic.Emph */ 57 | .monokai .gi { color: #a6e22e } /* Generic.Inserted */ 58 | .monokai .gs { font-weight: bold } /* Generic.Strong */ 59 | .monokai .gu { color: #75715e } /* Generic.Subheading */ 60 | .monokai .kc { color: #66d9ef } /* Keyword.Constant */ 61 | .monokai .kd { color: #66d9ef } /* Keyword.Declaration */ 62 | .monokai .kn { color: #f92672 } /* Keyword.Namespace */ 63 | .monokai .kp { color: #66d9ef } /* Keyword.Pseudo */ 64 | .monokai .kr { color: #66d9ef } /* Keyword.Reserved */ 65 | .monokai .kt { color: #66d9ef } /* Keyword.Type */ 66 | .monokai .ld { color: #e6db74 } /* Literal.Date */ 67 | .monokai .m { color: #ae81ff } /* Literal.Number */ 68 | .monokai .s { color: #e6db74 } /* Literal.String */ 69 | .monokai .na { color: #a6e22e } /* Name.Attribute */ 70 | .monokai .nb { color: #f8f8f2 } /* Name.Builtin */ 71 | .monokai .nc { color: #a6e22e } /* Name.Class */ 72 | .monokai .no { color: #66d9ef } /* Name.Constant */ 73 | .monokai .nd { color: #a6e22e } /* Name.Decorator */ 74 | .monokai .ni { color: #f8f8f2 } /* Name.Entity */ 75 | .monokai .ne { color: #a6e22e } /* Name.Exception */ 76 | .monokai .nf { color: #a6e22e } /* Name.Function */ 77 | .monokai .nl { color: #f8f8f2 } /* Name.Label */ 78 | .monokai .nn { color: #f8f8f2 } /* Name.Namespace */ 79 | .monokai .nx { color: #a6e22e } /* Name.Other */ 80 | .monokai .py { color: #f8f8f2 } /* Name.Property */ 81 | .monokai .nt { color: #f92672 } /* Name.Tag */ 82 | .monokai .nv { color: #f8f8f2 } /* Name.Variable */ 83 | .monokai .ow { color: #f92672 } /* Operator.Word */ 84 | .monokai .w { color: #f8f8f2 } /* Text.Whitespace */ 85 | .monokai .mb { color: #ae81ff } /* Literal.Number.Bin */ 86 | .monokai .mf { color: #ae81ff } /* Literal.Number.Float */ 87 | .monokai .mh { color: #ae81ff } /* Literal.Number.Hex */ 88 | .monokai .mi { color: #ae81ff } /* Literal.Number.Integer */ 89 | .monokai .mo { color: #ae81ff } /* Literal.Number.Oct */ 90 | .monokai .sa { color: #e6db74 } /* Literal.String.Affix */ 91 | .monokai .sb { color: #e6db74 } /* Literal.String.Backtick */ 92 | .monokai .sc { color: #e6db74 } /* Literal.String.Char */ 93 | .monokai .dl { color: #e6db74 } /* Literal.String.Delimiter */ 94 | .monokai .sd { color: #e6db74 } /* Literal.String.Doc */ 95 | .monokai .s2 { color: #e6db74 } /* Literal.String.Double */ 96 | .monokai .se { color: #ae81ff } /* Literal.String.Escape */ 97 | .monokai .sh { color: #e6db74 } /* Literal.String.Heredoc */ 98 | .monokai .si { color: #e6db74 } /* Literal.String.Interpol */ 99 | .monokai .sx { color: #e6db74 } /* Literal.String.Other */ 100 | .monokai .sr { color: #e6db74 } /* Literal.String.Regex */ 101 | .monokai .s1 { color: #e6db74 } /* Literal.String.Single */ 102 | .monokai .ss { color: #e6db74 } /* Literal.String.Symbol */ 103 | .monokai .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ 104 | .monokai .fm { color: #a6e22e } /* Name.Function.Magic */ 105 | .monokai .vc { color: #f8f8f2 } /* Name.Variable.Class */ 106 | .monokai .vg { color: #f8f8f2 } /* Name.Variable.Global */ 107 | .monokai .vi { color: #f8f8f2 } /* Name.Variable.Instance */ 108 | .monokai .vm { color: #f8f8f2 } /* Name.Variable.Magic */ 109 | .monokai .il { color: #ae81ff } /* Literal.Number.Integer.Long */ 110 | -------------------------------------------------------------------------------- /gdbgui/static/vendor/css/pygments/vim.css: -------------------------------------------------------------------------------- 1 | .vim .hll { background-color: #222222 } 2 | .vim .c { color: #000080 } /* Comment */ 3 | .vim .err { color: #cccccc; border: 1px solid #FF0000 } /* Error */ 4 | .vim .esc { color: #cccccc } /* Escape */ 5 | .vim .g { color: #cccccc } /* Generic */ 6 | .vim .k { color: #cdcd00 } /* Keyword */ 7 | .vim .l { color: #cccccc } /* Literal */ 8 | .vim .n { color: #cccccc } /* Name */ 9 | .vim .o { color: #3399cc } /* Operator */ 10 | .vim .x { color: #cccccc } /* Other */ 11 | .vim .p { color: #cccccc } /* Punctuation */ 12 | .vim .ch { color: #000080 } /* Comment.Hashbang */ 13 | .vim .cm { color: #000080 } /* Comment.Multiline */ 14 | .vim .cp { color: #000080 } /* Comment.Preproc */ 15 | .vim .cpf { color: #000080 } /* Comment.PreprocFile */ 16 | .vim .c1 { color: #000080 } /* Comment.Single */ 17 | .vim .cs { color: #cd0000; font-weight: bold } /* Comment.Special */ 18 | .vim .gd { color: #cd0000 } /* Generic.Deleted */ 19 | .vim .ge { color: #cccccc; font-style: italic } /* Generic.Emph */ 20 | .vim .gr { color: #FF0000 } /* Generic.Error */ 21 | .vim .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .vim .gi { color: #00cd00 } /* Generic.Inserted */ 23 | .vim .go { color: #888888 } /* Generic.Output */ 24 | .vim .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 25 | .vim .gs { color: #cccccc; font-weight: bold } /* Generic.Strong */ 26 | .vim .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .vim .gt { color: #0044DD } /* Generic.Traceback */ 28 | .vim .kc { color: #cdcd00 } /* Keyword.Constant */ 29 | .vim .kd { color: #00cd00 } /* Keyword.Declaration */ 30 | .vim .kn { color: #cd00cd } /* Keyword.Namespace */ 31 | .vim .kp { color: #cdcd00 } /* Keyword.Pseudo */ 32 | .vim .kr { color: #cdcd00 } /* Keyword.Reserved */ 33 | .vim .kt { color: #00cd00 } /* Keyword.Type */ 34 | .vim .ld { color: #cccccc } /* Literal.Date */ 35 | .vim .m { color: #cd00cd } /* Literal.Number */ 36 | .vim .s { color: #cd0000 } /* Literal.String */ 37 | .vim .na { color: #cccccc } /* Name.Attribute */ 38 | .vim .nb { color: #cd00cd } /* Name.Builtin */ 39 | .vim .nc { color: #00cdcd } /* Name.Class */ 40 | .vim .no { color: #cccccc } /* Name.Constant */ 41 | .vim .nd { color: #cccccc } /* Name.Decorator */ 42 | .vim .ni { color: #cccccc } /* Name.Entity */ 43 | .vim .ne { color: #666699; font-weight: bold } /* Name.Exception */ 44 | .vim .nf { color: #cccccc } /* Name.Function */ 45 | .vim .nl { color: #cccccc } /* Name.Label */ 46 | .vim .nn { color: #cccccc } /* Name.Namespace */ 47 | .vim .nx { color: #cccccc } /* Name.Other */ 48 | .vim .py { color: #cccccc } /* Name.Property */ 49 | .vim .nt { color: #cccccc } /* Name.Tag */ 50 | .vim .nv { color: #00cdcd } /* Name.Variable */ 51 | .vim .ow { color: #cdcd00 } /* Operator.Word */ 52 | .vim .w { color: #cccccc } /* Text.Whitespace */ 53 | .vim .mb { color: #cd00cd } /* Literal.Number.Bin */ 54 | .vim .mf { color: #cd00cd } /* Literal.Number.Float */ 55 | .vim .mh { color: #cd00cd } /* Literal.Number.Hex */ 56 | .vim .mi { color: #cd00cd } /* Literal.Number.Integer */ 57 | .vim .mo { color: #cd00cd } /* Literal.Number.Oct */ 58 | .vim .sa { color: #cd0000 } /* Literal.String.Affix */ 59 | .vim .sb { color: #cd0000 } /* Literal.String.Backtick */ 60 | .vim .sc { color: #cd0000 } /* Literal.String.Char */ 61 | .vim .dl { color: #cd0000 } /* Literal.String.Delimiter */ 62 | .vim .sd { color: #cd0000 } /* Literal.String.Doc */ 63 | .vim .s2 { color: #cd0000 } /* Literal.String.Double */ 64 | .vim .se { color: #cd0000 } /* Literal.String.Escape */ 65 | .vim .sh { color: #cd0000 } /* Literal.String.Heredoc */ 66 | .vim .si { color: #cd0000 } /* Literal.String.Interpol */ 67 | .vim .sx { color: #cd0000 } /* Literal.String.Other */ 68 | .vim .sr { color: #cd0000 } /* Literal.String.Regex */ 69 | .vim .s1 { color: #cd0000 } /* Literal.String.Single */ 70 | .vim .ss { color: #cd0000 } /* Literal.String.Symbol */ 71 | .vim .bp { color: #cd00cd } /* Name.Builtin.Pseudo */ 72 | .vim .fm { color: #cccccc } /* Name.Function.Magic */ 73 | .vim .vc { color: #00cdcd } /* Name.Variable.Class */ 74 | .vim .vg { color: #00cdcd } /* Name.Variable.Global */ 75 | .vim .vi { color: #00cdcd } /* Name.Variable.Instance */ 76 | .vim .vm { color: #00cdcd } /* Name.Variable.Magic */ 77 | .vim .il { color: #cd00cd } /* Literal.Number.Integer.Long */ 78 | -------------------------------------------------------------------------------- /gdbgui/static/vendor/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/static/vendor/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /gdbgui/static/vendor/images/splitjs/grips/horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/gdbgui/static/vendor/images/splitjs/grips/horizontal.png -------------------------------------------------------------------------------- /gdbgui/static/vendor/js/awesomeplete.min.js: -------------------------------------------------------------------------------- 1 | // Awesomplete - Lea Verou - MIT license 2 | !function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):s===!1?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-autocomplete","list"),n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:r.SORT_BYLENGTH,item:r.ITEM,replace:r.REPLACE},n),this.index=-1,this.container=i.create("div",{className:"awesomplete",around:t}),this.ul=i.create("ul",{hidden:"hidden",inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-relevant":"additions",inside:this.container}),i.bind(this.input,{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}}),i.bind(this.input.form,{submit:this.close.bind(this,{reason:"submit"})}),i.bind(this.ul,{mousedown:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if(t=i(t),t&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.ul.removeAttribute("hidden"),this.isOpened=!0,this.autoFirst&&this.index===-1&&this.goto(0),i.fire(this.input,"awesomplete-open")},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index],s=i.fire(this.input,"awesomplete-select",{text:n,origin:e||t});s&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}).sort(this.sort).slice(0,this.maxItems),this.suggestions.forEach(function(t){e.ul.appendChild(e.item(t,i))}),0===this.ul.children.length?this.close({reason:"nomatches"}):this.open()):this.close({reason:"nomatches"})}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&");return i.create("li",{innerHTML:n,"aria-selected":"false"})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;return i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o)}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"undefined"!=typeof self&&(self.Awesomplete=r),"object"==typeof module&&module.exports&&(module.exports=r),r}(); 3 | //# sourceMappingURL=awesomplete.min.js.map 4 | -------------------------------------------------------------------------------- /gdbgui/static/vendor/js/splitjs.min-1.2.0.js: -------------------------------------------------------------------------------- 1 | /*! Split.js - v1.2.0 */ 2 | "use strict";(function(){var a=this,b=a.attachEvent&&!a[d],c=a.document,d="addEventListener",e="removeEventListener",f="getBoundingClientRect",g=.5,h=function(){for(var a,b=["","-webkit-","-moz-","-o-"],d=0;d=this.size-(this.bMin+k.snapOffset+this.bGutterSize)&&(b=this.size-(this.bMin+this.bGutterSize)),b-=g,y.call(this,b),k.onDrag&&k.onDrag())},x=function(){var b=a.getComputedStyle(this.parent),c=this.parent[n]-parseFloat(b[r])-parseFloat(b[s]);this.size=this.a[f]()[l]+this.b[f]()[l]+this.aGutterSize+this.bGutterSize,this.percentage=Math.min(this.size/c*100,100),this.start=this.a[f]()[p]},y=function(a){z(this.a,a/this.size*this.percentage,this.aGutterSize),z(this.b,this.percentage-a/this.size*this.percentage,this.bGutterSize)},z=function(a,b,c){for(var d=k.elementStyle(l,b,c),e=Object.keys(d),f=0;f0&&(F={a:i(j[m-1]),b:H,aMin:k.minSize[m-1],bMin:k.minSize[m],dragging:!1,parent:C,isFirst:I,isLast:J,direction:k.direction},F.aGutterSize=k.gutterSize,F.bGutterSize=k.gutterSize,I&&(F.aGutterSize=k.gutterSize/2),J&&(F.bGutterSize=k.gutterSize/2),"row-reverse"!==M&&"column-reverse"!==M||(G=F.a,F.a=F.b,F.b=G)),!b){if(m>0){var N=c.createElement("div");N.className=q,A(N,L),N[d]("mousedown",u.bind(F)),N[d]("touchstart",u.bind(F)),C.insertBefore(N,H),F.gutter=N}0!==m&&m!=j.length-1||(L=k.gutterSize/2)}if(z(H,K,L),m>0){var O=F.a[f]()[l],P=F.b[f]()[l];O0&&t.push(F)}return{setSizes:function(a){for(var b=0;b0){var c=t[b-1];z(c.a,a[b-1],c.aGutterSize),z(c.b,a[b],c.bGutterSize)}},getSizes:function(){for(var b=[],c=0;c 2 | 3 | 4 | 5 | 6 | gdbgui - dashboard 7 | 8 | 9 | 10 | 11 |
    Loading dashboard...
    12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /gdbgui/templates/gdbgui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | gdbgui - gdb in a browser 7 | 8 | 9 | 10 | 11 |
    12 |
    13 |

    Loading application, please wait.

    14 |

    15 | Note: JavaScript and cookies must be enabled. 16 |

    17 |
    18 |
    19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% for theme in themes %} 39 | 40 | {% endfor %} 41 | 42 | -------------------------------------------------------------------------------- /images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/images/favicon.ico -------------------------------------------------------------------------------- /images/gdbgui.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/images/gdbgui.xcf -------------------------------------------------------------------------------- /images/gdbgui_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/images/gdbgui_banner.png -------------------------------------------------------------------------------- /images/gdbgui_small.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/images/gdbgui_small.xcf -------------------------------------------------------------------------------- /images/gdbgui_square.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/images/gdbgui_square.xcf -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "preset": 'ts-jest', 3 | "verbose": true, 4 | "testMatch": [__dirname + '/gdbgui/src/js/tests/**'], 5 | "transform": { 6 | '^.+\.(j|t)sx?$': 'ts-jest' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /make_executable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Build an executable of gdbgui for the current platform 5 | """ 6 | 7 | 8 | import subprocess 9 | from sys import platform 10 | from gdbgui import __version__ 11 | import hashlib 12 | from pathlib import Path 13 | import logging 14 | 15 | logging.basicConfig(level=logging.INFO) 16 | 17 | 18 | def write_spec_with_gdbgui_version_in_name(spec_path, binary_name): 19 | 20 | spec = f"""# This pyinstaller spec file was generated by {__file__} 21 | 22 | block_cipher = None 23 | 24 | 25 | a = Analysis(['gdbgui/cli.py'], # noqa 26 | pathex=['.'], 27 | binaries=[], 28 | datas=[ 29 | ('./gdbgui/static*', './static'), 30 | ('./gdbgui/templates*', './templates'), 31 | ('./gdbgui/VERSION.txt*', './') 32 | ], 33 | hiddenimports=[ 34 | 'engineio.async_threading', 35 | 'engineio.async_drivers.threading', 36 | 'pkg_resources.py2_warn', 37 | ], 38 | hookspath=[], 39 | runtime_hooks=[], 40 | excludes=[], 41 | win_no_prefer_redirects=False, 42 | win_private_assemblies=False, 43 | cipher=block_cipher, 44 | ) 45 | 46 | pyz = PYZ(a.pure, a.zipped_data, # noqa 47 | cipher=block_cipher) 48 | 49 | exe = EXE(pyz, # noqa 50 | a.scripts, 51 | a.binaries, 52 | a.zipfiles, 53 | a.datas, 54 | name="{binary_name}", 55 | debug=False, 56 | strip=False, 57 | upx=False, 58 | runtime_tmpdir=None, 59 | console=True) 60 | 61 | """ 62 | 63 | with open(spec_path, "w+") as f: 64 | f.write(spec) 65 | 66 | 67 | def verify(binary_path: str, version: str): 68 | cmd = [str(binary_path), "--version"] 69 | logging.info(f"Smoke test: Running {' '.join(cmd)}") 70 | proc = subprocess.run(cmd, stdout=subprocess.PIPE) 71 | output = proc.stdout.decode().strip() 72 | if output != __version__: 73 | raise ValueError(f"Expected {__version__}. Got {output}") 74 | logging.info("Success!") 75 | 76 | 77 | def generate_md5(binary: Path, output_file: Path): 78 | checksum = hashlib.md5(binary.read_bytes()).hexdigest() 79 | with open(output_file, "w+") as f: 80 | f.write(checksum + "\n") 81 | logging.info(f"Wrote md5 to {output_file}") 82 | 83 | 84 | def main(): 85 | binary_name = "gdbgui_%s" % __version__ 86 | spec_path = "gdbgui_pyinstaller.spec" 87 | distpath = Path("build/executable").resolve() 88 | extension = ".exe" if platform == "win32" else "" 89 | binary_path = Path(distpath) / f"{binary_name}{extension}" 90 | 91 | write_spec_with_gdbgui_version_in_name(spec_path, binary_name) 92 | subprocess.run( 93 | [ 94 | "pyinstaller", 95 | spec_path, 96 | "--distpath", 97 | distpath, 98 | ], 99 | check=True, 100 | ) 101 | verify(binary_path, __version__) 102 | generate_md5(binary_path, distpath / f"{binary_name}.md5") 103 | logging.info(f"Saved executable to {binary_path}") 104 | 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: gdbgui 2 | site_description: A browser-based frontend to gdb (gnu debugger) 3 | 4 | theme: 5 | name: "material" 6 | repo_name: cs01/gdbgui 7 | repo_url: https://github.com/cs01/gdbgui 8 | 9 | nav: 10 | - Home: "index.md" 11 | - Screenshot Tour: "screenshots.md" 12 | - Installation: "installation.md" 13 | - Getting Started: "gettingstarted.md" 14 | - Examples: "examples.md" 15 | - Guides: "guides.md" 16 | - API: "api.md" 17 | - FAQ: "faq.md" 18 | - Contributing: "contributing.md" 19 | - How it Works: "howitworks.md" 20 | - Contact: "contact.md" 21 | - Changelog: "changelog.md" 22 | 23 | markdown_extensions: 24 | - admonition # note blocks, warning blocks -- https://github.com/mkdocs/mkdocs/issues/1659 25 | - markdown.extensions.codehilite: 26 | guess_lang: false 27 | 28 | extra: 29 | analytics: 30 | provider: google 31 | property: UA-90243909-2 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gdbgui", 3 | "version": "0.1.0", 4 | "license": "GPL-3.0", 5 | "scripts": { 6 | "start": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=development webpack --mode development --watch --config webpack.config.js", 7 | "test": "jest", 8 | "build": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production webpack --mode production --config webpack.config.js", 9 | "format": "prettier ./gdbgui/src/js/** --write", 10 | "lint": "prettier ./gdbgui/src/js/** --check" 11 | }, 12 | "dependencies": { 13 | "react": "^16.8", 14 | "react-dom": "^16.4", 15 | "socket.io": "^4.1", 16 | "socket.io-client": "^4.1", 17 | "statorgfc": "^0.1.6", 18 | "xterm": "4.8.0", 19 | "xterm-addon-fit": "^0.4.0", 20 | "xterm-addon-web-links": "^0.4.0", 21 | "xterm-addon-webgl": "^0.4.0" 22 | }, 23 | "devDependencies": { 24 | "@types/jest": "^24.0.11", 25 | "@types/jquery": "^3.5.1", 26 | "@types/react": "^16.8.7", 27 | "@types/react-dom": "^16.8.2", 28 | "@types/socket.io": "^2.1.11", 29 | "@types/socket.io-client": "^1.4.33", 30 | "autoprefixer": "^9.8.5", 31 | "cross-env": "^7.0.2", 32 | "css-loader": "^3.6.0", 33 | "fork-ts-checker-webpack-plugin": "^1.0.0", 34 | "jest": "^24.3.1", 35 | "mini-css-extract-plugin": "^0.9.0", 36 | "postcss-cli": "^7.1.1", 37 | "postcss-loader": "^3.0.0", 38 | "prettier": "^1.12.0", 39 | "style-loader": "^1.2.1", 40 | "tailwindcss": "^1.5.1", 41 | "ts-jest": "^24.0.0", 42 | "ts-loader": "^5.3.3", 43 | "typescript": "^3.3.3333", 44 | "webpack": "4.19.0", 45 | "webpack-cli": "^2.0.14" 46 | } 47 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const tailwindcss = require("tailwindcss"); 2 | module.exports = { 3 | plugins: [tailwindcss("./tailwind.config.js"), require("autoprefixer")] 4 | }; 5 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | Flask-SocketIO>5.3, <6 2 | Flask-Compress>1.10, <1.11 3 | pygdbmi>=0.10.0.2, <0.11 4 | Pygments>=2.2.0, <3.0 5 | eventlet 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.11 3 | # by the following command: 4 | # 5 | # pip-compile 6 | # 7 | bidict==0.22.1 8 | # via python-socketio 9 | blinker==1.6.3 10 | # via flask 11 | brotli==1.1.0 12 | # via flask-compress 13 | click==8.1.7 14 | # via flask 15 | dnspython==2.4.2 16 | # via eventlet 17 | eventlet==0.33.3 18 | # via -r requirements.in 19 | flask==3.0.0 20 | # via 21 | # flask-compress 22 | # flask-socketio 23 | flask-compress==1.10.1 24 | # via -r requirements.in 25 | flask-socketio==5.3.6 26 | # via -r requirements.in 27 | greenlet==3.0.0 28 | # via eventlet 29 | h11==0.14.0 30 | # via wsproto 31 | itsdangerous==2.1.2 32 | # via flask 33 | jinja2==3.1.2 34 | # via flask 35 | markupsafe==2.1.3 36 | # via 37 | # jinja2 38 | # werkzeug 39 | pygdbmi==0.10.0.2 40 | # via -r requirements.in 41 | pygments==2.16.1 42 | # via -r requirements.in 43 | python-engineio==4.8.0 44 | # via python-socketio 45 | python-socketio==5.10.0 46 | # via flask-socketio 47 | simple-websocket==1.0.0 48 | # via python-engineio 49 | six==1.16.0 50 | # via eventlet 51 | werkzeug==3.0.0 52 | # via flask 53 | wsproto==1.2.0 54 | # via simple-websocket 55 | -------------------------------------------------------------------------------- /screenshots/assembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/assembly.png -------------------------------------------------------------------------------- /screenshots/authentication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/authentication.png -------------------------------------------------------------------------------- /screenshots/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/console.png -------------------------------------------------------------------------------- /screenshots/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/controls.png -------------------------------------------------------------------------------- /screenshots/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/dashboard.png -------------------------------------------------------------------------------- /screenshots/expressions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/expressions.png -------------------------------------------------------------------------------- /screenshots/gdbgui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/gdbgui.png -------------------------------------------------------------------------------- /screenshots/gdbgui2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/gdbgui2.png -------------------------------------------------------------------------------- /screenshots/gdbgui_animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/gdbgui_animation.gif -------------------------------------------------------------------------------- /screenshots/hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/hover.png -------------------------------------------------------------------------------- /screenshots/load_binary_and_args.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/load_binary_and_args.png -------------------------------------------------------------------------------- /screenshots/locals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/locals.png -------------------------------------------------------------------------------- /screenshots/memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/memory.png -------------------------------------------------------------------------------- /screenshots/plots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/plots.png -------------------------------------------------------------------------------- /screenshots/radix.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/radix.gif -------------------------------------------------------------------------------- /screenshots/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/ready.png -------------------------------------------------------------------------------- /screenshots/registers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/registers.png -------------------------------------------------------------------------------- /screenshots/reverse_debugging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/reverse_debugging.png -------------------------------------------------------------------------------- /screenshots/rust_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/rust_main.png -------------------------------------------------------------------------------- /screenshots/send_signal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/send_signal.png -------------------------------------------------------------------------------- /screenshots/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/source.png -------------------------------------------------------------------------------- /screenshots/source_with_assembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/source_with_assembly.png -------------------------------------------------------------------------------- /screenshots/stack_and_threads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/stack_and_threads.png -------------------------------------------------------------------------------- /screenshots/tree_explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/screenshots/tree_explorer.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import distutils.text_file # type: ignore 5 | 6 | USING_WINDOWS = os.name == "nt" 7 | if USING_WINDOWS: 8 | raise RuntimeError( 9 | "Windows is not supported at this time. " 10 | + "Versions lower than 0.14.x. are Windows compatible." 11 | ) 12 | import io 13 | from setuptools import find_packages, setup # type: ignore 14 | 15 | CURDIR = os.path.abspath(os.path.dirname(__file__)) 16 | 17 | EXCLUDE_FROM_PACKAGES = ["tests"] 18 | 19 | README = io.open(os.path.join(CURDIR, "README.md"), "r", encoding="utf-8").read() 20 | VERSION = ( 21 | io.open(os.path.join(CURDIR, "gdbgui/VERSION.txt"), "r", encoding="utf-8") 22 | .read() 23 | .strip() 24 | ) 25 | 26 | setup( 27 | name="gdbgui", 28 | version=VERSION, 29 | author="Chad Smith", 30 | author_email="chadsmith.software@gmail.com", 31 | description="Browser-based frontend to gdb. Debug C, C++, Go, or Rust.", 32 | long_description=README, 33 | long_description_content_type="text/markdown", 34 | url="https://github.com/cs01/gdbgui", 35 | license="License :: GNU GPLv3", 36 | packages=find_packages(exclude=EXCLUDE_FROM_PACKAGES), 37 | include_package_data=True, 38 | keywords=[ 39 | "gdb", 40 | "debug", 41 | "c", 42 | "c++", 43 | "go", 44 | "rust", 45 | "python", 46 | "machine-interface", 47 | "parse", 48 | "frontend", 49 | "flask", 50 | "browser", 51 | "gui", 52 | ], 53 | scripts=[], 54 | entry_points={ 55 | "console_scripts": [ 56 | # allow user to type gdbgui from terminal to automatically launch 57 | # the server and a tab in a browser 58 | "gdbgui = gdbgui.cli:main" 59 | ] 60 | }, 61 | zip_safe=False, 62 | install_requires=distutils.text_file.TextFile( 63 | filename="./requirements.txt" 64 | ).readlines(), 65 | classifiers=[ 66 | "Intended Audience :: Developers", 67 | "Operating System :: MacOS", 68 | "Operating System :: Unix", 69 | "Operating System :: POSIX", 70 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 71 | "Programming Language :: Python", 72 | "Programming Language :: Python :: 3", 73 | ], 74 | python_requires=">=3.6", 75 | project_urls={ 76 | "Documentation": "https://cs01.github.io/gdbgui/", 77 | "Source Code": "https://github.com/cs01/gdbgui", 78 | "Bug Tracker": "https://github.com/cs01/gdbgui/issues", 79 | }, 80 | ) 81 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: ["./gdbgui/src/js/**", "./gdbgui/templates/*.html"], 3 | theme: { 4 | extend: {} 5 | }, 6 | variants: {}, 7 | plugins: [] 8 | }; 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cs01/gdbgui/be9521751f747856a21814f5ce71ec63d3740045/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_backend.py: -------------------------------------------------------------------------------- 1 | from flask_socketio import send, SocketIO # type: ignore 2 | import pytest # type: ignore 3 | 4 | from gdbgui.server.server import run_server 5 | from gdbgui.server.app import app, socketio 6 | from gdbgui import cli 7 | 8 | run_server(testing=True, app=app, socketio=socketio) 9 | 10 | 11 | def test_connect(): 12 | test_ws = SocketIO() 13 | 14 | @test_ws.on("connect") 15 | def on_connect(): 16 | send({"connected": "foo"}, json=True) 17 | 18 | test_ws.init_app(app, cookie="foo") 19 | client = test_ws.test_client(app) 20 | received = client.get_received() 21 | assert len(received) == 1 22 | assert received[0]["args"] == {"connected": "foo"} 23 | 24 | 25 | @pytest.fixture 26 | def test_client(): 27 | return app.test_client() 28 | 29 | 30 | def test_load_main_page(test_client): 31 | response = test_client.get("/") 32 | assert response.status_code == 200 33 | assert "" in response.data.decode() 34 | 35 | 36 | def test_load_dashboard(test_client): 37 | response = test_client.get("/dashboard") 38 | assert response.status_code == 200 39 | assert "" in response.data.decode() 40 | 41 | 42 | def test_cant_load_bad_url(test_client): 43 | response = test_client.get("/asdf") 44 | assert response.status_code == 404 45 | assert "404 Not Found" in response.data.decode() 46 | 47 | 48 | def test_same_port(): 49 | run_server(testing=True, app=app, socketio=socketio) 50 | 51 | 52 | def test_get_initial_binary_and_args(): 53 | assert cli.get_initial_binary_and_args([], "./program --args") == [ 54 | "./program", 55 | "--args", 56 | ] 57 | assert cli.get_initial_binary_and_args(["./program", "--args",], None) == [ 58 | "./program", 59 | "--args", 60 | ] 61 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import List 3 | from unittest import mock 4 | 5 | import gdbgui 6 | import pytest # type: ignore 7 | 8 | 9 | def run_gdbgui_cli(gdbgui_args: List[str]): 10 | with mock.patch.object(sys, "argv", ["gdbgui"] + gdbgui_args): 11 | return gdbgui.cli.main() # type: ignore 12 | 13 | 14 | # @pytest.mark.parametrize( 15 | # "argv", 16 | # ( 17 | # [], 18 | # ["-n"], 19 | # ["myprogram"], 20 | # ["-g", "gdb -nx"], 21 | # ["--args", "can", "pass", "many", "args"], 22 | # ), 23 | # ) 24 | # def skip_test_cli(monkeypatch, argv): 25 | # # TODO fix this patch 26 | # with mock.patch("gdbgui.server.server.run_server") as mock_run_server: 27 | # run_gdbgui_cli(argv) 28 | # mock_run_server.assert_called_once() 29 | 30 | 31 | @mock.patch("gdbgui.server.server.run_server") 32 | @pytest.mark.parametrize( 33 | "argv", (["--gdb-cmd"], ["myprogram", "cannot pass second arg"]) 34 | ) 35 | def test_cli_fails(monkeypatch, argv): 36 | mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) 37 | with mock.patch.object(sys, "exit", mock_exit), pytest.raises( 38 | ValueError, match="raised in test to exit early" 39 | ): 40 | run_gdbgui_cli(argv) 41 | mock_exit.assert_called_once_with(2) 42 | 43 | 44 | @mock.patch("gdbgui.server.server.run_server") 45 | def test_cli_help(monkeypatch): 46 | mock_exit = mock.Mock(side_effect=ValueError("raised in test to exit early")) 47 | with mock.patch.object(sys, "exit", mock_exit), pytest.raises( 48 | ValueError, match="raised in test to exit early" 49 | ): 50 | run_gdbgui_cli(["--help"]) 51 | mock_exit.assert_called_once_with(0) 52 | -------------------------------------------------------------------------------- /tests/test_ptylib.py: -------------------------------------------------------------------------------- 1 | from gdbgui.server import ptylib 2 | 3 | import os 4 | 5 | 6 | def test_pty(): 7 | pty = ptylib.Pty() 8 | assert pty.name 9 | os.write(pty.stdin, "hello".encode()) 10 | output = os.read(pty.stdout, 1024).decode() 11 | assert output == "hello" 12 | -------------------------------------------------------------------------------- /tests/test_sessionmanager.py: -------------------------------------------------------------------------------- 1 | from gdbgui.server import sessionmanager 2 | 3 | 4 | def test_SessionManager(): 5 | manager = sessionmanager.SessionManager() 6 | db_session = manager.add_new_debug_session( 7 | gdb_command="gdb", mi_version="mi3", client_id="test" 8 | ) 9 | pid = manager.get_pid_from_debug_session(db_session) 10 | assert pid 11 | dashboard_data = manager.get_dashboard_data() 12 | assert len(dashboard_data) == 1 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "downlevelIteration": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "jsx": "react", 7 | "lib": ["dom", "es2015", "es2016.array.include"], 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "newLine": "LF", 11 | "noEmitOnError": false, 12 | "outDir": "dist", 13 | "preserveConstEnums": true, 14 | "skipLibCheck": true, 15 | "sourceMap": true, 16 | "strict": true, 17 | "target": "es5", 18 | "allowSyntheticDefaultImports": true 19 | }, 20 | "include": ["./gdbgui/src/js"] 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jquery": true 6 | }, 7 | "extends": ["tslint-config-prettier"], 8 | "globals": { 9 | "initial_data": true, 10 | "module": true, 11 | "_": true, 12 | "moment": true 13 | }, 14 | "jsRules": { 15 | "indent": { 16 | "options": ["spaces"] 17 | } 18 | }, 19 | "rules": { 20 | "quotemark": true, 21 | "semicolon": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); 3 | 4 | module.exports = { 5 | entry: { 6 | main: "./gdbgui/src/js/gdbgui.tsx", 7 | dashboard: "./gdbgui/src/js/dashboard.tsx" 8 | }, 9 | devtool: "source-map", 10 | output: { 11 | path: path.resolve(__dirname, "gdbgui/static/js/") 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.css$/, 17 | use: ["style-loader", "css-loader", "postcss-loader"] 18 | }, 19 | { 20 | test: /\.(j|t)sx?$/, 21 | use: [ 22 | { 23 | loader: "ts-loader", 24 | options: { 25 | experimentalFileCaching: true, 26 | experimentalWatchApi: true, 27 | transpileOnly: true 28 | } 29 | } 30 | ], 31 | exclude: /node_modules/ 32 | } 33 | ] 34 | }, 35 | plugins: [new ForkTsCheckerWebpackPlugin({})], 36 | resolve: { 37 | extensions: [".js", ".ts", ".tsx", ".css"] 38 | } 39 | }; 40 | --------------------------------------------------------------------------------