[cost] Add trace for autoreleasepool size

Summary: This diff adds trace field for autoreleasepool size. Unlike to the other checkers, eg inferbo and operation cost, the autoreleasepool size checker should have traces for constants.

Reviewed By: ezgicicek

Differential Revision: D23678084

fbshipit-source-id: 35e6cf5f5
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent e4426acb8a
commit 6dc4612363

@ -89,6 +89,8 @@ module Unsafe : sig
val all_issues : unit -> t list
val set_enabled : t -> bool -> unit
module IssueSet : PrettyPrintable.PPUniqRankSet with type elt = t
end = struct
module T = struct
type t =
@ -978,10 +980,18 @@ let wrong_argument_number =
let unreachable_cost_call ~kind = register_cost ~enabled:false ~kind "%s_UNREACHABLE_AT_EXIT"
(* register enabled cost issues *)
let () =
let is_autoreleasepool_size_issue =
let autoreleasepool_size_issues = ref IssueSet.empty in
let add_autoreleasepool_size_issue ~kind issue_type =
match (kind : CostKind.t) with
| AutoreleasepoolSize ->
autoreleasepool_size_issues := IssueSet.add !autoreleasepool_size_issues issue_type
| OperationCost | AllocationCost ->
()
in
List.iter CostKind.enabled_cost_kinds ~f:(fun CostKind.{kind} ->
List.iter [true; false] ~f:(fun is_on_ui_thread ->
ignore (unreachable_cost_call ~kind) ;
ignore (infinite_cost_call ~kind) ;
ignore (complexity_increase ~kind ~is_on_ui_thread) ;
() ) )
add_autoreleasepool_size_issue ~kind (unreachable_cost_call ~kind) ;
add_autoreleasepool_size_issue ~kind (infinite_cost_call ~kind) ;
add_autoreleasepool_size_issue ~kind (complexity_increase ~kind ~is_on_ui_thread) ) ) ;
fun issue_type -> IssueSet.mem issue_type !autoreleasepool_size_issues

@ -351,3 +351,5 @@ val weak_self_in_noescape_block : t
val wrong_argument_number : t
val unreachable_cost_call : kind:CostKind.t -> t
val is_autoreleasepool_size_issue : t -> bool

@ -1294,6 +1294,8 @@ module BoundTrace = struct
let of_loop location = Loop location
let of_modeled_function pname location = ModeledFunction {pname; location}
end
(** A NonNegativeBound is a Bound that is either non-negative or symbolic but will be evaluated to a

@ -145,7 +145,11 @@ module BoundTrace : sig
val make_err_trace : depth:int -> t -> Errlog.loc_trace
val call : callee_pname:Procname.t -> location:Location.t -> t -> t
val of_loop : Location.t -> t
val of_modeled_function : string -> Location.t -> t
end
type ('c, 's, 't) valclass = Constant of 'c | Symbolic of 's | ValTop of 't

@ -777,7 +777,8 @@ module MemPure = struct
acc )
| None ->
acc )
mem Polynomials.NonNegativePolynomial.one
mem
(Polynomials.NonNegativePolynomial.one ())
let join oenv astate1 astate2 =

