[proc-cfg][3/5] stop caching whole-file cfgs in exe_env

Summary:
Load proc descs from the "procedures" sqlite table instead of from
file-wide cfgs stored in the "source_files" table. This removes the need
for a cache of these file-wide CFGs, which was needed because loading
them is expensive and potentially needed in case we need to load the
proc descs of several procedures in the same file. Now we can just load
the proc descs one by one and not worry about caching.

Reviewed By: jberdine

Differential Revision: D10173355

fbshipit-source-id: 665636121
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent 7615963bf4
commit 31e01a9aa0

@ -61,7 +61,7 @@ FROM (
FROM procedures FROM procedures
WHERE proc_name = :pname ) WHERE proc_name = :pname )
WHERE attr_kind < :akind WHERE attr_kind < :akind
OR (attr_kind = :akind AND source_file < :sfile) )|} OR (attr_kind = :akind AND source_file <= :sfile) )|}
let replace pname pname_blob akind loc_file attr_blob proc_desc = let replace pname pname_blob akind loc_file attr_blob proc_desc =

@ -63,6 +63,9 @@ type t =
; proc_name: Typ.Procname.t (** name of the procedure *) ; proc_name: Typ.Procname.t (** name of the procedure *)
; ret_type: Typ.t (** return type *) ; ret_type: Typ.t (** return type *)
; has_added_return_param: bool (** whether or not a return param was added *) } ; has_added_return_param: bool (** whether or not a return param was added *) }
[@@deriving compare]
let equal = [%compare.equal: t]
let default translation_unit proc_name = let default translation_unit proc_name =
{ access= PredSymb.Default { access= PredSymb.Default
@ -165,7 +168,7 @@ let pp f
F.fprintf f "; clang_method_kind= %a@," F.fprintf f "; clang_method_kind= %a@,"
(Pp.to_string ~f:ClangMethodKind.to_string) (Pp.to_string ~f:ClangMethodKind.to_string)
clang_method_kind ; clang_method_kind ;
if not (Location.equal default.loc loc) then F.fprintf f "; loc= %a@," Location.pp loc ; if not (Location.equal default.loc loc) then F.fprintf f "; loc= %a@," Location.pp_file_pos loc ;
if not ([%compare.equal: var_data list] default.locals locals) then if not ([%compare.equal: var_data list] default.locals locals) then
F.fprintf f "; locals= [@[%a@]]@," F.fprintf f "; locals= [@[%a@]]@,"
(Pp.semicolon_seq ~print_env:Pp.text_break pp_var_data) (Pp.semicolon_seq ~print_env:Pp.text_break pp_var_data)

@ -47,6 +47,9 @@ type t =
; ret_type: Typ.t (** return type *) ; ret_type: Typ.t (** return type *)
; has_added_return_param: bool (** whether or not a return param was added *) } ; has_added_return_param: bool (** whether or not a return param was added *) }
val equal : t -> t -> bool [@@warning "-32"]
(** can be useful for debugging *)
val default : SourceFile.t -> Typ.Procname.t -> t val default : SourceFile.t -> Typ.Procname.t -> t
(** Create a proc_attributes with default values. *) (** Create a proc_attributes with default values. *)

@ -755,3 +755,15 @@ let is_connected proc_desc =
module SQLite = SqliteUtils.MarshalledNullableData (struct module SQLite = SqliteUtils.MarshalledNullableData (struct
type nonrec t = t type nonrec t = t
end) end)
let load_statement =
ResultsDatabase.register_statement "SELECT cfg FROM procedures WHERE proc_name = :k"
let load pname =
ResultsDatabase.with_registered_statement load_statement ~f:(fun db stmt ->
Typ.Procname.SQLite.serialize pname
|> Sqlite3.bind stmt 1
|> SqliteUtils.check_result_code db ~log:"load bind proc name" ;
SqliteUtils.result_single_column_option ~finalize:false ~log:"Procdesc.load" db stmt
|> Option.bind ~f:SQLite.deserialize )

@ -291,3 +291,5 @@ val is_connected : t -> (unit, [`Join | `Other]) Result.t
(** per-procedure CFGs are stored in the SQLite "procedures" table as NULL if the procedure has no (** per-procedure CFGs are stored in the SQLite "procedures" table as NULL if the procedure has no
CFG *) CFG *)
module SQLite : SqliteUtils.Data with type t = t option module SQLite : SqliteUtils.Data with type t = t option
val load : Typ.Procname.t -> t option

@ -44,11 +44,10 @@ let register_cluster_callback ~name language (callback : cluster_callback_t) =
(** Collect what we need to know about a procedure for the analysis. *) (** Collect what we need to know about a procedure for the analysis. *)
let get_procedure_definition exe_env proc_name = let get_procedure_definition exe_env proc_name =
Option.map Procdesc.load proc_name
~f:(fun proc_desc -> |> Option.map ~f:(fun proc_desc ->
let tenv = Exe_env.get_tenv exe_env proc_name in let tenv = Exe_env.get_tenv exe_env proc_name in
(tenv, proc_desc) ) (tenv, proc_desc) )
(Exe_env.get_proc_desc exe_env proc_name)
(** Invoke all registered procedure callbacks on the given procedure. *) (** Invoke all registered procedure callbacks on the given procedure. *)
@ -57,11 +56,14 @@ let iterate_procedure_callbacks exe_env summary proc_desc =
let procedure_language = Typ.Procname.get_language proc_name in let procedure_language = Typ.Procname.get_language proc_name in
Language.curr_language := procedure_language ; Language.curr_language := procedure_language ;
let get_procs_in_file proc_name = let get_procs_in_file proc_name =
match Exe_env.get_cfg exe_env proc_name with let source_file =
| Some cfg -> match Attributes.load proc_name with
Cfg.get_all_defined_proc_names cfg | Some {ProcAttributes.translation_unit} ->
| None -> Some translation_unit
[] | None ->
None
in
Option.value_map source_file ~default:[] ~f:SourceFiles.proc_names_of_source
in in
let tenv = Exe_env.get_tenv exe_env proc_name in let tenv = Exe_env.get_tenv exe_env proc_name in
let is_specialized = Procdesc.is_specialized proc_desc in let is_specialized = Procdesc.is_specialized proc_desc in

