From 1bd59cd42fd4d5bec61c1c3af5dec02f9f81bf71 Mon Sep 17 00:00:00 2001
From: Andre Maroneze <andre.maroneze@cea.fr>
Date: Tue, 2 Aug 2022 16:29:26 +0200
Subject: [PATCH] [Doc] update ViewCfg tutorial

---
 doc/developer/Makefile                        |   9 +
 doc/developer/tutorial.tex                    | 488 +++++++++---------
 doc/developer/tutorial/viewcfg/pdfs/bogue.pdf | Bin 0 -> 13321 bytes
 .../viewcfg/{v1 => v1-simple}/ViewCfg.ml      |   0
 doc/developer/tutorial/viewcfg/v1-simple/dune |  10 +
 .../tutorial/viewcfg/v1-simple/dune-project   |   5 +
 .../viewcfg/v1-simple/tests/ptests_config     |   1 +
 .../viewcfg/v1-simple/tests/test_config       |   1 +
 .../v1-simple/tests/viewcfg/oracle/cfg.dot    |  32 ++
 .../tests/viewcfg/oracle/test.res.oracle      |   1 +
 .../{v1 => v1-simple/tests/viewcfg}/test.c    |   6 +-
 .../viewcfg/{v1 => v1-simple}/view_cfg.ml     |  24 +-
 doc/developer/tutorial/viewcfg/v1/dune        |  34 --
 .../tutorial/viewcfg/v1/dune-project          |  27 -
 .../tutorial/viewcfg/v2-options/ViewCfg.ml    |   3 +
 .../tutorial/viewcfg/v2-options/dune          |  10 +
 .../tutorial/viewcfg/v2-options/dune-project  |   5 +
 .../tutorial/viewcfg/v2-options/view_cfg.ml   |  74 +++
 .../tutorial/viewcfg/v3-eva/ViewCfg.ml        |   3 +
 doc/developer/tutorial/viewcfg/v3-eva/dune    |  10 +
 .../tutorial/viewcfg/v3-eva/dune-project      |   5 +
 .../viewcfg/v3-eva/tests/ptests_config        |   1 +
 .../tutorial/viewcfg/v3-eva/tests/test_config |   1 +
 .../v3-eva/tests/viewcfg/oracle/cfg.dot       |  32 ++
 .../tests/viewcfg/oracle/test.res.oracle      |  23 +
 .../viewcfg/v3-eva/tests/viewcfg/test.c       |  24 +
 .../tutorial/viewcfg/v3-eva/view_cfg.ml       |  82 +++
 .../tutorial/viewcfg/v4-bogue/ViewCfg.ml      |   3 +
 .../tutorial/viewcfg/v4-bogue/dump.ml         |   9 +
 doc/developer/tutorial/viewcfg/v4-bogue/dune  |  11 +
 .../tutorial/viewcfg/v4-bogue/dune-project    |   5 +
 .../tutorial/viewcfg/v4-bogue/gui.ml          |  37 ++
 .../tutorial/viewcfg/v4-bogue/options.ml      |  11 +
 .../tutorial/viewcfg/v4-bogue/run.ml          |   5 +
 .../tutorial/viewcfg/v4-bogue/test.c          |  24 +
 .../tutorial/viewcfg/v4-bogue/visit.ml        |  47 ++
 .../tutorial/viewcfg/v5-state/ViewCfg.ml      |   3 +
 .../tutorial/viewcfg/v5-state/dump.ml         |  24 +
 doc/developer/tutorial/viewcfg/v5-state/dune  |  11 +
 .../tutorial/viewcfg/v5-state/dune-project    |   5 +
 .../tutorial/viewcfg/v5-state/gui.ml          |  37 ++
 .../tutorial/viewcfg/v5-state/options.ml      |  11 +
 .../tutorial/viewcfg/v5-state/run.ml          |   5 +
 .../tutorial/viewcfg/v5-state/test.c          |  24 +
 .../tutorial/viewcfg/v5-state/visit.ml        |  47 ++
 .../viewcfg/v6-state-clear/ViewCfg.ml         |   3 +
 .../tutorial/viewcfg/v6-state-clear/dump.ml   |  37 ++
 .../tutorial/viewcfg/v6-state-clear/dune      |  11 +
 .../viewcfg/v6-state-clear/dune-project       |   5 +
 .../tutorial/viewcfg/v6-state-clear/gui.ml    |  37 ++
 .../viewcfg/v6-state-clear/options.ml         |  11 +
 .../tutorial/viewcfg/v6-state-clear/run.ml    |   5 +
 .../tutorial/viewcfg/v6-state-clear/test.c    |  24 +
 .../tutorial/viewcfg/v6-state-clear/visit.ml  |  47 ++
 doc/frama-c-book.cls                          |   4 +-
 55 files changed, 1091 insertions(+), 323 deletions(-)
 create mode 100644 doc/developer/tutorial/viewcfg/pdfs/bogue.pdf
 rename doc/developer/tutorial/viewcfg/{v1 => v1-simple}/ViewCfg.ml (100%)
 create mode 100644 doc/developer/tutorial/viewcfg/v1-simple/dune
 create mode 100644 doc/developer/tutorial/viewcfg/v1-simple/dune-project
 create mode 100644 doc/developer/tutorial/viewcfg/v1-simple/tests/ptests_config
 create mode 100644 doc/developer/tutorial/viewcfg/v1-simple/tests/test_config
 create mode 100644 doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/cfg.dot
 create mode 100644 doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/test.res.oracle
 rename doc/developer/tutorial/viewcfg/{v1 => v1-simple/tests/viewcfg}/test.c (80%)
 rename doc/developer/tutorial/viewcfg/{v1 => v1-simple}/view_cfg.ml (52%)
 delete mode 100644 doc/developer/tutorial/viewcfg/v1/dune
 delete mode 100644 doc/developer/tutorial/viewcfg/v1/dune-project
 create mode 100644 doc/developer/tutorial/viewcfg/v2-options/ViewCfg.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v2-options/dune
 create mode 100644 doc/developer/tutorial/viewcfg/v2-options/dune-project
 create mode 100644 doc/developer/tutorial/viewcfg/v2-options/view_cfg.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/ViewCfg.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/dune
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/dune-project
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/tests/ptests_config
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/tests/test_config
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/cfg.dot
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/test.res.oracle
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/test.c
 create mode 100644 doc/developer/tutorial/viewcfg/v3-eva/view_cfg.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/ViewCfg.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/dump.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/dune
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/dune-project
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/gui.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/options.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/run.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/test.c
 create mode 100644 doc/developer/tutorial/viewcfg/v4-bogue/visit.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/ViewCfg.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/dump.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/dune
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/dune-project
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/gui.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/options.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/run.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/test.c
 create mode 100644 doc/developer/tutorial/viewcfg/v5-state/visit.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/ViewCfg.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/dump.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/dune
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/dune-project
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/gui.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/options.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/run.ml
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/test.c
 create mode 100644 doc/developer/tutorial/viewcfg/v6-state-clear/visit.ml

diff --git a/doc/developer/Makefile b/doc/developer/Makefile
index 7825f1cba79..c5016b3524f 100644
--- a/doc/developer/Makefile
+++ b/doc/developer/Makefile
@@ -154,6 +154,15 @@ install:
 	rm -f ../manuals/plugin-development-guide.pdf
 	cp developer.pdf ../manuals/plugin-development-guide.pdf
 
+viewcfg: viewcfg-clean viewcfg-v1
+
+viewcfg-clean:
+	$(ECHO) Cleaning ViewCfg files...
+	rm -rf tutorial/viewcfg/v*/_build
+
+viewcfg-v1:
+	cd tutorial/viewcfg/v1 && $(duneb) @install && $(duneb) @ptests
+
 tutorial/viewcfg/generated:
 	cd tutorial/viewcfg && make
 
diff --git a/doc/developer/tutorial.tex b/doc/developer/tutorial.tex
index bbc2322f6b2..fddcbad4b78 100644
--- a/doc/developer/tutorial.tex
+++ b/doc/developer/tutorial.tex
@@ -212,7 +212,8 @@ afterwards. In case of success, there will be no visible output at the end.
   so make sure that your Hello directory is not inside another one containing
   a Dune project (\eg if you are running this directly from the \framac source
   archive), otherwise \verb|dune build| may fail. You can always add option
-  ``\verb|--root .|'' to force Dune to ignore parent directories.
+  ``\verb|--root .|'', or create an empty \texttt{dune-workspace} file in
+  the current directory to force Dune to ignore parent directories.
 \end{important}
 
 Note a few details about the naming conventions:
@@ -784,9 +785,8 @@ In this section, we create a new ViewCfg plug-in that computes the control
 flow graph of a function and outputs it in the \dottool format. Through its
 implementation, we explain some of \framac APIs such as how to visit
 an AST\footnote{Abstract Syntax Tree}, how to hook a plug-in, how to interface
-a plug-in with other plug-ins, how to extend the
-GUI\footnote{Graphical User Interface}, how to make a plug-in usable by
-others, and how to make a plug-in usable in a multi-project setting.
+a plug-in with other plug-ins, and how to make a plug-in usable in a
+multi-project setting (which also allows it to save/load data).
 
 This section assumes the reader is already familiar with the basics of plug-ins
 for \framac as covered by the Hello plug-in in the previous
@@ -815,7 +815,7 @@ suitable for us, as it will recursively print substatements of compound
 statements (blocks, if, while, ...), while we only want to pretty-print
 the node representing the current statement: substatements will be represented
 by other nodes. Thus we will use the following small function:
-\ocamlinput[firstline=29,lastline=43]{./tutorial/viewcfg/v1/view_cfg.ml}
+\ocamlinput[linerange={7-21}]{./tutorial/viewcfg/v1-simple/view_cfg.ml}
 \sscodeidx{Cil\_types}{stmtkind}{Instr}
 \sscodeidx{Cil\_types}{stmtkind}{Return}
 \sscodeidx{Cil\_types}{stmtkind}{Goto}
@@ -852,7 +852,7 @@ children. By inheriting from the visitor, and redefining some of the
 methods, one can perform actions on selected parts of the AST, without
 the need to traverse the AST explicitly.
 
-\ocamlinput[firstline=45,lastline=46]{./tutorial/viewcfg/v1/view_cfg.ml}
+\ocamlinput[linerange={23-24}]{./tutorial/viewcfg/v1-simple/view_cfg.ml}
 \scodeidx{Visitor}{frama\_c\_inplace}
 
 Here we used the so-called ``in place'' visitor, which should be used for
@@ -866,7 +866,7 @@ at the file level, we create the whole graph structure.
 %in the code below, the fancy 'at'-terms such as ``\texttt{@[<hov 2>}'' are
 %  related to OCaml's Format module, and can be ignored for now.}.
 
-\ocamlinput[firstline=48,lastline=50]{./tutorial/viewcfg/v1/view_cfg.ml}
+\ocamlinput[linerange={26-28}]{./tutorial/viewcfg/v1-simple/view_cfg.ml}
 \sscodeidx{Cil}{visitAction}{DoChildrenPost}
 \sscodeidx{Cil}{cilVisitor}{vfile}
 
@@ -880,7 +880,7 @@ in the file.
 Then, for each function, we encapsulate the CFG in a subgraph, and do
 nothing for the other globals.
 
-\ocamlinput[firstline=52,lastline=58]{./tutorial/viewcfg/v1/view_cfg.ml}
+\ocamlinput[linerange={30-36}]{./tutorial/viewcfg/v1-simple/view_cfg.ml}
 \sscodeidx{Cil}{visitAction}{DoChildrenPost}
 \sscodeidx{Cil}{visitAction}{SkipChildren}
 \sscodeidx{Visitor}{frama\_c\_visitor}{vglob\_aux}
@@ -895,7 +895,7 @@ should have been used instead.}.
 Last, for each statement, we create a node in the graph, and create
 the edges toward its successors:
 
