Cint: the C interpreter

Cint is a C interpreter built into the SDK. It runs C-like programs inside syncd and lets them call the SDK API (the bcm_* functions) directly, with real structures, enums and constants. Use it for API features that have no diag-shell command, to read state in the SDK’s own terms, or to repeat a sequence of calls from a script. Start it with cint.

This chapter covers running Cint and the interpreter itself; The Cint language describes the C subset it accepts, and Cint recipes has ready-made read-only programs for Trident 3.

Warning

A Cint program runs inside syncd with full access to the SDK. A call that changes state (*_set, *_add, *_create, *_destroy, *_delete…) changes the hardware behind the SAI’s back: SONiC does not know about the change, will not undo it, and may later fail or overwrite it. A wrong argument, such as a bad pointer or an array that is too small, can crash syncd; SONiC then restarts syncd and swss and traffic stops while the switch is reprogrammed. There is no way to interrupt a running program: a loop that never ends keeps the shell busy until syncd is restarted. On a production switch, run only read-only programs that you have read and understood.

When to use Cint

  • Diag commands first. For displaying state, the diag commands (l3 defip show, l2 show, ps, …) are quicker to use and harder to get wrong. Prefer them when they show what you need.

  • Cint when no command shows it, when you want the API’s view of an object (the values SAI passed to the SDK, before they are encoded into table entries), or when you need to loop, filter or combine values. Cint can also run diag commands itself; see Running diag commands from Cint.

  • Raw tables with dump, getreg or listmem when you need the hardware encoding rather than the API view.

Starting and leaving the interpreter

cint with no argument prints Entering C Interpreter. Type 'exit;' to quit. and starts an interactive session at the cint> prompt. Statements end with ; and are run as soon as they are complete; a function definition or a { … } block may span several lines. exit; or quit; returns to drivshell>.

Unvalidated

drivshell>cint
cint> print bcm_info_t;
cint> exit;

Interactive mode needs a terminal, so use it through bcmsh. Through bcmcmd the shell stays at cint> and bcmcmd waits until its timeout; use a file instead.

Running a file

cint <file> [<arg> …] reads and runs a file, then returns to drivshell>. The file is opened by syncd, so the path is a path inside the syncd container. Copy the file to /root there with docker cp (as root), and use an absolute path:

Hardware-validated

$ sudo docker cp port_status.c syncd:/root/
$ bcmcmd -t 120 "cint /root/port_status.c"

Do not copy into /tmp or /var/tmp: inside the syncd container both are separate in-memory file systems, and docker cp writes underneath them, where syncd cannot see the file. docker cp still exits 0, and cint then reports error: could not open file.

A file is read and run one top-level statement at a time, as it is parsed. This has three consequences:

  • Statements before an error have already run when the error is reported.

  • After a syntax error, the rest of the file is still read and checked but nothing more is run. Every later line is skipped, including any cleanup code at the end of the file.

  • A run-time error (an undeclared name, a wrong argument type, a failed printf conversion) abandons the top-level statement it occurs in. If that statement is a function call, the rest of the function, and of every function that called it, is skipped. The file continues with the next top-level statement.

exit; in a file stops reading it at that point.

cint always returns success to the shell, even when the program failed or the file could not be opened, and bcmcmd exits 0. Make your program print a final line such as DONE and check for it, and read the whole output for error lines.

bcmcmd waits 30 seconds for the prompt by default. Programs that walk large tables (the L2 or route table) can take longer; give a larger -t. If bcmcmd times out, the program keeps running inside syncd.

Arguments

Words after the file name are passed to the program in two global variables: ARGC (an int) and ARGV (an array of strings), starting with the first word after the file name. At most 16 arguments are allowed. There is no atoi in Cint; convert numbers yourself (the Cint recipes show a small helper).

if (ARGC > 0) {
    printf("first argument: %s\n", ARGV[0]);
}

Line numbers in error messages

Syntax errors always show the offending line and its number. Run-time errors are printed as ** error: <message> without a location. To get the file and line in run-time errors too, put allow_file_info before the file name:

Unvalidated

drivshell> cint allow_file_info /root/port_status.c

The word allow_file_info is then also passed to the program as ARGV[0], so the program’s own arguments start at ARGV[1].

#include

A file can include another with #include "/path/file.c". A relative name is looked up from syncd’s working directory and then in the directories of the CINT_INCLUDE_PATH environment variable of syncd; use absolute paths. Includes may nest 64 deep. #include is the only preprocessor directive; see The Cint language.

