From 47fdee60003d8008c6c310d712a48982a9c001c8 Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Mon, 13 Aug 2018 08:31:45 -0700 Subject: [PATCH] add tips about debugging infer Summary: Useful tips and tricks to debug infer's OCaml code. Also emit a developer warning when the database is not initialised (since it's only expected to happen when running infer from the toplevel). Reviewed By: mbouaziz Differential Revision: D9295782 fbshipit-source-id: 09b7b9a02 --- CONTRIBUTING.md | 33 ++++++++++++- infer/src/base/ResultsDatabase.ml | 79 +++++++++++++++++++------------ 2 files changed, 80 insertions(+), 32 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e486259e0..115482f6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ run: make devsetup ``` -### Tips and Tricks +### Building Infer for Development - Build the code faster: `make -j BUILD_MODE=default`. By default `make` builds infer with flambda enabled, which makes it very slow (but makes infer significantly faster). @@ -49,6 +49,37 @@ make devsetup - To switch the default build mode to flambda disabled, you can `export BUILD_MODE=default` in your shell. +### Debugging OCaml Code + +- Printf-debug using `Logging.debug_dev`. It comes with a warning so + that you don't accidentally push code with calls to `debug_dev` to + the repo. + +- Browse the documentation of OCaml modules in your browser with `make doc` + +- When using `ocamldebug`, and in particular when setting break points + with `break @ ` don't forget that an infer module `M` + is in reality called `InferModules__M`, or `InferBase__M`, or + ... See the html documentation of the OCaml modules from `make doc` + if you're unsure of a module name. + +```console +$ ledit ocamldebug infer/bin/infer.bc +(ocd) break @ InferModules__InferAnalyze 100 +Breakpoint 1 at 9409684: file backend/InferAnalyze.ml, line 99, characters 18-78 +``` + +- To test the infer OCaml code you can use the OCaml toplevel. To + build the OCaml toplevel with the infer modules pre-loaded, run + `make toplevel` and follow the instructions. + + To pass infer options to the toplevel, use `INFER_ARGS`, for + instance: `INFER_ARGS=--debug^-o^infer-out-foo`. + + Many operations require the results directory and database to be + initialized with `ResultsDir.assert_results_dir ""`. + + ## Hacking on the Code in facebook-clang-plugins Infer uses `ASTExporter` from the [facebook-clang-plugins](https://github.com/facebook/facebook-clang-plugins) diff --git a/infer/src/base/ResultsDatabase.ml b/infer/src/base/ResultsDatabase.ml index 728170c88..12ba8f3b8 100644 --- a/infer/src/base/ResultsDatabase.ml +++ b/infer/src/base/ResultsDatabase.ml @@ -8,8 +8,6 @@ open! IStd module L = Logging -let database : Sqlite3.db option ref = ref None - let database_filename = "results.db" let database_fullpath = Config.results_dir ^/ database_filename @@ -71,21 +69,6 @@ let close_db_callbacks = ref [] let on_close_database ~f = close_db_callbacks := f :: !close_db_callbacks -let get_database () = Option.value_exn !database - -let reset_capture_tables () = - let db = get_database () in - SqliteUtils.exec db ~log:"drop procedures table" ~stmt:"DROP TABLE procedures" ; - create_procedures_table db ; - SqliteUtils.exec db ~log:"drop source_files table" ~stmt:"DROP TABLE source_files" ; - create_source_files_table db - - -let db_canonicalize () = - let db = get_database () in - SqliteUtils.exec db ~log:"running VACUUM" ~stmt:"VACUUM" - - type registered_stmt = unit -> Sqlite3.stmt * Sqlite3.db let register_statement = @@ -126,22 +109,56 @@ let do_db_close db = SqliteUtils.db_close db -let db_close () = - Option.iter !database ~f:do_db_close ; - database := None +module UnsafeDatabaseRef : sig + val get_database : unit -> Sqlite3.db + val db_close : unit -> unit -let new_database_connection () = - (* we always want at most one connection alive throughout the lifetime of the module *) - db_close () ; - let db = - Sqlite3.db_open ~mode:`NO_CREATE ~cache:`PRIVATE ~mutex:`FULL ?vfs:Config.sqlite_vfs - database_fullpath - in - Sqlite3.busy_timeout db 10_000 ; - SqliteUtils.exec db ~log:"synchronous=OFF" ~stmt:"PRAGMA synchronous=OFF" ; - database := Some db ; - List.iter ~f:(fun callback -> callback db) !new_db_callbacks + val new_database_connection : unit -> unit +end = struct + let database : Sqlite3.db option ref = ref None + + let get_database () = + match !database with + | Some db -> + db + | None -> + L.die InternalError + "Could not open the database. Did you forget to call `ResultsDir.assert_results_dir \ + \"\"` or `ResultsDir.create_results_dir ()`?" + + + let db_close () = + Option.iter !database ~f:do_db_close ; + database := None + + + let new_database_connection () = + (* we always want at most one connection alive throughout the lifetime of the module *) + db_close () ; + let db = + Sqlite3.db_open ~mode:`NO_CREATE ~cache:`PRIVATE ~mutex:`FULL ?vfs:Config.sqlite_vfs + database_fullpath + in + Sqlite3.busy_timeout db 10_000 ; + SqliteUtils.exec db ~log:"synchronous=OFF" ~stmt:"PRAGMA synchronous=OFF" ; + database := Some db ; + List.iter ~f:(fun callback -> callback db) !new_db_callbacks +end + +include UnsafeDatabaseRef + +let reset_capture_tables () = + let db = get_database () in + SqliteUtils.exec db ~log:"drop procedures table" ~stmt:"DROP TABLE procedures" ; + create_procedures_table db ; + SqliteUtils.exec db ~log:"drop source_files table" ~stmt:"DROP TABLE source_files" ; + create_source_files_table db + + +let db_canonicalize () = + let db = get_database () in + SqliteUtils.exec db ~log:"running VACUUM" ~stmt:"VACUUM" let () = Config.register_late_epilogue db_close