-\ocamlinput[firstline=60,lastline=65]{./tutorial/viewcfg/v1/view_cfg.ml}
+\ocamlinput[linerange={38-43}]{./tutorial/viewcfg/v1-simple/view_cfg.ml}
 \sscodeidx{Cil}{visitAction}{DoChildren}
 \sscodeidx{Visitor}{frama\_c\_visitor}{vstmt\_aux}
 
@@ -905,7 +905,7 @@ cannot contain other statements, like \texttt{Instr}, and avoid
 visiting the expressions.
 
 Finally, we close the \texttt{object} definition:
-\ocamlinput[firstline=67,lastline=67]{./tutorial/viewcfg/v1/view_cfg.ml}
+\ocamlinput[linerange={45-45}]{./tutorial/viewcfg/v1-simple/view_cfg.ml}
 
 \subsubsection*{Hooking into \framac}
 
@@ -916,7 +916,7 @@ will not have had the time to parse the sources and produce its AST
 (used by \verb|Ast.get ()|). For more details about initialization issues,
 see Section~\ref{adv:init}.
 
-\ocamlinput[firstline=69,lastline=75]{./tutorial/viewcfg/v1/view_cfg.ml}
+\ocamlinput[linerange={47-53}]{./tutorial/viewcfg/v1-simple/view_cfg.ml}
 
 Now, since \framac uses Dune, this code needs to be integrated as a Dune
 project, as has been done in the Hello tutorial. We need to create a new
@@ -925,10 +925,10 @@ file. We will then add the corresponding \texttt{dune} and
 \texttt{dune-project} files:
 
 \listingname{dune}
-\duneinput[linerange={23-999}]{./tutorial/viewcfg/v1/dune}
+\duneinput{./tutorial/viewcfg/v1-simple/dune}
 
 \listingname{dune-project}
-\duneinput[linerange={1-1,24-999}]{./tutorial/viewcfg/v1/dune-project}
+\duneinput{./tutorial/viewcfg/v1-simple/dune-project}
 
 Finally, we need an (empty) interface file, called \texttt{ViewCfg.ml}, in
 the same directory.
@@ -948,7 +948,7 @@ And run it as a \framac plugin:
 And the graph can be visualized with an external tool, such as {\em dotty} or
 {\em xdot}.
 \begin{shell}
-  dotty cfg.out
+  dotty cfg.dot
 \end{shell}
 
 This produces a graph like in Figure~\ref{fig:tut:basiccfg}
@@ -957,7 +957,7 @@ This produces a graph like in Figure~\ref{fig:tut:basiccfg}
   \centering
   \begin{minipage}[h]{0.45\linewidth}
   \listingname{test.c}
-  \cinput{./tutorial/viewcfg/tests/test.c}
+  \cinput[linerange={5-999}]{./tutorial/viewcfg/v1-simple/tests/viewcfg/test.c}
   \end{minipage}%
   \begin{minipage}[h]{0.4\linewidth}
     \includegraphics[width=\textwidth]{./tutorial/viewcfg/pdfs/cfg.pdf}
@@ -995,9 +995,9 @@ for our plug-in.
 We have already seen how to register options in the previous ``Hello'' tutorial.
 We now apply these principles to the ViewCfg plug-in.
 
-\ocamlinput{./tutorial/viewcfg/src/register_and_options.ml}
+\ocamlinput[linerange={9-26}]{./tutorial/viewcfg/v2-options/view_cfg.ml}
 \scodeidx{Plugin}{Register}
-\ocamlinput{./tutorial/viewcfg/src/extend_with_run_with_options.ml}
+\ocamlinput[linerange={66-74}]{./tutorial/viewcfg/v2-options/view_cfg.ml}
 \scodeidx{Visitor}{visitFramacFileSameGlobals}
 \scodeidx{Ast}{get}
 
@@ -1025,44 +1025,62 @@ section~\ref{adv:project} of this manual should help you understand the
 underlying notion of states).
 
 With these command-line options, we can properly interface our ViewCfg plug-in
-with the \texttt{value} plug-in.
+with the \texttt{eva} plug-in.
 
-\subsection{Interfacing with a kernel-integrated plug-in}
+\subsection{Interfacing with other plug-ins}
 
-Kernel-integrated plug-ins, such as \texttt{value}, use a special mechanism
-to statically register their APIs for other plug-ins that wish to access them.
-This mechanism is the \texttt{Db} module of the \framac kernel,
-the entry point for all kernel-integrated plug-ins. By using the functions
-exported through the \texttt{Db.Value} module, our plug-in will obtain
-reachability information computed by \texttt{value}.
+Plug-ins can use functions specified in the public interfaces of other
+plug-ins, as long as they are declared as dependencies. To do so, you only
+need to add them to the \texttt{libraries} stanza\footnote{A stanza is,
+roughly speaking, a ``term'' in \texttt{dune} parlance: a parenthesized
+expression.} in the \texttt{dune} file.
+We will use a function from the \textsf{Eva} plug-in in our example,
+so we will add \texttt{frama-c-eva.core} to the \texttt{dune} file:
 
+\duneinput[linerange={5-5}]{./tutorial/viewcfg/v3-eva/dune-project}
+
+Now our plug-in can call all functions and access all types declared in
+\textsf{Eva}'s public interface.
+
+For historical reasons, several kernel-integrated plug-ins, such as
+\textsf{From}, \textsf{InOut} and \textsf{Slicing}, had their API exposed
+via the \texttt{Db} module of the \framac kernel. This has been deprecated
+for \textsf{Eva}, and newer plug-ins expose their public interface directly.
+
+In our example, we will use \textsf{Eva}'s new API to obtain reachability
+information computed by the value analysis.
 The code modification we propose is to color in pink the nodes guaranteed to
 be unreachable by the value analysis. For this purpose, we change the
 \texttt{vstmt\_aux} method in the visitor:
 
-\ocamlinput{./tutorial/viewcfg/src/print_cfg_vstmt_aux_value.ml}
-\sscodeidx{Db}{Value}{is\_computed}
-\sscodeidx{Db}{Value}{get\_stmt\_state}
-\sscodeidx{Db}{Value}{is\_reachable}
+\ocamlinput[linerange={57-70}]{./tutorial/viewcfg/v3-eva/view_cfg.ml}
+\sscodeidx{Eva}{Analysis}{is\_computed}
+\sscodeidx{Eva}{Results}{is\_reachable}
 \sscodeidx{Cil}{visitAction}{DoChildren}
 \sscodeidx{Visitor}{frama\_c\_visitor}{vstmt\_aux}
 
 This code fills the nodes with green if the node may be
 reachable, and in pink if the node is guaranteed not to be
-reachable; but only if the value analysis was previously computed. 
+reachable; but only if the value analysis was previously computed.
 
-To test this code, frama-c should be launched with:
+To test this code, we recompile the plug-in with the modified \texttt{dune}
+file to take into account the dependency on \textsf{Eva}, as well as the
+modified \texttt{vstmt\_aux}. We run \verb|dune build @install| and then
+we run \textsf{Eva}, and then our plug-in:
 \begin{shell}
-  frama-c test.c -load-script cfg_print.ml -val -then -cfg && dotty cfg.out
+  dune exec -- frama-c test.c -eva -then -cfg && dotty cfg.dot
 \end{shell}
 
-Note that the relative order of the parameters \texttt{-load-script} and
-\texttt{-val} in this example is not relevant
-(see Section~\ref{adv:init} for details).
-However, it is important to ensure that \texttt{-cfg} is separated from
-\texttt{-val} by a \texttt{-then}; otherwise, it is not guaranteed that
-the \texttt{value} plug-in will run before the ViewCfg plug-in, which might
-lead to a non-colored graph in some cases, and colored in others.
+The relative order of most options and file names is not important
+{\em between} occurrences of \texttt{-then}, but it {\em is} important
+whether they are before or after \texttt{-then}-related options
+(\texttt{-then}, \texttt{-then-on}, \texttt{-then-last});
+see Section~\ref{adv:init} for details. Here, we want to ensure that
+\textsf{Eva} is run {\em before} our plug-in, so we order it as
+\verb|-eva -then -cfg|. Without \texttt{-then}, even if \texttt{-eva}
+is before \texttt{-cfg} in the command line, it is {\em not} guaranteed
+that it will run before; options in the same ``block'' can be thought of as
+{\em concurrent}: there is no specified order between them.
 
 The resulting graph is shown in Figure~\ref{fig:tut:coloredcfg}.
 
@@ -1070,7 +1088,7 @@ The resulting graph is shown in Figure~\ref{fig:tut:coloredcfg}.
   \centering
   \begin{minipage}[h]{0.45\linewidth}
   \listingname{test.c}
-  \cinput{./tutorial/viewcfg/tests/test.c}
+  \cinput[linerange={5-999}]{./tutorial/viewcfg/v3-eva/tests/viewcfg/test.c}
   \end{minipage}%
   \begin{minipage}[h]{0.4\linewidth}
     \includegraphics[width=\textwidth]{./tutorial/viewcfg/pdfs/cfg_colored.pdf}
@@ -1079,168 +1097,124 @@ The resulting graph is shown in Figure~\ref{fig:tut:coloredcfg}.
   \label{fig:tut:coloredcfg}
 \end{figure}
 
-\subsection{Extending the Frama-C GUI}
-\label{tut2:gui}
-\index{Plug-in!GUI}
+\subsection{Splitting files and providing a mini-GUI for testing}
+\label{tut2:split-and-gui}
 
-In this section, we will extend our plug-in so that the control flow
-graph can be displayed interactively. For that, we will extend the \framac GUI
-so that when you right-click on a function in the code, a new ``Show
-CFG'' item appears, that displays the control flow graph of the
-function in a dialog box. This is achieved just by appending the following
-pieces of code at the end of the \texttt{cfg\_print.ml} file.
+Our plug-in is starting to amass enough code that we should envisage to split
+it into several modules, for better organizing it. Dune automatically compiles
+all source files it finds, unless specified otherwise, and it handles
+dependencies between them, so it is essentially free to do so. As we did in the
+{\em Hello} tutorial, we will split our \verb|view_cfg.ml| file into smaller
+modules.
 
-\begin{important}
-Frama-C's GUI can be compiled against two versions of lablgtk (the OCaml
-bindings to the Gtk toolkit). The actual rendering of the control flow graph
-is actually done by the external OCamlgraph library, and only available
-when compiling against lablgtk2. If you're using lablgtk3,
-you will only see a pop-up window indicating that there's no support for
-displaying graphs.
-\end{important}
+We will create the following files:
 
-Currently, we used a visitor that outputs a \dottool file with the CFG of
-all functions of all files. We use \texttt{dump\_function} to output
-the CFG of a single function instead.
+\begin{description}
+\item[options.ml]: will contain the module registration (\verb|Self|) and
+  command-line options (\verb|Enabled| and \verb|OutputFile|);
+\item[visit.ml]: will contain the \verb|print_stmt| function and the visitor;
+\item[run.ml]: will contain the definition of function \verb|run| and the
+  call to \verb|Db.Main.extend|.
+\end{description}
 
-\ocamlinput{./tutorial/viewcfg/src/dump_function.ml}
+Note that a few changes are needed to the code: functions from other files
+need to include that file name as module, \eg
+\verb|Enabled.get| becomes \verb|Options.Enabled.get|.
 
-We reused the \texttt{print\_cfg} visitor, but we selected a different
-starting point. The argument \texttt{fundec} gets type
-\texttt{Cil\_types.fundec}\scodeidx{Cil\_types}{fundec}, which is the CIL type
-representing a function definition.
+For simplicity's sake, we will remove options \verb|-cfg| and \verb|-cfg-output|
+and replace them with a single boolean option, \verb|-cfg-gui|, to launch the
+GUI (this prevents unsuitable combinations of \verb|-cfg| and \verb|-cfg-gui|).
+The new option is defined as below:
 
-Now we write the GUI extension code:
+\ocamlinput[linerange={7-11}]{./tutorial/viewcfg/v4-bogue/options.ml}
 
-\ocamlinput{./tutorial/viewcfg/src/gui.ml}
-\sscodeidx{Pretty\_source}{localizable}{PVDecl}
-\sscodeidx{Globals}{Functions}{get}
-\scodeidx{Kernel\_function}{get\_definition}
-\scodeidx{Dgraph\_helper}{graph\_window}
-\scodeidx{Design}{register\_extension}
-\sscodeidx{Design}{main\_window\_extension\_points}{register\_source\_selector}
-
-Let us explain this code from the end. \texttt{Design.register\_extension} is
-the entry point for extending the GUI. Its argument is a function which takes as
-argument an object corresponding to the main window of the \framac GUI. This
-object provides access to the main widgets of the window, and several
-extension points.
-
-Here we have implemented a single extension, the ``source selector'',
-that allows to add entries to menu obtained when right-clicking on the
-source. This is implemented by the \texttt{cfg\_selector} function.
-
-This function takes a \texttt{localizable} argument, which gives information on
-where the user clicks on the source. Here we do something only if the user
-clicks on the declaration of a variable whose type is a function (\ie when the
-user clicked on a function declaration or definition).  In that case, we add an
-item to the popup menu, that calls the \texttt{callback} function if
-clicked. The \texttt{callback} function calls a \framac GUI function that
-displays a graph from \dottool printing functions. It uses several important \framac
-APIs: \texttt{Globals} and \texttt{Kernel\_function}, which contain several
-functions for manipulating globals and functions.
-
-Note that this GUI extension could also have been done through a script
-(instead of a plug-in), but it would have been less than ideal.
-In particular, the GUI \ocaml modules are available only when a script
-is loaded with \texttt{frama-c-gui}, and not when loaded with
-\texttt{frama-c}. When the user wants to view the CFG from the GUI,
-outputting the CFG of all functions in \texttt{cfg.out} is useless.
-A better architectural solution is to split our plug-in in several files,
-with its own Makefile, to better manage its functionalities.
-
-\subsection{Splitting files and writing a Makefile}\label{tut2:makefile}
-\index{Plug-in!Makefile}
-
-The \framac plug-in development environment allows to split GUI-related
-and non-GUI related modules, so that GUI-related modules are loaded
-and run only if \framac is executed with \texttt{frama-c-gui}. This
-requires splitting the module into several files. We choose the
-following architecture:
+And the \verb|run| function in \verb|run.ml| becomes simply:
 
-\begin{itemize}
-\item \texttt{cfg\_options.ml} implements plug-in registration and
-  configuration options;
-\item \texttt{cfg\_core.ml} implements the main functions for
-  computing the CFG;
-\item \texttt{cfg\_register.ml} implements ``global'' computation of
-  the CFG using the \texttt{-cfg} option, and hooking into the \framac
-  main loop;
-\item \texttt{cfg\_gui.ml} implements GUI registration.
-\end{itemize}
+\ocamlinput[linerange={1-3}]{./tutorial/viewcfg/v4-bogue/run.ml}
 
-Dependencies between the modules\footnote{This graphic is generated in
-  file \texttt{doc/code/modules.dot} after running \texttt{make doc}.}
-is presented on Figure \ref{fig:tut:cfgarchitecture}.
+We can now erase \verb|view_cfg.ml| and re-run \verb|dune build @install| to
+compile the plug-in.
 
-\begin{figure}[htbp]
-  \centering
-    \includegraphics[width=0.5\textwidth]{./tutorial/viewcfg/pdfs/modules.pdf}
-  \caption{CFG plug-in architecture}
-  \label{fig:tut:cfgarchitecture}
-\end{figure}
+\subsubsection{Mini-GUI for testing}
 
-To break recursive dependencies between \ocaml modules, it is typical
-that plug-in registration is done at the bottom of the module
-hierarchy, while definition of the \texttt{run} function is at the
-top. The GUI is also at the top of the hierarchy: the \framac
-\texttt{Makefile} requires that normal plug-in modules do not depend on
-GUI modules. Note that currently, the dependency from
-\texttt{Cfg\_core} and \texttt{Cfg\_gui} to \texttt{Cfg\_register} is
-artificial, but in future evolutions the GUI could depend on
-configuration options.
+Extending \framac's Ivette graphical user interface is a task too large for
+this tutorial; Ivette being a desktop Electron application, written in
+TypeScript and using React, there is a substantial amount of explaining to
+do before one can show how to integrate a \framac plug-in in it.
 
-\listingname{Makefile}
-\codeidx{Makefile.dynamic}
-\codeidx{FRAMAC\_SHARE}
-\codeidx{PLUGIN\_CMO}
-\codeidx{PLUGIN\_GUI\_CMO}
-\codeidx{PLUGIN\_NAME}
-\makefileinput{./tutorial/viewcfg/generated/split/Makefile}
+Instead, for this tutorial, we will use a lightweight OCaml GUI library,
+BOGUE\footnote{\url{http://sanette.github.io/bogue/Principles.html}}.
+You can install it through opam:
 
-In the \texttt{Makefile}, the \texttt{PLUGIN\_CMO} variable must
-contain the list of file names of the ml files, in the correct \ocaml
-build order. Modules in \texttt{PLUGIN\_CMO} must not depend on modules in
-\texttt{PLUGIN\_GUI\_CMO}.
+\begin{shell}
+\$ opam install bogue
+\end{shell}
+
+It is based on SDL2, which means you may need to install non-OCaml
+dependencies\footnote{With opam $< 2.1$, you may need to install and
+run \texttt{depext}. With opam $\geq 2.1$, depext is already included.}.
+
+Since we will be using BOGUE in our plug-in, we need to declare it in the
+\texttt{dune} file:
+
+\duneinput[linerange={5-5}]{./tutorial/viewcfg/v4-bogue/dune}
 
-We also need to add an interface for the whole plug-in:
-\listingname{ViewCfg.mli}
-\ocamlinput{./tutorial/viewcfg/generated/split/ViewCfg.mli}
+We also need a way to print an individual function as a standalone graph,
+without having to call the file visitor (\verb|Visit.vfile|). We will call
+it \verb|dump_function| and put it in a separate file, \verb|dump.ml|:
 
-Here is the listing for the different modules:
+\listingname{./dump.ml}
+\ocamlinput{./tutorial/viewcfg/v4-bogue/dump.ml}
+\scodeidx{Visitor}{visitFramacFunction}
 
-\listingname{cfg\_options.ml}
-\ocamlinput{./tutorial/viewcfg/generated/split/cfg_options.ml}
+The code prints a feedback message, then the header, calls the visitor,
+and prints the footer. This function will be called by our ``mini-GUI'',
+and the output will be sent to \texttt{dotty}, which will open a window
+with our graph.
 
-\listingname{cfg\_core.ml}
-\ocamlinput{./tutorial/viewcfg/generated/split/cfg_core.ml}
+The actual GUI code is put inside a file appropriately named \texttt{gui.ml}:
 
-\listingname{cfg\_register.ml}
-\ocamlinput{./tutorial/viewcfg/generated/split/cfg_register.ml}
+\listingname{./gui.ml}
+\ocamlinput{./tutorial/viewcfg/v4-bogue/gui.ml}
+\sscodeidx{Globals}{Functions}{find\_by\_name}
+\scodeidx{Kernel\_function}{get\_definition}
 
-\listingname{cfg\_gui.ml}
-\ocamlinput{./tutorial/viewcfg/generated/split/cfg_gui.ml}
+Most of the code is boilerplate for Bogue. The only \framac-related part is
+the checking of the function name: \verb|Globals.Functions.find_by_name| raises
+exception \verb|Not_found| if the name input by the user does not exist. But
+even if it does, since we need a function {\em definition}, we must check that
+it is not simply {\em declared}. Besides that, we simply call
+\verb|dump_function|. Figure~\ref{fig:tut:bogue} shows what this mini-GUI looks
+like.
 
-\subsection{Getting your Plug-in Usable by Others}\label{tut2:api}
-\index{Plug-in!API}
-\todo
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=0.35\textwidth]{./tutorial/viewcfg/pdfs/bogue.pdf}
+  \caption{Mini-GUI with Bogue for testing our plug-in.}
+  \label{fig:tut:bogue}
+\end{figure}
 
-% TODO: A script that uses "dump_function" of the CFG plug-in
+Whenever we click the ``Show CFG'' button, a new \texttt{dotty} window is
+opened. After we close it, we can repeat the operation as we like.
 
-% Detect \dottool at compile time.
-% Link to section 5.3: plug-in specific configure.in
+Note that the feedback message ``Computing CFG for function'' is emitted
+each time we click the button, which means we perform a new visit.
+For large computations and programs, this is wasteful,
+and we should be able to easily cache the result.
+The next section will show how to do it using {\em states}.
 
-\subsection{Getting your plug-in Usable in a Multi Projects Setting}
+\subsection{Saving/Loading Data, and Usability in a Multi-Project Setting}
 \label{tut2:project-and-state}
 \index{Project}
 
 \subsubsection{Registering and using state}
 
 In this section, we will learn how to register state into \framac. A
-\emph{state} is a piece of information kept by a plug-in. For instance,
-the \texttt{value} plug-in computes, for each statement, a table associating to
-each AST's variable a set of values the program may have at runtime: this
-association table is a state.
+\emph{state} is a piece of information kept by a plug-in. For instance, we can
+use a boolean state to store whether an expensive analysis has been executed,
+to avoid recomputing it. Another example of state: the \textsf{Eva} plug-in
+computes, for each statement, a table associating to each AST variable a set of
+values the program may have at runtime; this association table is a state.
 
 State registration provides several features:
 \begin{itemize}
@@ -1251,99 +1225,143 @@ State registration provides several features:
   parameters of the analysis of the different plug-ins.
 \end{itemize}
 
-In this tutorial, we will store the file representing the \dottool
-output of the control flow graph of a function (as needed by
-\texttt{dump\_function}) as a string, by using a hashtable from \texttt{fundec}
-to \texttt{string}. Storing this string will allow us to memoize~\cite{michie68}
+We will modify our \verb|Dump| module to output the \dottool graph as a string,
+and store it in a hash table from \texttt{fundec} to \texttt{string}.
+Storing this string will allow us to memoize~\cite{michie68}
 our computation: the string is computed the first time the CFG of a
 function is displayed, while the following requests will reuse the result of
-the computation. Registering the hashtable as a \framac state is
+the computation. Registering the hash table as a \framac state is
 \emph{mandatory} to ensure \framac consistency: for instance, by using a
-standard \caml hashtable, a user that would have loaded several session through
-the GUI could observe the CFG of function of a previous session instead of the
+standard \caml hash table, a user that would have loaded several sessions through
+a GUI could observe the CFG of function of a previous session instead of the
 one he wants to observe.
 
 Registering a state is done by a functor application:
 
-\ocamlinput{./tutorial/viewcfg/src/register_cfg_graph_state.ml}
+\ocamlinput[linerange={10-17}]{./tutorial/viewcfg/v5-state/dump.ml}
 \scodeidx{State\_builder}{Hashtbl}
 \scodeidx{Cil\_datatype}{Fundec}
 \scodeidx{Datatype}{String}
 \scodeidx{Ast}{self}
-\sscodeidx{Db}{Value}{self}
+\sscodeidx{Eva}{Analysis}{self}
 
 The \texttt{State\_builder} module provides several functors that help
 registering states. \texttt{State\_builder.Hashtbl} allows the developer to
-create a hashtable. It is parameterized by a module describing the hashtable
+create a hash table. It is parameterized by a module describing the hash table
 and its key, a module describing the data associated to keys, and
-other informations. 
+other information.
 
 The \texttt{Datatype} and \texttt{Cil\_datatype} modules describe the
-hashtable and its associated data, and explain for instance how the
+hash table and its associated data, and explain for instance how the
 datatype should be copied, printed, or marshalled to the disk. They
 are part of the \texttt{Type} library~\cite{signoles:jfla11}, described in
 Section~\ref{adv:datatype}. \texttt{Datatype} provides
 descriptions for standard \caml types, and \texttt{Cil\_datatype} for
 the CIL types (in the \texttt{Cil\_types} module).
 
-The last module argument describes the initial size of the hashtable, a name
-(mainly used for internal debugging), and a list of \emph{dependencies}. Here we
-expressed that our hashtable depends on the Ast and the results of the
-\texttt{Value} plug-in. For instance, whenever the \framac kernel updates one of these
-states, it will automatically reset our hashtable. This ensures consistency of
-the analysis: if the Ast of a function changes, or the value analysis is
-executed with a different entry point, this potentially affects the display of
-the control flow graph, that we must recompute.
+The last module argument describes the initial size of the hash table%
+\footnote{This initial size is an optimization feature; the table automatically
+grows when needed.}, a name (mainly used for internal debugging),
+and a list of \emph{dependencies}.
+Here we expressed that our hash table depends on the AST and the results of the
+\textsf{Eva} plug-in. For instance, whenever the \framac kernel updates one of
+these states, it will automatically reset our hash table.
+This ensures consistency of the analysis: if the AST of a function changes,
+or the value analysis is executed with a different entry point,
+this potentially affects the display of the control flow graph,
+that we must recompute.
 
 Once the module has been declared, it is fairly easy to use it.
 
-\ocamlinput{./tutorial/viewcfg/src/dump_to_string_memoized.ml}
+\ocamlinput[linerange={3-8}]{./tutorial/viewcfg/v5-state/dump.ml}
 \scodeidx{Visitor}{visitFramacFunction}
-\ocamlinput{./tutorial/viewcfg/src/dump_function_memo_no_clear_cache.ml}
+\ocamlinput[linerange={19-24}]{./tutorial/viewcfg/v5-state/dump.ml}
 
 \texttt{dump\_function} now takes two steps: first the CFG is printed
 to a string, then the string is printed to the \texttt{fmt}
 argument. This allows the \texttt{dump\_to\_string} part to be
 \emph{memoized}, \ie the results of \texttt{dump\_to\_string} are
 saved so that later calls to \texttt{dump\_function} with the same
-\texttt{fundec} argument reuse that result. 
+\texttt{fundec} argument reuse that result.
 
-If you launch \texttt{frama-c-gui} with the above code, click on
-functions to view their CFG, and inspect the console, you will observe
-that the string ``Computing CFG for function ...'' is displayed only
-once per function.
+To check this, we re-run our mini-GUI with:
 
-One can see the effects of the dependency on the \texttt{Value} plug-in
-by first launching the value analysis, inspecting the CFG for the
-\texttt{f} function, then changing the entry point to \texttt{f} in the
-CFG and re-running the value analysis. The console indicates that the
-\texttt{CFG} have been recomputed. Indeed the state of the
-\texttt{Value} plug-in, and of its dependencies, was reset when the
-entry point was changed.
+\begin{shell}
+\$ dune exec -- frama-c <file> -cfg-gui
+\end{shell}
+
+The first graph for each function will show the message
+``Computing CFG for function ...'', but subsequent calls will no longer do it.
+
+Also, we can see the effects of the dependency on the \textsf{Eva} plug-in
+by first launching the value analysis and then our plug-in:
+
+\begin{shell}
+\$ dune exec -- frama-c <file> -eva -then -cfg-gui
+\end{shell}
+
+In this case, the graphs will be colored.
+
+Finally, to check that the state dependency on \textsf{Eva} works, we will use
+a more complex command line:
+
+\begin{shell}
+\$ dune exec -- frama-c <file> -eva -then -cfg-gui -then -main f
+\end{shell}
+
+We have three stages: first run \textsf{Eva}, then open the mini-GUI, then
+change the entry point (\verb|-main f| means that the program will start
+executing from function \verb|f|) {\em and} re-open the mini-GUI.
+Remember that most \framac options {\em persist} from one stage to the next:
+the same way that we do not have to repeat \verb|-eva| after the first
+\verb|-then|, we do not have to repeat \verb|-cfg-gui| after the second
+\verb|-then|. If we want to avoid re-running the mini-GUI in future stages,
+we need to use \texttt{apply\_once}, as mentioned in Section~\ref{tut2:options}.
+
+What we observe is the following: when the mini-GUI opens, we click
+{\em Show CFG}, see a ``Computing CFG for function ...'' message, and get a
+mostly-green CFG. Then, we close the mini-GUI, and it opens again. Clicking
+{\em Show CFG} will show the same ``Computing CFG'' message, but the graph will
+be entirely pink: with \verb|f| as the entry point, function \verb|main| is
+never called, therefore entirely unreachable. Because the entry point changed,
+and \textsf{Eva} depends on its state, it is automatically recomputed.
+Because our plug-in depends on \textsf{Eva}'s state, it recomputes the graph
+when we ask again for the CFG.
 
 Another way to observe how \framac automatically handles states is to display a
-CFG, save the session, close and restart \framac, 
-and then reload the session: the control flow graph is not recomputed,
-which means that \framac has automatically saved the
-\texttt{Cfg\_graph\_state} with the rest of the session. Everything
-should also work properly when loading several sessions.
+CFG, save the session (option \verb|-save <file>|), and then load it again:
+
+\begin{shell}
+\$ dune exec -- frama-c <file> -eva -then -cfg-gui -save session.sav
+\end{shell}
+
+Then click on the ``Show CFG'' (see the feedback message), then close the CFG
+and the mini-GUI. Reload the session:
+
+\begin{shell}
+\$ dune exec -- frama-c -load session.sav
+\end{shell}
+
+Click ``Show CFG'' and you will {\em not} see the ``Computing CFG'' message:
+the state \texttt{Cfg\_graph\_state} had beem automatically saved by \framac
+and has just been loaded from the session.
 
 \subsubsection{Clearing states, selection and projects}
 
 There is one caveat though: if the user computes the CFG before
-running the \texttt{Value} analysis, and then runs \texttt{Value}, he
-will not see a colored graph (unless he re-launch the \texttt{Value}
-analysis with different parameters). This is because the state of the
-CFG is reset when the state of \texttt{Value} is reset, not when it is
+running the \textsf{Eva} analysis, and then runs \textsf{Eva}, they
+will not see a colored graph (unless they re-launch \textsf{Eva}
+with different parameters). This is because the state of the
+CFG is reset when the state of \textsf{Eva} is reset, not when it is
 first computed.
 
 To solve this problem, we will manually reset the
-\texttt{Cfg\_graph\_state} if we detect that the \texttt{Value}
-analysis has been run since the last time we computed the CFG. For
+\texttt{Cfg\_graph\_state} if we detect that \textsf{Eva}
+has been run since the last time we computed the CFG. For
 that, we have to remember the previous value of
-\texttt{Db.Value.is\_computed ()}, \ie to register another state:
+\texttt{Eva.Analysis.is\_computed ()}, \ie to register another state:
 
-\ocamlinput{./tutorial/viewcfg/src/register_value_computed_state.ml}
+\ocamlinput[linerange={19-25}]{./tutorial/viewcfg/v6-state-clear/dump.ml}
 \scodeidx{State\_builder}{Ref}
 \scodeidx{Datatype}{Bool}
 
@@ -1352,29 +1370,29 @@ This new state only consists of a reference to a boolean value.
 Then we just replace \texttt{dump\_function} in the code above by the
 following.
 
-\ocamlinput{./tutorial/viewcfg/src/dump_function_memo_clear_cache.ml}
-\sscodeidx{Db}{Value}{is\_computed}
+\ocamlinput[linerange={29-37}]{./tutorial/viewcfg/v6-state-clear/dump.ml}
+\sscodeidx{Eva}{Analysis}{is\_computed}
 \sscodeidx{State\_selection}{S}{with\_dependencies}
 \scodeidx{Project}{clear}
 
-The only part that need to be explained is the notion of
+The only parts that need to be explained are the notions of
 \emph{selection} and \emph{project}. A selection is just a set of
 states; here we selected the state \texttt{Cfg\_graph\_state} with all
-its dependencies, as resetting this state would also impact states
-that would depend on it (even if there is none for now). We use
+of its dependencies, as resetting this state would also impact states
+that would depend on it (even if there are none for now). We use
 \texttt{Project.clear} to reset the selection.
 
 \subsubsection{Project explanation}
 
 A \emph{project}~\cite{project} is a consistent version of all the \emph{states}
 of \framac. \framac is multi-AST, \ie \framac plug-ins can change the AST of the
-program, or perform incompatible analysis (\eg with different entry
-points). Projects consistently groups a version of the AST of the program, with
-the states related to this AST.
+program, or perform incompatible analyses (\eg with different entry
+points). Projects consistently group a version of the program's AST with
+the states related to it.
 
-The \texttt{Project.clear} function has type :
+The \texttt{Project.clear} function has type:
 \begin{ocamlcode}
-val clear: ?selection:State_selection.t -> ?project:t -> unit -> unit  
+val clear: ?selection:State_selection.t -> ?project:t -> unit -> unit
 \end{ocamlcode}
 \scodeidxdef{Project}{clear}
 \scodeidx{State\_selection}{t}
@@ -1406,16 +1424,16 @@ To summarize:
 \item As a plug-in developer, you have to remember that is up to you to preserve
   consistency between your states and their dependencies by clearing the latter
   when the former is modified in an incompatible way. For instance, it would
-  have been incorrect to not call 
+  have been incorrect not to call
   \texttt{State\_selection.with\_dependencies}
-  \sscodeidx{State\_selection}{S}{with\_dependencies} in the last code snippet of
-  this tutorial.
+  \sscodeidx{State\_selection}{S}{with\_dependencies}
+  in the last code snippet of this tutorial.
 \end{itemize}
 
 Projects are generally created using copy visitors. We encourage the reader to
-experiment with multiple projects development by using them. An interesting
+experiment with multi-project development by using them. An interesting
 exercise would be to change the AST so that execution of each instruction is
-logged to a file, and then re-read that file to print in the CFG how much time
+logged to a file, and then re-read that file to print in the CFG how many times
 each instruction has been executed. Another interesting exercise would be
 to use the \texttt{apply\_once} function so that the ViewCfg plug-in is executed
 only once, as explained in section~\ref{tut2:options} of this tutorial.
diff --git a/doc/developer/tutorial/viewcfg/pdfs/bogue.pdf b/doc/developer/tutorial/viewcfg/pdfs/bogue.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..a965650b1995fdad3e13a04b2da4ec7ed9b6bd84
GIT binary patch
literal 13321
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

literal 0
HcmV?d00001

diff --git a/doc/developer/tutorial/viewcfg/v1/ViewCfg.ml b/doc/developer/tutorial/viewcfg/v1-simple/ViewCfg.ml
similarity index 100%
rename from doc/developer/tutorial/viewcfg/v1/ViewCfg.ml
rename to doc/developer/tutorial/viewcfg/v1-simple/ViewCfg.ml
diff --git a/doc/developer/tutorial/viewcfg/v1-simple/dune b/doc/developer/tutorial/viewcfg/v1-simple/dune
new file mode 100644
index 00000000000..5d8c9ca6e63
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v1-simple/dune
@@ -0,0 +1,10 @@
+(library
+ (name ViewCfg)
+ (public_name frama-c-view-cfg.core)
+ (flags -open Frama_c_kernel :standard)
+ (libraries frama-c.kernel))
+
+(plugin
+ (name view-cfg)
+ (libraries frama-c-view-cfg.core)
+ (site (frama-c plugins)))
diff --git a/doc/developer/tutorial/viewcfg/v1-simple/dune-project b/doc/developer/tutorial/viewcfg/v1-simple/dune-project
new file mode 100644
index 00000000000..78c9ebfe3fd
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v1-simple/dune-project
@@ -0,0 +1,5 @@
+(lang dune 3.0)
+(using dune_site 0.1)
+
+(name frama-c-view-cfg)
+(package (name frama-c-view-cfg))
diff --git a/doc/developer/tutorial/viewcfg/v1-simple/tests/ptests_config b/doc/developer/tutorial/viewcfg/v1-simple/tests/ptests_config
new file mode 100644
index 00000000000..c286d4b312b
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v1-simple/tests/ptests_config
@@ -0,0 +1 @@
+DEFAULT_SUITES= viewcfg
diff --git a/doc/developer/tutorial/viewcfg/v1-simple/tests/test_config b/doc/developer/tutorial/viewcfg/v1-simple/tests/test_config
new file mode 100644
index 00000000000..f02f756a6e5
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v1-simple/tests/test_config
@@ -0,0 +1 @@
+PLUGIN: view-cfg
diff --git a/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/cfg.dot b/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/cfg.dot
new file mode 100644
index 00000000000..6d18d21ac40
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/cfg.dot
@@ -0,0 +1,32 @@
+digraph cfg {
+  subgraph cluster_f {
+    graph [label="f"];
+    s1 [label="g ++;"];
+    s1 -> s2;
+    s2 [label="g --;"];
+    s2 -> s16;
+    s16 [label="<return>"];
+  }
+  subgraph cluster_main {
+    graph [label="main"];
+    s5 [label="int i = 3;"];
+    s5 -> s7;
+    s7 [label="if i > 0"];
+    s7 -> s8;
+    s7 -> s13;
+    s8 [label="<loop>"];
+    s8 -> s9;
+    s9 [label="i --;"];
+    s9 -> s10;
+    s10 [label="if i"];
+    s10 -> s8;
+    s10 -> s11;
+    s11 [label="<break>"];
+    s11 -> s14;
+    s13 [label="f(3);"];
+    s13 -> s14;
+    s14 [label="__retres = 0;"];
+    s14 -> s18;
+    s18 [label="<return>"];
+  }
+}
diff --git a/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/test.res.oracle b/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/test.res.oracle
new file mode 100644
index 00000000000..ccfd0298d17
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/oracle/test.res.oracle
@@ -0,0 +1 @@
+[kernel] Parsing test.c (with preprocessing)
diff --git a/doc/developer/tutorial/viewcfg/v1/test.c b/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/test.c
similarity index 80%
rename from doc/developer/tutorial/viewcfg/v1/test.c
rename to doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/test.c
index 727f15d69fd..78b057cb38e 100644
--- a/doc/developer/tutorial/viewcfg/v1/test.c
+++ b/doc/developer/tutorial/viewcfg/v1-simple/tests/viewcfg/test.c
@@ -1,8 +1,11 @@
+/* run.config
+   LOG: cfg.dot
+   OPT:
+ */
 void f(int g)
 {
   g++;
   g--;
-  
 }
 
 int main(int argc, char **argv)
@@ -17,6 +20,5 @@ int main(int argc, char **argv)
     {
       f(3);
     }
-  
   return 0;
 }
