rdc-cli 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. rdc_cli-0.2.0/LICENSE +21 -0
  2. rdc_cli-0.2.0/PKG-INFO +130 -0
  3. rdc_cli-0.2.0/README.md +106 -0
  4. rdc_cli-0.2.0/pyproject.toml +45 -0
  5. rdc_cli-0.2.0/setup.cfg +4 -0
  6. rdc_cli-0.2.0/src/rdc/__init__.py +4 -0
  7. rdc_cli-0.2.0/src/rdc/adapter.py +61 -0
  8. rdc_cli-0.2.0/src/rdc/cli.py +65 -0
  9. rdc_cli-0.2.0/src/rdc/commands/capture.py +62 -0
  10. rdc_cli-0.2.0/src/rdc/commands/completion.py +49 -0
  11. rdc_cli-0.2.0/src/rdc/commands/counters.py +50 -0
  12. rdc_cli-0.2.0/src/rdc/commands/doctor.py +98 -0
  13. rdc_cli-0.2.0/src/rdc/commands/events.py +140 -0
  14. rdc_cli-0.2.0/src/rdc/commands/export.py +57 -0
  15. rdc_cli-0.2.0/src/rdc/commands/info.py +125 -0
  16. rdc_cli-0.2.0/src/rdc/commands/pipeline.py +330 -0
  17. rdc_cli-0.2.0/src/rdc/commands/resources.py +126 -0
  18. rdc_cli-0.2.0/src/rdc/commands/search.py +67 -0
  19. rdc_cli-0.2.0/src/rdc/commands/session.py +57 -0
  20. rdc_cli-0.2.0/src/rdc/commands/unix_helpers.py +79 -0
  21. rdc_cli-0.2.0/src/rdc/commands/usage.py +57 -0
  22. rdc_cli-0.2.0/src/rdc/commands/vfs.py +206 -0
  23. rdc_cli-0.2.0/src/rdc/daemon_client.py +32 -0
  24. rdc_cli-0.2.0/src/rdc/daemon_server.py +2079 -0
  25. rdc_cli-0.2.0/src/rdc/discover.py +84 -0
  26. rdc_cli-0.2.0/src/rdc/formatters/json_fmt.py +20 -0
  27. rdc_cli-0.2.0/src/rdc/formatters/tsv.py +60 -0
  28. rdc_cli-0.2.0/src/rdc/protocol.py +61 -0
  29. rdc_cli-0.2.0/src/rdc/services/__init__.py +1 -0
  30. rdc_cli-0.2.0/src/rdc/services/query_service.py +512 -0
  31. rdc_cli-0.2.0/src/rdc/services/session_service.py +174 -0
  32. rdc_cli-0.2.0/src/rdc/session_state.py +86 -0
  33. rdc_cli-0.2.0/src/rdc/vfs/__init__.py +0 -0
  34. rdc_cli-0.2.0/src/rdc/vfs/formatter.py +62 -0
  35. rdc_cli-0.2.0/src/rdc/vfs/router.py +191 -0
  36. rdc_cli-0.2.0/src/rdc/vfs/tree_cache.py +308 -0
  37. rdc_cli-0.2.0/src/rdc_cli.egg-info/PKG-INFO +130 -0
  38. rdc_cli-0.2.0/src/rdc_cli.egg-info/SOURCES.txt +40 -0
  39. rdc_cli-0.2.0/src/rdc_cli.egg-info/dependency_links.txt +1 -0
  40. rdc_cli-0.2.0/src/rdc_cli.egg-info/entry_points.txt +2 -0
  41. rdc_cli-0.2.0/src/rdc_cli.egg-info/requires.txt +17 -0
  42. rdc_cli-0.2.0/src/rdc_cli.egg-info/top_level.txt +1 -0
