This branch adds the capability to automatically generate counters of IPC
operations using the Hubris counters crate to Idol code generation. Counters
can be generated for both client and server stubs, and will generate a separate
counter for each IPC operation in an interface. Fallible IPC operations will
generate separate counters of Ok and Err responses, with the Err path
including child counters of each variant of the IPC's error type, provided it
derives the Count trait.
In order to configure how and where counters are generated, I've added a new
Generator struct that represents a code-generation configuration. This type
has flags for whether counters are enabled, and a flag that changes the
server-side counter generation to track all ClientError variants globally
rather than per-IPC, if this is necessary to limit the size of the generated
counter table. I've moved several functions into methods on the Generator
type and changed the free functions to call those methods with
Generator::default() for API compatibility. Unfortunately, this makes the diff
kind of unpleasant to review, since it required moving and re-indenting a bunch
of code. It probably makes sense to review the diff prior to that change first
--- sorry about that.
This also requires additions to the counters crate in the corresponding Hubris
PR #1642.
As an example, the counters generated by this branch for the Gimlet sequencer
IPC API look like this in Humility. Note that a separate set of counters is
generated for each task which is a client of the sequencer:
This branch adds the capability to automatically generate counters of IPC operations using the Hubris
counters
crate to Idol code generation. Counters can be generated for both client and server stubs, and will generate a separate counter for each IPC operation in an interface. Fallible IPC operations will generate separate counters ofOk
andErr
responses, with theErr
path including child counters of each variant of the IPC's error type, provided it derives theCount
trait.In order to configure how and where counters are generated, I've added a new
Generator
struct that represents a code-generation configuration. This type has flags for whether counters are enabled, and a flag that changes the server-side counter generation to track allClientError
variants globally rather than per-IPC, if this is necessary to limit the size of the generated counter table. I've moved several functions into methods on theGenerator
type and changed the free functions to call those methods withGenerator::default()
for API compatibility. Unfortunately, this makes the diff kind of unpleasant to review, since it required moving and re-indenting a bunch of code. It probably makes sense to review the diff prior to that change first --- sorry about that.This also requires additions to the
counters
crate in the corresponding Hubris PR #1642.As an example, the counters generated by this branch for the Gimlet sequencer IPC API look like this in Humility. Note that a separate set of counters is generated for each task which is a client of the sequencer:
Humility output:
```console $ humility -d hubris.c12.autocounts.core.3 counters --full __SEQUENCER_CLIENT` humility: attached to dump control_plane_agent | +---> drv_gimlet_seq_api::__SEQUENCER_CLIENT_COUNTERS: 0 get_state(_) 0 | get_state(Ok) 0 | get_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 set_state(_) 0 | set_state(Ok) 0 | set_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_on(_) 0 | fans_on(Ok) 0 | fans_on(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_off(_) 0 | fans_off(Ok) 0 | fans_off(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 send_hardware_nmi(_) 0 | send_hardware_nmi(Ok) 0 | send_hardware_nmi(Err) 0 read_fpga_regs(_) 0 | read_fpga_regs(Ok) 0 | read_fpga_regs(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) gimlet_inspector | +---> drv_gimlet_seq_api::__SEQUENCER_CLIENT_COUNTERS: 0 get_state(_) 0 | get_state(Ok) 0 | get_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 set_state(_) 0 | set_state(Ok) 0 | set_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_on(_) 0 | fans_on(Ok) 0 | fans_on(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_off(_) 0 | fans_off(Ok) 0 | fans_off(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 send_hardware_nmi(_) 0 | send_hardware_nmi(Ok) 0 | send_hardware_nmi(Err) 0 read_fpga_regs(_) 0 | read_fpga_regs(Ok) 0 | read_fpga_regs(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) host_sp_comms | +---> drv_gimlet_seq_api::__SEQUENCER_CLIENT_COUNTERS: 4 get_state(_) 4 +---> get_state(Ok) 0 | get_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 set_state(_) 0 | set_state(Ok) 0 | set_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_on(_) 0 | fans_on(Ok) 0 | fans_on(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_off(_) 0 | fans_off(Ok) 0 | fans_off(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 send_hardware_nmi(_) 0 | send_hardware_nmi(Ok) 0 | send_hardware_nmi(Err) 0 read_fpga_regs(_) 0 | read_fpga_regs(Ok) 0 | read_fpga_regs(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) power | +---> drv_gimlet_seq_api::__SEQUENCER_CLIENT_COUNTERS: 67 get_state(_) 67 +---> get_state(Ok) 0 | get_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 set_state(_) 0 | set_state(Ok) 0 | set_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_on(_) 0 | fans_on(Ok) 0 | fans_on(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_off(_) 0 | fans_off(Ok) 0 | fans_off(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 send_hardware_nmi(_) 0 | send_hardware_nmi(Ok) 0 | send_hardware_nmi(Err) 0 read_fpga_regs(_) 0 | read_fpga_regs(Ok) 0 | read_fpga_regs(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) thermal | +---> drv_gimlet_seq_api::__SEQUENCER_CLIENT_COUNTERS: 146 get_state(_) 146 +---> get_state(Ok) 0 | get_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 set_state(_) 0 | set_state(Ok) 0 | set_state(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_on(_) 0 | fans_on(Ok) 0 | fans_on(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 fans_off(_) 0 | fans_off(Ok) 0 | fans_off(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) 0 send_hardware_nmi(_) 0 | send_hardware_nmi(Ok) 0 | send_hardware_nmi(Err) 0 read_fpga_regs(_) 0 | read_fpga_regs(Ok) 0 | read_fpga_regs(Err(_)) 0 | Err(IllegalTransition) 0 | Err(MuxToHostCPUFailed) 0 | Err(MuxToSPFailed) 0 | Err(ReadRegsFailed) 0 | Err(CPUNotPresent) 0 | Err(UnrecognizedCPU) 0 | Err(A1Timeout) 0 | Err(A0TimeoutGroupC) 0 | Err(A0Timeout) 0 | Err(I2cFault) 0 | Err(ServerRestarted) ```