pyGdbToolkit 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,99 @@
1
+ # SPDX-FileCopyrightText: 2026 H2Lab Development Team
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """The architecture-neutral ``fault_info`` GDB command entry point."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import gdb
9
+ from rich import box
10
+ from rich.console import Console
11
+ from rich.panel import Panel
12
+ from rich.table import Table
13
+
14
+ from .arch import (
15
+ DiagnosticPanel,
16
+ DiagnosticReport,
17
+ DiagnosticServiceName,
18
+ DiagnosticTable,
19
+ )
20
+ from .diagnostic_runtime import gdb_diagnostic_access
21
+ from .session import SESSION, ToolkitSession
22
+ from .target_memory import TargetReadError
23
+
24
+ CONSOLE = Console(force_terminal=True)
25
+
26
+
27
+ def run_fault_analysis(
28
+ session: ToolkitSession = SESSION,
29
+ ) -> DiagnosticReport:
30
+ """Run the registered fault-analysis service against the selected target.
31
+
32
+ Parameters
33
+ ----------
34
+ session : ToolkitSession
35
+ Unified session providing target access and the portable diagnostic runtime.
36
+
37
+ Returns
38
+ -------
39
+ DiagnosticReport
40
+ Renderer-independent diagnostic report returned by the selected service.
41
+
42
+ Raises
43
+ ------
44
+ gdb.GdbError
45
+ If target access fails or no service supports the selected target.
46
+ """
47
+ try:
48
+ result = session.diagnose(
49
+ DiagnosticServiceName.FAULT_ANALYSIS,
50
+ gdb_diagnostic_access(),
51
+ )
52
+ except TargetReadError as error:
53
+ raise gdb.GdbError(str(error)) from error
54
+ if result.report is not None:
55
+ return result.report
56
+ if result.access_error:
57
+ raise gdb.GdbError(result.unavailable_reason or "target unavailable")
58
+ raise gdb.GdbError("fault_info only supports ARM Cortex-M targets")
59
+
60
+
61
+ def render_report(report: DiagnosticReport) -> None:
62
+ """Render a generic diagnostic report through the shared Rich console."""
63
+ blocks = report.blocks or (*report.tables, *report.panels)
64
+ for block in blocks:
65
+ if isinstance(block, DiagnosticTable):
66
+ table = Table(
67
+ title=block.title,
68
+ box=box.SIMPLE_HEAVY,
69
+ header_style="bold cyan",
70
+ show_header=True,
71
+ )
72
+ for column in block.columns:
73
+ table.add_column(column, style="bold" if column in {"Property", "Register"} else "")
74
+ for row in block.rows:
75
+ table.add_row(*("" if value is None else str(value) for value in row.values))
76
+ CONSOLE.print(table)
77
+ elif isinstance(block, DiagnosticPanel):
78
+ CONSOLE.print(
79
+ Panel(
80
+ "\n".join(block.lines),
81
+ title=f"[bold yellow]{block.title}[/bold yellow]",
82
+ box=box.SIMPLE_HEAVY,
83
+ )
84
+ )
85
+
86
+
87
+ class FaultInfoCmd(gdb.Command):
88
+ """Run portable fault analysis and render the returned diagnostic report."""
89
+
90
+ def __init__(self) -> None:
91
+ """Register the command with GDB."""
92
+ super().__init__("fault_info", gdb.COMMAND_USER)
93
+
94
+ def invoke(self, arg: str, from_tty: bool) -> None:
95
+ """Execute ``fault_info`` without command-line arguments."""
96
+ del from_tty
97
+ if arg.strip():
98
+ raise gdb.GdbError("fault_info does not accept arguments")
99
+ render_report(run_fault_analysis())
@@ -0,0 +1,116 @@
1
+ # SPDX-FileCopyrightText: 2026 H2Lab Development Team
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """The ``lscpu`` GDB command for Cortex-M targets."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import gdb
9
+ from rich import box
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+ from rich.text import Text
13
+
14
+ from .arch.arm.coresight import RomTableDiscovery
15
+ from .arch.arm.models import DeviceReport, FieldValue
16
+ from .arch.arm.session_state import device_report
17
+ from .target_memory import TargetReadError
18
+
19
+ CONSOLE = Console(force_terminal=True)
20
+
21
+
22
+ class LscpuCmd(gdb.Command):
23
+ """Display CPU and electronic-signature information for a Cortex-M target."""
24
+
25
+ def __init__(self) -> None:
26
+ """Register the command with GDB."""
27
+ super().__init__("lscpu", gdb.COMMAND_USER)
28
+
29
+ def invoke(self, arg: str, from_tty: bool) -> None:
30
+ """Run the command.
31
+
32
+ Parameters
33
+ ----------
34
+ arg
35
+ The user-supplied argument string. This command accepts no arguments.
36
+ from_tty
37
+ Whether GDB invoked the command from its terminal.
38
+
39
+ Raises
40
+ ------
41
+ gdb.GdbError
42
+ If arguments are supplied or the target CPUID cannot be read.
43
+ """
44
+ del from_tty
45
+ if arg.strip():
46
+ raise gdb.GdbError("lscpu does not accept arguments")
47
+
48
+ try:
49
+ report = device_report()
50
+ except TargetReadError as error:
51
+ raise gdb.GdbError(str(error)) from error
52
+
53
+ render_report(report)
54
+
55
+
56
+ def render_report(report: DeviceReport) -> None:
57
+ """Render a stable device report through the shared Rich console.
58
+
59
+ Parameters
60
+ ----------
61
+ report
62
+ Decoded CPU and device information.
63
+ """
64
+ table = Table(
65
+ title="Cortex-M CPU report",
66
+ box=box.SIMPLE_HEAVY,
67
+ header_style="bold cyan",
68
+ show_header=True,
69
+ )
70
+ table.add_column("Property", style="bold", no_wrap=True)
71
+ table.add_column("Value")
72
+ table.add_row("Core type", report.target.core_name)
73
+ table.add_row("Core revision", report.target.rnp_revision)
74
+ table.add_row("Implementer", report.target.implementer_name)
75
+ table.add_row(
76
+ "MCU ROM JEP106 identity",
77
+ _field_text(_rom_jep106_field(report.discovery.mcu_rom)),
78
+ )
79
+ table.add_row("Vendor", report.vendor)
80
+ table.add_row("Product line", _field_text(report.product_line))
81
+ table.add_row("Part number", _field_text(report.part_number))
82
+ table.add_row("RAM", _field_text(report.ram))
83
+ table.add_row("Flash", _field_text(report.flash))
84
+ table.add_row("Package type", _field_text(report.package))
85
+ table.add_row("Serial number", _field_text(report.serial_number))
86
+ CONSOLE.print(table)
87
+
88
+
89
+ def _field_text(field: FieldValue) -> Text:
90
+ """Create a Rich value cell with a visible unavailable-state reason.
91
+
92
+ Parameters
93
+ ----------
94
+ field
95
+ A ``FieldValue`` supplied by the report model.
96
+
97
+ Returns
98
+ -------
99
+ Text
100
+ The formatted field cell.
101
+ """
102
+ display = field.display()
103
+ if field.is_available:
104
+ return Text(display)
105
+ return Text.assemble(("Unavailable: ", "yellow"), display.removeprefix("Unavailable: "))
106
+
107
+
108
+ def _rom_jep106_field(discovery: RomTableDiscovery) -> FieldValue:
109
+ """Format a table root's validated JEP106 identity when it is available."""
110
+ if discovery.table is None:
111
+ assert discovery.unavailable_reason is not None
112
+ return FieldValue.unavailable(discovery.unavailable_reason)
113
+ identity = discovery.table.identity.peripheral_id.jep106
114
+ if identity is None:
115
+ return FieldValue.unavailable("ROM-table root does not advertise a JEDEC manufacturer")
116
+ return FieldValue.known(identity.display())