[proc-cfg][1/5] add per-procedure cfgs to the procedures table

Summary:
First step: record the proc desc of each procedure in the "procedures"
table. Update them according to the attributes logic. Bonus: this
proc-desc for a procedure is now always in sync with its attributes.

For now nothing uses these per-procedure cfgs. Later diffs make more and
more use of them and eventually kill off file-wide CFGs from the
database.

Reviewed By: jberdine

Differential Revision: D10173350

fbshipit-source-id: b6d222bee
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent b8240eadc1
commit 7e20c8d380

@ -52,7 +52,7 @@ let replace_statement =
ResultsDatabase.register_statement
{|
INSERT OR REPLACE INTO procedures
SELECT :pname, :proc_name_hum, :akind, :sfile, :pattr
SELECT :pname, :proc_name_hum, :akind, :sfile, :pattr, :cfg
FROM (
SELECT NULL
FROM (
@ -64,7 +64,7 @@ FROM (
OR (attr_kind = :akind AND source_file < :sfile) )|}
let replace pname pname_blob akind loc_file attr_blob =
let replace pname pname_blob akind loc_file attr_blob proc_desc =
ResultsDatabase.with_registered_statement replace_statement ~f:(fun db replace_stmt ->
Sqlite3.bind replace_stmt 1 (* :pname *) pname_blob
|> SqliteUtils.check_result_code db ~log:"replace bind pname" ;
@ -77,6 +77,8 @@ let replace pname pname_blob akind loc_file attr_blob =
|> SqliteUtils.check_result_code db ~log:"replace bind source file" ;
Sqlite3.bind replace_stmt 5 (* :pattr *) attr_blob
|> SqliteUtils.check_result_code db ~log:"replace bind proc attributes" ;
Sqlite3.bind replace_stmt 6 (* :cfg *) (Procdesc.SQLite.serialize proc_desc)
|> SqliteUtils.check_result_code db ~log:"replace bind cfg" ;
SqliteUtils.result_unit db ~finalize:false ~log:"Attributes.replace" replace_stmt )
@ -124,13 +126,14 @@ let find ~defined pname_blob =
let load pname = Typ.Procname.SQLite.serialize pname |> find ~defined:false
let store (attr : ProcAttributes.t) =
let store ~proc_desc (attr : ProcAttributes.t) =
let pkind = proc_kind_of_attr attr in
let key = Typ.Procname.SQLite.serialize attr.proc_name in
if should_try_to_update key pkind then
replace attr.proc_name key pkind
(SourceFile.SQLite.serialize attr.loc.Location.file)
(ProcAttributes.SQLite.serialize attr)
proc_desc
let load_defined pname = Typ.Procname.SQLite.serialize pname |> find ~defined:true

@ -13,7 +13,7 @@ type attributes_kind
val deserialize_attributes_kind : Sqlite3.Data.t -> attributes_kind
val store : ProcAttributes.t -> unit
val store : proc_desc:Procdesc.t option -> ProcAttributes.t -> unit
(** Save .attr file for the procedure into the attributes database. *)
val load : Typ.Procname.t -> ProcAttributes.t option

@ -67,16 +67,16 @@ let load source =
|> Option.map ~f:SQLite.deserialize )
let save_attributes source_file cfg =
let save_proc _ pdesc =
let attributes = Procdesc.get_attributes pdesc in
let store source_file cfg =
let save_proc _ proc_desc =
let attributes = Procdesc.get_attributes proc_desc in
let loc = attributes.loc in
let attributes' =
let loc' = if Location.equal loc Location.dummy then {loc with file= source_file} else loc in
{attributes with loc= loc'; translation_unit= source_file}
in
Attributes.store attributes' ;
Procdesc.set_attributes pdesc attributes'
Procdesc.set_attributes proc_desc attributes' ;
Attributes.store ~proc_desc:(Some proc_desc) attributes'
in
Typ.Procname.Hash.iter save_proc cfg

@ -19,6 +19,10 @@ val load : SourceFile.t -> t option
val get_all_defined_proc_names : t -> Typ.Procname.t list
(** get all the procedure names that are defined in the current file *)
val store : SourceFile.t -> t -> unit
(** Save the individual [Procdesc.t] and [ProcAttributes.t] to the database for the procedures in
the cfg. *)
(** {2 Functions for manipulating an interprocedural CFG} *)
val create : unit -> t
@ -30,9 +34,6 @@ val create_proc_desc : t -> ProcAttributes.t -> Procdesc.t
val iter_all_nodes : sorted:bool -> t -> f:(Procdesc.t -> Procdesc.Node.t -> unit) -> unit
(** Iterate over all the nodes in the cfg *)
val save_attributes : SourceFile.t -> t -> unit
(** Save the .attr files for the procedures in the cfg. *)
val inline_java_synthetic_methods : t -> unit
(** Inline the java synthetic methods in the cfg (in-place) *)

@ -750,3 +750,8 @@ let is_connected proc_desc =
Ok ()
| Error _ as error ->
error
module SQLite = SqliteUtils.MarshalledNullableData (struct
type nonrec t = t
end)

@ -8,6 +8,8 @@
open! IStd
(** {1 Per-procedure CFG} *)
module NodeKey : sig
type t
@ -285,3 +287,7 @@ val has_modify_in_block_attr : t -> Pvar.t -> bool
val is_connected : t -> (unit, [`Join | `Other]) Result.t
(** checks whether a cfg for the given procdesc is connected or not *)
(** per-procedure CFGs are stored in the SQLite "procedures" table as NULL if the procedure has no
CFG *)
module SQLite : SqliteUtils.Data with type t = t option

@ -53,7 +53,7 @@ let add source_file cfg tenv =
OndemandCapture module relies on it - it uses existance of the cfg as a barrier to make
sure that all attributes were written to disk (but not necessarily flushed) *)
SqliteUtils.with_transaction (ResultsDatabase.get_database ()) ~f:(fun () ->
Cfg.save_attributes source_file cfg ) ;
Cfg.store source_file cfg ) ;
ResultsDatabase.with_registered_statement store_statement ~f:(fun db store_stmt ->
SourceFile.SQLite.serialize source_file
|> Sqlite3.bind store_stmt 1

@ -172,7 +172,7 @@ let with_formals_types ?(has_clang_model = false) callee_pdesc resolved_pname ar
; is_specialized= true
; translation_unit }
in
Attributes.store resolved_attributes ;
Attributes.store ~proc_desc:None resolved_attributes ;
let resolved_pdesc = Procdesc.from_proc_attributes resolved_attributes in
with_formals_types_proc callee_pdesc resolved_pdesc substitutions
@ -339,5 +339,5 @@ let with_block_args callee_pdesc pname_with_block_args block_args =
convert_cfg ~callee_pdesc ~resolved_pdesc
~f_instr_list:(with_block_args_instrs resolved_pdesc substitutions)
in
Attributes.store resolved_attributes ;
Attributes.store ~proc_desc:(Some proc_desc) resolved_attributes ;
proc_desc