rdc_cli-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
rdc_cli-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: rdc-cli
3
+ Version: 0.2.0
4
+ Summary: Unix-friendly CLI for RenderDoc captures
5
+ Author: Jim
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: click>=8.1
11
+ Provides-Extra: rich
12
+ Requires-Dist: rich>=13.0; extra == "rich"
13
+ Provides-Extra: imaging
14
+ Requires-Dist: Pillow>=10.0; extra == "imaging"
15
+ Provides-Extra: diff
16
+ Requires-Dist: Pillow>=10.0; extra == "diff"
17
+ Requires-Dist: numpy>=1.24; extra == "diff"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=8.0; extra == "dev"
20
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
21
+ Requires-Dist: mypy>=1.10; extra == "dev"
22
+ Requires-Dist: ruff>=0.6; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # rdc-cli
26
+
27
+ Unix-friendly CLI for [RenderDoc](https://renderdoc.org/) `.rdc` captures. Pipe-friendly TSV output, JSON mode, 33 commands, daemon-backed session for interactive exploration.
28
+
29
+ ```bash
30
+ rdc open capture.rdc # Start session
31
+ rdc draws # List draw calls (TSV)
32
+ rdc pipeline 142 # Pipeline state at EID 142
33
+ rdc shader 142 ps # Pixel shader disassembly
34
+ rdc texture 5 -o out.png # Export texture
35
+ rdc draws --json | jq '...' # Machine-readable output
36
+ rdc close # End session
37
+ ```
38
+
39
+ ## Install
40
+
41
+ ### PyPI (recommended)
42
+
43
+ ```bash
44
+ pipx install rdc-cli
45
+ ```
46
+
47
+ ### AUR (Arch Linux)
48
+
49
+ ```bash
50
+ yay -S rdc-cli-git
51
+ ```
52
+
53
+ This builds the renderdoc Python module automatically — no extra setup needed.
54
+
55
+ ### From source
56
+
57
+ ```bash
58
+ git clone https://github.com/BANANASJIM/rdc-cli.git
59
+ cd rdc-cli
60
+ pixi install && pixi run sync
61
+ ```
62
+
63
+ ## Setup renderdoc
64
+
65
+ `rdc` requires the renderdoc Python module (`renderdoc.cpython-*.so`), which is **not** included in most system packages. Your Python version must match the one used to compile renderdoc.
66
+
67
+ ### Build from source
68
+
69
+ ```bash
70
+ git clone --depth 1 https://github.com/baldurk/renderdoc.git
71
+ cd renderdoc
72
+ cmake -B build -DENABLE_PYRENDERDOC=ON -DENABLE_QRENDERDOC=OFF
73
+ cmake --build build -j$(nproc)
74
+ export RENDERDOC_PYTHON_PATH=$PWD/build/lib
75
+ ```
76
+
77
+ ### Module discovery order
78
+
79
+ 1. `RENDERDOC_PYTHON_PATH` environment variable
80
+ 2. `/usr/lib/renderdoc`, `/usr/local/lib/renderdoc`
81
+ 3. Sibling directory of `renderdoccmd` on PATH
82
+
83
+ ### Verify
84
+
85
+ ```bash
86
+ rdc doctor
87
+ ```
88
+
89
+ ## Commands
90
+
91
+ Run `rdc --help` for the full command list, or `rdc <command> --help` for details.
92
+
93
+ | Category | Commands |
94
+ |----------|----------|
95
+ | Session | `open`, `close`, `status`, `goto` |
96
+ | Inspection | `info`, `stats`, `events`, `draws`, `event`, `draw`, `log` |
97
+ | GPU state | `pipeline`, `bindings`, `shader`, `shaders`, `shader-map` |
98
+ | Resources | `resources`, `resource`, `passes`, `pass`, `usage` |
99
+ | Export | `texture`, `rt`, `buffer` |
100
+ | Search | `search`, `counters` |
101
+ | VFS | `ls`, `cat`, `tree` |
102
+ | Utility | `doctor`, `completion`, `capture`, `count` |
103
+
104
+ All commands support `--json` for machine-readable output.
105
+
106
+ ### Shell completions
107
+
108
+ ```bash
109
+ rdc completion bash > ~/.local/share/bash-completion/completions/rdc
110
+ rdc completion zsh > ~/.zfunc/_rdc
111
+ eval "$(rdc completion bash)"
112
+ ```
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ pixi run sync # Install Python deps
118
+ pixi run check # lint + typecheck + test (653 tests, 92% coverage)
119
+ ```
120
+
121
+ GPU integration tests require a real renderdoc module:
122
+
123
+ ```bash
124
+ export RENDERDOC_PYTHON_PATH=/path/to/renderdoc/build/lib
125
+ pixi run test-gpu
126
+ ```
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1,106 @@
1
+ # rdc-cli
2
+
3
+ Unix-friendly CLI for [RenderDoc](https://renderdoc.org/) `.rdc` captures. Pipe-friendly TSV output, JSON mode, 33 commands, daemon-backed session for interactive exploration.
4
+
5
+ ```bash
6
+ rdc open capture.rdc # Start session
7
+ rdc draws # List draw calls (TSV)
8
+ rdc pipeline 142 # Pipeline state at EID 142
9
+ rdc shader 142 ps # Pixel shader disassembly
10
+ rdc texture 5 -o out.png # Export texture
11
+ rdc draws --json | jq '...' # Machine-readable output
12
+ rdc close # End session
13
+ ```
14
+
15
+ ## Install
16
+
17
+ ### PyPI (recommended)
18
+
19
+ ```bash
20
+ pipx install rdc-cli
21
+ ```
22
+
23
+ ### AUR (Arch Linux)
24
+
25
+ ```bash
26
+ yay -S rdc-cli-git
27
+ ```
28
+
29
+ This builds the renderdoc Python module automatically — no extra setup needed.
30
+
31
+ ### From source
32
+
33
+ ```bash
34
+ git clone https://github.com/BANANASJIM/rdc-cli.git
35
+ cd rdc-cli
36
+ pixi install && pixi run sync
37
+ ```
38
+
39
+ ## Setup renderdoc
40
+
41
+ `rdc` requires the renderdoc Python module (`renderdoc.cpython-*.so`), which is **not** included in most system packages. Your Python version must match the one used to compile renderdoc.
42
+
43
+ ### Build from source
44
+
45
+ ```bash
46
+ git clone --depth 1 https://github.com/baldurk/renderdoc.git
47
+ cd renderdoc
48
+ cmake -B build -DENABLE_PYRENDERDOC=ON -DENABLE_QRENDERDOC=OFF
49
+ cmake --build build -j$(nproc)
50
+ export RENDERDOC_PYTHON_PATH=$PWD/build/lib
51
+ ```
52
+
53
+ ### Module discovery order
54
+
55
+ 1. `RENDERDOC_PYTHON_PATH` environment variable
56
+ 2. `/usr/lib/renderdoc`, `/usr/local/lib/renderdoc`
57
+ 3. Sibling directory of `renderdoccmd` on PATH
58
+
59
+ ### Verify
60
+
61
+ ```bash
62
+ rdc doctor
63
+ ```
64
+
65
+ ## Commands
66
+
67
+ Run `rdc --help` for the full command list, or `rdc <command> --help` for details.
68
+
69
+ | Category | Commands |
70
+ |----------|----------|
71
+ | Session | `open`, `close`, `status`, `goto` |
72
+ | Inspection | `info`, `stats`, `events`, `draws`, `event`, `draw`, `log` |
73
+ | GPU state | `pipeline`, `bindings`, `shader`, `shaders`, `shader-map` |
74
+ | Resources | `resources`, `resource`, `passes`, `pass`, `usage` |
75
+ | Export | `texture`, `rt`, `buffer` |
76
+ | Search | `search`, `counters` |
77
+ | VFS | `ls`, `cat`, `tree` |
78
+ | Utility | `doctor`, `completion`, `capture`, `count` |
79
+
80
+ All commands support `--json` for machine-readable output.
81
+
82
+ ### Shell completions
83
+
84
+ ```bash
85
+ rdc completion bash > ~/.local/share/bash-completion/completions/rdc
86
+ rdc completion zsh > ~/.zfunc/_rdc
87
+ eval "$(rdc completion bash)"
88
+ ```
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ pixi run sync # Install Python deps
94
+ pixi run check # lint + typecheck + test (653 tests, 92% coverage)
95
+ ```
96
+
97
+ GPU integration tests require a real renderdoc module:
98
+
99
+ ```bash
100
+ export RENDERDOC_PYTHON_PATH=/path/to/renderdoc/build/lib
101
+ pixi run test-gpu
102
+ ```
103
+
104
+ ## License
105
+
106
+ MIT
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rdc-cli"
7
+ version = "0.2.0"
8
+ description = "Unix-friendly CLI for RenderDoc captures"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [{name = "Jim"}]
13
+ dependencies = [
14
+ "click>=8.1",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ rich = ["rich>=13.0"]
19
+ imaging = ["Pillow>=10.0"]
20
+ diff = ["Pillow>=10.0", "numpy>=1.24"]
21
+ dev = [
22
+ "pytest>=8.0",
23
+ "pytest-cov>=5.0",
24
+ "mypy>=1.10",
25
+ "ruff>=0.6",
26
+ ]
27
+
28
+ [project.scripts]
29
+ rdc = "rdc.cli:main"
30
+
31
+ [tool.setuptools]
32
+ package-dir = {"" = "src"}
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["src"]
36
+
37
+ [tool.pytest.ini_options]
38
+ addopts = "-q"
39
+ testpaths = ["tests"]
40
+ markers = ["gpu: requires real renderdoc module and GPU"]
41
+
42
+ [tool.mypy]
43
+ python_version = "3.10"
44
+ strict = true
45
+ warn_unused_configs = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ """rdc package."""
2
+
3
+ __all__ = ["__version__"]
4
+ __version__ = "0.2.0"
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+
7
+
8
+ def parse_version_tuple(value: str) -> tuple[int, int]:
9
+ """Parse RenderDoc version string into (major, minor)."""
10
+ match = re.search(r"(\d+)\.(\d+)", value)
11
+ if not match:
12
+ return (0, 0)
13
+ return int(match.group(1)), int(match.group(2))
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class RenderDocAdapter:
18
+ """Compatibility adapter for RenderDoc API changes across versions."""
19
+
20
+ controller: Any
21
+ version: tuple[int, int]
22
+
23
+ def get_root_actions(self) -> Any:
24
+ """Return root actions with compatibility handling."""
25
+ if self.version >= (1, 32) and hasattr(self.controller, "GetRootActions"):
26
+ return self.controller.GetRootActions()
27
+ if hasattr(self.controller, "GetDrawcalls"):
28
+ return self.controller.GetDrawcalls()
29
+ raise AttributeError("controller has neither GetRootActions nor GetDrawcalls")
30
+
31
+ def get_api_properties(self) -> Any:
32
+ """Return API properties from the controller."""
33
+ return self.controller.GetAPIProperties()
34
+
35
+ def get_resources(self) -> Any:
36
+ """Return all resources from the controller."""
37
+ return self.controller.GetResources()
38
+
39
+ def get_pipeline_state(self) -> Any:
40
+ """Return current pipeline state."""
41
+ return self.controller.GetPipelineState()
42
+
43
+ def get_structured_file(self) -> Any:
44
+ """Return structured file from the controller."""
45
+ return self.controller.GetStructuredFile()
46
+
47
+ def set_frame_event(self, eid: int, force: bool = True) -> None:
48
+ """Move the replay to the given event ID."""
49
+ self.controller.SetFrameEvent(eid, force)
50
+
51
+ def get_textures(self) -> Any:
52
+ """Return all texture descriptions from the controller."""
53
+ return self.controller.GetTextures()
54
+
55
+ def get_buffers(self) -> Any:
56
+ """Return all buffer descriptions from the controller."""
57
+ return self.controller.GetBuffers()
58
+
59
+ def shutdown(self) -> None:
60
+ """Shutdown the replay controller."""
61
+ self.controller.Shutdown()
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ import click
4
+
5
+ from rdc import __version__
6
+ from rdc.commands.capture import capture_cmd
7
+ from rdc.commands.completion import completion_cmd
8
+ from rdc.commands.counters import counters_cmd
9
+ from rdc.commands.doctor import doctor_cmd
10
+ from rdc.commands.events import draw_cmd, draws_cmd, event_cmd, events_cmd
11
+ from rdc.commands.export import buffer_cmd, rt_cmd, texture_cmd
12
+ from rdc.commands.info import info_cmd, log_cmd, stats_cmd
13
+ from rdc.commands.pipeline import bindings_cmd, pipeline_cmd, shader_cmd, shaders_cmd
14
+ from rdc.commands.resources import pass_cmd, passes_cmd, resource_cmd, resources_cmd
15
+ from rdc.commands.search import search_cmd
16
+ from rdc.commands.session import close_cmd, goto_cmd, open_cmd, status_cmd
17
+ from rdc.commands.unix_helpers import count_cmd, shader_map_cmd
18
+ from rdc.commands.usage import usage_cmd
19
+ from rdc.commands.vfs import cat_cmd, complete_cmd, ls_cmd, tree_cmd
20
+
21
+
22
+ @click.group(context_settings={"help_option_names": ["-h", "--help"]})
23
+ @click.version_option(version=__version__, prog_name="rdc")
24
+ def main() -> None:
25
+ """rdc: Unix-friendly CLI for RenderDoc captures."""
26
+
27
+
28
+ main.add_command(doctor_cmd, name="doctor")
29
+ main.add_command(capture_cmd, name="capture")
30
+ main.add_command(open_cmd, name="open")
31
+ main.add_command(close_cmd, name="close")
32
+ main.add_command(status_cmd, name="status")
33
+ main.add_command(goto_cmd, name="goto")
34
+ main.add_command(info_cmd, name="info")
35
+ main.add_command(stats_cmd, name="stats")
36
+ main.add_command(events_cmd, name="events")
37
+ main.add_command(draws_cmd, name="draws")
38
+ main.add_command(event_cmd, name="event")
39
+ main.add_command(draw_cmd, name="draw")
40
+ main.add_command(count_cmd, name="count")
41
+ main.add_command(shader_map_cmd, name="shader-map")
42
+ main.add_command(pipeline_cmd, name="pipeline")
43
+ main.add_command(bindings_cmd, name="bindings")
44
+ main.add_command(shader_cmd, name="shader")
45
+ main.add_command(shaders_cmd, name="shaders")
46
+ main.add_command(resources_cmd, name="resources")
47
+ main.add_command(resource_cmd, name="resource")
48
+ main.add_command(passes_cmd, name="passes")
49
+ main.add_command(pass_cmd, name="pass")
50
+ main.add_command(log_cmd, name="log")
51
+ main.add_command(ls_cmd, name="ls")
52
+ main.add_command(cat_cmd, name="cat")
53
+ main.add_command(tree_cmd, name="tree")
54
+ main.add_command(complete_cmd, name="_complete")
55
+ main.add_command(texture_cmd, name="texture")
56
+ main.add_command(rt_cmd, name="rt")
57
+ main.add_command(buffer_cmd, name="buffer")
58
+ main.add_command(search_cmd, name="search")
59
+ main.add_command(usage_cmd, name="usage")
60
+ main.add_command(completion_cmd, name="completion")
61
+ main.add_command(counters_cmd, name="counters")
62
+
63
+
64
+ if __name__ == "__main__":
65
+ main()
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ import subprocess
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+
10
+ def _find_renderdoccmd() -> str | None:
11
+ in_path = shutil.which("renderdoccmd")
12
+ if in_path:
13
+ return in_path
14
+
15
+ common_paths = [
16
+ Path("/opt/renderdoc/bin/renderdoccmd"),
17
+ Path("/usr/local/bin/renderdoccmd"),
18
+ ]
19
+ for path in common_paths:
20
+ if path.exists() and path.is_file():
21
+ return str(path)
22
+ return None
23
+
24
+
25
+ @click.command(
26
+ "capture",
27
+ context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
28
+ )
29
+ @click.option("--api", "api_name", type=str, help="Capture API (maps to --opt-api).")
30
+ @click.option("-o", "--output", type=click.Path(path_type=Path), help="Output capture file path.")
31
+ @click.option("--list-apis", is_flag=True, help="List capture APIs via renderdoccmd and exit.")
32
+ @click.pass_context
33
+ def capture_cmd(
34
+ ctx: click.Context,
35
+ api_name: str | None,
36
+ output: Path | None,
37
+ list_apis: bool,
38
+ ) -> None:
39
+ """Thin wrapper around renderdoccmd capture."""
40
+ bin_path = _find_renderdoccmd()
41
+ if not bin_path:
42
+ click.echo("error: renderdoccmd not found in PATH", err=True)
43
+ raise SystemExit(1)
44
+
45
+ argv: list[str] = [bin_path, "capture"]
46
+
47
+ if list_apis:
48
+ argv.append("--list-apis")
49
+ else:
50
+ if api_name:
51
+ argv.extend(["--opt-api", api_name])
52
+ if output:
53
+ argv.extend(["--capture-file", str(output)])
54
+ argv.extend(ctx.args)
55
+
56
+ result = subprocess.run(argv, check=False)
57
+ if result.returncode != 0:
58
+ raise SystemExit(result.returncode)
59
+
60
+ if output and not list_apis:
61
+ click.echo(f"capture saved: {output}", err=True)
62
+ click.echo(f"next: rdc open {output}", err=True)
@@ -0,0 +1,49 @@
1
+ """Shell completion script generation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+
8
+ import click
9
+
10
+ _SUPPORTED_SHELLS = ("bash", "zsh", "fish")
11
+
12
+
13
+ def _detect_shell() -> str:
14
+ """Detect current shell from $SHELL."""
15
+ name = Path(os.environ.get("SHELL", "bash")).name
16
+ return name if name in _SUPPORTED_SHELLS else "bash"
17
+
18
+
19
+ def _generate(shell: str) -> str:
20
+ """Generate completion script via Click's built-in mechanism."""
21
+ from click.shell_completion import get_completion_class
22
+
23
+ from rdc.cli import main # deferred: rdc.cli imports this module
24
+
25
+ cls = get_completion_class(shell)
26
+ if cls is None:
27
+ raise click.ClickException(f"Unsupported shell: {shell}")
28
+ comp = cls(cli=main, ctx_args={}, prog_name="rdc", complete_var="_RDC_COMPLETE")
29
+ return comp.source()
30
+
31
+
32
+ @click.command("completion")
33
+ @click.argument("shell", required=False, type=click.Choice(_SUPPORTED_SHELLS))
34
+ def completion_cmd(shell: str | None) -> None:
35
+ """Generate shell completion script.
36
+
37
+ Prints the completion script to stdout. Redirect or eval as needed.
38
+
39
+ \b
40
+ Examples:
41
+ rdc completion bash > ~/.local/share/bash-completion/completions/rdc
42
+ rdc completion zsh > ~/.zfunc/_rdc
43
+ eval "$(rdc completion bash)"
44
+ """
45
+ if shell is None:
46
+ shell = _detect_shell()
47
+ click.echo(f"# Detected shell: {shell}", err=True)
48
+
49
+ click.echo(_generate(shell))
@@ -0,0 +1,50 @@
1
+ """rdc counters command — GPU performance counters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import click
8
+
9
+ from rdc.commands.info import _daemon_call
10
+ from rdc.formatters.json_fmt import write_json
11
+
12
+
13
+ @click.command("counters")
14
+ @click.option("--list", "show_list", is_flag=True, help="List available counters.")
15
+ @click.option("--eid", type=int, default=None, help="Filter to specific event ID.")
16
+ @click.option("--name", "name_filter", default=None, help="Filter counters by name substring.")
17
+ @click.option("--json", "use_json", is_flag=True, help="JSON output.")
18
+ def counters_cmd(
19
+ show_list: bool,
20
+ eid: int | None,
21
+ name_filter: str | None,
22
+ use_json: bool,
23
+ ) -> None:
24
+ """Query GPU performance counters.
25
+
26
+ Use --list to enumerate available counters, or run without --list
27
+ to fetch counter values for all draw events.
28
+ """
29
+ if show_list:
30
+ result = _daemon_call("counter_list")
31
+ if use_json:
32
+ write_json(result)
33
+ return
34
+ click.echo("ID\tNAME\tUNIT\tTYPE\tCATEGORY")
35
+ for c in result.get("counters", []):
36
+ click.echo(f"{c['id']}\t{c['name']}\t{c['unit']}\t{c['type']}\t{c['category']}")
37
+ return
38
+
39
+ params: dict[str, Any] = {}
40
+ if eid is not None:
41
+ params["eid"] = eid
42
+ if name_filter is not None:
43
+ params["name"] = name_filter
44
+ result = _daemon_call("counter_fetch", params)
45
+ if use_json:
46
+ write_json(result)
47
+ return
48
+ click.echo("EID\tCOUNTER\tVALUE\tUNIT")
49
+ for r in result.get("rows", []):
50
+ click.echo(f"{r['eid']}\t{r['counter']}\t{r['value']}\t{r['unit']}")