The interpreter keeps its state

The interpreter lives as long as syncd. Global variables and functions defined by one cint run are still there in the next run, and in the next interactive session. Two consequences:

  • Running a file twice fails on the second run with identifier '<name>' redeclared for every global variable it declares. Defining a function again is allowed and replaces the old definition.

  • A name defined by an earlier program can hide a mistake in a new one.

Start files with cint_reset();, as Broadcom’s own examples do (OpenBCM sdk-6.5.27 CINT interpreter sources (src/appl/cint, src/appl/diag/cint) and Trident 3 CINT examples with logs (src/examples/xgs/TD3)). It discards everything defined so far and reloads the SDK’s functions and types. It affects only the interpreter, not the switch.

What the interpreter knows

Cint can only call functions and use types that were registered with it when the SDK was built. On this build that is:

  • the SDK API: 10,521 bcm_* functions (1,445 of them SDK macros such as BCM_GPORT_LOCAL_SET, registered as functions), 1,385 structures, 1,180 enums, 212 typedefs, 304 callback types and 11,916 constants;

  • a few soc_* functions (soc_mem_read, soc_property_get …), diag_* register and memory helpers, sal_* utilities (sal_memset, sal_strcmp, sal_sleep …) and COMPILER_64_* helpers for 64-bit values;

  • PHY and port-macro APIs (phymod_*, portmod_*);

  • logical-table (bcmlt_*) and embedded-core (edki_*) functions, which belong to newer switch families and may not work on Trident 3;

  • the interpreter’s built-ins, listed below.

In total that is 11,457 functions, 1,585 structures, 1,294 enums, 331 callback types and 12,482 constants. Cint API on this build lists all of them with prototypes, and lists separately everything that is not in the public OpenBCM 6.5.27 release.

A function that exists in the library but is not registered cannot be called, and an extern declaration does not change that: for example soc_mem_search is not available. Some registered functions are unusable because a type they need is missing; soc_reg_get fails because soc_reg_t is not a known type. Check a name with cint_defined("name") (1 if known) or list names with cint list.

Interpreter commands

Statements starting with the word cint are commands to the interpreter itself. Arguments are bare words, not quoted strings.

cint list [<kind> …] [<substring>];

List registered names containing <substring>, with their kind. <kind> restricts the list to function, struct, enum, constant, atomic, fp (callback types), macro or iterator. Without a substring the list is very long. Example: cint list function bcm_port_info;.

cint var <name>;

Show a variable’s type, address and size.

cint typecheck;

Check every registered type for consistency.

cint load (loading a shared library) and cint yapi are not included in this build and print an error.

To see the layout of a structure or the values of an enum, print the type name:

Unvalidated

cint> print bcm_port_info_t;
cint> print bcm_stat_val_t;
cint> cint list function bcm_port_info;

Looking up a function

Cint tells you whether a name exists and how a type is laid out, but not what a function does. Combine it with these references:

On the switch

cint list function <part of name>; finds functions, print <type>; shows a structure’s members and size or an enum’s values as this build has them, and cint_defined("name") checks a single name. When a public header and the switch disagree about a structure, the switch is right: its SDK is newer than any public release.

This guide

Cint API on this build has the prototype of every registered function, grouped by module, and separate lists of the functions, structures, enums and values, callback types and constants that are not in public OpenBCM. For those there is no public documentation; the prototype and layout are what you have to go on. Cint recipes has complete read-only programs for this switch.

OpenBCM headers

