[cost] Refactoring: autorelease size models

Summary:
This diff simply changes the type of autorelease size models as `CostUtils.model` instead of
`unit`.  In the following diff, it will add a model that has more complicated semantics that does not return the unit cost.

Reviewed By: ezgicicek

Differential Revision: D23785159

fbshipit-source-id: 300808574
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent ecb409504d
commit 336e1c7b99

@ -52,10 +52,6 @@ module InstrBasicCostWithReason = struct
List.exists allocation_functions ~f:(fun f -> Procname.equal callee_pname f) List.exists allocation_functions ~f:(fun f -> Procname.equal callee_pname f)
let is_autorelease_function tenv callee_pname fun_arg_list =
CostAutoreleaseModels.Call.dispatch tenv callee_pname fun_arg_list |> Option.is_some
(** The methods whose name start with one of the prefixes return an object that is owned by the (** The methods whose name start with one of the prefixes return an object that is owned by the
caller. Therefore ARC will not add any objects they return into an autorelease pool. *) caller. Therefore ARC will not add any objects they return into an autorelease pool. *)
let return_object_owned_by_caller = let return_object_owned_by_caller =
@ -74,7 +70,7 @@ module InstrBasicCostWithReason = struct
let get_instr_cost_record tenv extras cfg instr_node instr = let get_instr_cost_record tenv extras cfg instr_node instr =
match instr with match instr with
| Sil.Call (((_, ret_typ) as ret), Exp.Const (Const.Cfun callee_pname), params, location, _) | Sil.Call (((_, ret_typ) as ret), Exp.Const (Const.Cfun callee_pname), params, location, _)
when Config.inclusive_cost -> when Config.inclusive_cost -> (
let { inferbo_invariant_map let { inferbo_invariant_map
; integer_type_widths ; integer_type_widths
; inferbo_get_summary ; inferbo_get_summary
@ -86,63 +82,66 @@ module InstrBasicCostWithReason = struct
List.map params ~f:(fun (exp, typ) -> List.map params ~f:(fun (exp, typ) ->
ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} )
in in
let inferbo_mem_opt =
BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map
in
let model_env =
lazy
(let node_hash = InstrCFG.Node.hash instr_node in
BufferOverrunUtils.ModelEnv.mk_model_env callee_pname ~node_hash location tenv
integer_type_widths inferbo_get_summary )
in
let cost = let cost =
match match inferbo_mem_opt with
BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map
with
| None -> | None ->
CostDomain.unit_cost_atomic_operation CostDomain.unit_cost_atomic_operation
| Some inferbo_mem -> ( | Some inferbo_mem -> (
let loc = InstrCFG.Node.loc instr_node in match CostModels.Call.dispatch tenv callee_pname fun_arg_list with
match CostModels.Call.dispatch tenv callee_pname fun_arg_list with | Some model ->
| Some model -> CostDomain.of_operation_cost (model (Lazy.force model_env) ~ret inferbo_mem)
let node_hash = InstrCFG.Node.hash instr_node in | None -> (
let model_env = match (get_summary callee_pname, get_formals callee_pname) with
BufferOverrunUtils.ModelEnv.mk_model_env callee_pname ~node_hash loc tenv | Some {CostDomain.post= callee_cost_record}, Some callee_formals -> (
integer_type_widths inferbo_get_summary let instantiated_cost =
CostDomain.map callee_cost_record ~f:(fun callee_cost ->
instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem
~callee_pname ~callee_formals ~params ~callee_cost ~loc:location )
in in
CostDomain.of_operation_cost (model model_env ~ret inferbo_mem) match CostDomain.get_operation_cost callee_cost_record with
| None -> ( | {cost; top_pname_opt= None} when BasicCost.is_top cost ->
match (get_summary callee_pname, get_formals callee_pname) with CostDomain.add_top_pname_opt CostKind.OperationCost instantiated_cost
| Some {CostDomain.post= callee_cost_record}, Some callee_formals -> ( (Some callee_pname)
let instantiated_cost = | _ ->
CostDomain.map callee_cost_record ~f:(fun callee_cost -> instantiated_cost )
instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem | _, _ ->
~callee_pname ~callee_formals ~params ~callee_cost ~loc ) ScubaLogging.cost_log_message ~label:"unmodeled_function_cost_analysis"
in ~message:
match CostDomain.get_operation_cost callee_cost_record with (F.asprintf "Unmodeled Function[Cost Analysis] : %a" Procname.pp callee_pname) ;
| {cost; top_pname_opt= None} when BasicCost.is_top cost -> CostDomain.unit_cost_atomic_operation ) )
CostDomain.add_top_pname_opt CostKind.OperationCost instantiated_cost
(Some callee_pname)
| _ ->
instantiated_cost )
| _, _ ->
ScubaLogging.cost_log_message ~label:"unmodeled_function_cost_analysis"
~message:
(F.asprintf "Unmodeled Function[Cost Analysis] : %a" Procname.pp
callee_pname) ;
CostDomain.unit_cost_atomic_operation ) )
in in
let cost = let cost =
if is_allocation_function callee_pname then if is_allocation_function callee_pname then
CostDomain.plus CostDomain.unit_cost_allocation cost CostDomain.plus CostDomain.unit_cost_allocation cost
else cost else cost
in in
if is_autorelease_function tenv callee_pname fun_arg_list then match
let autoreleasepool_trace = (inferbo_mem_opt, CostAutoreleaseModels.Call.dispatch tenv callee_pname fun_arg_list)
Bounds.BoundTrace.of_modeled_function (Procname.to_string callee_pname) location with
in | Some inferbo_mem, Some model ->
CostDomain.plus cost (CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace) let autoreleasepool_size = model (Lazy.force model_env) ~ret inferbo_mem in
else if CostDomain.plus_autoreleasepool_size autoreleasepool_size cost
is_objc_call_from_no_arc_to_arc extras cfg callee_pname | _, _ ->
&& Typ.is_pointer_to_objc_class ret_typ if
&& not (return_object_owned_by_caller callee_pname) is_objc_call_from_no_arc_to_arc extras cfg callee_pname
then && Typ.is_pointer_to_objc_class ret_typ
let autoreleasepool_trace = && not (return_object_owned_by_caller callee_pname)
Bounds.BoundTrace.of_arc_from_non_arc (Procname.to_string callee_pname) location then
in let autoreleasepool_trace =
CostDomain.plus cost (CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace) Bounds.BoundTrace.of_arc_from_non_arc (Procname.to_string callee_pname) location
else cost in
CostDomain.plus cost
(CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace)
else cost )
| Sil.Call (_, Exp.Const (Const.Cfun _), _, _, _) -> | Sil.Call (_, Exp.Const (Const.Cfun _), _, _, _) ->
CostDomain.zero_record CostDomain.zero_record
| Sil.Load {id= lhs_id} when Ident.is_none lhs_id -> | Sil.Load {id= lhs_id} when Ident.is_none lhs_id ->

@ -6,19 +6,28 @@
*) *)
open! IStd open! IStd
module BasicCost = CostDomain.BasicCost
open BufferOverrunUtils.ModelEnv
let unit_cost {pname; location} ~ret:_ _inferbo_mem =
let autoreleasepool_trace =
Bounds.BoundTrace.of_modeled_function (Procname.to_string pname) location
in
BasicCost.one ~autoreleasepool_trace ()
module Call = struct module Call = struct
let dispatch : (Tenv.t, unit, unit) ProcnameDispatcher.Call.dispatcher = let dispatch : (Tenv.t, CostUtils.model, unit) ProcnameDispatcher.Call.dispatcher =
let open ProcnameDispatcher.Call in let open ProcnameDispatcher.Call in
make_dispatcher make_dispatcher
[ +PatternMatch.ObjectiveC.implements "NSObject" &:: "autorelease" &--> () [ +PatternMatch.ObjectiveC.implements "NSObject" &:: "autorelease" &--> unit_cost
; -"CFAutorelease" &--> () ; -"CFAutorelease" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "initForReadingFromData:error:" &--> () &:: "initForReadingFromData:error:" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "initForReadingWithData:" &--> () &:: "initForReadingWithData:" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "unarchivedObjectOfClass:fromData:error:" &--> () &:: "unarchivedObjectOfClass:fromData:error:" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "unarchivedObjectOfClasses:fromData:error:" &--> () ] &:: "unarchivedObjectOfClasses:fromData:error:" &--> unit_cost ]
end end

