[cost] Add expensive autoreleasepool size issue type

Summary: This diff adds expensive autoreleasepool size issue type.

Reviewed By: ezgicicek

Differential Revision: D24859196

fbshipit-source-id: 62c6ae103
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent 47c1a327c7
commit ac009cb3aa

@ -0,0 +1,2 @@
\[EXPERIMENTAL\] This warning indicates that non-constant and non-top ObjC autoreleasepool's size in
the procedure. By default, this issue type is disabled.

@ -0,0 +1,2 @@
\[EXPERIMENTAL\] This warning indicates that non-constant and non-top execution time complexity of
the procedure. By default, this issue type is disabled.

@ -12,7 +12,9 @@ type issue_spec =
; complexity_increase_issue: is_on_ui_thread:bool -> IssueType.t
; unreachable_issue: IssueType.t
; infinite_issue: IssueType.t
; top_and_unreachable: bool }
; expensive_issue: IssueType.t
; top_and_unreachable: bool
; expensive: bool }
module CostKindMap = struct
include PrettyPrintable.MakePPMap (CostKind)
@ -32,7 +34,7 @@ end
let enabled_cost_map =
List.fold CostKind.enabled_cost_kinds ~init:CostKindMap.empty
~f:(fun acc CostKind.{kind; top_and_unreachable} ->
~f:(fun acc CostKind.{kind; top_and_unreachable; expensive} ->
let kind_spec =
{ name= Format.asprintf "The %a" CostKind.pp kind
; extract_cost_f= (fun c -> CostKind.to_json_cost_info c kind)
@ -40,6 +42,8 @@ let enabled_cost_map =
(fun ~is_on_ui_thread -> IssueType.complexity_increase ~kind ~is_on_ui_thread)
; unreachable_issue= IssueType.unreachable_cost_call ~kind
; infinite_issue= IssueType.infinite_cost_call ~kind
; top_and_unreachable }
; expensive_issue= IssueType.expensive_cost_call ~kind
; top_and_unreachable
; expensive }
in
CostKindMap.add kind kind_spec acc )

@ -13,7 +13,9 @@ type issue_spec =
; complexity_increase_issue: is_on_ui_thread:bool -> IssueType.t
; unreachable_issue: IssueType.t
; infinite_issue: IssueType.t
; top_and_unreachable: bool }
; expensive_issue: IssueType.t
; top_and_unreachable: bool
; expensive: bool }
module CostKindMap : sig
include PrettyPrintable.PPMap with type key = CostKind.t

@ -225,6 +225,7 @@ end = struct
; ( "EXECUTION_TIME_UNREACHABLE_AT_EXIT"
, [%blob "../../documentation/issues/EXECUTION_TIME_UNREACHABLE_AT_EXIT.md"] )
; ("INFINITE_EXECUTION_TIME", [%blob "../../documentation/issues/INFINITE_EXECUTION_TIME.md"])
; ("EXPENSIVE_EXECUTION_TIME", [%blob "../../documentation/issues/EXPENSIVE_EXECUTION_TIME.md"])
; ( "AUTORELEASEPOOL_SIZE_COMPLEXITY_INCREASE"
, [%blob "../../documentation/issues/AUTORELEASEPOOL_SIZE_COMPLEXITY_INCREASE.md"] )
; ( "AUTORELEASEPOOL_SIZE_COMPLEXITY_INCREASE_UI_THREAD"
@ -233,7 +234,9 @@ end = struct
; ( "AUTORELEASEPOOL_SIZE_UNREACHABLE_AT_EXIT"
, [%blob "../../documentation/issues/AUTORELEASEPOOL_SIZE_UNREACHABLE_AT_EXIT.md"] )
; ( "INFINITE_AUTORELEASEPOOL_SIZE"
, [%blob "../../documentation/issues/INFINITE_AUTORELEASEPOOL_SIZE.md"] ) ]
, [%blob "../../documentation/issues/INFINITE_AUTORELEASEPOOL_SIZE.md"] )
; ( "EXPENSIVE_AUTORELEASEPOOL_SIZE"
, [%blob "../../documentation/issues/EXPENSIVE_AUTORELEASEPOOL_SIZE.md"] ) ]
(** cost issues are already registered below.*)
@ -590,6 +593,8 @@ let exposed_insecure_intent_handling =
register ~id:"EXPOSED_INSECURE_INTENT_HANDLING" Error Quandary ~user_documentation:"Undocumented."
let expensive_cost_call ~kind = register_cost ~enabled:false "EXPENSIVE_%s" ~kind
let failure_exe = register_hidden ~is_silent:true ~id:"Failure_exe" Info Biabduction
let field_not_null_checked =

@ -192,6 +192,8 @@ val eradicate_meta_class_is_nullsafe : t
val exposed_insecure_intent_handling : t
val expensive_cost_call : kind:CostKind.t -> t
val failure_exe : t
val field_not_null_checked : t

