Skip to content

Reading counters, meters, registers, and action-profile entries

I want to: read the per-cell values of a counter, meter, or register array on the device, or read the members and groups of an action profile — using the same fluent query-builder shape jp4 uses for table reads.

The query-builder shape

Every entity-read family on P4Switch (v1.4+) follows the same shape:

MethodReturnsServer-side filterClient-side filterTerminals
sw.readCounter("name")CounterReadQuery.index(long).where(Predicate).all() / .one() / .stream() / .allAsync() / .oneAsync()
sw.readMeter("name")MeterReadQuery.index(long).where(Predicate)same
sw.readRegister("name")RegisterReadQuery.index(long).where(Predicate)same
sw.readActionProfileMember(n)ActionProfileMemberReadQuery.memberId(long).where(Predicate)same
sw.readActionProfileGroup(n)ActionProfileGroupReadQuery.groupId(long).where(Predicate)same

Learn one shape, use all five.

Reading counters

java
import io.github.zhh2001.jp4.P4Switch;
import io.github.zhh2001.jp4.entity.CounterEntry;

import java.util.List;
import java.util.Optional;

List<CounterEntry> all = sw.readCounter("MyIngress.pkt_counter").all();
for (CounterEntry e : all) {
    System.out.printf("cell %d: packets=%d bytes=%d%n",
            e.index(), e.packetCount(), e.byteCount());
}

// Read one specific cell.
Optional<CounterEntry> cell0 = sw.readCounter("MyIngress.pkt_counter")
        .index(0L)
        .one();

// Only cells that have seen traffic.
List<CounterEntry> nonZero = sw.readCounter("MyIngress.pkt_counter")
        .where(e -> e.packetCount() > 0L)
        .all();

CounterEntry carries the resolved counter name, cell index, and both packetCount and byteCount as primitive longs. Which value is meaningful is determined by the counter's unit in P4Info (BYTES / PACKETS / BOTH).

Reading meters

java
import io.github.zhh2001.jp4.entity.MeterEntry;
import io.github.zhh2001.jp4.entity.MeterConfig;
import io.github.zhh2001.jp4.entity.MeterCounterData;

for (MeterEntry e : sw.readMeter("MyIngress.rate_meter").all()) {
    MeterConfig cfg = e.config();
    MeterCounterData cd = e.counterData();
    System.out.printf("cell %d cir=%d cburst=%d green=%d red=%d%n",
            e.index(), cfg.cir(), cfg.cburst(),
            cd.green().packetCount(), cd.red().packetCount());
}

MeterEntry is a nested record: meter name + index + MeterConfig (cir, cburst, pir, pburst, eburst) + MeterCounterData (green, yellow, red per-color counter data). The eburst field is only used by srTCM meters; trTCM meters surface it as zero.

Reading registers

java
import io.github.zhh2001.jp4.entity.RegisterEntry;
import p4.v1.P4DataOuterClass.P4Data;

for (RegisterEntry e : sw.readRegister("MyIngress.flow_counters").all()) {
    P4Data datum = P4Data.parseFrom(e.data().toByteArray());
    byte[] value = datum.getBitstring().toByteArray();
    System.out.printf("cell %d value-bytes=%d%n", e.index(), value.length);
}

RegisterEntry.data carries the serialised bytes of the wire p4.v1.P4Data proto — that is, what proto.getData().toByteArray() returns. The bit<W> / int<W> case is overwhelmingly common in practice (extract via P4Data.parseFrom(...).getBitstring()), but the full P4Data oneof — struct, header, header_stack, enum, error, varbit, bool — is reachable.

Reading action profile members and groups

java
import io.github.zhh2001.jp4.entity.ActionProfileMember;
import io.github.zhh2001.jp4.entity.ActionProfileGroup;
import io.github.zhh2001.jp4.entity.WeightedMember;

// Members of an action profile.
for (ActionProfileMember m : sw.readActionProfileMember("MyIngress.lb_ap").all()) {
    System.out.printf("member %d → action %s%n",
            m.memberId(), m.action().name());
}

// Groups of an action profile.
for (ActionProfileGroup g : sw.readActionProfileGroup("MyIngress.lb_ap").all()) {
    System.out.printf("group %d maxSize=%d members=%d%n",
            g.groupId(), g.maxSize(), g.members().size());
    for (WeightedMember wm : g.members()) {
        String watch = wm.watchPort() == null
                ? "<unset>"
                : "(" + wm.watchPort().toByteArray().length + " bytes)";
        System.out.printf("  weighted member=%d weight=%d watchPort=%s%n",
                wm.memberId(), wm.weight(), watch);
    }
}

ActionProfileMember.action is an ActionInstance — the same value type used by TableEntry.action(), so an action-profile member round-trips into the same shape a direct-action table entry does.

BMv2 caveat: register reads return UNIMPLEMENTED

BMv2 simple_switch_grpc 1.15.1 (the version jp4 tests against) returns Status{code=UNIMPLEMENTED, description=Register reads are not supported yet} for the RegisterEntry Read RPC. Counter, meter, action-profile, multicast, and clone reads work normally; only register reads hit this. See Troubleshooting: BMv2 returns UNIMPLEMENTED on register read.

See also

Released under the Apache License 2.0.