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
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent 65491b79ff
commit 47fdee6000

@ -27,7 +27,7 @@ run:
make devsetup 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 - 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). 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 - To switch the default build mode to flambda disabled, you can `export BUILD_MODE=default` in your
shell. 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 @ <module> <line>` 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 ## Hacking on the Code in facebook-clang-plugins
Infer uses `ASTExporter` from the [facebook-clang-plugins](https://github.com/facebook/facebook-clang-plugins) Infer uses `ASTExporter` from the [facebook-clang-plugins](https://github.com/facebook/facebook-clang-plugins)

@ -8,8 +8,6 @@
open! IStd open! IStd
module L = Logging module L = Logging
let database : Sqlite3.db option ref = ref None
let database_filename = "results.db" let database_filename = "results.db"
let database_fullpath = Config.results_dir ^/ database_filename 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 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 type registered_stmt = unit -> Sqlite3.stmt * Sqlite3.db
let register_statement = let register_statement =
@ -126,22 +109,56 @@ let do_db_close db =
SqliteUtils.db_close db SqliteUtils.db_close db
let db_close () = module UnsafeDatabaseRef : sig
Option.iter !database ~f:do_db_close ; val get_database : unit -> Sqlite3.db
database := None
val db_close : unit -> unit
let new_database_connection () = val new_database_connection : unit -> unit
(* we always want at most one connection alive throughout the lifetime of the module *) end = struct
db_close () ; let database : Sqlite3.db option ref = ref None
let db =
Sqlite3.db_open ~mode:`NO_CREATE ~cache:`PRIVATE ~mutex:`FULL ?vfs:Config.sqlite_vfs let get_database () =
database_fullpath match !database with
in | Some db ->
Sqlite3.busy_timeout db 10_000 ; db
SqliteUtils.exec db ~log:"synchronous=OFF" ~stmt:"PRAGMA synchronous=OFF" ; | None ->
database := Some db ; L.die InternalError
List.iter ~f:(fun callback -> callback db) !new_db_callbacks "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 let () = Config.register_late_epilogue db_close

Loading…
Cancel
Save