@ -8,5 +8,5 @@
open! IStd open! IStd
module Call : sig module Call : sig
val dispatch : (Tenv.t, unit, unit) ProcnameDispatcher.Call.dispatcher val dispatch : (Tenv.t, CostUtils.model, unit) ProcnameDispatcher.Call.dispatcher
end end

@ -120,6 +120,16 @@ let plus cost_record1 cost_record2 =
cost_record1 cost_record2 cost_record1 cost_record2
let plus_autoreleasepool_size autoreleasepool_size cost =
VariantCostMap.update AutoreleasepoolSize
(function
| None ->
Some {BasicCostWithReason.cost= autoreleasepool_size; top_pname_opt= None}
| Some prev ->
Some {prev with cost= BasicCost.plus prev.cost autoreleasepool_size} )
cost
let unit_cost_atomic_operation = VariantCostMap.increment CostKind.OperationCost zero_record let unit_cost_atomic_operation = VariantCostMap.increment CostKind.OperationCost zero_record
let unit_cost_allocation = VariantCostMap.increment CostKind.AllocationCost zero_record let unit_cost_allocation = VariantCostMap.increment CostKind.AllocationCost zero_record

@ -75,6 +75,9 @@ val mult_by : t -> nb_exec:BasicCost.t -> t
val plus : t -> t -> t val plus : t -> t -> t
(** Union of two maps where common costs are added together *) (** Union of two maps where common costs are added together *)
val plus_autoreleasepool_size : BasicCost.t -> t -> t
(** Add an autoreleasepool size to the cost map *)
val unit_cost_atomic_operation : t val unit_cost_atomic_operation : t
(** Map representing cost record \{OperationCost:1; AllocationCost:0; AutoreleasepoolSize:0\} *) (** Map representing cost record \{OperationCost:1; AllocationCost:0; AutoreleasepoolSize:0\} *)

Loading…
Cancel
Save