├── .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 |
13 |
14 |
15 |
16 |
17 |
18 |
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 |
13 |
14 |
15 |
16 |
17 |
18 |
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 |
44 |
45 | "_Seriously, great front-end to gdb for those of us who are not always using a full IDE. Great project._"
46 |
47 |
50 |
51 | "_Where were you all my life? And why did I use DDD?_"
52 |
53 |
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 | 
2 | 
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 | 
8 |
9 | Intuitive control of your program. From left to right: Run, Continue,
10 | Next, Step, Return, Next Instruction, Step Instruction.
11 |
12 | 
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 | 
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 | 
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 | 
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 | 
36 |
37 | With assembly. Note the bold line is the current instruction that gdb is
38 | stopped on.
39 |
40 | 
41 |
42 | If the source file is not found, it will display assembly, and allow you to step through it as desired.
43 | 
44 |
45 |
46 | ## Variables and Expressions
47 |
48 | All local variables are automatically displayed, and are clickable to
49 | explore their fields.
50 |
51 | 
52 |
53 | Hover over a variable and explore it, just like in the Chrome debugger.
54 |
55 | 
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 | 
60 |
61 | 
62 |
63 | Expressions record their previous values, and can be displayed in an x/y
64 | plot.
65 |
66 | 
67 |
68 | Expressions can be interactively explored in a tree view.
69 |
70 | 
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 | 
80 |
81 | ## Registers
82 |
83 | View all registers. If a register was updated it is highlighted in
84 | yellow.
85 |
86 | 
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 | 
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 | 
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 | 
109 |
110 | ## gdbgui at launch
111 |
112 | 
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 | GdbApi.click_run_button()}
24 | type="button"
25 | title="Start inferior program from the beginning keyboard shortcut: r"
26 | className={btn_class}
27 | >
28 |
29 |
30 |
31 | GdbApi.click_continue_button()}
34 | type="button"
35 | title={
36 | "Continue until breakpoint is hit or inferior program exits keyboard shortcut: c" +
37 | (this.state.reverse_supported ? ". shift + c for reverse." : "")
38 | }
39 | className={btn_class}
40 | >
41 |
42 |
43 |
44 | Actions.send_signal("SIGINT", this.state.gdb_pid)}
46 | type="button"
47 | title="Send Interrupt signal (SIGINT) to gdb process to pause it (if it's running)"
48 | className={btn_class}
49 | >
50 |
51 |
52 |
53 | GdbApi.click_next_button()}
56 | type="button"
57 | title={
58 | "Step over next function call keyboard shortcut: n or right arrow" +
59 | (this.state.reverse_supported ? ". shift + n for reverse." : "")
60 | }
61 | className={btn_class}
62 | >
63 |
64 |
65 |
66 | GdbApi.click_step_button()}
69 | type="button"
70 | title={
71 | "Step into next function call keyboard shortcut: s or down arrow" +
72 | (this.state.reverse_supported ? ". shift + s for reverse." : "")
73 | }
74 | className={btn_class}
75 | >
76 |
77 |
78 |
79 | GdbApi.click_return_button()}
82 | type="button"
83 | title="Step out of current function keyboard shortcut: u or up arrow"
84 | className={btn_class}
85 | >
86 |
87 |
88 |
89 | GdbApi.click_next_instruction_button()}
92 | type="button"
93 | title={
94 | "Next Instruction: Execute one machine instruction, stepping over function calls keyboard shortcut: m" +
95 | (this.state.reverse_supported ? ". shift + m for reverse." : "")
96 | }
97 | className="btn btn-default"
98 | >
99 | NI
100 |
101 | GdbApi.click_step_instruction_button()}
104 | type="button"
105 | title={
106 | "Step Instruction: Execute one machine instruction, stepping into function calls keyboard shortcut: ','" +
107 | (this.state.reverse_supported ? ". shift + , for reverse." : "")
108 | }
109 | className="btn btn-default"
110 | >
111 | SI
112 |
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 |
store.set("gdb_mi_output", [])}
39 | >
40 | clear output
41 |
42 |
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 |
33 | ×
34 |
35 |
36 |
37 |
{this.state.modal_header}
38 |
39 |
{this.state.modal_body}
40 |
41 |
47 | Close
48 |
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 |
62 | {this.state.selected_signal}
63 |
64 | {" "}
65 |
66 |
67 |
68 | {this.get_signal_choices("selected_signal")}
69 |
70 |
71 | );
72 | }
73 | render() {
74 | let gdb_button = (
75 |
82 | Actions.send_signal(this.state.selected_signal, this.state.gdb_pid)
83 | }
84 | >
85 | {`gdb (pid ${this.state.gdb_pid})`}
86 |
87 | );
88 |
89 | let inferior_button = null;
90 | if (this.state.inferior_pid) {
91 | inferior_button = (
92 |
97 | Actions.send_signal(this.state.selected_signal, this.state.inferior_pid)
98 | }
99 | >
100 | {`debug program (pid ${this.state.inferior_pid})`}
101 |
102 | );
103 | }
104 |
105 | let other_input_and_button = (
106 |
112 | Actions.send_signal(this.state.selected_signal, this.state.other_pid)
113 | }
114 | >
115 | {`other pid ${this.state.other_pid}`}
116 |
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 |
44 | Settings.toggle_key(store_key)}
48 | />
49 | {text}
50 |
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 | (this.save_button = n)}
68 | onClick={() => {
69 | let new_value = parseInt(this.max_source_file_lines_input.value);
70 | Actions.update_max_lines_of_code_to_fetch(new_value);
71 | // @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
72 | ToolTip.show_tooltip_on_node("saved!", this.save_button, 1);
73 | }}
74 | >
75 | save
76 |
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 |
109 | Theme:{" "}
110 |
117 | {store.get("themes").map((t: any) => (
118 | {t}
119 | ))}
120 |
121 |
122 |
123 |
124 |
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 |
Settings.toggle_key("show_settings")}>
141 | ×
142 |
143 |
Settings
144 | {this.get_table()}
145 |
146 | Settings.toggle_key("show_settings")}
149 | data-dismiss="modal"
150 | >
151 | Close
152 |
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 |
52 |
53 |
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(`${h} `);
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(`${cell} `);
40 | }
41 | result.push(" ");
42 | }
43 | }
44 | result.push(" ");
45 | result.push("
");
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 |
--------------------------------------------------------------------------------