Stoat Tutorial/Example
In order to get started with stoat let’s consider using it on an existing program. For this brief tutorial we will use non-timeline. The first step is going to be downloading the non tools.
git clone https://github.com/original-male/non
Now, in order to get the LLVM IR bitcode files, the -emit-llvm argument needs to be passed to clang, however this does result in output files which cannot be linked together with ld. For a number of build systems, this results in a reasonable claim that the compiler cannot produce usable binaries. To avoid this we can sneak this flag in after most of the configuration. The needed patch is shown below.
diff --git a/wscript b/wscript index f6118c6..29305e3 100644 --- a/wscript +++ b/wscript @@ -136,6 +136,7 @@ def configure(conf): for i in pl: Logs.pprint('YELLOW', 'Configuring %s' % i) conf.recurse(i); + conf.env.append_value('CXXFLAGS', ['-emit-llvm'] ) def run(ctx): if not Options.options.cmd:
With this out of the way, the build system can produce the LLVM intermediate files. For now this will fail once the build system gets to linking, but in the future a wrapper script will likely be used in place of this hackery.
CXX=clang++ ./waf configure ./waf || ./waf || ./waf
Now it is possible to process the project. To verify the previous steps just run stoat --recursive . . This should produce no errors, but it should list the files that it processed to get zero errors. In order for it to actually produce useful results some function needs to be whitelisted. This can be done either with inline attributes or via external whitelists. Let’s just make an external whitelist named "non-whitelist.txt" with the below contents:
Engine::process
With this done rerun stoat:
stoat --recursive . --whitelist non-whitelist.txt --graph-view non-callgraph.png
Now a list of errors should be produced, along with the graph view. The error list right now directly prints out the internal structure showing the deductions needed to find something contradictory. The first few errors are shown below:
"warnf(warning_t, char const*, char const*, char const*, int, char const*, ...)" #<DeductionChain:0x87d479c @contradicted_by=#<Set: {"fprintf", "fputs", "vfprintf", "fwrite"}>, @contradicted_p=true, @deduction_source="_ZN6Engine7processEj", @has_body_p=true, @non_realtime_p=false, @realtime_p=true, @reason= "The Function Was Deduced To Need To Be RealTime As It Was Called By A Realtime Function"> ##The Deduction Chain: - Engine::process(unsigned int) : The Function Was Declared Realtime By A Whitelist ##The Contradiction Reasons: - fprintf : No Code Or Annotations, So The Function is Assumed Unsafe - fputs : No Code Or Annotations, So The Function is Assumed Unsafe - vfprintf : No Code Or Annotations, So The Function is Assumed Unsafe - fwrite : No Code Or Annotations, So The Function is Assumed Unsafe "sigc::internal::signal_emit1<int, double, sigc::nil>::emit(sigc::internal::signal_impl*, double const&)" #<DeductionChain:0x8749750 @contradicted_by= #<Set: {"_ZN4sigc9slot_baseC1Ev", "_ZNSt8__detail15_List_node_base9_M_unhookEv", "_ZdlPv", "_ZN4sigc8internal11signal_impl5sweepEv"}>, @contradicted_p=true, @deduction_source="_ZN9Transport4pollEv", @has_body_p=true, @non_realtime_p=false, @realtime_p=true, @reason= "The Function Was Deduced To Need To Be RealTime As It Was Called By A Realtime Function"> ##The Deduction Chain: - Transport::poll() : The Function Was Deduced To Need To Be RealTime As It Was Called By A Realtime Function - Engine::process(unsigned int) : The Function Was Declared Realtime By A Whitelist ##The Contradiction Reasons: - sigc::slot_base::slot_base() : No Code Or Annotations, So The Function is Assumed Unsafe - std::__detail::_List_node_base::_M_unhook() : No Code Or Annotations, So The Function is Assumed Unsafe - operator delete(void*) : No Code Or Annotations, So The Function is Assumed Unsafe - sigc::internal::signal_impl::sweep() : No Code Or Annotations, So The Function is Assumed Unsafe "Playback_DS::process(unsigned int)" #<DeductionChain:0x8747cd4 @contradicted_by=#<Set: {"usleep"}>, @contradicted_p=true, @deduction_source="class.Playback_DS4", @has_body_p=true, @non_realtime_p=false, @realtime_p=true, @reason= "The Function Was Deduced To Need To Be RealTime As It Was Called By A Realtime Function"> ##The Deduction Chain: - class.Playback_DS4 : The Function Was Deduced To Need To Be RealTime As It Was Called By A Realtime Function - Track::process_output(unsigned int) : The Function Was Deduced To Need To Be RealTime As It Was Called By A Realtime Function - Timeline::process_output(unsigned int) : The Function Was Deduced To Need To Be RealTime As It Was Called By A Realtime Function - Engine::process(unsigned int) : The Function Was Declared Realtime By A Whitelist ##The Contradiction Reasons: - usleep : No Code Or Annotations, So The Function is Assumed Unsafe
The first error shows the detection of some basic debugging prints, the second one ends up showing some possibility (though no guarantee of it) of some memory deallocation within libsigc++, and the last error shows a possible callout to usleep through a virtual function. Thus stoat has been able to traverse the callgraph in search of possible issues.
A more visual representation is available via the graph which was dumped out in the last invocation of stoat:
Hopefully this brief example has given some ideas on how stoat can be used to quickly get some information on possible safety issues within a realtime program.