GOTO-INSTRUMENT(1)
๐ฆ GOTO-INSTRUMENT(1) ๐ฆ
okay bestie, welcome to the goto-instrument page, section 1 of the User Commands manual. buckle up, she's a long one ๐
โจ NAME โจ
goto-instrument โ do analysis stuff or instrumentation stuff on goto binaries ๐
โจ SYNOPSIS โจ
goto-instrument [-?] [-h] [--help]
show help
goto-instrument --version
show version and exit
goto-instrument [options] in [out]
perform analysis or instrumentation
so basically: no input file = you're just asking for help or the version. give it an in and she does the thing. ๐ธ
โจ DESCRIPTION โจ
goto-instrument reads a GOTO binary, does whatever program transformation you asked for, and then writes the resulting program back out to disk as a GOTO binary. that's the whole vibe. no cap. ๐ฆ
โจ OPTIONS โจ
๐ Dump Source ๐
--dump-cโ spit out C source, bestie โจ--dump-c-type-headerm โ generate a C header for the types that are local in m--dump-cppโ spit out C++ source instead--no-system-headersโ generate C source but expand the libc includes--use-all-headersโ generate C source with all the includes, go big--harnessโ include the input generator in the output ๐ซถ--hornโ print the program as constrained horn clauses
๐ฉบ Diagnosis ๐ฉบ
--show-propertiesโ show the properties but don't run the analysis, we're just looking--document-properties-htmlโ generate HTML property documentation ๐--document-properties-latexโ generate Latex property documentation ๐--show-symbol-tableโ show the loaded symbol table--list-symbolsโ list symbols with their type info--show-goto-functionsโ show the loaded goto program--list-goto-functionsโ list the loaded goto functions--count-elocโ count effective lines of code (eloc my beloved)--list-elocโ list the full path names of the lines containing code--print-global-state-sizeโ count the total number of bits of global objects--print-path-lengthsโ print statistics about the paths in the control-flow graph--show-locationsโ show all the source locations--dotโ generate the CFG graph in DOT format ๐ฅ--print-internal-representationโ show the verbose internal representation of the program--list-undefined-functionsโ list the functions that have no body--list-calls-argsโ list every function call with its arguments--call-graphโ show the graph of function calls--reachable-call-graphโ show the graph of function calls that are potentially reachable from the main function--show-class-hierarchyโ show the class hierarchy ๐--validate-goto-modelโ turn on extra well-formedness checks on the goto program--validate-ssa-equationโ turn on extra well-formedness checks on the SSA representation--validate-goto-binaryโ check that the GOTO binary you passed in is well-formed, then dip--interpreterโ do concrete execution
๐ Data-flow analyses ๐
--show-struct-alignmentโ show the struct members that might be accessed concurrently--show-threadedโ show the instructions that may be executed by more than one thread--show-local-safe-pointersโ show pointer expressions that are trivially dominated by a not-null check--show-safe-dereferencesโ show pointer expressions that are trivially dominated by a not-null check and used as a dereference operand--show-value-setsโ show points-to information (using value sets)--show-global-may-aliasโ show may-alias information over globals--show-local-bitvector-analysisโ show the procedure-local pointer analysis--escape-analysisโ perform escape analysis--show-escape-analysisโ show the results of escape analysis--custom-bitvector-analysisโ perform configurable bitvector analysis--show-custom-bitvector-analysisโ show the results of configurable bitvector analysis--interval-analysisโ perform interval analysis--show-intervalsโ show the results of interval analysis--show-uninitializedโ show the maybe-uninitialized variables--show-points-toโ show points-to information--show-rw-setโ show read-write sets--show-call-sequencesโ show function call sequences--show-reaching-definitionsโ show reaching definitions--show-dependence-graphโ show the program-dependence graph--show-sese-regionsโ show single-entry-single-exit regions
๐ก๏ธ Safety checks ๐ก๏ธ
--no-assertionsโ ignore user assertions ๐--bounds-checkโ enable array bounds checks--pointer-checkโ enable pointer checks--memory-leak-checkโ enable memory leak checks--memory-cleanup-checkโ enable memory cleanup checks: assert that all dynamically allocated memory is explicitly freed before the program terminates ๐งน--div-by-zero-checkโ enable division by zero checks for integer division--float-div-by-zero-checkโ enable division by zero checks for floating-point division--signed-overflow-checkโ enable signed arithmetic over- and underflow checks--unsigned-overflow-checkโ enable arithmetic over- and underflow checks--pointer-overflow-checkโ enable pointer arithmetic over- and underflow checks--conversion-checkโ check whether values can still be represented after a type cast--undefined-shift-checkโ check for a shift greater than the bit-width--float-overflow-checkโ check floating-point for +/-Inf--nan-checkโ check floating-point for NaN--enum-range-checkโ check that all enum type expressions have values inside the enum range--pointer-primitive-checkโ check that all pointers in pointer primitives are valid or null--retain-trivial-checksโ include the checks that are trivially true--error-labellabel โ check that label is unreachable--no-built-in-assertionsโ ignore assertions in the built-in library--no-assertionsโ ignore user assertions (yes bestie, she's listed twice in the original, i'm not making the rules ๐)--no-assumptionsโ ignore user assumptions--assert-to-assumeโ convert user assertions into assumptions--uninitialized-checkโ add checks for uninitialized locals (experimental, so like, be aware โ ๏ธ)--stack-depthn โ add a check that the call stack size of non-inlined functions never exceeds n--race-checkโ add floating-point data race checks
๐ฎ Semantic transformations ๐ฎ
--nondet-volatile--nondet-volatile-variablevariable
so here's the tea ๐ต: by default, cbmc(1) treats volatile variables exactly the same as non-volatile variables. meaning it assumes a volatile variable does not change between subsequent reads, unless the program itself wrote to it. with the options above, you can tell goto-instrument to instrument the given goto program so that either (1) reads from all volatile expressions become non-deterministic (--nondet-volatile), (2) reads from specific variables become non-deterministic (--nondet-volatile-variable), or (3) reads from specific variables get modelled by given models (--nondet-volatile-model).
and now, two usage examples, because she ate and left no crumbs โจ consider the following test, for the function get_celsius and with the harness test_get_celsius:
#include <assert.h>
#include <limits.h>
#include <stdint.h>
// hardware sensor for temperature in kelvin
extern volatile uint16_t temperature;
int get_celsius() {
if (temperature > (1000 + 273)) {
return INT_MIN; // value indicating error
}
return temperature - 273;
}
void test_get_celsius() {
int t = get_celsius();
assert(t == INT_MIN || t <= 1000);
assert(t == INT_MIN || t >= -273);
}
here the variable temperature is basically a hardware sensor ๐ก it returns the current temperature on each read. get_celsius converts the value from Kelvin into degrees Celsius, assuming the value is in the expected range. but โ and this is the plot twist โ it has a bug where it reads temperature a second time after the check, which may yield a value for which the check would not succeed ๐ญ verifying this program as-is with cbmc(1) would give you a verification success, which is a lie. so we use goto-instrument to make reads from temperature non-deterministic:
goto-cc -o get_celsius_test.gb get_celsius_test.c
goto-instrument --nondet-volatile-variable temperature \
get_celsius_test.gb get_celsius_test-mod.gb
cbmc --function test_get_celsius get_celsius_test-mod.gb
and now the final invocation of cbmc(1) correctly reports a verification failure. slay. caught the bug. ๐ฅ
--nondet-volatile-modelvariable:model
ngl, simply treating volatile variables as non-deterministic can be too inaccurate for some use cases. so consider this next test, for the function get_message and with the harness test_get_message:
#include <assert.h>
#include <stdint.h>
extern volatile uint32_t clock;
typedef struct message {
uint32_t timestamp;
void *data;
} message_t;
void *read_data();
message_t get_message() {
message_t msg;
msg.timestamp = clock;
msg.data = read_data();
return msg;
}
void test_get_message() {
message_t msg1 = get_message();
message_t msg2 = get_message();
assert(msg1.timestamp <= msg2.timestamp);
}
the harness verifies that get_message assigns non-decreasing timestamps to the returned messages โฐ but just treating clock as non-deterministic would not be enough to prove this property. so instead, we can supply a model for reads from clock:
// model for reads of the variable clock
uint32_t clock_read_model() {
static uint32_t clock_value = 0;
uint32_t increment;
__CPROVER_assume(increment <= 100);
clock_value += increment;
return clock_value;
}
this model is stateful โ it keeps the current clock value between invocations in the variable clock_value. on each invocation it increments the clock by a non-deterministic value in the range 0 to 100. and we can tell goto-instrument to use the model clock_read_model for reads from the variable clock as follows:
goto-cc -o get_message_test.gb get_message_test.c
goto-instrument --nondet-volatile-model clock:clock_read_model \
get_message_test.gb get_message_test-mod.gb
cbmc --function get_message_test get_message_test-mod.gb
and boom โ that final cbmc(1) run now reports verification success ๐ like it's supposed to.
--isrfunction โ instruments an interrupt service routine โจ--mmioโ instruments memory-mapped I/O ๐--nondet-staticโ adds nondeterministic initialization for variables with static lifetime (yes bestie, the static ones get the โจnondetโจ treatment)--nondet-static-excludee โ exactly the same asnondet-staticexcept it skips the variablee(use it multiple times if you need to, she's flexible ๐)--nondet-static-matchingr โ adds nondeterministic initialization for variables with static lifetime that match the regexr--function-enterf,--function-exitf,--branchf โ instruments a call tofat the beginning, at the exit, or at a branch point, respectively ๐--splice-callcaller,callee โ prepends a call tocalleeinside the body ofcaller--check-call-sequenceseq โ instruments checks that assert every call sequence matchesseq๐--undefined-function-is-assume-falseโ converts each call to an undefined function intoassume(false)--insert-final-assert-falsefunction โ generatesassert(false)at the end offunction๐ซถ--generate-function-bodyregex โ this transformation inserts implementations of functions that don't have a definition, i.e. it gives them a body. What that generated body actually does is chosen with the--generate-function-body-optionsoption:--generate-function-body-optionsoption โ one ofassert-false,assume-false,nondet-return,assert-false-assume-falseandhavoc[,params:regex][,globals:regex][,params:p_n1;p_n2;..](default:nondet-return)assert-false: the body is a single command:assert(0).assume-false: the body is a single command:assume(0).assert-false-assume-false: two commands, exactly as above.nondet-return: the generated function returns a non-deterministic value of its return type.havoc[,params:p-regex][,globals:g-regex]: assigns non-deterministic values to the targets of pointer-to-non-constant parameters matching the regular expressionp-regex, and to non-constant globals matchingg-regex, and then (if it's a non-void function) returns likenondet-returndoes. Here's an example showing it off:
// main.c
int global;
const int c_global;
int function(int *param, const int *c_param);
okay so a lot of the time we want to not overwrite internal symbols, i.e. the ones with an __ prefix, and that's what the pattern (?!__) is for ๐
goto-cc main.c -o main.gb
goto-instrument main.gb main-out.gb \
--generate-function-body-options 'havoc,params:(?!__).*,globals:(?!__).*' \
--generate-funtion-body function
This gives you a GOTO binary equivalent to the following C code:
// main-mod.c
int function(int *param, const int *c_param) {
*param = nondet_int();
global = nondet_int();
return nondet_int();
}
The parameters that should get non-deterministically updated can be picked either with a regular expression (like above) or with a semicolon-separated list of their numbers. For example havoc,params:0;3;4 assigns non-deterministic values to the first, fourth, and fifth parameter ๐ซ
Heads up bestie: only parameters of pointer type can be havoced, and goto-instrument will produce an error report if you hand it a parameter number that belongs to a non-pointer parameter. Asking to havoc a parameter whose number is higher than the number of parameters that function takes will also give you an error report. No cap ๐จ
--generate-havocing-bodyoption fun_name,params:p_n1;p_n2;..--generate-havocing-bodyoption fun_name[,call-site-id,params:p_n1;p_n2;..>]+ โ requests a different implementation for a bunch of call-sites of a single function. The--generate-havocing-bodyoption inserts brand new functions for the selected call-sites and swaps the calls to the original function out for calls to the respective new functions ๐
// main.c
int function(int *first, int *second, int *third);
int main() {
int a = 10;
int b = 10;
int c = 10;
function(&a, &b, &c);
function(&a, &b, &c);
}
You can specify different behaviour for each call-site like this:
goto-cc main.c -o main.gb
goto-instrument main.gb main-mod.gb \
--generate-havocing-body 'function,1,params:0;2,2,params:1'
which results in a GOTO binary equivalent to:
// main-mod.c
int function_1(int *first, int *second, int *third) {
*first = nondet_int();
*third = nondet_int();
}
int function_2(int *first, int *second, int *third) { *second = nondet_int(); }
int main() {
int a = 10;
int b = 10;
int c = 10;
function_1(&a, &b, &c);
function_2(&a, &b, &c);
}
--restrict-function-pointer*pointer_name/target[,targets]** โ replaces function pointers with a user-defined set of targets. You might need this when--remove-function-pointerscreates too large a set of direct calls. Think about the example that was presented for--remove-function-pointers. Say that call will always receive pointers to eitherforgduring actual executions of the program, and symbolic execution forhis just way too expensive to simply ignore the cost of its branch ๐ธ
To make this controlled replacement possible, we label the places in each function where function pointers get called, using this pattern:
function-name.function_pointer_call.N
where N refers to the N-th function call via a function pointer inside function-name, i.e. the first call to a function pointer in a function has N=1, the fifth has N=5, etc. Alternatively, if the calls carry labels in the source code, we can also refer to a function pointer as
function-name.label
So to implement the assumption that the first call to a function pointer in call can only ever be a call to f or g, use:
goto-instrument --restrict-function-pointer \
call.function_pointer_call.1/f,g in.gb out.gb
The resulting output (written to the GOTO binary out.gb) looks similar to the original example, except now there won't be a call to h:
void call(fptr_t fptr) {
int r;
if (fptr == &f) {
r = f(10);
} else if (fptr == &g) {
r = g(10);
} else {
// sanity check
assert(false);
assume(false);
}
return r;
}
okay another example โ imagine we have a simple virtual filesystem API and implementation like this:
typedef struct filesystem_t filesystem_t;
struct filesystem_t {
int (*open)(filesystem_t *filesystem, const char *file_name);
};
int fs_open(filesystem_t *filesystem, const char *file_name) {
filesystem->open(filesystem, file_name);
}
int nullfs_open(filesystem_t *filesystem, const char *file_name) { return -1; }
filesystem_t nullfs_val = {.open = nullfs_open};
filesystem *const nullfs = &nullfs_val;
filesystem_t *get_fs_impl() {
// some fancy logic to determine
// which filesystem we're getting -
// in-memory, backed by a database, OS file system
// - but in our case, we know that
// it always ends up being nullfs
// for the cases we care about
return nullfs;
}
int main(void) {
filesystem_t *fs = get_fs_impl();
assert(fs_open(fs, "hello.txt") != -1);
}
In this case the assumption is that inside function main, fs can be nothing other than nullfs. But maybe the logic is too complicated and symbolic execution just can't figure that out, so in the call to fs_open we end up branching on every function matching the signature of filesystem_t::open โ which could be quite a few functions across the program ๐ญ And worst of all: if its address is ever taken anywhere in the program, then as far as function pointer removal via --remove-function-pointers is concerned, it could be fs_open itself, since it has a matching signature. That forces symbolic execution to follow a potentially infinite recursion until its unwind limit ๐
So in this case we can once again restrict the function pointer to the value we know it will have:
goto-instrument --restrict-function-pointer \
fs_open.function_pointer_call.1/nullfs_open in.gb out.gb
--function-pointer-restrictions-filefile_name โ if you've got a lot of places where you want to restrict function pointers, typing them all out on the command line would be such a nuisance ๐ In those cases you can pass a file to load the restrictions from instead, and you can hand it the name of a JSON file in this format:
{
"function_call_site_name": ["function1", "function2", ...],
...
}
If you pass in multiple files, or a mix of files and command line restrictions, the final restrictions will be the set union of every restriction you specified ๐ซถ
Note that if something goes wrong during type checking (i.e. making sure that all function pointer replacements refer to functions in the symbol table that have the correct type), the error message will refer to the command line option --restrict-function-pointer regardless of whether the restriction in question actually came from the command line or from a file. Slightly confusing, but that's the vibe โจ
--restrict-function-pointer-by-name symbol_name/target[,targets]*โ okay so you literally just tell it which function pointers to keep, by unmangled name, and it does the restricting before it goes labeling function pointers. Very demure, very mindful. ๐--remove-calls-no-bodyโ if a function has no body, the calls to it just โจvanishโจ. No body, no calls, no problem.--add-libraryโ sprinkles in models of C library functions so your analysis isn't out here guessing whatprintfdoes. ๐--malloc-may-failโ letsmalloccalls return a null pointer. Yes bestie, it can just fail on you. Character development.--malloc-fail-assertโ sets malloc failure mode to assert-then-assume. It's giving "assert it, then assume it."--malloc-fail-nullโ sets malloc failure mode to return null. Straight up, no chaser.--no-malloc-may-failโ do not let malloc calls fail by default. The paranoid setting, lowkey.--string-abstractionโ tracks C string lengths and zero-termination. She's keeping receipts on your strings, fr fr. ๐งต--dfcc-debug-libโ turns on debug assertions in the cprover contracts library. Debug girlies rise up. ๐--dfcc-simple-invalid-pointer-modelโ uses the simplified invalid pointer model in the cprover contracts library. It's faster but unsound, so like... be aware, bestie. Speed vs. correctness is the eternal vibe check.--model-argc-argv nโ creates up to n non-deterministic C strings as entries toargvand setsargcaccordingly. If you don't model this,argvis just left uninitialized except for a terminating NULL pointer, which is not the moment. Consider this lil example:
// needs_argv.c
#include <assert.h>
int main(int argc, char *argv[]) {
if (argc >= 2)
assert(argv[1] != 0);
return 0;
}
If cbmc(1) is run directly on this example, it'll report a failing assertion because nobody modeled argv. Rude. To make the assertion succeed, as expected, you do:
goto-cc needs_argv.c
goto-instrument --model-argc-argv 2 a.out a.out
cbmc a.out
Slay. โจ
--remove-function-body fโ removes the implementation of function f. (may be repeated, so go off, remove as many as you want)--remove-function-body-regex regexโ removes the implementation of every function matching the regular expression regex. Regex girlies, this one's for you. ๐ฆ--replace-calls f:gโ replaces calls to f with calls to g. Swapping out the whole personality.--max-nondet-tree-depth Nโ limits the size of the nondet (like, input) object tree. At level N, pointers get set to null. Draws the line.--min-null-tree-depth Nโ sets the minimum level at which a pointer can first be NULL in a recursively nondet initialized struct. So it can't be null before level N, no cap.
โจ Semantics-preserving transformations: โจ
These ones keep the meaning but change the shape. Glow-up, not rewrite. ๐
--ensure-one-backedge-per-targetโ transforms loop bodies so there's a single edge back to the loop head. One backedge. That's it. That's the whole vibe.--drop-unused-functionsโ drops functions that are trivially unreachable from the main function. If nobody can get to you, you're out. ๐--remove-pointersโ converts pointer arithmetic into base+offset expressions. Pointers hate to see her coming.--constant-propagatorโ propagates constants and simplifies expressions. She's simplifying. She's streamlining. She's iconic. ๐--inlineโ performs full inlining. Go big or go home.--partial-inlineโ performs partial inlining. The โจcuratedโจ version.--function-inline functionโ transitively inlines all calls function makes. It's giving full commitment.--no-cachingโ disables caching of intermediate results during transitive function inlining. Yes it's slower, no we don't care.--log fileโ logs, in JSON format, which code segments were inlined. Use it with--function-inline. Receipts, but make them machine-readable. ๐--remove-function-pointersโ resolves calls through function pointers into direct function calls. Candidate functions get chosen based on their signature and whether or not their address is taken somewhere in the program. Here's the approach in action: say you've got these signatures available in the program:
int f(int x);
int g(int x);
int h(int x);
And you have a call site like this:
typedef int (*fptr_t)(int x);
void call(fptr_t fptr) {
int r = fptr(10);
assert(r > 0);
}
Function pointer removal will turn that into code similar to this:
void call(fptr_t fptr) {
int r;
if (fptr == &f) {
r = f(10);
} else if (fptr == &g) {
r = g(10);
} else if (fptr == &h) {
r = h(10);
} else {
// sanity check
assert(false);
assume(false);
}
return r;
}
Beware bestie!! There may be many functions matching a particular signature, and some of them may be costly for a later analysis to deal with. Consider using--restrict-function-pointerto manually specify the set of functions, or--value-set-fi-fp-removal. Protect your runtime. ๐ซถ
--remove-const-function-pointersโ removes function pointers that are constant, or a constant part of an array. Constants get the โ๏ธ.--value-set-fi-fp-removalโ builds a flow-insensitive value set and swaps function pointers for a case statement over the possible assignments. If that set of possible assignments is empty, the function pointer gets removed using the standard--remove-function-pointerspass. No assignments, no pointer, periodt.
๐ Loop information and transformations: ๐
--show-loopsโ shows the loops in the program. Just... shows them. Look at them. ๐--unwind nrโ unwinds all loops nr times. All of them. Every last one.--unwindset [T:]L:B,...โ unwinds loop L with a bound of B, optionally restricted to thread T. Use--show-loopsto get the loop IDs, iykyk.--unwindset-file fileโ reads the unwindset from file. Because typing it all out is a lot. ๐ซ--partial-loopsโ permits paths that execute loops only partially (up to the given unwinding bound) and then continue past the loop even when the loop condition would still hold. > Heads up: such paths may be spurious, which results in false alarms. She's a little chaotic. ๐ญ--no-unwinding-assertionsโ do not generate unwinding assertions. Simple as.--unwinding-assertionsโ generate unwinding assertions. These are enabled by default, and this overrides--no-unwinding-assertionswhen both of these are given. She wins the argument. ๐--continue-as-loopsโ adds a loop for the remaining iterations after the unwound part. Finishing what the unwind started.--k-induction kโ checks loops with k-induction. Math girlies, this one's yours.--step-caseโ k-induction: do step-case.--base-caseโ k-induction: do base-case. (Yes, they come as a set. Iconic duo.)--havoc-loopsโ over-approximates all loops. Chaos mode, but make it sound.--accelerateโ adds loop accelerators. Vroom vroom, we're going faster than iteration. ๐๏ธ--z3โ use Z3 when computing loop accelerators. Shoutout to Z3. ๐--skip-loops loop-idsโ adds gotos to skip selected loops during execution. Yeet those loops.--show-lexical-loopsโ shows lexical loops. So a lexical loop is a block of goto program instructions with a single entry edge at the top and a single backedge leading from bottom to top, where "top" and "bottom" refer to program order. The loop may have holes: instructions that sit between top and bottom in program order but can't reach the loop backedge. Lexical loops are a subset of the natural loops, which are cheaper to compute and include most natural loops generated from typical C code. Basically the low-effort, high-reward loop detection. โจ--show-natural-loopsโ shows natural loop heads. A natural loop is when the nodes and edges of a graph make one self-encapsulating circle with no incoming edges from external nodes. For example,A -> B -> C -> D -> Ais a natural loop, but ifBhas an incoming edge fromX, then it isn't a natural loop, becauseXis an external node. Outgoing edges don't affect the natural-ness of a loop. Outgoing? Don't care. Incoming? Now we have beef. ๐ซ
๐ง Memory model instrumentations: ๐ง
--mm [tso|pso|rmo|power]โ instruments the program so it can be verified against different weak memory models, using a model-checker that verifies sequentially consistent programs. Weak memory models are the plot twist. ๐--sccโ detects critical cycles per SCC (one thread per SCC). Every thread gets its own little SCC kingdom. ๐--one-event-per-cycleโ only instruments one event per cycle. Minimalism, but make it technical.--minimum-interferenceโ instruments an optimal number of events. Optimal. She's efficient.--my-eventsโ only instruments events whose ids appear ininst.evt. Picky, and honestly valid.--read-first,--write-firstโ only instrument cycles where a read or a write occurs as the first event, respectively. Choose your fighter. โ๏ธ--max-var Nโ limits cycles to N variables read/written. Draws the boundary.--max-po-trans Nโ limits cycles to N program-order edges. Same energy, different metric.--ignore-arraysโ instruments arrays as a single object. Whole array = one thing. Efficient. ๐ธ--cav11โ always instruments shared variables, even when they're not part of any cycle. It's giving "I don't care if it's not in a cycle, I'm instrumenting it anyway." ๐--force-loop-duplication,--no-loop-duplicationโ optional program transformation to construct cycles in program loops. Pick a side, bestie.--cfg-killโ enables symbolic execution used to reduce spurious cycles. Killing the fake ones, keeping the real ones. ๐ฅ--no-dependenciesโ no dependency analysis. Skip it.--no-po-renderingโ no representation of the threads in the dot. The dot stays quiet about threads.--hide-internalsโ does not include thread-internal (Rfi) events in the dot output. Privacy settings: on. ๐--render-cluster-fileโ clusterises the dot by files. Clusters, but file-shaped.--render-cluster-functionโ clusterises the dot by functions. Clusters, but function-shaped.
๐ Slicing: ๐
--fp-reachability-slice fโ removes instructions that can't appear on a trace that visits all the given functions. The list of functions has to be given as a comma separated list f. Nothing survives unless it's on the guest list. ๐--reachability-sliceโ removes instructions that can't appear on a trace from entry point to a property. Cut.--reachability-slice-fbโ removes instructions that can't appear on a trace from entry point through a property. Same energy, but it goes through.--full-sliceโ slices away instructions that don't affect assertions. If it doesn't matter to the assert, it's gone. โ๏ธ--property idโ slice with respect to a specific property id only. One property. Just that one.--slice-global-initsโ slices away initializations of unused global variables. Unused? Uninitialized. Bye. ๐--aggressive-sliceโ removes the bodies of any functions not on the shortest path between the start function and the function containing the property(s). She's not just slicing, she's aggressively slicing. ๐--aggressive-slice-call-depth nโ used with--aggressive-slice, preserves all functions within n function calls of the functions on the shortest path. A little mercy, as a treat.--aggressive-slice-preserve-function fโ forces the aggressive slicer to preserve function f. She's on the do-not-touch list. ๐ซถ--aggressive-slice-preserve-functions-containing fโ forces the aggressive slicer to preserve all functions with names containing f. Any function with that name-vibe survives.--aggressive-slice-preserve-all-direct-pathsโ forces the aggressive slicer to preserve all direct paths. Every direct path lives. Periodt.
๐ Code contracts: ๐
--apply-loop-contracts-disable-loop-contracts-side-effect-checkโ UNSOUND OPTION, bestie, I'm not even joking ๐ this turns off the check that makes sure there are no side effects hiding in your loop contract clauses. With that check gone, your loop contract clauses will happily accept way more expressions โ think pure functions, statement expressions, the whole chaotic little buffet. But then you have to personally guarantee the loop contracts are side-effect free yourself, or your results are not sound. Like, you're on your own with this one. ๐-loop-contracts-no-unwindโ do not unwind transformed loops. Just... leave them un-unwound, okay?-loop-contracts-filefile โ annotate loop contracts from file onto the goto program. Feeding it the good stuff from a file. ๐--replace-call-with-contractfun โ swap out calls to fun with fun's own contract. Trading the call for the receipts, iconic.--enforce-contractfun โ wrap fun up with an assertion of its contract. Like putting a lil bow on it but the bow is an assertion. ๐--enforce-contract-recfun โ same energy as above but the assertion can actually handle recursive calls, which is lowkey a flex.--dfccfun โ instrument dynamic frame condition checks, using fun as the entry point.
๐ User-interface options (the glow-up section) โจ
--flushโ flush every single line of output. No buffering, no secrets, we're being transparent.--xmlโ output files in XML, wherever that's supported. XML girlies rise--xml-uiโ use XML-formatted output for the interface. Same vibe, different wrapper.--json-uiโ use JSON-formatted output for the interface. For when your parser is a JSON simp ๐ซถ--verbosityn โ set the verbosity level to n. Pick your volume. ๐--timestamp [monotonic|wall]โ print microsecond-precision timestamps. So extra, so precise. Two flavors:monotonicโ the stamps increase monotonically, always moving forward, never looking back, main character energy fr fr.wallโ ISO-8601 wall clock timestamps. Actual real-world time, clocking you in. โฐ
๐ ENVIRONMENT (the vibes around us)
Every single one of these tools honors the TMPDIR environment variable when it's generating temporary files and directories. So if you want your temp stuff to live somewhere specific, set TMPDIR and it'll respect that. ๐
๐ BUGS (not me...)
If you hit a problem, please make an issue over at https://github.com/diffblue/cbmc/issues โ the devs genuinely want to know, no cap. ๐
๐ SEE ALSO (the friend group)
Go say hi, they're all part of the same little family. ๐ฆ
๐ COPYRIGHT
2008-2013, Daniel Kroening
And that's the whole page, bestie โ you made it to the end, which is honestly iconic of you. ๐ฅณ This was goto-instrument-5.59.0, dated June 2022, living in GOTO-INSTRUMENT(1). Now go forth and instrument some gotos. You've got this. ๐โจ
๐ฆ where this page comes from
- fedora 44: cbmc 6.8.0-1.fc44 ยท BSD-4-Clause
- nixos 26.05: cbmc 6.9.0
- nixpkgs unstable: cbmc 6.11.0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms