diff --git a/Makefile b/Makefile
index 8ea18cfabc8ee2ee552292fa7fc2615a07b696c7..49b29bb737853c53dbacb402a7853791203c3a1a 100644
--- a/Makefile
+++ b/Makefile
@@ -839,7 +839,7 @@ endif
 # General rules for ordering files within PLUGIN_CMO:
 # - try to keep the legacy Value before Eva
 PLUGIN_CMO:= partitioning/split_strategy domains/domain_mode value_parameters \
-	utils/value_perf utils/eva_annotations \
+	utils/eva_audit utils/value_perf utils/eva_annotations \
 	utils/value_util utils/red_statuses \
 	utils/mark_noresults \
 	utils/widen_hints_ext utils/widen \
diff --git a/doc/value/main.tex b/doc/value/main.tex
index a1e9c8363d968adcd504324032e94b471bb1bc52..dac4374ae5f2379dcc3cc12637a44b3d7b220cbd 100644
--- a/doc/value/main.tex
+++ b/doc/value/main.tex
@@ -2033,6 +2033,50 @@ by the Report plugin.
 The summary display can be disabled by using option
 \lstinline|-eva-msg-key=-summary|.
 
+\subsection{Audit messages ({\em experimental})}
+
+Using options \lstinline|-audit-prepare| and \lstinline|-audit-check|, the
+user can produce and check some information relevant for auditing purposes.
+
+\lstinline|-audit-prepare <file>| outputs the following information:
+
+\begin{itemize}
+\item the list of all source files used during parsing: not only those
+  given in the command line, but also those in \lstinline|#include|
+  directives, recursively. This usually includes Frama-C's standard library.
+  For each file, its MD5 checksum is also printed;
+\item the list of {\em enabled} and {\em disabled} warning categories,
+  for the kernel as well as for \Eva{}. Warning categories are considered
+  disabled when set to one of the following actions: {\em inactive},
+  {\em feedback}, and {\em feedback-once}. All other actions are considered
+  as enabled;
+\item the list of Eva's {\em correctness parameters}, along with their values
+  at the moment when Eva is run.
+\end{itemize}
+
+The information is written to \lstinline|<file>|, in JSON format, unless it
+is the special value \lstinline|-| (given as \lstinline|-audit-prepare=-|),
+in which case the information is printed to the standard output in text format.
+
+The complementary option \lstinline|-audit-check file| takes a JSON file
+produced by \lstinline|-audit-prepare| and checks if all checksums, warning
+category statuses and correctness parameters match. Any discrepancies are
+reported and result in an error by default.
+
+The main intent of these options is to help users perform audit-related
+tasks concerning an analysis performed with Eva. It also helps fine-tuning
+analyses without affecting its correctness; for instance, since it omits
+information related to parameters which do not affect correctness, the user
+is free to adjust them to improve the speed and precision of the analysis.
+
+Note that these options are still in experimental stage, and should not be
+blindly relied upon for strict auditing purposes. For instance, they do
+not record information related to syntactic transformations performed by
+plugins such as \textsf{Variadic}, which can have an indirect impact on
+its correctness.
+
+Please contact the Frama-C development team if you intend to use these
+options in a production setting.
 
 \section{About these alarms the user is supposed to check\ldots}
 
diff --git a/headers/header_spec.txt b/headers/header_spec.txt
index 412e563ceb435794e2ffdfa97b05aa111af3a4ed..12f47d738bde8c22fc84e36987e7250514c3f6bb 100644
--- a/headers/header_spec.txt
+++ b/headers/header_spec.txt
@@ -1386,6 +1386,8 @@ src/plugins/value/utils/library_functions.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/utils/mark_noresults.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/utils/eva_annotations.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/utils/eva_annotations.mli: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/utils/eva_audit.ml: CEA_LGPL_OR_PROPRIETARY
+src/plugins/value/utils/eva_audit.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/utils/structure.ml: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/utils/structure.mli: CEA_LGPL_OR_PROPRIETARY
 src/plugins/value/utils/unit_tests.ml: CEA_LGPL_OR_PROPRIETARY
diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml
index ec61b34cb9f1eea70091b98cd29963cbfe82d93f..6cd0cc0cecf3212337ddc80ec4ea9b66baa0ffb8 100644
--- a/src/kernel_services/ast_queries/file.ml
+++ b/src/kernel_services/ast_queries/file.ml
@@ -87,9 +87,8 @@ let get_suffixes () =
     check_suffixes
     [ ".c"; ".i"; ".h" ]
 
