[ConfigImpact] Ignore unknown library calls with no cost model

Summary:
Looking at the recent silent analysis results, it seems that we report many direct unknown library calls (often cheap)... However, if these were called inside some other callee we wouldn't report them because their costs would be assumed to be constant by the cost analysis.

This is a bit awkward. We should either report all unknown calls or suppress them altogether.

Since we have too many reports per day and not a good way to determine whether an unknown library call is cheap/expensive, let's take option 2.

Then, we would be only relying on two things to determine whether to report/not:
- instantiated cost's degree > 1
- explicitly known to be expensive (i.e. modeled in ConfigImpact, like string append)

Reviewed By: skcho

Differential Revision: D27909003

fbshipit-source-id: 0391d226d
master
Ezgi Çiçek 4 years ago committed by Facebook GitHub Bot
parent 4920cce2f3
commit aae741419c

@ -379,7 +379,8 @@ module Dom = struct
dispatch tenv pname args dispatch tenv pname args
let call tenv analyze_dependency ~is_cheap_call callee args location let call tenv analyze_dependency ~(instantiated_cost : CostInstantiate.instantiated_cost) callee
args location
({config_checks; field_checks; unchecked_callees; unchecked_callees_cond} as astate) = ({config_checks; field_checks; unchecked_callees; unchecked_callees_cond} as astate) =
let join_unchecked_callees new_unchecked_callees new_unchecked_callees_cond = let join_unchecked_callees new_unchecked_callees new_unchecked_callees_cond =
if FieldChecks.is_top field_checks then if FieldChecks.is_top field_checks then
@ -413,6 +414,8 @@ module Dom = struct
let has_expensive_callee = let has_expensive_callee =
Option.exists callee_summary ~f:Summary.has_known_expensive_callee Option.exists callee_summary ~f:Summary.has_known_expensive_callee
in in
let is_cheap_call = match instantiated_cost with Cheap -> true | _ -> false in
let is_unmodeled_call = match instantiated_cost with NoModel -> true | _ -> false in
match expensiveness_model with match expensiveness_model with
| None when is_cheap_call && not has_expensive_callee -> | None when is_cheap_call && not has_expensive_callee ->
(* If callee is cheap by heuristics, ignore it. *) (* If callee is cheap by heuristics, ignore it. *)
@ -446,8 +449,8 @@ module Dom = struct
join_unchecked_callees join_unchecked_callees
(UncheckedCallees.replace_location_by_call location callee_summary) (UncheckedCallees.replace_location_by_call location callee_summary)
(UncheckedCalleesCond.replace_location_by_call location callee_summary_cond) (UncheckedCalleesCond.replace_location_by_call location callee_summary_cond)
| None when Procname.is_objc_init callee -> | None when Procname.is_objc_init callee || is_unmodeled_call ->
(* If callee is unknown ObjC initializer, ignore it. *) (* If callee is unknown ObjC initializer or has no cost model, ignore it. *)
astate astate
| _ -> | _ ->
(* Otherwise, add callee's name. *) (* Otherwise, add callee's name. *)
@ -462,7 +465,7 @@ type analysis_data =
{ interproc: { interproc:
(BufferOverrunAnalysisSummary.t option * Summary.t option * CostDomain.summary option) (BufferOverrunAnalysisSummary.t option * Summary.t option * CostDomain.summary option)
InterproceduralAnalysis.t InterproceduralAnalysis.t
; get_is_cheap_call: CostInstantiate.Call.t -> bool } ; get_instantiated_cost: CostInstantiate.Call.t -> CostInstantiate.instantiated_cost }
module TransferFunctions = struct module TransferFunctions = struct
module CFG = ProcCfg.Normal module CFG = ProcCfg.Normal
@ -514,7 +517,8 @@ module TransferFunctions = struct
fun tenv pname -> dispatch tenv pname |> Option.is_some fun tenv pname -> dispatch tenv pname |> Option.is_some
let exec_instr astate {interproc= {tenv; analyze_dependency}; get_is_cheap_call} node idx instr = let exec_instr astate {interproc= {tenv; analyze_dependency}; get_instantiated_cost} node idx
instr =
match (instr : Sil.instr) with match (instr : Sil.instr) with
| Load {id; e= Lvar pvar} -> | Load {id; e= Lvar pvar} ->
Dom.load_config id pvar astate Dom.load_config id pvar astate
@ -541,8 +545,8 @@ module TransferFunctions = struct
CostInstantiate.Call. CostInstantiate.Call.
{loc= location; pname= callee; node= CFG.Node.to_instr idx node; args; ret} {loc= location; pname= callee; node= CFG.Node.to_instr idx node; args; ret}
in in
let is_cheap_call = get_is_cheap_call call in let instantiated_cost = get_instantiated_cost call in
Dom.call tenv analyze_dependency ~is_cheap_call callee args location astate ) Dom.call tenv analyze_dependency ~instantiated_cost callee args location astate )
| Prune (e, _, _, _) -> | Prune (e, _, _, _) ->
Dom.prune e astate Dom.prune e astate
| _ -> | _ ->
@ -564,8 +568,8 @@ let has_call_stmt proc_desc =
let checker ({InterproceduralAnalysis.proc_desc} as analysis_data) = let checker ({InterproceduralAnalysis.proc_desc} as analysis_data) =
let get_is_cheap_call = CostInstantiate.get_is_cheap_call analysis_data in let get_instantiated_cost = CostInstantiate.get_instantiated_cost analysis_data in
let analysis_data = {interproc= analysis_data; get_is_cheap_call} in let analysis_data = {interproc= analysis_data; get_instantiated_cost} in
Option.map (Analyzer.compute_post analysis_data ~initial:Dom.init proc_desc) ~f:(fun astate -> Option.map (Analyzer.compute_post analysis_data ~initial:Dom.init proc_desc) ~f:(fun astate ->
let has_call_stmt = has_call_stmt proc_desc in let has_call_stmt = has_call_stmt proc_desc in
Dom.to_summary ~has_call_stmt astate ) Dom.to_summary ~has_call_stmt astate )

