[ConfigImpact] Collect all object fields that may have config values

Summary:
This diff add semantics for collecting all object fields that may have config values. The collected information is used to instantiate conditional unchecked callees introduced in the previous diff.

How it works:

* The summary is extended to have `config_fields:Fields.t`. It has all fields that may have config values intra-procedurally.
* Before reporting to `config-impact-report.json`, it unions all `config_fields` from all specs.
* Using `all_config_fields`, it instantiates each summaries and writes results to `config-impact-report.json`.

Reviewed By: ezgicicek

Differential Revision: D27326306

fbshipit-source-id: 42f16ca45
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent 4ad7d23216
commit 76d4563f8c

@ -166,23 +166,28 @@ module Summary = struct
{ unchecked_callees: UncheckedCallees.t (** Set of unchecked callees *)
; unchecked_callees_cond: UncheckedCalleesCond.t
(** Sets of unchecked callees that are called conditionally by object fields *)
; has_call_stmt: bool (** True if a function includes a call statement *) }
; has_call_stmt: bool (** True if a function includes a call statement *)
; config_fields: Fields.t
(** Intra-procedurally collected fields that may have config values *) }
let pp f {unchecked_callees; unchecked_callees_cond; has_call_stmt} =
let pp f {unchecked_callees; unchecked_callees_cond; has_call_stmt; config_fields} =
F.fprintf f
"@[@[unchecked callees:@,%a@],@ @[unchecked callees cond:@,%a@],@ @[has_call_stmt:%b@]@]"
UncheckedCallees.pp unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond
has_call_stmt
"@[@[unchecked callees:@,\
%a@],@ @[unchecked callees cond:@,\
%a@],@ @[has_call_stmt:%b@],@ @[config fields:%a@]@]" UncheckedCallees.pp unchecked_callees
UncheckedCalleesCond.pp unchecked_callees_cond has_call_stmt Fields.pp config_fields
let get_config_fields {config_fields} = config_fields
let get_unchecked_callees {unchecked_callees} = unchecked_callees
let instantiate_unchecked_callees_cond ~config_fields
let instantiate_unchecked_callees_cond ~all_config_fields
({unchecked_callees; unchecked_callees_cond} as x) =
let unchecked_callees =
UncheckedCalleesCond.fold
(fun fields callees acc ->
if Fields.is_empty (Fields.inter config_fields fields) then
if Fields.is_empty (Fields.inter all_config_fields fields) then
UncheckedCallees.union acc callees
else acc )
unchecked_callees_cond unchecked_callees
@ -196,17 +201,22 @@ module Dom = struct
; field_checks: FieldChecks.t
; unchecked_callees: UncheckedCallees.t
; unchecked_callees_cond: UncheckedCalleesCond.t
; mem: Mem.t }
; mem: Mem.t
; config_fields: Fields.t }
let pp f {config_checks; field_checks; unchecked_callees; unchecked_callees_cond; mem} =
let pp f
{config_checks; field_checks; unchecked_callees; unchecked_callees_cond; mem; config_fields} =
F.fprintf f
"@[@[config checks:@,\
%a@]@ @[field checks:@,\
%a@]@ @[unchecked callees:@,\
%a@]@ @[unchecked callees cond:@,\
%a@]@ @[mem:%,%a@]@]" ConfigChecks.pp config_checks FieldChecks.pp field_checks
UncheckedCallees.pp unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond Mem.pp
mem
%a@]@ @[mem:@,\
%a@]@ @[config fields:@,\
%a@]@]"
ConfigChecks.pp config_checks FieldChecks.pp field_checks UncheckedCallees.pp
unchecked_callees UncheckedCalleesCond.pp unchecked_callees_cond Mem.pp mem Fields.pp
config_fields
let leq ~lhs ~rhs =
@ -215,6 +225,7 @@ module Dom = struct
&& UncheckedCallees.leq ~lhs:lhs.unchecked_callees ~rhs:rhs.unchecked_callees
&& UncheckedCalleesCond.leq ~lhs:lhs.unchecked_callees_cond ~rhs:rhs.unchecked_callees_cond
&& Mem.leq ~lhs:lhs.mem ~rhs:rhs.mem
&& Fields.leq ~lhs:lhs.config_fields ~rhs:rhs.config_fields
let join x y =
@ -223,7 +234,8 @@ module Dom = struct
; unchecked_callees= UncheckedCallees.join x.unchecked_callees y.unchecked_callees
; unchecked_callees_cond=
UncheckedCalleesCond.join x.unchecked_callees_cond y.unchecked_callees_cond
; mem= Mem.join x.mem y.mem }
; mem= Mem.join x.mem y.mem
; config_fields= Fields.join x.config_fields y.config_fields }
let widen ~prev ~next ~num_iters =
@ -234,11 +246,12 @@ module Dom = struct
; unchecked_callees_cond=
UncheckedCalleesCond.widen ~prev:prev.unchecked_callees_cond
~next:next.unchecked_callees_cond ~num_iters
; mem= Mem.widen ~prev:prev.mem ~next:next.mem ~num_iters }
; mem= Mem.widen ~prev:prev.mem ~next:next.mem ~num_iters
; config_fields= Fields.widen ~prev:prev.config_fields ~next:next.config_fields ~num_iters }
let to_summary has_call_stmt {unchecked_callees; unchecked_callees_cond} =
{Summary.unchecked_callees; unchecked_callees_cond; has_call_stmt}
let to_summary has_call_stmt {unchecked_callees; unchecked_callees_cond; config_fields} =
{Summary.unchecked_callees; unchecked_callees_cond; has_call_stmt; config_fields}
let init =
@ -246,7 +259,8 @@ module Dom = struct
; field_checks= FieldChecks.top
; unchecked_callees= UncheckedCallees.bottom
; unchecked_callees_cond= UncheckedCalleesCond.bottom
; mem= Mem.bottom }
; mem= Mem.bottom
; config_fields= Fields.bottom }
let add_mem loc v ({mem} as astate) = {astate with mem= Mem.add loc v mem}
@ -261,6 +275,12 @@ module Dom = struct
let store_config pvar id astate = copy_mem ~tgt:(Loc.of_pvar pvar) ~src:(Loc.of_id id) astate
let store_field fn id ({mem; config_fields} as astate) =
let {Val.config} = Mem.lookup (Loc.of_id id) mem in
if ConfigLifted.is_top config then astate
else {astate with config_fields= Fields.add fn config_fields}
let boolean_value id_tgt id_src astate =
copy_mem ~tgt:(Loc.of_id id_tgt) ~src:(Loc.of_id id_src) astate
@ -371,6 +391,8 @@ module TransferFunctions = struct
Dom.load_field id fn astate
| Store {e1= Lvar pvar; e2= Var id} ->
Dom.store_config pvar id astate
| Store {e1= Lfield (_, fn, _); e2= Var id} ->
Dom.store_field fn id astate
| Call ((ret, _), Const (Cfun callee), [(Var id, _)], _, _)
when is_java_boolean_value_method callee ->
Dom.boolean_value ret id astate