@ -16,7 +16,7 @@ let merge_procedures_table ~db_file =
Sqlite3.exec db
{|
INSERT OR REPLACE INTO procedures
SELECT sub.proc_name, sub.proc_name_hum, sub.attr_kind, sub.source_file, sub.proc_attributes
SELECT sub.proc_name, sub.proc_name_hum, sub.attr_kind, sub.source_file, sub.proc_attributes, sub.cfg
FROM (
attached.procedures AS sub
LEFT OUTER JOIN procedures AS main

@ -18,7 +18,9 @@ let procedures_schema =
, proc_name_hum TEXT
, attr_kind INTEGER NOT NULL
, source_file TEXT NOT NULL
, proc_attributes BLOB NOT NULL )|}
, proc_attributes BLOB NOT NULL
, cfg BLOB
)|}
let source_files_schema =

@ -119,3 +119,23 @@ struct
let serialize x = Sqlite3.Data.BLOB (Marshal.to_string x [])
end
module MarshalledNullableData (D : sig
type t
end) =
struct
type t = D.t option
let deserialize = function[@warning "-8"]
| Sqlite3.Data.BLOB b ->
Some (Marshal.from_string b 0)
| Sqlite3.Data.NULL ->
None
let serialize = function
| None ->
Sqlite3.Data.NULL
| Some x ->
Sqlite3.Data.BLOB (Marshal.to_string x [])
end

@ -76,3 +76,8 @@ end
module MarshalledData (D : sig
type t
end) : Data with type t = D.t
(** A default implementation of the Data API that encodes None as a NULL SQLite value *)
module MarshalledNullableData (D : sig
type t
end) : Data with type t = D.t option

@ -73,7 +73,9 @@ let save_tenv tenv =
let store_callee_attributes tenv program =
let f proc_name cn ms =
Option.iter ~f:Attributes.store (JTrans.create_callee_attributes tenv program cn ms proc_name)
Option.iter
~f:(Attributes.store ~proc_desc:None)
(JTrans.create_callee_attributes tenv program cn ms proc_name)
in
JClasspath.iter_missing_callees program ~f

Loading…
Cancel
Save