@ -32,7 +32,9 @@ type cost_args =
type 'a interproc_analysis = type 'a interproc_analysis =
(BufferOverrunAnalysisSummary.t option * 'a * CostDomain.summary option) InterproceduralAnalysis.t (BufferOverrunAnalysisSummary.t option * 'a * CostDomain.summary option) InterproceduralAnalysis.t
let get_symbolic_cost type instantiated_cost = Cheap | NoModel | Symbolic of CostDomain.BasicCost.t
let get_instantiated_cost
{ tenv { tenv
; integer_type_widths ; integer_type_widths
; get_callee_cost_summary_and_formals ; get_callee_cost_summary_and_formals
@ -44,9 +46,7 @@ let get_symbolic_cost
(BufferOverrunAnalysis.extract_pre (ProcCfg.InstrNode.id node) inferbo_invariant_map) (BufferOverrunAnalysis.extract_pre (ProcCfg.InstrNode.id node) inferbo_invariant_map)
in in
let loc = ProcCfg.InstrNode.loc node in let loc = ProcCfg.InstrNode.loc node in
let get_symbolic cost = let get_symbolic cost = if CostDomain.BasicCost.is_symbolic cost then Symbolic cost else Cheap in
if CostDomain.BasicCost.is_symbolic cost then `SymbolicCost cost else `Cheap
in
let get_summary pname = Option.map ~f:fst (get_callee_cost_summary_and_formals pname) in let get_summary pname = Option.map ~f:fst (get_callee_cost_summary_and_formals pname) in
match get_callee_cost_summary_and_formals pname with match get_callee_cost_summary_and_formals pname with
| Some (CostDomain.{post= cost_record}, callee_formals) -> | Some (CostDomain.{post= cost_record}, callee_formals) ->
@ -56,14 +56,14 @@ let get_symbolic_cost
~inferbo_caller_mem:inferbo_mem ~callee_pname:pname ~callee_formals ~args ~callee_cost ~inferbo_caller_mem:inferbo_mem ~callee_pname:pname ~callee_formals ~args ~callee_cost
~loc) ~loc)
.cost |> get_symbolic .cost |> get_symbolic
else `Cheap else Cheap
| None -> | None ->
let fun_arg_list = let fun_arg_list =
List.map args ~f:(fun (exp, typ) -> List.map args ~f:(fun (exp, typ) ->
ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} )
in in
CostModels.Call.dispatch tenv pname fun_arg_list CostModels.Call.dispatch tenv pname fun_arg_list
|> Option.value_map ~default:`NoModel ~f:(fun model -> |> Option.value_map ~default:NoModel ~f:(fun model ->
let model_env = let model_env =
let node_hash = ProcCfg.InstrNode.hash node in let node_hash = ProcCfg.InstrNode.hash node in
BufferOverrunUtils.ModelEnv.mk_model_env pname ~node_hash loc tenv BufferOverrunUtils.ModelEnv.mk_model_env pname ~node_hash loc tenv
@ -102,20 +102,12 @@ let prepare_call_args
let get_cost_if_expensive analysis_data call = let get_cost_if_expensive analysis_data call =
match prepare_call_args analysis_data call |> get_symbolic_cost with match prepare_call_args analysis_data call |> get_instantiated_cost with
| `SymbolicCost cost -> | Symbolic cost ->
Some cost Some cost
| `Cheap | `NoModel -> | Cheap | NoModel ->
None None
let get_is_cheap_call analysis_data call = let get_instantiated_cost analysis_data call =
match prepare_call_args analysis_data call |> get_symbolic_cost with prepare_call_args analysis_data call |> get_instantiated_cost
| `SymbolicCost _ ->
(* symbolic costs (e.g. 4n+5 or log(n) are considered expensive) *)
false
| `Cheap ->
true
| `NoModel ->
(* unmodeled calls are considered expensive *)
false

@ -22,6 +22,8 @@ end
type 'a interproc_analysis = type 'a interproc_analysis =
(BufferOverrunAnalysisSummary.t option * 'a * CostDomain.summary option) InterproceduralAnalysis.t (BufferOverrunAnalysisSummary.t option * 'a * CostDomain.summary option) InterproceduralAnalysis.t
type instantiated_cost = Cheap | NoModel | Symbolic of CostDomain.BasicCost.t
val get_cost_if_expensive : 'a interproc_analysis -> Call.t -> CostDomain.BasicCost.t option val get_cost_if_expensive : 'a interproc_analysis -> Call.t -> CostDomain.BasicCost.t option
val get_is_cheap_call : 'a interproc_analysis -> Call.t -> bool val get_instantiated_cost : 'a interproc_analysis -> Call.t -> instantiated_cost

Loading…
Cancel
Save