-let get_name s =
-  let file = match s with  NeedCPP (s,_,_,_) | NoCPP s | External (s,_) -> s in
-  Filepath.Normalized.to_pretty_string file
+let get_filepath = function NeedCPP (s, _, _, _) | NoCPP s | External (s, _) -> s
+let get_name s = Filepath.Normalized.to_pretty_string (get_filepath s)
 
 (* ************************************************************************* *)
 (** {2 Preprocessor command} *)
@@ -407,6 +406,12 @@ let pretty_machdep ?fmt ?machdep () =
 (** {2 Initializations} *)
 (* ************************************************************************* *)
 
+let create_temp_file ?debug name suffix =
+  let of_string = Filepath.Normalized.of_string in
+  try of_string (Extlib.temp_file_cleanup_at_exit ?debug name suffix)
+  with Extlib.Temp_file_error s ->
+    Kernel.abort "cannot create temporary file: %s" s
+
 let safe_remove_file (f : Datatype.Filepath.t) =
   if not (Kernel.is_debug_key_enabled Kernel.dkey_parser) then
     Extlib.safe_remove (f :> string)
@@ -462,14 +467,7 @@ let build_cpp_cmd = function
           opt;
         [opt]
     in
-    let ppf =
-      try
-        Datatype.Filepath.of_string
-          (Extlib.temp_file_cleanup_at_exit ~debug
-             (Filename.basename (f :> string)) ".i")
-      with Extlib.Temp_file_error s ->
-        Kernel.abort "cannot create temporary file: %s" s
-    in
+    let ppf = create_temp_file ~debug (Filename.basename (f :> string)) ".i" in
     (* Hypothesis: the preprocessor is POSIX compliant,
        hence understands -I and -D. *)
     let include_args =
@@ -1605,19 +1603,160 @@ let init_cil () =
   Logic_env.Builtins.apply ();
   Logic_env.prepare_tables ()
 