diff --git a/doc/developer/tutorial/viewcfg/v1/view_cfg.ml b/doc/developer/tutorial/viewcfg/v1-simple/view_cfg.ml
similarity index 52%
rename from doc/developer/tutorial/viewcfg/v1/view_cfg.ml
rename to doc/developer/tutorial/viewcfg/v1-simple/view_cfg.ml
index a208d9f1bbb..0841af991e9 100644
--- a/doc/developer/tutorial/viewcfg/v1/view_cfg.ml
+++ b/doc/developer/tutorial/viewcfg/v1-simple/view_cfg.ml
@@ -1,25 +1,3 @@
-(**************************************************************************)
-(*                                                                        *)
-(*  This file is part of Frama-C.                                         *)
-(*                                                                        *)
-(*  Copyright (C) 2007-2022                                               *)
-(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
-(*         alternatives)                                                  *)
-(*                                                                        *)
-(*  you can redistribute it and/or modify it under the terms of the GNU   *)
-(*  Lesser General Public License as published by the Free Software       *)
-(*  Foundation, version 2.1.                                              *)
-(*                                                                        *)
-(*  It is distributed in the hope that it will be useful,                 *)
-(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
-(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
-(*  GNU Lesser General Public License for more details.                   *)
-(*                                                                        *)
-(*  See the GNU Lesser General Public License version 2.1                 *)
-(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
-(*                                                                        *)
-(**************************************************************************)
-
 (*
    This file is used by the ViewCfg tutorial.
    NOTE: any modifications changing line numbers will impact the
@@ -67,7 +45,7 @@ class print_cfg out = object
 end
 
 let run () =
-  let chan = open_out "cfg.out" in
+  let chan = open_out "cfg.dot" in
   let fmt = Format.formatter_of_out_channel chan in
   Visitor.visitFramacFileSameGlobals (new print_cfg fmt) (Ast.get ());
   close_out chan
diff --git a/doc/developer/tutorial/viewcfg/v1/dune b/doc/developer/tutorial/viewcfg/v1/dune
deleted file mode 100644
index abc1f859c79..00000000000
--- a/doc/developer/tutorial/viewcfg/v1/dune
+++ /dev/null
@@ -1,34 +0,0 @@
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;                                                                        ;;
-;;  This file is part of Frama-C.                                         ;;
-;;                                                                        ;;
-;;  Copyright (C) 2007-2022                                               ;;
-;;    CEA (Commissariat à l'énergie atomique et aux énergies              ;;
-;;         alternatives)                                                  ;;
-;;                                                                        ;;
-;;  you can redistribute it and/or modify it under the terms of the GNU   ;;
-;;  Lesser General Public License as published by the Free Software       ;;
-;;  Foundation, version 2.1.                                              ;;
-;;                                                                        ;;
-;;  It is distributed in the hope that it will be useful,                 ;;
-;;  but WITHOUT ANY WARRANTY; without even the implied warranty of        ;;
-;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ;;
-;;  GNU Lesser General Public License for more details.                   ;;
-;;                                                                        ;;
-;;  See the GNU Lesser General Public License version 2.1                 ;;
-;;  for more details (enclosed in the file licenses/LGPLv2.1).            ;;
-;;                                                                        ;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(library
- (name ViewCfg)
- (public_name frama-c-view-cfg.core)
- (flags -open Frama_c_kernel :standard)
- (libraries frama-c.kernel)
-)
-
-(plugin
- (name view-cfg)
- (libraries frama-c-view-cfg.core)
- (site (frama-c plugins))
-)
diff --git a/doc/developer/tutorial/viewcfg/v1/dune-project b/doc/developer/tutorial/viewcfg/v1/dune-project
deleted file mode 100644
index 4e3c156be6c..00000000000
--- a/doc/developer/tutorial/viewcfg/v1/dune-project
+++ /dev/null
@@ -1,27 +0,0 @@
-(lang dune 3.0)
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;                                                                        ;;
-;;  This file is part of Frama-C.                                         ;;
-;;                                                                        ;;
-;;  Copyright (C) 2007-2022                                               ;;
-;;    CEA (Commissariat à l'énergie atomique et aux énergies              ;;
-;;         alternatives)                                                  ;;
-;;                                                                        ;;
-;;  you can redistribute it and/or modify it under the terms of the GNU   ;;
-;;  Lesser General Public License as published by the Free Software       ;;
-;;  Foundation, version 2.1.                                              ;;
-;;                                                                        ;;
-;;  It is distributed in the hope that it will be useful,                 ;;
-;;  but WITHOUT ANY WARRANTY; without even the implied warranty of        ;;
-;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ;;
-;;  GNU Lesser General Public License for more details.                   ;;
-;;                                                                        ;;
-;;  See the GNU Lesser General Public License version 2.1                 ;;
-;;  for more details (enclosed in the file licenses/LGPLv2.1).            ;;
-;;                                                                        ;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(using dune_site 0.1)
-
-(name frama-c-view-cfg)
-(package (name frama-c-view-cfg))
diff --git a/doc/developer/tutorial/viewcfg/v2-options/ViewCfg.ml b/doc/developer/tutorial/viewcfg/v2-options/ViewCfg.ml
new file mode 100644
index 00000000000..9107a4fed90
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v2-options/ViewCfg.ml
@@ -0,0 +1,3 @@
+(** ViewCfg plug-in.
+
+    No function is exported. *)
diff --git a/doc/developer/tutorial/viewcfg/v2-options/dune b/doc/developer/tutorial/viewcfg/v2-options/dune
new file mode 100644
index 00000000000..9351560d08d
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v2-options/dune
@@ -0,0 +1,10 @@
+(library
+ (name ViewCfg)
+ (public_name frama-c-view-cfg.core)
+ (flags -open Frama_c_kernel :standard)
+ (libraries frama-c.kernel frama-c-eva.core))
+
+(plugin
+ (name view-cfg)
+ (libraries frama-c-view-cfg.core)
+ (site (frama-c plugins)))
diff --git a/doc/developer/tutorial/viewcfg/v2-options/dune-project b/doc/developer/tutorial/viewcfg/v2-options/dune-project
new file mode 100644
index 00000000000..78c9ebfe3fd
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v2-options/dune-project
@@ -0,0 +1,5 @@
+(lang dune 3.0)
+(using dune_site 0.1)
+
+(name frama-c-view-cfg)
+(package (name frama-c-view-cfg))
diff --git a/doc/developer/tutorial/viewcfg/v2-options/view_cfg.ml b/doc/developer/tutorial/viewcfg/v2-options/view_cfg.ml
new file mode 100644
index 00000000000..8fa8244e8d4
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v2-options/view_cfg.ml
@@ -0,0 +1,74 @@
+(*
+   This file is used by the ViewCfg tutorial.
+   NOTE: any modifications changing line numbers will impact the
+   generated PDF for the development guide!
+*)
+
+open Cil_types
+
+module Self = Plugin.Register(struct
+    let name = "control flow graph"
+    let shortname = "viewcfg"
+    let help = "control flow graph computation and display"
+  end)
+
+module Enabled = Self.False(struct
+    let option_name = "-cfg"
+    let help =
+      "when on (off by default), computes the CFG of all functions."
+  end)
+
+module OutputFile = Self.String(struct
+    let option_name = "-cfg-output"
+    let default = "cfg.dot"
+    let arg_name = "output-file"
+    let help = "file where the graph is output, in dot format."
+  end)
+
+let print_stmt out = function
+  | Instr i -> Printer.pp_instr out i
+  | Return _ -> Format.pp_print_string out "<return>"
+  | Goto _ -> Format.pp_print_string out "<goto>"
+  | Break _ -> Format.pp_print_string out "<break>"
+  | Continue _ -> Format.pp_print_string out "<continue>"
+  | If (e,_,_,_) -> Format.fprintf out "if %a" Printer.pp_exp e
+  | Switch(e,_,_,_) -> Format.fprintf out "switch %a" Printer.pp_exp e
+  | Loop _ -> Format.fprintf out "<loop>"
+  | Block _ -> Format.fprintf out "<block>"
+  | UnspecifiedSequence _ -> Format.fprintf out "<unspecified sequence>"
+  | TryFinally _ | TryExcept _ | TryCatch _ -> Format.fprintf out "<try>"
+  | Throw _ -> Format.fprintf out "<throw>"
+
+class print_cfg out = object
+  inherit Visitor.frama_c_inplace
+
+  method! vfile _ =
+    Format.fprintf out "digraph cfg {\n";
+    Cil.DoChildrenPost (fun f -> Format.fprintf out "}\n%!"; f)
+
+  method! vglob_aux g =
+    match g with
+    | GFun(f,_) ->
+      Format.fprintf out "  subgraph cluster_%a {\n" Printer.pp_varinfo f.svar;
+      Format.fprintf out "    graph [label=\"%a\"];\n" Printer.pp_varinfo f.svar;
+      Cil.DoChildrenPost (fun g -> Format.fprintf out "  }\n"; g)
+    | _ -> Cil.SkipChildren
+
+  method! vstmt_aux s =
+    Format.fprintf out "    s%d [label=%S];\n"
+      s.sid (Pretty_utils.to_string print_stmt s.skind);
+    List.iter (fun succ -> Format.fprintf out "    s%d -> s%d;\n" s.sid succ.sid)
+      s.succs;
+    Cil.DoChildren
+
+end
+
+let run () =
+  if Enabled.get() then
+    let filename = OutputFile.get () in
+    let chan = open_out filename in
+    let fmt = Format.formatter_of_out_channel chan in
+    Visitor.visitFramacFileSameGlobals (new print_cfg fmt) (Ast.get ());
+    close_out chan
+
+let () = Db.Main.extend run
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/ViewCfg.ml b/doc/developer/tutorial/viewcfg/v3-eva/ViewCfg.ml
new file mode 100644
index 00000000000..9107a4fed90
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/ViewCfg.ml
@@ -0,0 +1,3 @@
+(** ViewCfg plug-in.
+
+    No function is exported. *)
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/dune b/doc/developer/tutorial/viewcfg/v3-eva/dune
new file mode 100644
index 00000000000..9351560d08d
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/dune
@@ -0,0 +1,10 @@
+(library
+ (name ViewCfg)
+ (public_name frama-c-view-cfg.core)
+ (flags -open Frama_c_kernel :standard)
+ (libraries frama-c.kernel frama-c-eva.core))
+
+(plugin
+ (name view-cfg)
+ (libraries frama-c-view-cfg.core)
+ (site (frama-c plugins)))
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/dune-project b/doc/developer/tutorial/viewcfg/v3-eva/dune-project
new file mode 100644
index 00000000000..78c9ebfe3fd
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/dune-project
@@ -0,0 +1,5 @@
+(lang dune 3.0)
+(using dune_site 0.1)
+
+(name frama-c-view-cfg)
+(package (name frama-c-view-cfg))
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/tests/ptests_config b/doc/developer/tutorial/viewcfg/v3-eva/tests/ptests_config
new file mode 100644
index 00000000000..c286d4b312b
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/tests/ptests_config
@@ -0,0 +1 @@
+DEFAULT_SUITES= viewcfg
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/tests/test_config b/doc/developer/tutorial/viewcfg/v3-eva/tests/test_config
new file mode 100644
index 00000000000..f02f756a6e5
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/tests/test_config
@@ -0,0 +1 @@
+PLUGIN: view-cfg
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/cfg.dot b/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/cfg.dot
new file mode 100644
index 00000000000..6d604ab84c8
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/cfg.dot
@@ -0,0 +1,32 @@
+digraph cfg {
+  subgraph cluster_f {
+    graph [label="f"];
+    s1 [label="g ++;" fillcolor=pink style=filled]
+    s1 -> s2;
+    s2 [label="g --;" fillcolor=pink style=filled]
+    s2 -> s16;
+    s16 [label="<return>" fillcolor=pink style=filled]
+  }
+  subgraph cluster_main {
+    graph [label="main"];
+    s5 [label="int i = 3;" fillcolor="#ccffcc" style=filled]
+    s5 -> s7;
+    s7 [label="if i > 0" fillcolor="#ccffcc" style=filled]
+    s7 -> s8;
+    s7 -> s13;
+    s8 [label="<loop>" fillcolor="#ccffcc" style=filled]
+    s8 -> s9;
+    s9 [label="i --;" fillcolor="#ccffcc" style=filled]
+    s9 -> s10;
+    s10 [label="if i" fillcolor="#ccffcc" style=filled]
+    s10 -> s8;
+    s10 -> s11;
+    s11 [label="<break>" fillcolor="#ccffcc" style=filled]
+    s11 -> s14;
+    s13 [label="f(3);" fillcolor=pink style=filled]
+    s13 -> s14;
+    s14 [label="__retres = 0;" fillcolor="#ccffcc" style=filled]
+    s14 -> s18;
+    s18 [label="<return>" fillcolor="#ccffcc" style=filled]
+  }
+}
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/test.res.oracle b/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/test.res.oracle
new file mode 100644
index 00000000000..911280d1b80
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/oracle/test.res.oracle
@@ -0,0 +1,23 @@
+[kernel] Parsing test.c (with preprocessing)
+[eva] Warning: The inout plugin is missing: some features are disabled, and the analysis may have degraded precision and performance.
+[eva] Analyzing a complete application starting at main
+[eva] Computing initial state
+[eva] Initial state computed
+[eva:initial-state] Values of globals at initialization
+  
+[eva] test.c:17: starting to merge loop iterations
+[eva] done for function main
+[eva] Warning: The scope plugin is missing: cannot remove redundant alarms.
+[eva:summary] ====== ANALYSIS SUMMARY ======
+  ----------------------------------------------------------------------------
+  1 function analyzed (out of 2): 50% coverage.
+  In this function, 8 statements reached (out of 9): 88% coverage.
+  ----------------------------------------------------------------------------
+  Some errors and warnings have been raised during the analysis:
+    by the Eva analyzer:      0 errors    2 warnings
+    by the Frama-C kernel:    0 errors    0 warnings
+  ----------------------------------------------------------------------------
+  0 alarms generated by the analysis.
+  ----------------------------------------------------------------------------
+  No logical properties have been reached by the analysis.
+  ----------------------------------------------------------------------------
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/test.c b/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/test.c
new file mode 100644
index 00000000000..cf5d86680d7
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/tests/viewcfg/test.c
@@ -0,0 +1,24 @@
+/* run.config
+   LOG: cfg.dot
+   OPT: -eva -then -cfg
+ */
+void f(int g)
+{
+  g++;
+  g--;
+}
+
+int main(int argc, char **argv)
+{
+  int i = 3;
+
+  if( i > 0)
+    {
+      while(--i);
+    }
+  else
+    {
+      f(3);
+    }
+  return 0;
+}
diff --git a/doc/developer/tutorial/viewcfg/v3-eva/view_cfg.ml b/doc/developer/tutorial/viewcfg/v3-eva/view_cfg.ml
new file mode 100644
index 00000000000..1eefd2a6aaf
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v3-eva/view_cfg.ml
@@ -0,0 +1,82 @@
+(*
+   This file is used by the ViewCfg tutorial.
+   NOTE: any modifications changing line numbers will impact the
+   generated PDF for the development guide!
+*)
+
+open Cil_types
+
+module Self = Plugin.Register(struct
+    let name = "control flow graph"
+    let shortname = "viewcfg"
+    let help = "control flow graph computation and display"
+  end)
+
+module Enabled = Self.False(struct
+    let option_name = "-cfg"
+    let help =
+      "when on (off by default), computes the CFG of all functions."
+  end)
+
+module OutputFile = Self.String(struct
+    let option_name = "-cfg-output"
+    let default = "cfg.dot"
+    let arg_name = "output-file"
+    let help = "file where the graph is output, in dot format."
+  end)
+
+let print_stmt out = function
+  | Instr i -> Printer.pp_instr out i
+  | Return _ -> Format.pp_print_string out "<return>"
+  | Goto _ -> Format.pp_print_string out "<goto>"
+  | Break _ -> Format.pp_print_string out "<break>"
+  | Continue _ -> Format.pp_print_string out "<continue>"
+  | If (e,_,_,_) -> Format.fprintf out "if %a" Printer.pp_exp e
+  | Switch(e,_,_,_) -> Format.fprintf out "switch %a" Printer.pp_exp e
+  | Loop _ -> Format.fprintf out "<loop>"
+  | Block _ -> Format.fprintf out "<block>"
+  | UnspecifiedSequence _ -> Format.fprintf out "<unspecified sequence>"
+  | TryFinally _ | TryExcept _ | TryCatch _ -> Format.fprintf out "<try>"
+  | Throw _ -> Format.fprintf out "<throw>"
+
+class print_cfg out = object
+  inherit Visitor.frama_c_inplace
+
+  method! vfile _ =
+    Format.fprintf out "digraph cfg {\n";
+    Cil.DoChildrenPost (fun f -> Format.fprintf out "}\n%!"; f)
+
+  method! vglob_aux g =
+    match g with
+    | GFun(f,_) ->
+      Format.fprintf out "  subgraph cluster_%a {\n" Printer.pp_varinfo f.svar;
+      Format.fprintf out "    graph [label=\"%a\"];\n" Printer.pp_varinfo f.svar;
+      Cil.DoChildrenPost (fun g -> Format.fprintf out "  }\n"; g)
+    | _ -> Cil.SkipChildren
+
+  method! vstmt_aux s =
+    let color =
+      if Eva.Analysis.is_computed () then
+        if Eva.Results.is_reachable s
+        then "fillcolor=\"#ccffcc\" style=filled"
+        else "fillcolor=pink style=filled"
+      else ""
+    in
+    Format.fprintf out "    s%d [label=%S %s]\n"
+      s.sid (Pretty_utils.to_string print_stmt s.skind) color;
+    List.iter
+      (fun succ -> Format.fprintf out "    s%d -> s%d;\n" s.sid succ.sid)
+      s.succs;
+    Cil.DoChildren
+
+end
+
+let run () =
+  if Enabled.get() then
+    let filename = OutputFile.get () in
+    let chan = open_out filename in
+    let fmt = Format.formatter_of_out_channel chan in
+    Visitor.visitFramacFileSameGlobals (new print_cfg fmt) (Ast.get ());
+    close_out chan
+
+let () = Db.Main.extend run
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/ViewCfg.ml b/doc/developer/tutorial/viewcfg/v4-bogue/ViewCfg.ml
new file mode 100644
index 00000000000..9107a4fed90
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/ViewCfg.ml
@@ -0,0 +1,3 @@
+(** ViewCfg plug-in.
+
+    No function is exported. *)
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/dump.ml b/doc/developer/tutorial/viewcfg/v4-bogue/dump.ml
new file mode 100644
index 00000000000..a83309c18d9
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/dump.ml
@@ -0,0 +1,9 @@
+open Cil_types
+
+let dump_function fundec fmt =
+  Options.Self.feedback "Computing CFG for function %s"
+    (fundec.svar.vorig_name);
+  Format.fprintf fmt "digraph %s {\n" fundec.svar.vorig_name;
+  ignore
+    (Visitor.visitFramacFunction (new Visit.print_cfg fmt) fundec);
+  Format.fprintf fmt "\n}\n"
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/dune b/doc/developer/tutorial/viewcfg/v4-bogue/dune
new file mode 100644
index 00000000000..bc08d2353b4
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/dune
@@ -0,0 +1,11 @@
+(library
+ (name ViewCfg)
+ (public_name frama-c-view-cfg.core)
+ (flags -open Frama_c_kernel :standard)
+ (libraries frama-c.kernel frama-c-eva.core bogue))
+
+(plugin
+ (name view-cfg)
+ (libraries frama-c-view-cfg.core)
+ (site (frama-c plugins))
+)
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/dune-project b/doc/developer/tutorial/viewcfg/v4-bogue/dune-project
new file mode 100644
index 00000000000..78c9ebfe3fd
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/dune-project
@@ -0,0 +1,5 @@
+(lang dune 3.0)
+(using dune_site 0.1)
+
+(name frama-c-view-cfg)
+(package (name frama-c-view-cfg))
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/gui.ml b/doc/developer/tutorial/viewcfg/v4-bogue/gui.ml
new file mode 100644
index 00000000000..ea90922dcb0
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/gui.ml
@@ -0,0 +1,37 @@
+open Bogue
+module W = Widget
+module L = Layout
+
+let show () =
+  (* Create a few widgets for our GUI: a label, a text input with the function
+     to be displayed, and a button to show it. *)
+  let l = W.label "Show graph for function:" in
+  let t = W.text_input ~text:"main" () in
+  let b = W.button "Show CFG" in
+  let status = W.label (String.make 50 '-') in (* used for error messages *)
+  let layout = L.tower_of_w [l;t;b;status] in
+  let show_graph _button =
+    let name = W.get_text t in
+    try
+      (* Check the function name exists and is defined (not just declared). *)
+      let kf = Globals.Functions.find_by_name name in
+      let fd = Kernel_function.get_definition kf in
+      W.set_text status "";
+
+      (* Create a temporary file with the graph and pass it to 'dotty'. *)
+      let (tmpname, oc) = Filename.open_temp_file "cfg_view" ".dot" in
+      Dump.dump_function fd (Format.formatter_of_out_channel oc);
+      close_out oc;
+      let cmd = Format.asprintf "dotty %S" tmpname in
+      ignore (Sys.command cmd);
+      W.set_text status (String.make 50 '-');
+      Unix.unlink tmpname
+    with
+    | Not_found ->
+      W.set_text status ("Error: function " ^ name ^ " not found.")
+    | Kernel_function.No_Definition ->
+      W.set_text status ("Error: function " ^ name ^ " is not defined.")
+  in
+  W.on_release ~release:show_graph b;
+  let board = Bogue.make [] [layout] in
+  Bogue.run board
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/options.ml b/doc/developer/tutorial/viewcfg/v4-bogue/options.ml
new file mode 100644
index 00000000000..be309367bc4
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/options.ml
@@ -0,0 +1,11 @@
+module Self = Plugin.Register(struct
+    let name = "control flow graph"
+    let shortname = "viewcfg"
+    let help = "control flow graph computation and display"
+  end)
+
+module Gui = Self.False(struct
+    let option_name = "-cfg-gui"
+    let help =
+      "when on (off by default), displays a mini-GUI for showing graphs."
+  end)
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/run.ml b/doc/developer/tutorial/viewcfg/v4-bogue/run.ml
new file mode 100644
index 00000000000..adc46ac9afe
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/run.ml
@@ -0,0 +1,5 @@
+let run () =
+  if Options.Gui.get() then
+    Gui.show ()
+
+let () = Db.Main.extend run
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/test.c b/doc/developer/tutorial/viewcfg/v4-bogue/test.c
new file mode 100644
index 00000000000..cf5d86680d7
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/test.c
@@ -0,0 +1,24 @@
+/* run.config
+   LOG: cfg.dot
+   OPT: -eva -then -cfg
+ */
+void f(int g)
+{
+  g++;
+  g--;
+}
+
+int main(int argc, char **argv)
+{
+  int i = 3;
+
+  if( i > 0)
+    {
+      while(--i);
+    }
+  else
+    {
+      f(3);
+    }
+  return 0;
+}
diff --git a/doc/developer/tutorial/viewcfg/v4-bogue/visit.ml b/doc/developer/tutorial/viewcfg/v4-bogue/visit.ml
new file mode 100644
index 00000000000..48f6a28b0c5
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v4-bogue/visit.ml
@@ -0,0 +1,47 @@
+open Cil_types
+
+let print_stmt out = function
+  | Instr i -> Printer.pp_instr out i
+  | Return _ -> Format.pp_print_string out "<return>"
+  | Goto _ -> Format.pp_print_string out "<goto>"
+  | Break _ -> Format.pp_print_string out "<break>"
+  | Continue _ -> Format.pp_print_string out "<continue>"
+  | If (e,_,_,_) -> Format.fprintf out "if %a" Printer.pp_exp e
+  | Switch(e,_,_,_) -> Format.fprintf out "switch %a" Printer.pp_exp e
+  | Loop _ -> Format.fprintf out "<loop>"
+  | Block _ -> Format.fprintf out "<block>"
+  | UnspecifiedSequence _ -> Format.fprintf out "<unspecified sequence>"
+  | TryFinally _ | TryExcept _ | TryCatch _ -> Format.fprintf out "<try>"
+  | Throw _ -> Format.fprintf out "<throw>"
+
+class print_cfg out = object
+  inherit Visitor.frama_c_inplace
+
+  method! vfile _ =
+    Format.fprintf out "digraph cfg {\n";
+    Cil.DoChildrenPost (fun f -> Format.fprintf out "}\n%!"; f)
+
+  method! vglob_aux g =
+    match g with
+    | GFun(f,_) ->
+      Format.fprintf out "  subgraph cluster_%a {\n" Printer.pp_varinfo f.svar;
+      Format.fprintf out "    graph [label=\"%a\"];\n" Printer.pp_varinfo f.svar;
+      Cil.DoChildrenPost (fun g -> Format.fprintf out "  }\n"; g)
+    | _ -> Cil.SkipChildren
+
+  method! vstmt_aux s =
+    let color =
+      if Eva.Analysis.is_computed () then
+        if Eva.Results.is_reachable s
+        then "fillcolor=\"#ccffcc\" style=filled"
+        else "fillcolor=pink style=filled"
+      else ""
+    in
+    Format.fprintf out "    s%d [label=%S %s]\n"
+      s.sid (Pretty_utils.to_string print_stmt s.skind) color;
+    List.iter
+      (fun succ -> Format.fprintf out "    s%d -> s%d;\n" s.sid succ.sid)
+      s.succs;
+    Cil.DoChildren
+
+end
diff --git a/doc/developer/tutorial/viewcfg/v5-state/ViewCfg.ml b/doc/developer/tutorial/viewcfg/v5-state/ViewCfg.ml
new file mode 100644
index 00000000000..9107a4fed90
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/ViewCfg.ml
@@ -0,0 +1,3 @@
+(** ViewCfg plug-in.
+
+    No function is exported. *)
diff --git a/doc/developer/tutorial/viewcfg/v5-state/dump.ml b/doc/developer/tutorial/viewcfg/v5-state/dump.ml
new file mode 100644
index 00000000000..bccac711ae0
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/dump.ml
@@ -0,0 +1,24 @@
+open Cil_types
+
+let dump_to_string fundec =
+  Options.Self.feedback "Computing CFG for function %s"
+    (fundec.svar.vorig_name);
+  ignore
+    (Visitor.visitFramacFunction (new Visit.print_cfg Format.str_formatter) fundec);
+  Format.flush_str_formatter ()
+
+module Cfg_graph_state = State_builder.Hashtbl
+    (Cil_datatype.Fundec.Hashtbl)
+    (Datatype.String)
+    (struct
+      let name = "Dump.Cfg_graph_state"
+      let dependencies = [ Ast.self; Eva.Analysis.self ]
+      let size = 17
+    end)
+
+let dump_to_string_memoized = Cfg_graph_state.memo dump_to_string
+
+let dump_function fundec fmt =
+  Format.fprintf fmt "digraph %s {\n%s\n}\n"
+    fundec.svar.vorig_name
+    (dump_to_string_memoized fundec)
diff --git a/doc/developer/tutorial/viewcfg/v5-state/dune b/doc/developer/tutorial/viewcfg/v5-state/dune
new file mode 100644
index 00000000000..bc08d2353b4
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/dune
@@ -0,0 +1,11 @@
+(library
+ (name ViewCfg)
+ (public_name frama-c-view-cfg.core)
+ (flags -open Frama_c_kernel :standard)
+ (libraries frama-c.kernel frama-c-eva.core bogue))
+
+(plugin
+ (name view-cfg)
+ (libraries frama-c-view-cfg.core)
+ (site (frama-c plugins))
+)
diff --git a/doc/developer/tutorial/viewcfg/v5-state/dune-project b/doc/developer/tutorial/viewcfg/v5-state/dune-project
new file mode 100644
index 00000000000..78c9ebfe3fd
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/dune-project
@@ -0,0 +1,5 @@
+(lang dune 3.0)
+(using dune_site 0.1)
+
+(name frama-c-view-cfg)
+(package (name frama-c-view-cfg))
diff --git a/doc/developer/tutorial/viewcfg/v5-state/gui.ml b/doc/developer/tutorial/viewcfg/v5-state/gui.ml
new file mode 100644
index 00000000000..ea90922dcb0
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/gui.ml
@@ -0,0 +1,37 @@
+open Bogue
+module W = Widget
+module L = Layout
+
+let show () =
+  (* Create a few widgets for our GUI: a label, a text input with the function
+     to be displayed, and a button to show it. *)
+  let l = W.label "Show graph for function:" in
+  let t = W.text_input ~text:"main" () in
+  let b = W.button "Show CFG" in
+  let status = W.label (String.make 50 '-') in (* used for error messages *)
+  let layout = L.tower_of_w [l;t;b;status] in
+  let show_graph _button =
+    let name = W.get_text t in
+    try
+      (* Check the function name exists and is defined (not just declared). *)
+      let kf = Globals.Functions.find_by_name name in
+      let fd = Kernel_function.get_definition kf in
+      W.set_text status "";
+
+      (* Create a temporary file with the graph and pass it to 'dotty'. *)
+      let (tmpname, oc) = Filename.open_temp_file "cfg_view" ".dot" in
+      Dump.dump_function fd (Format.formatter_of_out_channel oc);
+      close_out oc;
+      let cmd = Format.asprintf "dotty %S" tmpname in
+      ignore (Sys.command cmd);
+      W.set_text status (String.make 50 '-');
+      Unix.unlink tmpname
+    with
+    | Not_found ->
+      W.set_text status ("Error: function " ^ name ^ " not found.")
+    | Kernel_function.No_Definition ->
+      W.set_text status ("Error: function " ^ name ^ " is not defined.")
+  in
+  W.on_release ~release:show_graph b;
+  let board = Bogue.make [] [layout] in
+  Bogue.run board
diff --git a/doc/developer/tutorial/viewcfg/v5-state/options.ml b/doc/developer/tutorial/viewcfg/v5-state/options.ml
new file mode 100644
index 00000000000..be309367bc4
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/options.ml
@@ -0,0 +1,11 @@
+module Self = Plugin.Register(struct
+    let name = "control flow graph"
+    let shortname = "viewcfg"
+    let help = "control flow graph computation and display"
+  end)
+
+module Gui = Self.False(struct
+    let option_name = "-cfg-gui"
+    let help =
+      "when on (off by default), displays a mini-GUI for showing graphs."
+  end)
diff --git a/doc/developer/tutorial/viewcfg/v5-state/run.ml b/doc/developer/tutorial/viewcfg/v5-state/run.ml
new file mode 100644
index 00000000000..adc46ac9afe
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/run.ml
@@ -0,0 +1,5 @@
+let run () =
+  if Options.Gui.get() then
+    Gui.show ()
+
+let () = Db.Main.extend run
diff --git a/doc/developer/tutorial/viewcfg/v5-state/test.c b/doc/developer/tutorial/viewcfg/v5-state/test.c
new file mode 100644
index 00000000000..cf5d86680d7
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/test.c
@@ -0,0 +1,24 @@
+/* run.config
+   LOG: cfg.dot
+   OPT: -eva -then -cfg
+ */
+void f(int g)
+{
+  g++;
+  g--;
+}
+
+int main(int argc, char **argv)
+{
+  int i = 3;
+
+  if( i > 0)
+    {
+      while(--i);
+    }
+  else
+    {
+      f(3);
+    }
+  return 0;
+}
diff --git a/doc/developer/tutorial/viewcfg/v5-state/visit.ml b/doc/developer/tutorial/viewcfg/v5-state/visit.ml
new file mode 100644
index 00000000000..48f6a28b0c5
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v5-state/visit.ml
@@ -0,0 +1,47 @@
+open Cil_types
+
+let print_stmt out = function
+  | Instr i -> Printer.pp_instr out i
+  | Return _ -> Format.pp_print_string out "<return>"
+  | Goto _ -> Format.pp_print_string out "<goto>"
+  | Break _ -> Format.pp_print_string out "<break>"
+  | Continue _ -> Format.pp_print_string out "<continue>"
+  | If (e,_,_,_) -> Format.fprintf out "if %a" Printer.pp_exp e
+  | Switch(e,_,_,_) -> Format.fprintf out "switch %a" Printer.pp_exp e
+  | Loop _ -> Format.fprintf out "<loop>"
+  | Block _ -> Format.fprintf out "<block>"
+  | UnspecifiedSequence _ -> Format.fprintf out "<unspecified sequence>"
+  | TryFinally _ | TryExcept _ | TryCatch _ -> Format.fprintf out "<try>"
+  | Throw _ -> Format.fprintf out "<throw>"
+
+class print_cfg out = object
+  inherit Visitor.frama_c_inplace
+
+  method! vfile _ =
+    Format.fprintf out "digraph cfg {\n";
+    Cil.DoChildrenPost (fun f -> Format.fprintf out "}\n%!"; f)
+
+  method! vglob_aux g =
+    match g with
+    | GFun(f,_) ->
+      Format.fprintf out "  subgraph cluster_%a {\n" Printer.pp_varinfo f.svar;
+      Format.fprintf out "    graph [label=\"%a\"];\n" Printer.pp_varinfo f.svar;
+      Cil.DoChildrenPost (fun g -> Format.fprintf out "  }\n"; g)
+    | _ -> Cil.SkipChildren
+
+  method! vstmt_aux s =
+    let color =
+      if Eva.Analysis.is_computed () then
+        if Eva.Results.is_reachable s
+        then "fillcolor=\"#ccffcc\" style=filled"
+        else "fillcolor=pink style=filled"
+      else ""
+    in
+    Format.fprintf out "    s%d [label=%S %s]\n"
+      s.sid (Pretty_utils.to_string print_stmt s.skind) color;
+    List.iter
+      (fun succ -> Format.fprintf out "    s%d -> s%d;\n" s.sid succ.sid)
+      s.succs;
+    Cil.DoChildren
+
+end
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/ViewCfg.ml b/doc/developer/tutorial/viewcfg/v6-state-clear/ViewCfg.ml
new file mode 100644
index 00000000000..9107a4fed90
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/ViewCfg.ml
@@ -0,0 +1,3 @@
+(** ViewCfg plug-in.
+
+    No function is exported. *)
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/dump.ml b/doc/developer/tutorial/viewcfg/v6-state-clear/dump.ml
new file mode 100644
index 00000000000..f3e8600ecca
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/dump.ml
@@ -0,0 +1,37 @@
+open Cil_types
+
+let dump_to_string fundec =
+  Options.Self.feedback "Computing CFG for function %s"
+    (fundec.svar.vorig_name);
+  ignore
+    (Visitor.visitFramacFunction (new Visit.print_cfg Format.str_formatter) fundec);
+  Format.flush_str_formatter ()
+
+module Cfg_graph_state = State_builder.Hashtbl
+    (Cil_datatype.Fundec.Hashtbl)
+    (Datatype.String)
+    (struct
+      let name = "Dump.Cfg_graph_state"
+      let dependencies = [ Ast.self; Eva.Analysis.self ]
+      let size = 17
+    end)
+
+module Eva_is_computed = State_builder.Ref
+    (Datatype.Bool)
+    (struct
+      let name = "Dump.Eva_is_computed"
+      let dependencies = []
+      let default () = false
+    end)
+
+let dump_to_string_memoized = Cfg_graph_state.memo dump_to_string
+
+let dump_function fundec fmt =
+  if not (Eva_is_computed.get ()) && Eva.Analysis.is_computed () then begin
+    Eva_is_computed.set true;
+    let selection = State_selection.with_dependencies Cfg_graph_state.self in
+    Project.clear ~selection ();
+  end;
+  Format.fprintf fmt "digraph %s {\n%s\n}\n"
+    fundec.svar.vorig_name
+    (dump_to_string_memoized fundec)
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/dune b/doc/developer/tutorial/viewcfg/v6-state-clear/dune
new file mode 100644
index 00000000000..bc08d2353b4
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/dune
@@ -0,0 +1,11 @@
+(library
+ (name ViewCfg)
+ (public_name frama-c-view-cfg.core)
+ (flags -open Frama_c_kernel :standard)
+ (libraries frama-c.kernel frama-c-eva.core bogue))
+
+(plugin
+ (name view-cfg)
+ (libraries frama-c-view-cfg.core)
+ (site (frama-c plugins))
+)
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/dune-project b/doc/developer/tutorial/viewcfg/v6-state-clear/dune-project
new file mode 100644
index 00000000000..78c9ebfe3fd
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/dune-project
@@ -0,0 +1,5 @@
+(lang dune 3.0)
+(using dune_site 0.1)
+
+(name frama-c-view-cfg)
+(package (name frama-c-view-cfg))
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/gui.ml b/doc/developer/tutorial/viewcfg/v6-state-clear/gui.ml
new file mode 100644
index 00000000000..ea90922dcb0
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/gui.ml
@@ -0,0 +1,37 @@
+open Bogue
+module W = Widget
+module L = Layout
+
+let show () =
+  (* Create a few widgets for our GUI: a label, a text input with the function
+     to be displayed, and a button to show it. *)
+  let l = W.label "Show graph for function:" in
+  let t = W.text_input ~text:"main" () in
+  let b = W.button "Show CFG" in
+  let status = W.label (String.make 50 '-') in (* used for error messages *)
+  let layout = L.tower_of_w [l;t;b;status] in
+  let show_graph _button =
+    let name = W.get_text t in
+    try
+      (* Check the function name exists and is defined (not just declared). *)
+      let kf = Globals.Functions.find_by_name name in
+      let fd = Kernel_function.get_definition kf in
+      W.set_text status "";
+
+      (* Create a temporary file with the graph and pass it to 'dotty'. *)
+      let (tmpname, oc) = Filename.open_temp_file "cfg_view" ".dot" in
+      Dump.dump_function fd (Format.formatter_of_out_channel oc);
+      close_out oc;
+      let cmd = Format.asprintf "dotty %S" tmpname in
+      ignore (Sys.command cmd);
+      W.set_text status (String.make 50 '-');
+      Unix.unlink tmpname
+    with
+    | Not_found ->
+      W.set_text status ("Error: function " ^ name ^ " not found.")
+    | Kernel_function.No_Definition ->
+      W.set_text status ("Error: function " ^ name ^ " is not defined.")
+  in
+  W.on_release ~release:show_graph b;
+  let board = Bogue.make [] [layout] in
+  Bogue.run board
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/options.ml b/doc/developer/tutorial/viewcfg/v6-state-clear/options.ml
new file mode 100644
index 00000000000..be309367bc4
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/options.ml
@@ -0,0 +1,11 @@
+module Self = Plugin.Register(struct
+    let name = "control flow graph"
+    let shortname = "viewcfg"
+    let help = "control flow graph computation and display"
+  end)
+
+module Gui = Self.False(struct
+    let option_name = "-cfg-gui"
+    let help =
+      "when on (off by default), displays a mini-GUI for showing graphs."
+  end)
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/run.ml b/doc/developer/tutorial/viewcfg/v6-state-clear/run.ml
new file mode 100644
index 00000000000..adc46ac9afe
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/run.ml
@@ -0,0 +1,5 @@
+let run () =
+  if Options.Gui.get() then
+    Gui.show ()
+
+let () = Db.Main.extend run
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/test.c b/doc/developer/tutorial/viewcfg/v6-state-clear/test.c
new file mode 100644
index 00000000000..cf5d86680d7
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/test.c
@@ -0,0 +1,24 @@
+/* run.config
+   LOG: cfg.dot
+   OPT: -eva -then -cfg
+ */
+void f(int g)
+{
+  g++;
+  g--;
+}
+
+int main(int argc, char **argv)
+{
+  int i = 3;
+
+  if( i > 0)
+    {
+      while(--i);
+    }
+  else
+    {
+      f(3);
+    }
+  return 0;
+}
diff --git a/doc/developer/tutorial/viewcfg/v6-state-clear/visit.ml b/doc/developer/tutorial/viewcfg/v6-state-clear/visit.ml
new file mode 100644
index 00000000000..48f6a28b0c5
--- /dev/null
+++ b/doc/developer/tutorial/viewcfg/v6-state-clear/visit.ml
@@ -0,0 +1,47 @@
+open Cil_types
+
+let print_stmt out = function
+  | Instr i -> Printer.pp_instr out i
+  | Return _ -> Format.pp_print_string out "<return>"
+  | Goto _ -> Format.pp_print_string out "<goto>"
+  | Break _ -> Format.pp_print_string out "<break>"
+  | Continue _ -> Format.pp_print_string out "<continue>"
+  | If (e,_,_,_) -> Format.fprintf out "if %a" Printer.pp_exp e
+  | Switch(e,_,_,_) -> Format.fprintf out "switch %a" Printer.pp_exp e
+  | Loop _ -> Format.fprintf out "<loop>"
+  | Block _ -> Format.fprintf out "<block>"
+  | UnspecifiedSequence _ -> Format.fprintf out "<unspecified sequence>"
+  | TryFinally _ | TryExcept _ | TryCatch _ -> Format.fprintf out "<try>"
+  | Throw _ -> Format.fprintf out "<throw>"
+
+class print_cfg out = object
+  inherit Visitor.frama_c_inplace
+
+  method! vfile _ =
+    Format.fprintf out "digraph cfg {\n";
+    Cil.DoChildrenPost (fun f -> Format.fprintf out "}\n%!"; f)
+
+  method! vglob_aux g =
+    match g with
+    | GFun(f,_) ->
+      Format.fprintf out "  subgraph cluster_%a {\n" Printer.pp_varinfo f.svar;
+      Format.fprintf out "    graph [label=\"%a\"];\n" Printer.pp_varinfo f.svar;
+      Cil.DoChildrenPost (fun g -> Format.fprintf out "  }\n"; g)
+    | _ -> Cil.SkipChildren
+
+  method! vstmt_aux s =
+    let color =
+      if Eva.Analysis.is_computed () then
+        if Eva.Results.is_reachable s
+        then "fillcolor=\"#ccffcc\" style=filled"
+        else "fillcolor=pink style=filled"
+      else ""
+    in
+    Format.fprintf out "    s%d [label=%S %s]\n"
+      s.sid (Pretty_utils.to_string print_stmt s.skind) color;
+    List.iter
+      (fun succ -> Format.fprintf out "    s%d -> s%d;\n" s.sid succ.sid)
+      s.succs;
+    Cil.DoChildren
+
+end
diff --git a/doc/frama-c-book.cls b/doc/frama-c-book.cls
index ef5703754df..cb6f16df585 100644
--- a/doc/frama-c-book.cls
+++ b/doc/frama-c-book.cls
@@ -335,8 +335,8 @@ morekeywords={include},%
   {language={[ANSI]C},alsolanguage=ACSL,style=framac-code-style,basicstyle=\lp@basic}
 \lstnewenvironment{ccode}[1][]%
 {\lstset{language={[ANSI]C},alsolanguage=ACSL,style=framac-code-style,basicstyle=\lp@basic,#1}}{}
-\newcommand{\cinput}[1]%
-{\lstinputlisting[language={[ANSI]C},alsolanguage=ACSL,style=framac-code-style,basicstyle=\lp@basic]{#1}}
+\newcommand{\cinput}[2][]%
+{\lstinputlisting[language={[ANSI]C},alsolanguage=ACSL,style=framac-code-style,basicstyle=\lp@basic,#1]{#2}}
 \newcommand{\cinline}[1]%
 {\lstinline[style=framac-code]{#1}}
 % --- Ocaml ----------------------------------------------------------------
-- 
GitLab