@ -14,15 +14,13 @@ open! IStd
module L = Logging module L = Logging
(** per-file data: type environment and cfg *) (** per-file data: type environment and cfg *)
type file_data = {source: SourceFile.t; mutable tenv: Tenv.t option; mutable cfg: Cfg.t option} type file_data = {source: SourceFile.t; mutable tenv: Tenv.t option}
(** create a new file_data *) (** create a new file_data *)
let new_file_data source = let new_file_data source =
(* Do not fill in tenv and cfg as they can be quite large. This makes calls to fork() cheaper (* Do not fill in tenv and cfg as they can be quite large. This makes calls to fork() cheaper
until we start filling out these fields. *) until we start filling out these fields. *)
{ source {source; tenv= None (* Sil.load_tenv_from_file tenv_file *)}
; tenv= None (* Sil.load_tenv_from_file tenv_file *)
; cfg= None (* Cfg.load_cfg_from_file cfg_file *) }
let create_file_data table source = let create_file_data table source =
@ -65,11 +63,6 @@ let file_data_to_tenv file_data =
file_data.tenv file_data.tenv
let file_data_to_cfg file_data =
if is_none file_data.cfg then file_data.cfg <- Cfg.load file_data.source ;
file_data.cfg
let java_global_tenv = let java_global_tenv =
lazy lazy
( match Tenv.load_global () with ( match Tenv.load_global () with
@ -102,22 +95,4 @@ let get_tenv exe_env proc_name =
SourceFile.pp loc.Location.file Location.pp loc ) SourceFile.pp loc.Location.file Location.pp loc )
(** return the cfg associated to the procedure *)
let get_cfg exe_env pname =
match get_file_data exe_env pname with
| None ->
None
| Some file_data ->
file_data_to_cfg file_data
(** return the proc desc associated to the procedure *)
let get_proc_desc exe_env pname =
match get_cfg exe_env pname with
| Some cfg ->
Typ.Procname.Hash.find_opt cfg pname
| None ->
None
let mk () = {proc_map= Typ.Procname.Hash.create 17; file_map= SourceFile.Hash.create 1} let mk () = {proc_map= Typ.Procname.Hash.create 17; file_map= SourceFile.Hash.create 1}

@ -8,7 +8,7 @@
open! IStd open! IStd
(** Execution environments: basically a cache of where procedures are and what is their CFG and type (** Execution environments: basically a cache of where procedures are and what is their type
environment *) environment *)
type file_data type file_data
@ -22,9 +22,3 @@ val mk : unit -> t
val get_tenv : t -> Typ.Procname.t -> Tenv.t val get_tenv : t -> Typ.Procname.t -> Tenv.t
(** return the type environment associated with the procedure *) (** return the type environment associated with the procedure *)
val get_cfg : t -> Typ.Procname.t -> Cfg.t option
(** return the cfg associated with the procedure *)
val get_proc_desc : t -> Typ.Procname.t -> Procdesc.t option
(** return the proc desc associated with the procedure *)

@ -236,8 +236,7 @@ let analyze_proc_desc ~caller_pdesc callee_pdesc =
(** Find a proc desc for the procedure, perhaps loading it from disk. *) (** Find a proc desc for the procedure, perhaps loading it from disk. *)
let get_proc_desc callee_pname = let get_proc_desc callee_pname =
let callbacks = Option.value_exn !callbacks_ref in match Procdesc.load callee_pname with
match Exe_env.get_proc_desc callbacks.exe_env callee_pname with
| Some _ as pdesc_opt -> | Some _ as pdesc_opt ->
pdesc_opt pdesc_opt
| None -> | None ->

@ -61,7 +61,7 @@ let do_source_file (translation_unit_context : CFrontend_config.translation_unit
Config.debug_mode || Config.testing_mode || Config.frontend_tests Config.debug_mode || Config.testing_mode || Config.frontend_tests
|| Option.is_some Config.icfg_dotty_outfile || Option.is_some Config.icfg_dotty_outfile
then Dotty.print_icfg_dotty source_file cfg ; then Dotty.print_icfg_dotty source_file cfg ;
L.(debug Capture Verbose) "%a" Cfg.pp_proc_signatures cfg ; L.(debug Capture Verbose) "Stored on disk:@[<v>%a@]@." Cfg.pp_proc_signatures cfg ;
let procedures_translated_summary = let procedures_translated_summary =
EventLogger.ProceduresTranslatedSummary EventLogger.ProceduresTranslatedSummary
{ procedures_translated_total= !CFrontend_config.procedures_attempted { procedures_translated_total= !CFrontend_config.procedures_attempted

Loading…
Cancel
Save