@ -32,9 +32,11 @@ module Summary : sig
val pp : Format.formatter -> t -> unit
val get_config_fields : t -> Fields.t
val get_unchecked_callees : t -> UncheckedCallees.t
val instantiate_unchecked_callees_cond : config_fields:Fields.t -> t -> t
val instantiate_unchecked_callees_cond : all_config_fields:Fields.t -> t -> t
end
val checker : Summary.t InterproceduralAnalysis.t -> Summary.t option

@ -343,15 +343,26 @@ let write_costs proc_name loc cost_opt (outfile : Utils.outfile) =
JsonCostsPrinter.pp outfile.fmt {loc; proc_name; cost_opt}
let write_config_impact proc_name loc config_impact_opt (outfile : Utils.outfile) =
let get_all_config_fields () =
lazy
(let all_config_fields = ref ConfigImpactAnalysis.Fields.empty in
Summary.OnDisk.iter_specs ~f:(fun summary ->
Payloads.config_impact_analysis summary.payloads
|> Option.iter ~f:(fun summary ->
all_config_fields :=
ConfigImpactAnalysis.Fields.union !all_config_fields
(ConfigImpactAnalysis.Summary.get_config_fields summary) ) ) ;
!all_config_fields )
let write_config_impact all_config_fields proc_name loc config_impact_opt (outfile : Utils.outfile)
=
if ExternalConfigImpactData.is_in_config_data_file proc_name then
let config_fields =
(* TODO: This value should be non-empty by an additional analysis. *)
ConfigImpactAnalysis.Fields.empty
in
let config_impact_opt =
Option.map config_impact_opt
~f:(ConfigImpactAnalysis.Summary.instantiate_unchecked_callees_cond ~config_fields)
~f:
(ConfigImpactAnalysis.Summary.instantiate_unchecked_callees_cond
~all_config_fields:(Lazy.force all_config_fields))
in
JsonConfigImpactPrinter.pp outfile.fmt {loc; proc_name; config_impact_opt}
@ -363,9 +374,9 @@ let write_lint_issues filters (issues_outf : Utils.outfile) linereader procname
let process_summary proc_name loc ~cost:(cost_opt, costs_outf)
~config_impact:(config_impact_opt, config_impact_outf) err_log issues_acc =
~config_impact:(config_impact_opt, config_impact_outf, all_config_fields) err_log issues_acc =
write_costs proc_name loc cost_opt costs_outf ;
write_config_impact proc_name loc config_impact_opt config_impact_outf ;
write_config_impact all_config_fields proc_name loc config_impact_opt config_impact_outf ;
collect_issues proc_name loc err_log issues_acc
@ -373,11 +384,12 @@ let process_all_summaries_and_issues ~issues_outf ~costs_outf ~config_impact_out
let linereader = LineReader.create () in
let filters = Inferconfig.create_filters () in
let all_issues = ref [] in
let all_config_fields = get_all_config_fields () in
Summary.OnDisk.iter_report_summaries_from_config
~f:(fun proc_name loc cost_opt config_impact_opt err_log ->
all_issues :=
process_summary proc_name loc ~cost:(cost_opt, costs_outf)
~config_impact:(config_impact_opt, config_impact_outf)
~config_impact:(config_impact_opt, config_impact_outf, all_config_fields)
err_log !all_issues ) ;
all_issues := Issue.sort_filter_issues !all_issues ;
List.iter

Loading…
Cancel
Save