+let re_included_file = Str.regexp "^[.]+ \\(.*\\)$"
+
+let file_hash file =
+  Digest.to_hex (Digest.file file)
+
+let add_source_if_new tbl (fp : Filepath.Normalized.t) =
+  if not (Hashtbl.mem tbl fp) then
+    Hashtbl.replace tbl fp (file_hash (fp:>string))
+
+(* Inserts, into the hashtbl of (Filepath.Normalized.t, Digest.t), [tbl],
+   the included sources listed in [file],
+   which contains the output of 'gcc -H -MM'. *)
+let add_included_sources tbl file =
+  let ic = open_in file in
+  try
+    while true; do
+      let line = input_line ic in
+      if Str.string_match re_included_file line 0 then
+        let f = Str.matched_group 1 line in
+        add_source_if_new tbl (Filepath.Normalized.of_string f)
+    done;
+    assert false
+  with End_of_file ->
+    close_in ic
+
+let print_all_sources out all_sources_tbl =
+  let elems =
+    Hashtbl.fold (fun f hash acc ->
+        (Filepath.Normalized.to_pretty_string f, hash) :: acc)
+      all_sources_tbl []
+  in
+  let sorted_elems =
+    List.sort (fun (f1, _) (f2, _) -> Extlib.compare_ignore_case f1 f2) elems
+  in
+  if Filepath.Normalized.is_special_stdout out then begin
+    (* text format, to stdout *)
+    Kernel.feedback "Audit: all used sources, with md5 hashes:@\n%a"
+      (Pretty_utils.pp_list ~sep:"@\n"
+         (Pretty_utils.pp_pair ~sep:": "
+            Format.pp_print_string Format.pp_print_string)) sorted_elems
+  end else begin
+    (* json format, into file [out] *)
+    let json =
+      `Assoc
+        [("sources",
+          `Assoc (List.map (fun (f, hash) -> f, `String hash) sorted_elems)
+         )]
+    in
+    Json.merge_object out json
+  end
+
+let compute_sources_table cpp_commands =
+  let all_sources_tbl = Hashtbl.create 7 in
+  let process_file (file, cmd_opt) =
+    add_source_if_new all_sources_tbl (get_filepath file);
+    match cmd_opt with
+    | None -> ()
+    | Some (cpp_cmd, _ppf, _sl) ->
+      let tmp_file = create_temp_file "audit_produce_sources" ".txt" in
+      let tmp_file = (tmp_file :> string) in
+      let cmd_for_sources = cpp_cmd ^ " -H -MM >/dev/null 2>" ^ tmp_file in
+      let exit_code = Sys.command cmd_for_sources in
+      if exit_code = 0
+      then add_included_sources all_sources_tbl tmp_file
+      else
+        let cause_frama_c_compliant =
+          if Kernel.CppGnuLike.get () then "" else
+            Format.asprintf
+              "\nPlease ensure preprocessor is Frama-C compliant \
+               (see option %s)" Kernel.CppGnuLike.option_name
+        in
+        Kernel.abort "error running command to obtain included sources \
+                      (exit code %d):@\n%s%s"
+          exit_code cmd_for_sources cause_frama_c_compliant;
+  in
+  List.iter process_file cpp_commands;
+  all_sources_tbl
+
+let source_hashes_of_json path =
+  try
+    let json = Json.from_file path in
+    let open Yojson.Basic.Util in
+    json |> member "sources" |> to_assoc |>
+    List.map (fun (k, h) -> k, to_string h)
+  with
+  | Yojson.Json_error msg ->
+    Kernel.abort "error reading %a: %s"
+      Filepath.Normalized.pretty path msg
+  | Yojson.Basic.Util.Type_error (msg, v) ->
+    Kernel.abort "error reading %a: %s - %s"
+      Filepath.Normalized.pretty path msg
+      (Yojson.Basic.pretty_to_string v)
+
+let check_source_hashes expected actual_table =
+  let checked, diffs =
+    Hashtbl.fold (fun fp hash (acc_checked, acc_diffs) ->
+        let fp = Filepath.Normalized.to_pretty_string fp in
+        let expected_hash = List.assoc_opt fp expected in
+        let checked = Datatype.String.Set.add fp acc_checked in
+        let diffs =
+          if Some hash = expected_hash then acc_diffs
+          else (fp, hash, expected_hash) :: acc_diffs
+        in
+        checked, diffs
+      ) actual_table (Datatype.String.Set.empty, [])
+  in
+  if diffs <> [] then begin
+    let diffs =
+      List.sort (fun (fp1, _, _) (fp2, _, _) ->
+          Extlib.compare_ignore_case fp1 fp2) diffs
+    in
+    List.iter (fun (fp, got, expected) ->
+        Kernel.warning ~wkey:Kernel.wkey_audit
+          "different hashes for %s: got %s, expected %s"
+          fp got (Option.value ~default:("<none> (not in list)") expected)
+      ) diffs
+  end;
+  let expected_names = List.map fst expected in
+  let missing =
+    List.filter (fun fp -> not (Datatype.String.Set.mem fp checked))
+      expected_names
+  in
+  if missing <> [] then begin
+    let missing = List.sort Extlib.compare_ignore_case missing in
+    Kernel.warning ~wkey:Kernel.wkey_audit
+      "missing files:@\n%a"
+      (Pretty_utils.pp_list ~sep:"@\n" Format.pp_print_string) missing
+  end
+
+let print_and_exit cpp_commands =
+  let print_cpp_cmd (cpp_cmd, _ppf, _) =
+    Kernel.result "Preprocessing command:@.%s" cpp_cmd
+  in
+  List.iter (fun (_f, ocmd) -> Option.iter print_cpp_cmd ocmd) cpp_commands;
+  raise Cmdline.Exit
+
 let prepare_from_c_files () =
   init_cil ();
   let files = Files.get () in (* Allow pre-registration of prolog files *)
   let cpp_commands = List.map (fun f -> (f, build_cpp_cmd f)) files in
-  if Kernel.PrintCppCommands.get () then begin
-    List.iter (fun (_f, opt_cpp_cmd) ->
-        match opt_cpp_cmd with
-        | None -> ()
-        | Some (cpp_cmd, _ppf, _) ->
-          Kernel.result
-            "Preprocessing command:@.%s" cpp_cmd
-      ) cpp_commands;
-    raise Cmdline.Exit
+  if Kernel.PrintCppCommands.get () then print_and_exit cpp_commands;
+  let audit_check_path = Kernel.AuditCheck.get () in
+  if not (Filepath.Normalized.is_unknown audit_check_path) then begin
+    let all_sources_tbl = compute_sources_table cpp_commands in
+    let expected_hashes = source_hashes_of_json audit_check_path in
+    check_source_hashes expected_hashes all_sources_tbl
+  end;
+  let audit_path = Kernel.AuditPrepare.get () in
+  if not (Filepath.Normalized.is_unknown audit_path) then begin
+    let all_sources_tbl = compute_sources_table cpp_commands in
+    print_all_sources audit_path all_sources_tbl;
+    if not (Filepath.Normalized.is_special_stdout audit_path) then
+      Kernel.feedback "Audit: sources list written to: %a@."
+        Filepath.Normalized.pretty audit_path;
   end;
   let cil, cabs_files = files_to_cabs_cil files cpp_commands in
   prepare_cil_file cil;
@@ -1763,8 +1902,7 @@ let create_rebuilt_project_from_visitor
       let name = "frama_c_project_" ^ prj_name ^ "_" in
       let ext = if preprocess then ".c" else ".i" in
       let debug = Kernel.Debug.get () > 0 in
-      let tmp_fname = Extlib.temp_file_cleanup_at_exit ~debug name ext in
-      Filepath.Normalized.of_string tmp_fname
+      create_temp_file ~debug name ext
     in
     let cout = open_out (f :> string) in
     let fmt = Format.formatter_of_out_channel cout in
@@ -1776,7 +1914,7 @@ let create_rebuilt_project_from_visitor
     in
     Project.on prj redo ();
     prj
-  with Extlib.Temp_file_error s | Sys_error s ->
+  with Sys_error s ->
     Kernel.abort "cannot create temporary file: %s" s
 
 (*
diff --git a/src/kernel_services/plugin_entry_points/kernel.ml b/src/kernel_services/plugin_entry_points/kernel.ml
index 4444276b7a8545a8c79636a82ec4e0651db45579..e21274bd4b33a08bc8ebb31782b30d56a9ebff6d 100644
--- a/src/kernel_services/plugin_entry_points/kernel.ml
+++ b/src/kernel_services/plugin_entry_points/kernel.ml
@@ -194,6 +194,9 @@ let wkey_acsl_extension = register_warn_category "acsl-extension"
 
 let wkey_cmdline = register_warn_category "cmdline"
 
+let wkey_audit = register_warn_category "audit"
+let () = set_warn_status wkey_audit Log.Werror
+
 (* ************************************************************************* *)
 (** {2 Specialised functors for building kernel parameters} *)
 (* ************************************************************************* *)
@@ -1040,6 +1043,41 @@ module PrintCppCommands =
                   and exits."
     end)
 
+let () = Parameter_customize.set_group parsing
+let () = Parameter_customize.do_not_reset_on_copy ()
+module AuditPrepare =
+  P.Filepath
+    (struct
+      let option_name = "-audit-prepare"
+      let arg_name = "path"
+      let existence = Filepath.Indifferent
+      let file_kind = "json"
+      let help = "produces audit-related information, such as the list of all \
+                  source files used during parsing (including those in include \
+                  directives) with checksums. Some plug-ins may produce \
+                  additional audit-related information. \
+                  Prints the information as JSON to the specified file, or \
+                  if the file is '-', prints as text to the standard output. \
+                  Requires -cpp-frama-c-compliant."
+    end)
+
+let () = Parameter_customize.set_group parsing
+let () = Parameter_customize.do_not_reset_on_copy ()
+module AuditCheck =
+  P.Filepath
+    (struct
+      let option_name = "-audit-check"
+      let arg_name = "path"
+      let existence = Filepath.Must_exist
+      let file_kind = "json"
+      let help = "reads an audit JSON file (produced by -audit-prepare) and \
+                  checks compliance w.r.t. it; e.g., if the source files \
+                  were declared and have the expected checksum. \
+                  Raises a warning (with warning key 'audit') in case of \
+                  failed checks. \
+                  Requires -cpp-frama-c-compliant."
+    end)
+
 let () = Parameter_customize.set_group parsing
 let () = Parameter_customize.do_not_reset_on_copy ()
 module FramaCStdLib =
diff --git a/src/kernel_services/plugin_entry_points/kernel.mli b/src/kernel_services/plugin_entry_points/kernel.mli
index bb6247d651b2769ba236ffb8aa07aa6f9e3e5634..a6e7e409bca45d77489b1e2576724809180c2e6e 100644
--- a/src/kernel_services/plugin_entry_points/kernel.mli
+++ b/src/kernel_services/plugin_entry_points/kernel.mli
@@ -186,6 +186,9 @@ val wkey_acsl_extension: warn_category
 val wkey_cmdline: warn_category
 (** Command-line related warning, e.g. for invalid options given by the user *)
 
+val wkey_audit: warn_category
+(** Warning related to options '-audit-*'. *)
+
 (* ************************************************************************* *)
 (** {2 Functors for late option registration}                                *)
 (** Kernel_function-related options cannot be registered in this module:
@@ -414,6 +417,12 @@ module CppGnuLike: Parameter_sig.Bool
 module PrintCppCommands: Parameter_sig.Bool
 (** Behavior of option "-print-cpp-commands" *)
 
+module AuditPrepare: Parameter_sig.Filepath
+(** Behavior of option "-audit-prepare" *)
+
+module AuditCheck: Parameter_sig.Filepath
+(** Behavior of option "-audit-check" *)
+
 module FramaCStdLib: Parameter_sig.Bool
 (** Behavior of option "-frama-c-stdlib" *)
 
diff --git a/src/plugins/value/engine/analysis.ml b/src/plugins/value/engine/analysis.ml
index 5cb98e37e081c78603150f34bbf50b2e184901ba..93fd504680369faf45e68100017c4acf5552ea39 100644
--- a/src/plugins/value/engine/analysis.ml
+++ b/src/plugins/value/engine/analysis.ml
@@ -173,6 +173,10 @@ let reset_analyzer () =
 let force_compute () =
   Ast.compute ();
   Value_parameters.configure_precision ();
+  if not (Filepath.Normalized.is_unknown (Kernel.AuditCheck.get ()))
+  then Eva_audit.check_configuration (Kernel.AuditCheck.get ());
+  if not (Filepath.Normalized.is_unknown (Kernel.AuditPrepare.get ()))
+  then Eva_audit.print_configuration (Kernel.AuditPrepare.get ());
   let kf, lib_entry = Globals.entry_point () in
   reset_analyzer ();
   let module Analyzer = (val snd !ref_analyzer) in
diff --git a/src/plugins/value/utils/eva_audit.ml b/src/plugins/value/utils/eva_audit.ml
new file mode 100644
index 0000000000000000000000000000000000000000..f7e10c46cf07a9449a120bde4371bba451f75233
--- /dev/null
+++ b/src/plugins/value/utils/eva_audit.ml
@@ -0,0 +1,146 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2021                                               *)
+(*    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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+let get_correctness_parameters () =
+  let get param =
+    let name = param.Typed_parameter.name in
+    let value = Typed_parameter.get_value param in
+    (name, value)
+  in
+  List.map get (Value_parameters.parameters_correctness)
+
+let parameters_of_json json =
+  try
+    let open Yojson.Basic.Util in
+    let params =
+      json |> member "eva" |> member "correctness-parameters" |> to_assoc
+    in
+    List.map (fun (key, value) -> (key, to_string value)) params
+  with
+  | Yojson.Json_error msg ->
+    Kernel.abort "error reading JSON file: %s" msg
+  | Yojson.Basic.Util.Type_error (msg, v) ->
+    Kernel.abort "error reading JSON file: %s - %s" msg
+      (Yojson.Basic.pretty_to_string v)
+
+let print_correctness_parameters path =
+  let parameters = get_correctness_parameters () in
+  if Filepath.Normalized.is_special_stdout path then begin
+    Value_parameters.feedback "Correctness parameters of the analysis:";
+    let print (name, value) = Value_parameters.printf "  %s: %s" name value in
+    List.iter print parameters
+  end else begin
+    let json = List.map (fun (name, value) -> name, `String value) parameters in
+    let params_json = `Assoc [("correctness-parameters", `Assoc json)] in
+    let eva_json = `Assoc [("eva", params_json)] in
+    Json.merge_object path eva_json
+  end
+
+let check_correctness_parameters json =
+  let parameters = get_correctness_parameters () in
+  let expected_parameters = parameters_of_json json in
+  (* Note: we could compare lengths and use a two-list iterator on sorted lists,
+     but in case of divergence, the error messages would be less clear. *)
+  List.iter (fun (exp_p, exp_v) ->
+      try
+        let v = List.assoc exp_p parameters in
+        if exp_v <> v then
+          Kernel.warning ~wkey:Kernel.wkey_audit
+            "correctness parameter %s: expected value %s, but got %s" exp_p
+            exp_v v
+      with Not_found ->
+        Kernel.warning ~wkey:Kernel.wkey_audit
+          "expected correctness parameter %s, but not found" exp_p
+    ) expected_parameters
+
+let compute_warning_status (module Plugin: Log.Messages) =
+  let warning_categories = Plugin.get_all_warn_categories_status () in
+  let is_active = function
+    | Log.Winactive | Wfeedback_once | Wfeedback -> false
+    | Wonce | Wactive | Werror_once | Werror | Wabort -> true
+  in
+  let is_enabled (_key, status) = is_active status in
+  let enabled, disabled = List.partition is_enabled warning_categories in
+  let pp_fst = List.map (fun (c, s) -> Plugin.wkey_name c, s) in
+  (pp_fst enabled, pp_fst disabled)
+
+let json_of_warning_statuses wkeys key_name =
+  let json_of_wkey = List.map (fun (c, _) -> `String c) in
+  (key_name, `List (json_of_wkey wkeys))
+
+let enabled_warning_of_json name json =
+  try
+    let open Yojson.Basic.Util in
+    json |> member name |> member "warning-categories" |>
+    member "enabled" |> to_list |> filter_string
+  with
+  | Yojson.Json_error msg ->
+    Kernel.abort "error reading JSON file: %s" msg
+  | Yojson.Basic.Util.Type_error (msg, v) ->
+    Kernel.abort "error reading JSON file: %s - %s" msg
+      (Yojson.Basic.pretty_to_string v)
+
+let print_warning_status path name (module Plugin: Log.Messages) =
+  let enabled, disabled = compute_warning_status (module Plugin) in
+  if Filepath.Normalized.is_special_stdout path then
+    begin
+      let pp_categories =
+        Pretty_utils.pp_list ~sep:",@ " Format.pp_print_string
+      in
+      Value_parameters.feedback "Audit: %s warning categories:" name;
+      Value_parameters.printf "  Enabled: @[%a@]"
+        pp_categories (List.map fst enabled);
+      Value_parameters.printf "  Disabled: @[%a@]"
+        pp_categories (List.map fst disabled)
+    end
+  else begin
+    let enabled_json = json_of_warning_statuses enabled "enabled"
+    and disabled_json = json_of_warning_statuses disabled "disabled" in
+    let warning_json = `Assoc [enabled_json; disabled_json] in
+    let name = Stdlib.String.lowercase_ascii name in
+    let json = `Assoc [(name, `Assoc [("warning-categories", warning_json)])] in
+    Json.merge_object path json
+  end
+
+let check_warning_status json name (module Plugin: Log.Messages) =
+  let name = Stdlib.String.lowercase_ascii name in
+  let enabled, _disabled = compute_warning_status (module Plugin) in
+  let enabled = List.map fst enabled in
+  let expected_enabled = enabled_warning_of_json name json in
+  let diff l1 l2 = List.filter (fun k -> not (List.mem k l2)) l1 in
+  let should_be_enabled = diff expected_enabled enabled in
+  if should_be_enabled <> [] then
+    Kernel.warning ~wkey:Kernel.wkey_audit
+      "the following warning categories were expected to be enabled,@ \
+       but are disabled: %a"
+      (Pretty_utils.pp_list ~sep:", " Format.pp_print_string) should_be_enabled
+
+let check_configuration path =
+  let json = Json.from_file path in
+  check_correctness_parameters json;
+  check_warning_status json "Kernel" (module Kernel);
+  check_warning_status json "Eva" (module Value_parameters)
+
+let print_configuration path =
+  print_correctness_parameters path;
+  print_warning_status path "Kernel" (module Kernel);
+  print_warning_status path "Eva" (module Value_parameters)
diff --git a/src/plugins/value/utils/eva_audit.mli b/src/plugins/value/utils/eva_audit.mli
new file mode 100644
index 0000000000000000000000000000000000000000..7cd6bff0d6ef912d07cb6db454a0c80c9b5458e9
--- /dev/null
+++ b/src/plugins/value/utils/eva_audit.mli
@@ -0,0 +1,24 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2021                                               *)
+(*    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).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+val check_configuration: Filepath.Normalized.t -> unit
+val print_configuration: Filepath.Normalized.t -> unit
diff --git a/tests/value/audit-in.json b/tests/value/audit-in.json
new file mode 100644
index 0000000000000000000000000000000000000000..d6abf18d0ffb9f05e82b0d158058b8715f36f417
--- /dev/null
+++ b/tests/value/audit-in.json
@@ -0,0 +1,77 @@
+{
+  "sources": {
+    "tests/value/audit.c": "01010101010101010101010101010101",
+    "tests/value/audit_included.h": "c2cc488143a476f69cf2ed04c3439e6e",
+    "tests/value/non_existing_file.h": "1234567890abcdef1234567890abcdef"
+  },
+  "kernel": {
+    "warning-categories": {
+      "enabled": [
+        "*", "CERT", "CERT:EXP", "CERT:EXP:46", "CERT:MSC", "CERT:MSC:38",
+        "acsl-extension", "annot", "annot:missing-spec", "annot-error",
+        "audit", "check", "check:volatile", "cmdline", "ghost",
+        "ghost:bad-use", "inline", "linker",
+        "linker:drop-conflicting-unused", "parser",
+        "parser:conditional-feature", "pp", "pp:compilation-db", "typing",
+        "typing:implicit-conv-void-ptr",
+        "typing:implicit-function-declaration",
+        "typing:incompatible-pointer-types",
+        "typing:incompatible-types-call", "typing:inconsistent-specifier",
+        "typing:int-conversion", "typing:no-proto"
+      ],
+      "disabled": [
+        "CERT:EXP:10", "acsl-float-compare", "ghost:already-ghost",
+        "parser:decimal-float", "transient-block"
+      ]
+    }
+  },
+  "eva": {
+    "correctness-parameters": {
+      "-absolute-valid-range": "",
+      "-eva-all-rounding-modes-constants": "false",
+      "-eva-alloc-returns-null": "true",
+      "-eva-builtin": "",
+      "-eva-builtins-auto": "true",
+      "-eva-context-depth": "2",
+      "-eva-context-valid-pointers": "false",
+      "-eva-context-width": "2",
+      "-eva-ignore-recursive-calls": "false",
+      "-eva-initialization-padding-globals": "yes",
+      "-eva-initialized-locals": "false",
+      "-eva-new-initial-state": "0",
+      "-eva-reduce-on-logic-alarms": "false",
+      "-eva-undefined-pointer-comparison-propagate-all": "false",
+      "-eva-use-spec": "",
+      "-eva-warn-copy-indeterminate": "",
+      "-eva-warn-pointer-subtraction": "true",
+      "-eva-warn-signed-converted-downcast": "false",
+      "-eva-warn-undefined-pointer-comparison": "pointer",
+      "-initialized-padding-locals": "true",
+      "-lib-entry": "false",
+      "-main": "main",
+      "-safe-arrays": "true",
+      "-unspecified-access": "false",
+      "-warn-invalid-bool": "true",
+      "-warn-invalid-pointer": "false",
+      "-warn-left-shift-negative": "true",
+      "-warn-pointer-downcast": "true",
+      "-warn-right-shift-negative": "false",
+      "-warn-signed-downcast": "false",
+      "-warn-signed-overflow": "true",
+      "-warn-special-float": "non-finite",
+      "-warn-unsigned-downcast": "false",
+      "-warn-unsigned-overflow": "false"
+    },
+    "warning-categories": {
+      "enabled": [
+        "*", "alarm", "builtins", "builtins:missing-spec",
+        "builtins:override", "experimental", "libc", "libc:unsupported-spec",
+        "locals-escaping", "malloc", "malloc:imprecise", "signed-overflow"
+      ],
+      "disabled": [
+        "garbled-mix", "invalid-assigns", "loop-unroll", "malloc:weak",
+        "missing-loop-unroll", "missing-loop-unroll:for"
+      ]
+    }
+  }
+}
diff --git a/tests/value/audit.c b/tests/value/audit.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b3e27c0eb13ce8bf3de6099b21131006f9de2be
--- /dev/null
+++ b/tests/value/audit.c
@@ -0,0 +1,11 @@
+/* run.config
+   LOG: audit-out.json
+   STDOPT: #"-audit-check @PTEST_DIR@/audit-in.json -audit-prepare @PTEST_DIR@/result/audit-out.json -kernel-warn-key audit=active"
+*/
+
+#include "audit_included.h"
+#include "audit_included_but_not_listed.h"
+
+void main() {
+  float f = 2.1; // to trigger a syntactic warning
+}
diff --git a/tests/value/audit_included.h b/tests/value/audit_included.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4b481cc2ee4f9d292b6ea835cb8d09ef16780b7
--- /dev/null
+++ b/tests/value/audit_included.h
@@ -0,0 +1 @@
+// This file is included by audit.c
diff --git a/tests/value/audit_included_but_not_listed.h b/tests/value/audit_included_but_not_listed.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4b481cc2ee4f9d292b6ea835cb8d09ef16780b7
--- /dev/null
+++ b/tests/value/audit_included_but_not_listed.h
@@ -0,0 +1 @@
+// This file is included by audit.c
diff --git a/tests/value/oracle/audit-out.json b/tests/value/oracle/audit-out.json
new file mode 100644
index 0000000000000000000000000000000000000000..f9aa254ce113486f162682dd6cebdc83f2a6768d
--- /dev/null
+++ b/tests/value/oracle/audit-out.json
@@ -0,0 +1,78 @@
+{
+  "eva": {
+    "correctness-parameters": {
+      "-absolute-valid-range": "",
+      "-eva-all-rounding-modes-constants": "false",
+      "-eva-alloc-returns-null": "true",
+      "-eva-builtin": "",
+      "-eva-builtins-auto": "true",
+      "-eva-context-depth": "2",
+      "-eva-context-valid-pointers": "false",
+      "-eva-context-width": "2",
+      "-eva-ignore-recursive-calls": "false",
+      "-eva-initialization-padding-globals": "yes",
+      "-eva-initialized-locals": "false",
+      "-eva-new-initial-state": "0",
+      "-eva-reduce-on-logic-alarms": "false",
+      "-eva-undefined-pointer-comparison-propagate-all": "false",
+      "-eva-use-spec": "",
+      "-eva-warn-copy-indeterminate": "",
+      "-eva-warn-pointer-subtraction": "true",
+      "-eva-warn-signed-converted-downcast": "false",
+      "-eva-warn-undefined-pointer-comparison": "pointer",
+      "-initialized-padding-locals": "true",
+      "-lib-entry": "false",
+      "-main": "main",
+      "-safe-arrays": "true",
+      "-unspecified-access": "false",
+      "-warn-invalid-bool": "true",
+      "-warn-invalid-pointer": "false",
+      "-warn-left-shift-negative": "true",
+      "-warn-pointer-downcast": "true",
+      "-warn-right-shift-negative": "false",
+      "-warn-signed-downcast": "false",
+      "-warn-signed-overflow": "true",
+      "-warn-special-float": "non-finite",
+      "-warn-unsigned-downcast": "false",
+      "-warn-unsigned-overflow": "false"
+    },
+    "warning-categories": {
+      "enabled": [
+        "*", "alarm", "builtins", "builtins:missing-spec",
+        "builtins:override", "experimental", "libc", "libc:unsupported-spec",
+        "locals-escaping", "malloc", "malloc:imprecise", "signed-overflow"
+      ],
+      "disabled": [
+        "garbled-mix", "invalid-assigns", "loop-unroll", "malloc:weak",
+        "missing-loop-unroll", "missing-loop-unroll:for"
+      ]
+    }
+  },
+  "kernel": {
+    "warning-categories": {
+      "enabled": [
+        "*", "CERT", "CERT:EXP", "CERT:EXP:46", "CERT:MSC", "CERT:MSC:38",
+        "acsl-extension", "annot", "annot:missing-spec", "annot:multi-from",
+        "annot-error", "audit", "check", "check:volatile", "cmdline",
+        "ghost", "ghost:bad-use", "inline", "linker",
+        "linker:drop-conflicting-unused", "parser",
+        "parser:conditional-feature", "pp", "pp:compilation-db", "typing",
+        "typing:implicit-conv-void-ptr",
+        "typing:implicit-function-declaration",
+        "typing:incompatible-pointer-types",
+        "typing:incompatible-types-call", "typing:inconsistent-specifier",
+        "typing:int-conversion", "typing:no-proto"
+      ],
+      "disabled": [
+        "CERT:EXP:10", "acsl-float-compare", "ghost:already-ghost",
+        "parser:decimal-float", "transient-block"
+      ]
+    }
+  },
+  "sources": {
+    "tests/value/audit.c": "08f73691217888d926a0ee15cbe18159",
+    "tests/value/audit_included.h": "c2cc488143a476f69cf2ed04c3439e6e",
+    "tests/value/audit_included_but_not_listed.h":
+      "c2cc488143a476f69cf2ed04c3439e6e"
+  }
+}
diff --git a/tests/value/oracle/audit.res.oracle b/tests/value/oracle/audit.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..338a77c21a7297b49c9f9bf0e4c7faba157e6082
--- /dev/null
+++ b/tests/value/oracle/audit.res.oracle
@@ -0,0 +1,34 @@
+[kernel:audit] Warning: 
+  different hashes for tests/value/audit.c: got 08f73691217888d926a0ee15cbe18159, expected 01010101010101010101010101010101
+[kernel:audit] Warning: 
+  different hashes for tests/value/audit_included_but_not_listed.h: got c2cc488143a476f69cf2ed04c3439e6e, expected <none> (not in list)
+[kernel:audit] Warning: 
+  missing files:
+  tests/value/non_existing_file.h
+[kernel] Audit: sources list written to: tests/value/result/audit-out.json
+[kernel] Parsing tests/value/audit.c (with preprocessing)
+[kernel:parser:decimal-float] tests/value/audit.c:10: Warning: 
+  Floating-point constant 2.1 is not represented exactly. Will use 0x1.0cccccccccccdp1.
+  (warn-once: no further messages from category 'parser:decimal-float' will be emitted)
+[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] Recording results for main
+[eva] done for function main
+[eva] ====== VALUES COMPUTED ======
+[eva:final-states] Values at end of function main:
+  f ∈ {2.09999990463}
+[from] Computing for function main
+[from] Done for function main
+[from] ====== DEPENDENCIES COMPUTED ======
+  These dependencies hold at termination for the executions that terminate:
+[from] Function main:
+  NO EFFECTS
+[from] ====== END OF DEPENDENCIES ======
+[inout] Out (internal) for function main:
+    f
+[inout] Inputs for function main:
+    \nothing
+[kernel] Wrote: tests/value/result/audit-out.json