@ -72,6 +72,8 @@ module type NonNegativeSymbol = sig
val pp : hum:bool -> F.formatter -> t -> unit
val split_mult : t -> (t * t) option
val make_err_trace : t -> string * Errlog.loc_trace
end
module type NonNegativeSymbolWithDegreeKind = sig
@ -86,6 +88,8 @@ module type NonNegativeSymbolWithDegreeKind = sig
val symbol : t -> t0
val split_mult : t -> (t * t) option
val make_err_trace_symbol : t0 -> string * Errlog.loc_trace
end
module MakeSymbolWithDegreeKind (S : NonNegativeSymbol) :
@ -135,6 +139,11 @@ module MakeSymbolWithDegreeKind (S : NonNegativeSymbol) :
let split_mult {degree_kind; symbol} =
Option.map (S.split_mult symbol) ~f:(fun (s1, s2) -> (make degree_kind s1, make degree_kind s2))
let make_err_trace {symbol} = S.make_err_trace symbol
let make_err_trace_symbol symbol = S.make_err_trace symbol
end
module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
@ -207,47 +216,83 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
- symbols children of a term are 'smaller' than its self symbol
- contents of terms are not zero
- symbols in terms are only symbolic values *)
type t = {const: NonNegativeInt.t; terms: t M.t} [@@deriving compare]
type poly = {const: NonNegativeInt.t; terms: poly M.t} [@@deriving compare]
type t = {poly: poly; autoreleasepool_trace: Bounds.BoundTrace.t option} [@@deriving compare]
let get_autoreleasepool_trace {autoreleasepool_trace} = autoreleasepool_trace
let join_autoreleasepool_trace x y = Option.first_some x y
let poly_of_non_negative_int : NonNegativeInt.t -> poly = fun const -> {const; terms= M.empty}
let of_non_negative_int : ?autoreleasepool_trace:Bounds.BoundTrace.t -> NonNegativeInt.t -> t =
fun ?autoreleasepool_trace const -> {poly= poly_of_non_negative_int const; autoreleasepool_trace}
let of_non_negative_int : NonNegativeInt.t -> t = fun const -> {const; terms= M.empty}
let zero_poly = poly_of_non_negative_int NonNegativeInt.zero
let zero = of_non_negative_int NonNegativeInt.zero
let one = of_non_negative_int NonNegativeInt.one
let one_poly = poly_of_non_negative_int NonNegativeInt.one
let of_int_exn : int -> t = fun i -> i |> NonNegativeInt.of_int_exn |> of_non_negative_int
let one ?autoreleasepool_trace () = of_non_negative_int ?autoreleasepool_trace NonNegativeInt.one
let is_zero : t -> bool = fun {const; terms} -> NonNegativeInt.is_zero const && M.is_empty terms
let of_int_exn : ?autoreleasepool_trace:Bounds.BoundTrace.t -> int -> t =
fun ?autoreleasepool_trace i ->
i |> NonNegativeInt.of_int_exn |> of_non_negative_int ?autoreleasepool_trace
let is_one : t -> bool = fun {const; terms} -> NonNegativeInt.is_one const && M.is_empty terms
let is_constant : t -> bool = fun {terms} -> M.is_empty terms
let is_zero_poly : poly -> bool =
fun {const; terms} -> NonNegativeInt.is_zero const && M.is_empty terms
let is_zero : t -> bool = fun {poly} -> is_zero_poly poly
let is_one_poly : poly -> bool =
fun {const; terms} -> NonNegativeInt.is_one const && M.is_empty terms
let is_one : t -> bool = fun {poly} -> is_one_poly poly
let is_constant : t -> bool = fun {poly= {terms}} -> M.is_empty terms
let is_symbolic : t -> bool = fun p -> not (is_constant p)
let rec plus : t -> t -> t =
let rec plus_poly : poly -> poly -> poly =
fun p1 p2 ->
{ const= NonNegativeInt.(p1.const + p2.const)
; terms= M.increasing_union ~f:plus p1.terms p2.terms }
; terms= M.increasing_union ~f:plus_poly p1.terms p2.terms }
let plus : t -> t -> t =
fun p1 p2 ->
{ poly= plus_poly p1.poly p2.poly
; autoreleasepool_trace=
join_autoreleasepool_trace p1.autoreleasepool_trace p2.autoreleasepool_trace }
let rec mult_const_positive : t -> PositiveInt.t -> t =
let rec mult_const_positive : poly -> PositiveInt.t -> poly =
fun {const; terms} c ->
{ const= NonNegativeInt.(const * (c :> NonNegativeInt.t))
; terms= M.map (fun p -> mult_const_positive p c) terms }
let mult_const : t -> NonNegativeInt.t -> t =
let mult_const_poly : poly -> NonNegativeInt.t -> poly =
fun p c ->
match PositiveInt.of_big_int (c :> Z.t) with None -> zero | Some c -> mult_const_positive p c
match PositiveInt.of_big_int (c :> Z.t) with
| None ->
zero_poly
| Some c ->
mult_const_positive p c
(* (c + r * R + s * S + t * T) x s
= 0 + r * (R x s) + s * (c + s * S + t * T) *)
let rec mult_symb : t -> S.t -> t =
let rec mult_symb_poly : poly -> S.t -> poly =
fun {const; terms} s ->
let less_than_s, equal_s_opt, greater_than_s = M.split s terms in
let less_than_s = M.map (fun p -> mult_symb p s) less_than_s in
let less_than_s = M.map (fun p -> mult_symb_poly p s) less_than_s in
let s_term =
let terms =
match equal_s_opt with
@ -258,17 +303,30 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
in
{const; terms}
in
let terms = if is_zero s_term then less_than_s else M.add s s_term less_than_s in
let terms = if is_zero_poly s_term then less_than_s else M.add s s_term less_than_s in
{const= NonNegativeInt.zero; terms}
let rec mult : t -> t -> t =
let mult_symb : t -> S.t -> t = fun x s -> {x with poly= mult_symb_poly x.poly s}
let rec mult_poly : poly -> poly -> poly =
fun p1 p2 ->
if is_zero p1 || is_zero p2 then zero
else if is_one p1 then p2
else if is_one p2 then p1
if is_zero_poly p1 || is_zero_poly p2 then zero_poly
else if is_one_poly p1 then p2
else if is_one_poly p2 then p1
else
mult_const p1 p2.const |> M.fold (fun s p acc -> plus (mult_symb (mult p p1) s) acc) p2.terms
mult_const_poly p1 p2.const
|> M.fold (fun s p acc -> plus_poly (mult_symb_poly (mult_poly p p1) s) acc) p2.terms
let mult : t -> t -> t =
fun p1 p2 ->
let poly = mult_poly p1.poly p2.poly in
let autoreleasepool_trace =
if is_zero_poly poly then None
else join_autoreleasepool_trace p1.autoreleasepool_trace p2.autoreleasepool_trace
in
{poly; autoreleasepool_trace}
let rec of_valclass : (NonNegativeInt.t, S.t, 't) Bounds.valclass -> ('t, t, 't) below_above =
@ -280,7 +338,9 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
| Symbolic s -> (
match S.split_mult s with
| None ->
Val {const= NonNegativeInt.zero; terms= M.singleton s one}
Val
{ poly= {const= NonNegativeInt.zero; terms= M.singleton s one_poly}
; autoreleasepool_trace= None }
| Some (s1, s2) -> (
match (of_valclass (S.classify s1), of_valclass (S.classify s2)) with
| Val s1, Val s2 ->
@ -311,34 +371,42 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
(* assumes symbols are not comparable *)
let rec leq : lhs:t -> rhs:t -> bool =
let rec leq_poly : lhs:poly -> rhs:poly -> bool =
fun ~lhs ~rhs ->
phys_equal lhs rhs
|| (NonNegativeInt.leq ~lhs:lhs.const ~rhs:rhs.const && M.le ~le_elt:leq lhs.terms rhs.terms)
|| NonNegativeInt.leq ~lhs:lhs.const ~rhs:rhs.const
&& M.le ~le_elt:leq_poly lhs.terms rhs.terms
|| Option.exists (int_ub lhs) ~f:(fun lhs_ub ->
NonNegativeInt.leq ~lhs:lhs_ub ~rhs:(int_lb rhs) )
let rec xcompare ~lhs ~rhs =
let leq ~lhs ~rhs = leq_poly ~lhs:lhs.poly ~rhs:rhs.poly
let rec xcompare_poly ~lhs ~rhs =
let cmp_const =
PartialOrder.of_compare ~compare:NonNegativeInt.compare ~lhs:lhs.const ~rhs:rhs.const
in
let cmp_terms = M.xcompare ~xcompare_elt:xcompare ~lhs:lhs.terms ~rhs:rhs.terms in
let cmp_terms = M.xcompare ~xcompare_elt:xcompare_poly ~lhs:lhs.terms ~rhs:rhs.terms in
PartialOrder.join cmp_const cmp_terms
let rec mask_min_max_constant {const; terms} =
let xcompare ~lhs ~rhs = xcompare_poly ~lhs:lhs.poly ~rhs:rhs.poly
let rec mask_min_max_constant_poly {const; terms} =
{ const
; terms=
M.fold
(fun s p acc ->
let p' = mask_min_max_constant p in
let p' = mask_min_max_constant_poly p in
M.update (S.mask_min_max_constant s)
(function None -> Some p' | Some p -> if leq ~lhs:p ~rhs:p' then Some p' else Some p)
(function
| None -> Some p' | Some p -> if leq_poly ~lhs:p ~rhs:p' then Some p' else Some p )
acc )
terms M.empty }
let mask_min_max_constant x = {x with poly= mask_min_max_constant_poly x.poly}
(* assumes symbols are not comparable *)
(* TODO: improve this for comparable symbols *)
let min_default_left : t -> t -> t =
@ -355,7 +423,7 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
let subst callee_pname location =
let exception ReturnTop of (S.t * Bounds.BoundTrace.t) in
(* avoids top-lifting everything *)
let rec subst {const; terms} eval_sym =
let rec subst_poly {const; terms} eval_sym =
M.fold
(fun s p acc ->
match S.subst callee_pname location s eval_sym with
@ -364,36 +432,48 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
| None ->
acc
| Some c ->
let p = subst p eval_sym in
mult_const_positive p c |> plus acc )
let p = subst_poly p eval_sym in
mult_const_positive p c |> plus_poly acc )
| ValTop trace ->
let p = subst p eval_sym in
if is_zero p then acc else raise (ReturnTop (s, trace))
let p = subst_poly p eval_sym in
if is_zero_poly p then acc else raise (ReturnTop (s, trace))
| Symbolic s ->
let p = subst p eval_sym in
mult_symb p s |> plus acc )
terms (of_non_negative_int const)
let p = subst_poly p eval_sym in
mult_symb_poly p s |> plus_poly acc )
terms (poly_of_non_negative_int const)
in
fun p eval_sym ->
match subst p eval_sym with p -> Val p | exception ReturnTop s_trace -> Above s_trace
fun {poly; autoreleasepool_trace} eval_sym ->
match subst_poly poly eval_sym with
| poly ->
let autoreleasepool_trace =
Option.map autoreleasepool_trace ~f:(fun autoreleasepool_trace ->
Bounds.BoundTrace.call ~callee_pname ~location autoreleasepool_trace )
in
Val {poly; autoreleasepool_trace}
| exception ReturnTop s_trace ->
Above s_trace
(** Emit a pair (d,t) where d is the degree of the polynomial and t is the first term with such
degree *)
let rec degree_with_term {terms} =
M.fold
(fun t p cur_max ->
let d, p' = degree_with_term p in
let degree_term = (Degree.succ (S.degree_kind t) d, mult_symb p' t) in
if [%compare: Degree.t * t] degree_term cur_max > 0 then degree_term else cur_max )
terms (Degree.zero, one)
let degree_with_term {poly; autoreleasepool_trace} =
let rec degree_with_term_poly {terms} =
M.fold
(fun t p cur_max ->
let d, p' = degree_with_term_poly p in
let degree_term = (Degree.succ (S.degree_kind t) d, mult_symb p' t) in
if [%compare: Degree.t * t] degree_term cur_max > 0 then degree_term else cur_max )
terms
(Degree.zero, one ?autoreleasepool_trace ())
in
degree_with_term_poly poly
let degree p = fst (degree_with_term p)
let multiplication_sep = F.sprintf " %s " SpecialChars.multiplication_sign
let pp : hum:bool -> F.formatter -> t -> unit =
let pp_poly : hum:bool -> F.formatter -> poly -> unit =
let add_symb s (((last_s, last_occ) as last), others) =
if Int.equal 0 (S.compare s last_s) then ((last_s, PositiveInt.succ last_occ), others)
else ((s, PositiveInt.one), last :: others)
@ -442,11 +522,22 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
|> ignore
let pp : hum:bool -> F.formatter -> t -> unit = fun ~hum fmt {poly} -> pp_poly ~hum fmt poly
let get_symbols p : S.t0 list =
let rec get_symbols_sub {terms} acc =
M.fold (fun s p acc -> get_symbols_sub p (S.symbol s :: acc)) terms acc
in
get_symbols_sub p []
get_symbols_sub p.poly []
let polynomial_traces ?(is_autoreleasepool_trace = false) p =
let traces = get_symbols p |> List.map ~f:S.make_err_trace_symbol in
if is_autoreleasepool_trace then
get_autoreleasepool_trace p
|> Option.value_map ~default:traces ~f:(fun trace ->
traces @ [("autorelease", Bounds.BoundTrace.make_err_trace ~depth:0 trace)] )
else traces
end
module NonNegativeBoundWithDegreeKind = MakeSymbolWithDegreeKind (Bounds.NonNegativeBound)
@ -594,13 +685,17 @@ module NonNegativePolynomial = struct
let zero = Val NonNegativeNonTopPolynomial.zero
let one = Val NonNegativeNonTopPolynomial.one
let one ?autoreleasepool_trace () =
Val (NonNegativeNonTopPolynomial.one ?autoreleasepool_trace ())
let of_unreachable node_loc =
Below (UnreachableTraces.singleton (UnreachableTrace.unreachable_node node_loc))
let of_int_exn i = Val (NonNegativeNonTopPolynomial.of_int_exn i)
let of_int_exn ?autoreleasepool_trace i =
Val (NonNegativeNonTopPolynomial.of_int_exn ?autoreleasepool_trace i)
let make_trace_set ~map_above =
AbstractDomain.StackedUtils.map
@ -694,11 +789,6 @@ module NonNegativePolynomial = struct
~f_below:Fn.id
let get_symbols =
AbstractDomain.StackedUtils.map ~f:NonNegativeNonTopPolynomial.get_symbols ~f_above:Fn.id
~f_below:Fn.id
let pp_degree ~only_bigO fmt = function
| Above _ ->
Format.pp_print_string fmt "Top"
@ -720,12 +810,12 @@ module NonNegativePolynomial = struct
""
let polynomial_traces p =
match get_symbols p with
let polynomial_traces ?is_autoreleasepool_trace = function
| Below trace ->
UnreachableTraces.make_err_trace trace
| Val symbols ->
List.map symbols ~f:Bounds.NonNegativeBound.make_err_trace |> Errlog.concat_traces
| Val p ->
NonNegativeNonTopPolynomial.polynomial_traces ?is_autoreleasepool_trace p
|> Errlog.concat_traces
| Above trace ->
TopTraces.make_err_trace trace

@ -22,7 +22,7 @@ end
module NonNegativeNonTopPolynomial : sig
type t
val get_symbols : t -> Bounds.NonNegativeBound.t list
val polynomial_traces : ?is_autoreleasepool_trace:bool -> t -> (string * Errlog.loc_trace) list
end
module TopTraces : sig
@ -56,9 +56,9 @@ module NonNegativePolynomial : sig
val zero : t
val one : t
val one : ?autoreleasepool_trace:Bounds.BoundTrace.t -> unit -> t
val of_int_exn : int -> t
val of_int_exn : ?autoreleasepool_trace:Bounds.BoundTrace.t -> int -> t
val is_symbolic : t -> bool
@ -91,7 +91,7 @@ module NonNegativePolynomial : sig
val pp_degree : only_bigO:bool -> Format.formatter -> degree_with_term -> unit
val polynomial_traces : t -> Errlog.loc_trace
val polynomial_traces : ?is_autoreleasepool_trace:bool -> t -> Errlog.loc_trace
val encode : t -> string

@ -36,7 +36,7 @@ let compute_upperbound_map node_cfg inferbo_invariant_map control_invariant_map
let node_id = NodeCFG.Node.id node in
match Procdesc.Node.get_kind node with
| Procdesc.Node.Exit_node ->
Node.IdMap.add node_id BasicCost.one bound_map
Node.IdMap.add node_id (BasicCost.one ()) bound_map
| _ -> (
let exit_state_opt =
let instr_node_id = InstrCFG.last_of_underlying_node node |> InstrCFG.Node.id in
@ -63,7 +63,7 @@ let compute_upperbound_map node_cfg inferbo_invariant_map control_invariant_map
unreachable returning cost 0 \n" ;
BasicCost.of_unreachable node_loc
| ExcRaised ->
BasicCost.one
BasicCost.one ()
| Reachable mem ->
let cost =
BufferOverrunDomain.MemReach.range ~filter_loc:(filter_loc control_map) ~node_id
@ -74,7 +74,7 @@ let compute_upperbound_map node_cfg inferbo_invariant_map control_invariant_map
values) does not make sense especially when the
abstract memory is non-bottom. This is a source
of unsoundness in the analysis. *)
if BasicCost.is_zero cost then BasicCost.one else cost
if BasicCost.is_zero cost then BasicCost.one () else cost
in
L.(debug Analysis Medium)
"@\n>>>Setting bound for node = %a to %a@\n\n" Node.pp_id node_id BasicCost.pp bound ;

@ -57,8 +57,8 @@ module InstrBasicCostWithReason = struct
let get_instr_cost_record tenv extras instr_node instr =
match instr with
| Sil.Call (ret, Exp.Const (Const.Cfun callee_pname), params, _, _) when Config.inclusive_cost
->
| Sil.Call (ret, Exp.Const (Const.Cfun callee_pname), params, location, _)
when Config.inclusive_cost ->
let { inferbo_invariant_map
; integer_type_widths
; inferbo_get_summary
@ -110,7 +110,10 @@ module InstrBasicCostWithReason = struct
if is_allocation_function callee_pname then
CostDomain.plus CostDomain.unit_cost_allocation operation_cost
else if is_autorelease_function callee_pname then
CostDomain.plus CostDomain.unit_cost_autoreleasepool_size operation_cost
CostDomain.plus
(CostDomain.unit_cost_autoreleasepool_size
~autoreleasepool_trace:(Bounds.BoundTrace.of_modeled_function "autorelease" location))
operation_cost
else operation_cost
| Sil.Call (_, Exp.Const (Const.Cfun _), _, _, _) ->
CostDomain.zero_record
@ -207,12 +210,19 @@ let is_report_suppressed pname =
module Check = struct
let report_top_and_unreachable pname proc_desc err_log loc ~name ~cost
let report_top_and_unreachable kind pname proc_desc err_log loc ~name ~cost
{CostIssues.unreachable_issue; infinite_issue} =
let report issue suffix =
let is_autoreleasepool_trace =
match (kind : CostKind.t) with
| AutoreleasepoolSize ->
true
| OperationCost | AllocationCost ->
false
in
let message = F.asprintf "%s of the function %a %s" name Procname.pp pname suffix in
Reporting.log_issue proc_desc err_log ~loc
~ltr:(BasicCostWithReason.polynomial_traces cost)
~ltr:(BasicCostWithReason.polynomial_traces ~is_autoreleasepool_trace cost)
~extras:(compute_errlog_extras cost) Cost issue message
in
if BasicCostWithReason.is_top cost then report infinite_issue "cannot be computed"
@ -226,9 +236,9 @@ module Check = struct
let proc_loc = Procdesc.get_loc proc_desc in
if not (is_report_suppressed pname) then
CostIssues.CostKindMap.iter2 CostIssues.enabled_cost_map cost
~f:(fun _kind (CostIssues.{name; top_and_unreachable} as issue_spec) cost ->
~f:(fun kind (CostIssues.{name; top_and_unreachable} as issue_spec) cost ->
if top_and_unreachable then
report_top_and_unreachable pname proc_desc err_log proc_loc ~name ~cost issue_spec )
report_top_and_unreachable kind pname proc_desc err_log proc_loc ~name ~cost issue_spec )
end
type bound_map = BasicCost.t Node.IdMap.t

@ -13,7 +13,7 @@ module BasicCost = struct
(* NOTE: Increment the version number if you changed the [t] type. This is for avoiding
demarshalling failure of cost analysis results in running infer-reportdiff. *)
let version = 7
let version = 8
end
module BasicCostWithReason = struct
@ -25,7 +25,9 @@ module BasicCostWithReason = struct
let zero = {cost= BasicCost.zero; top_pname_opt= None}
let one = {cost= BasicCost.one; top_pname_opt= None}
let one ?autoreleasepool_trace () =
{cost= BasicCost.one ?autoreleasepool_trace (); top_pname_opt= None}
let subst callee_pname location record eval_sym =
{record with cost= BasicCost.subst callee_pname location record.cost eval_sym}
@ -42,7 +44,9 @@ module BasicCostWithReason = struct
let mult_unreachable cost record = {record with cost= BasicCost.mult_unreachable cost record.cost}
let polynomial_traces {cost} = BasicCost.polynomial_traces cost
let polynomial_traces ~is_autoreleasepool_trace {cost} =
BasicCost.polynomial_traces ~is_autoreleasepool_trace cost
let pp format {cost} = BasicCost.pp format cost
@ -68,7 +72,8 @@ module VariantCostMap = struct
record
let increment kind record = increase_by kind BasicCostWithReason.one record
let increment ?autoreleasepool_trace kind record =
increase_by kind (BasicCostWithReason.one ?autoreleasepool_trace ()) record
end
type t = VariantCostMap.t
@ -119,8 +124,8 @@ let unit_cost_atomic_operation = VariantCostMap.increment CostKind.OperationCost
let unit_cost_allocation = VariantCostMap.increment CostKind.AllocationCost zero_record
let unit_cost_autoreleasepool_size =
VariantCostMap.increment CostKind.AutoreleasepoolSize zero_record
let unit_cost_autoreleasepool_size ~autoreleasepool_trace =
VariantCostMap.increment ~autoreleasepool_trace CostKind.AutoreleasepoolSize zero_record
let of_operation_cost operation_cost =

@ -39,7 +39,7 @@ module BasicCostWithReason : sig
val degree : t -> Polynomials.Degree.t option
val polynomial_traces : t -> Errlog.loc_trace
val polynomial_traces : is_autoreleasepool_trace:bool -> t -> Errlog.loc_trace
val pp_hum : Format.formatter -> t -> unit
end
@ -81,7 +81,7 @@ val unit_cost_atomic_operation : t
val unit_cost_allocation : t
(** Map representing cost record \{OperationCost:0; AllocationCost:1; AutoreleasepoolSize:0\} *)
val unit_cost_autoreleasepool_size : t
val unit_cost_autoreleasepool_size : autoreleasepool_trace:Bounds.BoundTrace.t -> t
(** Map representing cost record \{OperationCost:0; AllocationCost:0; AutoreleasepoolSize:1\} *)
val of_operation_cost : BasicCost.t -> t

@ -9,7 +9,7 @@ open! IStd
module BasicCost = CostDomain.BasicCost
open BufferOverrunUtils.ModelEnv
let unit_cost_model _model_env ~ret:_ _inferbo_mem = BasicCost.one
let unit_cost_model _model_env ~ret:_ _inferbo_mem = BasicCost.one ()
let cost_of_exp exp ~degree_kind ~of_function {integer_type_widths; location} ~ret:_ inferbo_mem =
let itv =

@ -209,12 +209,13 @@ module CostItem = struct
(pp_degree ~only_bigO:false) curr_item
end
let polynomial_traces = function
let polynomial_traces issue_type = function
| None ->
[]
| Some (Val (_, degree_term)) ->
Polynomials.NonNegativeNonTopPolynomial.get_symbols degree_term
|> List.map ~f:Bounds.NonNegativeBound.make_err_trace
Polynomials.NonNegativeNonTopPolynomial.polynomial_traces
~is_autoreleasepool_trace:(IssueType.is_autoreleasepool_size_issue issue_type)
degree_term
| Some (Below traces) ->
[("", Polynomials.UnreachableTraces.make_err_trace traces)]
| Some (Above traces) ->
@ -285,8 +286,10 @@ let issue_of_cost kind CostIssues.{complexity_increase_issue; unreachable_issue;
(Format.asprintf "%s %a" msg CostItem.pp_cost_msg cost_item)
[] ]
in
(("", marker_cost_trace "Previous" prev_item) :: polynomial_traces prev_degree_with_term)
@ (("", marker_cost_trace "Updated" curr_item) :: polynomial_traces curr_degree_with_term)
("", marker_cost_trace "Previous" prev_item)
:: polynomial_traces issue_type prev_degree_with_term
@ ("", marker_cost_trace "Updated" curr_item)
:: polynomial_traces issue_type curr_degree_with_term
|> Errlog.concat_traces
in
let severity = IssueType.Advice in

@ -249,14 +249,16 @@ module JsonCostsPrinter = MakeJsonListPrinter (struct
Format.asprintf "%a" (CostDomain.BasicCost.pp_degree ~only_bigO:true) degree_with_term
}
in
let cost_info cost =
let cost_info ?is_autoreleasepool_trace cost =
{ Jsonbug_t.polynomial_version= CostDomain.BasicCost.version
; polynomial= CostDomain.BasicCost.encode cost
; degree=
Option.map (CostDomain.BasicCost.degree cost) ~f:Polynomials.Degree.encode_to_int
; hum= hum cost
; trace= loc_trace_to_jsonbug_record (CostDomain.BasicCost.polynomial_traces cost) Advice
}
; trace=
loc_trace_to_jsonbug_record
(CostDomain.BasicCost.polynomial_traces ?is_autoreleasepool_trace cost)
Advice }
in
let cost_item =
let file =
@ -269,7 +271,8 @@ module JsonCostsPrinter = MakeJsonListPrinter (struct
; is_on_ui_thread
; exec_cost= cost_info (CostDomain.get_cost_kind CostKind.OperationCost post).cost
; autoreleasepool_size=
cost_info (CostDomain.get_cost_kind CostKind.AutoreleasepoolSize post).cost }
cost_info ~is_autoreleasepool_trace:true
(CostDomain.get_cost_kind CostKind.AutoreleasepoolSize post).cost }
in
Some (Jsonbug_j.string_of_cost_item cost_item)
| _ ->

@ -253,6 +253,8 @@ module type PPUniqRankSet = sig
val remove : elt -> t -> t
val mem : elt -> t -> bool
val union_prefer_left : t -> t -> t
val pp : ?print_rank:bool -> F.formatter -> t -> unit
@ -319,6 +321,8 @@ module MakePPUniqRankSet
let remove value map = Map.remove (Val.to_rank value) map
let mem value map = Map.mem (Val.to_rank value) map
let singleton value = add Map.empty value
let union_prefer_left m1 m2 = Map.union (fun _rank value1 _value2 -> Some value1) m1 m2

@ -204,6 +204,8 @@ module type PPUniqRankSet = sig
val remove : elt -> t -> t
val mem : elt -> t -> bool
val union_prefer_left : t -> t -> t
(** in case an element with the same rank is present both in [lhs] and [rhs], keep the one from
[lhs] in [union_prefer_left lhs rhs] *)

@ -1,17 +1,17 @@
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callAllocObject_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callCopyObject_zero:x:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_autoreleasepool_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_linear:, n, OnUIThread:false, [{n},Loop]
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callMutableCopyObject_zero:x:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callNewObject_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.dealloc, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autorelease_unreachable_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_autoreleasepool_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_linear:, n, OnUIThread:false, [{n},Loop]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_nested_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_constant:, 1, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_linear:, n, OnUIThread:false, [{n},Loop]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_autorelease_constant, 1, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_constant:, 1, OnUIThread:false, [autorelease,Call to Basic.call_autorelease_constant,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_linear:, n, OnUIThread:false, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_autorelease_constant, 1, OnUIThread:false, [autorelease,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.call_no_autorelease_zero, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.dealloc, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/basic.m, Basic.loop_in_autoreleasepool_zero:, 0, OnUIThread:false, []
@ -19,6 +19,6 @@ codetoanalyze/objc/autoreleasepool/basic.m, Basic.no_autoreleased_in_loop_zero:,
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.allocObject, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.copyObject:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.dealloc, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.giveMeObject, 1, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.giveMeObject, 1, OnUIThread:false, [autorelease,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.mutableCopyObject:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.newObject, 0, OnUIThread:false, []

Loading…
Cancel
Save