@ -49,8 +49,8 @@ let to_json_cost_info c = function
c.Jsonbug_t.autoreleasepool_size
type kind_spec = {kind: t; (* for non-diff analysis *) top_and_unreachable: bool}
type kind_spec = {kind: t; (* for non-diff analysis *) top_and_unreachable: bool; expensive: bool}
let enabled_cost_kinds =
[ {kind= OperationCost; top_and_unreachable= true}
; {kind= AutoreleasepoolSize; top_and_unreachable= true} ]
[ {kind= OperationCost; top_and_unreachable= true; expensive= false}
; {kind= AutoreleasepoolSize; top_and_unreachable= true; expensive= true} ]

@ -9,7 +9,7 @@ open! IStd
type t = OperationCost | AllocationCost | AutoreleasepoolSize [@@deriving compare]
type kind_spec = {kind: t; (* for non-diff analysis *) top_and_unreachable: bool}
type kind_spec = {kind: t; (* for non-diff analysis *) top_and_unreachable: bool; expensive: bool}
val compare : t -> t -> int

@ -49,6 +49,9 @@ module Degree = struct
NonNegativeInt.pp f d.linear ;
if not (NonNegativeInt.is_zero d.log) then
F.fprintf f " + %a%slog" NonNegativeInt.pp d.log SpecialChars.dot_operator
let is_constant {linear; log} = NonNegativeInt.is_zero linear && NonNegativeInt.is_zero log
end
module NonNegativeBoundWithDegreeKind = struct

@ -16,6 +16,8 @@ module Degree : sig
val encode_to_int : t -> int
(** Encodes the complex type [t] to an integer that can be used for comparison. *)
val is_constant : t -> bool
end
module NonNegativeNonTopPolynomial : sig

@ -287,35 +287,56 @@ let is_report_suppressed pname =
module Check = struct
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
let is_autoreleasepool_trace kind =
match (kind : CostKind.t) with
| AutoreleasepoolSize ->
true
| OperationCost | AllocationCost ->
false
let mk_report proc_desc pname err_log loc ~name ~is_autoreleasepool_trace cost =
let message suffix = F.asprintf "%s of the function %a %s" name Procname.pp pname suffix in
fun issue suffix ->
Reporting.log_issue proc_desc err_log ~loc
~ltr:(BasicCostWithReason.polynomial_traces ~is_autoreleasepool_trace cost)
~extras:(compute_errlog_extras cost) Cost issue message
in
~extras:(compute_errlog_extras cost) Cost issue (message suffix)
let report_top_and_unreachable ~report ~unreachable_issue ~infinite_issue cost =
if BasicCostWithReason.is_top cost then report infinite_issue "cannot be computed"
else if BasicCostWithReason.is_unreachable cost then
report unreachable_issue
"cannot be computed since the program's exit state is never reachable"
let report_expensive ~report ~expensive_issue cost =
Option.iter (BasicCostWithReason.degree cost) ~f:(fun degree ->
if not (Polynomials.Degree.is_constant degree) then
report expensive_issue "has non-constant cost" )
let check_and_report {InterproceduralAnalysis.proc_desc; err_log} cost =
let pname = Procdesc.get_proc_name proc_desc in
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
; unreachable_issue
; infinite_issue
; expensive_issue
; top_and_unreachable
; expensive }
cost
->
let report =
mk_report proc_desc pname err_log (Procdesc.get_loc proc_desc) ~name
~is_autoreleasepool_trace:(is_autoreleasepool_trace kind) cost
in
if top_and_unreachable then
report_top_and_unreachable kind pname proc_desc err_log proc_loc ~name ~cost issue_spec )
report_top_and_unreachable ~report ~unreachable_issue ~infinite_issue cost ;
if expensive then report_expensive ~report ~expensive_issue cost )
end
type bound_map = BasicCost.t Node.IdMap.t

@ -0,0 +1,9 @@
codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_linear:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{x->elements.length.ub},Modeled call to indexOfObjectPassingTest:,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease]
codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_param_linear:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{x->elements.length.ub},Modeled call to indexOfObjectPassingTest:,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease]
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_linear:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{n},Loop,autorelease,Call to NoArcCallee.giveMeObject,Modeled call to NSObject.autorelease]
codetoanalyze/objc/autoreleasepool/arc_enumerator.m, ArcEnumerator.callMyEnumerator_linear_FP:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{x->elements.length.ub + 2},Loop,{x->elements.length.ub + 1},Call to MyEnumerator.nextObject,Loop]
codetoanalyze/objc/autoreleasepool/arc_enumerator.m, ArcEnumerator.callMyEnumerator_nextObject_linear:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{x->elements.length.ub + 1},Call to MyEnumerator.nextObject,Loop]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_linear:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to NSObject.autorelease]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.autoreleased_in_loop_sequential_linear:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to NSObject.autorelease]
codetoanalyze/objc/autoreleasepool/basic.m, Basic.multiple_autorelease_constants:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{n},Loop,autorelease,Call to Basic.call_autorelease_constant,Modeled call to NSObject.autorelease]
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callGiveMeObject_linear:, 0, EXPENSIVE_AUTORELEASEPOOL_SIZE, no_bucket, ERROR, [{n},Loop,autorelease,ARC function call to ArcCallee.giveMeObject from non-ARC caller]
Loading…
Cancel
Save