The public SDK API headers, one per module (l3.h for bcm_l3_*, port.h for bcm_port_*, field.h for bcm_field_* …), declare each function with its structures, flags and short comments. They are the closest thing to an API reference (OpenBCM sdk-6.5.27 SDK API headers (include/bcm/*.h) and the Cint registration data generated from them (bcm_cint_data.c)).

OpenBCM example programs

Broadcom’s Cint examples for each XGS family, including a Trident 3 set with a summary table and console logs, show complete call sequences for VLANs, L3, MPLS, VXLAN, field processor and more (OpenBCM sdk-6.5.27 CINT interpreter sources (src/appl/cint, src/appl/diag/cint) and Trident 3 CINT examples with logs (src/examples/xgs/TD3)). They are written for a bare SDK (BCM.0>) and set up the switch from scratch; read them for the call order, do not run them on a SONiC switch.

OpenNSL documentation

OpenNSL is a subset of the SDK API with the opennsl_ prefix instead of bcm_ (opennsl_l3_route_add for bcm_l3_route_add). Its API reference and overview pages for L2, L3, ports, COS, KNET, statistics and trunks explain parameters and flags that carry over to the bcm_* functions (OpenNSL 3.x documentation (doc/html) and sal/driver.h (opennsl_driver_shell)).

Open-source network operating systems

FBOSS drives the bcm_* API on Trident and Tomahawk switches; its Broadcom agent shows production call sequences for egress objects, ECMP, routes, ports and ACLs (FBOSS Broadcom agent (fboss/agent/hw/bcm): C++ code driving the bcm_* SDK API). Stratum wraps about 150 bcm_* functions in a single file (Stratum BcmSdkWrapper (stratum/hal/lib/bcm/sdk/bcm_sdk_wrapper.cc): bcm_* SDK API calls).

SDK release notes

Broadcom’s per-release spreadsheets of resolved issues and improvements name the bcm_* functions and structures each fix concerns (SDK-6.5.20..6.5.27 Resolved-Issues-Improvements.xlsx (7 files)).

Broadcom’s SAI library, which makes the bcm_* calls on a SONiC switch, is not open source, so there is no public code showing which calls SONiC makes for a given SAI object. FBOSS and Stratum are the closest public equivalent.

Built-in functions

Function

Purpose

printf, sprintf, snprintf

Formatted output; see The Cint language.

cint_reset()

Forget all variables and functions defined so far.

cint_defined("name")

1 if name is a known variable, function, type, enum value or constant, else 0.

cint_variable_dump()

Print all variables.

bshell(unit, "command")

Run a diag-shell command line; see below.

rcload_file_noargs(unit, "file")

Run an rc script (see Scripting).

bcm_errmsg(rv)

Text for an SDK return code, e.g. Entry not found.

sal_sleep(sec), sal_usleep(usec), sal_time(), sal_time_usecs()

Delays and time.

cint_timer_start, cint_timer_stop, cint_timer_get, clock_gettime

Timing.

sal_strcmp, sal_strlen, sal_memset, sal_memcpy …

Replacements for the C library, which is not available.

Constants TRUE, FALSE and NULL are predefined.

Running diag commands from Cint

bshell(unit, "command line") runs a diag-shell command line, as if typed at the prompt, and returns its result: 0 for success, a negative value for failure (−3 for an unknown command, which also prints Unknown command: <word>). Its output goes to the same place as the program’s. It is useful for showing state between API calls, as Broadcom’s examples do with l3 egress show or vlan show. The same safety rules apply as at the prompt.

BCMSAI Cint

The SAI’s own BCMSAI Cint is not included in this build; it only prints a message. Use cint.

Troubleshooting

Message

Meaning

error: could not open file '<file>'

The path does not exist inside the syncd container. Copy the file to /root with docker cp (not to /tmp; see Running a file) and use an absolute path.

syntax error, unexpected … with a ^^^ marker

The line is not valid Cint. Common causes: a C cast such as (int) x, a declaration inside for ( … ), #define, struct tag var; for a structure defined in Cint (write tag var;), a missing ;. Nothing after this point in the file runs.

identifier '<name>' undeclared

The name is not defined, or the function is not registered with Cint (check with cint_defined). extern does not help.

identifier '<name>' redeclared

The variable exists from an earlier run. Add cint_reset(); at the top of the file.

identifier '<name>' has unknown type, cannot declare variable of type '<type>'

The type is not registered in this build.

arg <n> to function '<f>' was wrong type: expected <t>, received <t>

Pass a variable of the declared type; remember & for output parameters.

wrong number of parameters to function '<f>'

Check the prototype with cint list function <f>;.

printf expected integer type but received <type>

%d/%x was given an enum or an SDK type such as bcm_ip_t or uint64. Copy the value to an int first, or use $name (see The Cint language).

variable '<name>' is undefined

A $ in a printf format string is read as a variable name. Write $ only for that purpose.

attempt to dereference NULL pointer, array subscript out of range

Checked by Cint before the access; fix the program.

A negative number from an SDK call

An SDK return code (BCM_E_…); print bcm_errmsg(rv). See The Cint language.

No output at all and bcmcmd exit 62

The program is still running or the shell is at cint>; see Running a file.