From 94f4ded9b4f674456f5e0c9cd04107db547d72ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezgi=20=C3=87i=C3=A7ek?= Date: Tue, 12 Nov 2019 04:51:06 -0800 Subject: [PATCH] [cost] Introduce cost issue types for functions on UI Thread Summary: Let's introduce a set of new cost analysis issue types that are raised when the function is statically determined to run on the UI thread. For this, we rely on the existing `runs_on_ui_thread` check that is developed for RacerD. We also update the cost summary and `jsonbug.cost_item` to include whether a method is on the ui thread so that we don't repeatedly compute this at diff time for complexity increase issues. Note that `*_UI_THREAD` cost issues are assumed to be more strict than `*_COLD_START` reports at the moment. Next, we can also consider adding a new issue type that combines both such as `*_UI_THREAD_AND_COLD_START` (i.e. for methods that are both on cold start and run on ui thread). Reviewed By: ngorogiannis Differential Revision: D18428408 fbshipit-source-id: f18805716 --- infer/man/man1/infer-full.txt | 5 + infer/man/man1/infer-report.txt | 5 + infer/man/man1/infer.txt | 5 + infer/src/atd/jsonbug.atd | 1 + infer/src/backend/Differential.ml | 3 +- infer/src/backend/InferPrint.ml | 3 +- infer/src/base/CostIssues.ml | 10 +- infer/src/base/CostIssues.mli | 4 +- infer/src/base/IssueType.ml | 28 +++--- infer/src/base/IssueType.mli | 4 +- infer/src/checkers/cost.ml | 27 +++-- infer/src/checkers/costDomain.ml | 2 +- .../differential_of_costs_report/Makefile | 9 +- .../costs_summary.json.exp | 2 +- .../differential_of_costs_report/fixed.exp | 1 + .../introduced.exp | 1 + .../src/DiffExampleUIThread.java.current | 36 +++++++ .../src/DiffExampleUIThread.java.previous | 36 +++++++ .../java/performance/UIAnnotationTest.java | 98 +++++++++++++++++++ .../codetoanalyze/java/performance/issues.exp | 7 ++ 20 files changed, 250 insertions(+), 37 deletions(-) create mode 100644 infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.current create mode 100644 infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.previous create mode 100644 infer/tests/codetoanalyze/java/performance/UIAnnotationTest.java diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index db08d2fdf..b0bc18d17 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -337,6 +337,7 @@ OPTIONS ALLOCATION_COMPLEXITY_INCREASE (enabled by default), ALLOCATION_COMPLEXITY_INCREASE_COLD_START (enabled by default), + ALLOCATION_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), ANALYSIS_STOPS (disabled by default), ARRAY_OUT_OF_BOUNDS_L1 (disabled by default), ARRAY_OUT_OF_BOUNDS_L2 (disabled by default), @@ -400,11 +401,15 @@ OPTIONS ERADICATE_RETURN_OVER_ANNOTATED (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE_COLD_START (enabled by + default), + EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), EXPENSIVE_ALLOCATION (disabled by default), EXPENSIVE_ALLOCATION_COLD_START (disabled by default), + EXPENSIVE_ALLOCATION_UI_THREAD (disabled by default), EXPENSIVE_EXECUTION_TIME (disabled by default), EXPENSIVE_EXECUTION_TIME_COLD_START (disabled by default), + EXPENSIVE_EXECUTION_TIME_UI_THREAD (disabled by default), EXPENSIVE_LOOP_INVARIANT_CALL (enabled by default), EXPOSED_INSECURE_INTENT_HANDLING (enabled by default), Failure_exe (enabled by default), diff --git a/infer/man/man1/infer-report.txt b/infer/man/man1/infer-report.txt index 7416bc324..93a946621 100644 --- a/infer/man/man1/infer-report.txt +++ b/infer/man/man1/infer-report.txt @@ -79,6 +79,7 @@ OPTIONS ALLOCATION_COMPLEXITY_INCREASE (enabled by default), ALLOCATION_COMPLEXITY_INCREASE_COLD_START (enabled by default), + ALLOCATION_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), ANALYSIS_STOPS (disabled by default), ARRAY_OUT_OF_BOUNDS_L1 (disabled by default), ARRAY_OUT_OF_BOUNDS_L2 (disabled by default), @@ -142,11 +143,15 @@ OPTIONS ERADICATE_RETURN_OVER_ANNOTATED (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE_COLD_START (enabled by + default), + EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), EXPENSIVE_ALLOCATION (disabled by default), EXPENSIVE_ALLOCATION_COLD_START (disabled by default), + EXPENSIVE_ALLOCATION_UI_THREAD (disabled by default), EXPENSIVE_EXECUTION_TIME (disabled by default), EXPENSIVE_EXECUTION_TIME_COLD_START (disabled by default), + EXPENSIVE_EXECUTION_TIME_UI_THREAD (disabled by default), EXPENSIVE_LOOP_INVARIANT_CALL (enabled by default), EXPOSED_INSECURE_INTENT_HANDLING (enabled by default), Failure_exe (enabled by default), diff --git a/infer/man/man1/infer.txt b/infer/man/man1/infer.txt index 34a2e6eae..67a9fce81 100644 --- a/infer/man/man1/infer.txt +++ b/infer/man/man1/infer.txt @@ -337,6 +337,7 @@ OPTIONS ALLOCATION_COMPLEXITY_INCREASE (enabled by default), ALLOCATION_COMPLEXITY_INCREASE_COLD_START (enabled by default), + ALLOCATION_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), ANALYSIS_STOPS (disabled by default), ARRAY_OUT_OF_BOUNDS_L1 (disabled by default), ARRAY_OUT_OF_BOUNDS_L2 (disabled by default), @@ -400,11 +401,15 @@ OPTIONS ERADICATE_RETURN_OVER_ANNOTATED (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE (enabled by default), EXECUTION_TIME_COMPLEXITY_INCREASE_COLD_START (enabled by + default), + EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD (enabled by default), EXPENSIVE_ALLOCATION (disabled by default), EXPENSIVE_ALLOCATION_COLD_START (disabled by default), + EXPENSIVE_ALLOCATION_UI_THREAD (disabled by default), EXPENSIVE_EXECUTION_TIME (disabled by default), EXPENSIVE_EXECUTION_TIME_COLD_START (disabled by default), + EXPENSIVE_EXECUTION_TIME_UI_THREAD (disabled by default), EXPENSIVE_LOOP_INVARIANT_CALL (enabled by default), EXPOSED_INSECURE_INTENT_HANDLING (enabled by default), Failure_exe (enabled by default), diff --git a/infer/src/atd/jsonbug.atd b/infer/src/atd/jsonbug.atd index af05087c2..acc4143cf 100644 --- a/infer/src/atd/jsonbug.atd +++ b/infer/src/atd/jsonbug.atd @@ -75,6 +75,7 @@ type cost_item = { loc : loc; procedure_name : string; procedure_id : string; + is_on_ui_thread : bool; alloc_cost : cost_info; exec_cost : cost_info; } diff --git a/infer/src/backend/Differential.ml b/infer/src/backend/Differential.ml index 5fa7917f3..0bcecf426 100644 --- a/infer/src/backend/Differential.ml +++ b/infer/src/backend/Differential.ml @@ -212,6 +212,7 @@ let issue_of_cost kind CostIssues.{complexity_increase_issue; zero_issue; infini curr_item ) = let file = cost_info.Jsonbug_t.loc.file in let method_name = cost_info.Jsonbug_t.procedure_name in + let is_on_ui_thread = cost_info.Jsonbug_t.is_on_ui_thread in let class_name = match Str.split (Str.regexp_string ("." ^ method_name)) cost_info.Jsonbug_t.procedure_id with | [class_name; _] -> @@ -228,7 +229,7 @@ let issue_of_cost kind CostIssues.{complexity_increase_issue; zero_issue; infini else if CostItem.is_zero curr_item then zero_issue else let is_on_cold_start = ExternalPerfData.in_profiler_data_map procname in - complexity_increase_issue ~is_on_cold_start + complexity_increase_issue ~is_on_cold_start ~is_on_ui_thread in if (not Config.filtering) || issue_type.IssueType.enabled then let qualifier = diff --git a/infer/src/backend/InferPrint.ml b/infer/src/backend/InferPrint.ml index 0050968c0..43cd53258 100644 --- a/infer/src/backend/InferPrint.ml +++ b/infer/src/backend/InferPrint.ml @@ -323,7 +323,7 @@ module JsonCostsPrinter = MakeJsonListPrinter (struct let to_string {loc; proc_name; cost_opt} = match cost_opt with - | Some {post} when not (Typ.Procname.is_java_access_method proc_name) -> + | Some {post; is_on_ui_thread} when not (Typ.Procname.is_java_access_method proc_name) -> let hum cost = let degree_with_term = CostDomain.BasicCost.get_degree_with_term cost in { Jsonbug_t.hum_polynomial= Format.asprintf "%a" CostDomain.BasicCost.pp_hum cost @@ -349,6 +349,7 @@ module JsonCostsPrinter = MakeJsonListPrinter (struct ; loc= {file; lnum= loc.Location.line; cnum= loc.Location.col; enum= -1} ; procedure_name= Typ.Procname.get_method proc_name ; procedure_id= procedure_id_of_procname proc_name + ; is_on_ui_thread ; exec_cost= cost_info (CostDomain.get_cost_kind CostKind.OperationCost post) ; alloc_cost= cost_info (CostDomain.get_cost_kind CostKind.AllocationCost post) } in diff --git a/infer/src/base/CostIssues.ml b/infer/src/base/CostIssues.ml index 543fab403..62e78055f 100644 --- a/infer/src/base/CostIssues.ml +++ b/infer/src/base/CostIssues.ml @@ -10,8 +10,8 @@ type issue_spec = { extract_cost_f: Jsonbug_t.cost_item -> Jsonbug_t.cost_info ; name: string ; threshold: int option - ; complexity_increase_issue: is_on_cold_start:bool -> IssueType.t - ; expensive_issue: is_on_cold_start:bool -> IssueType.t + ; complexity_increase_issue: is_on_cold_start:bool -> is_on_ui_thread:bool -> IssueType.t + ; expensive_issue: is_on_cold_start:bool -> is_on_ui_thread:bool -> IssueType.t ; zero_issue: IssueType.t ; infinite_issue: IssueType.t ; top_and_bottom: bool } @@ -40,9 +40,11 @@ let enabled_cost_map = ; threshold= (if Config.use_cost_threshold then CostKind.to_threshold kind else None) ; extract_cost_f= (fun c -> CostKind.to_json_cost_info c kind) ; complexity_increase_issue= - (fun ~is_on_cold_start -> IssueType.complexity_increase ~kind ~is_on_cold_start) + (fun ~is_on_cold_start ~is_on_ui_thread -> + IssueType.complexity_increase ~kind ~is_on_cold_start ~is_on_ui_thread ) ; expensive_issue= - (fun ~is_on_cold_start -> IssueType.expensive_cost_call ~kind ~is_on_cold_start) + (fun ~is_on_cold_start ~is_on_ui_thread -> + IssueType.expensive_cost_call ~kind ~is_on_cold_start ~is_on_ui_thread ) ; zero_issue= IssueType.zero_cost_call ~kind ; infinite_issue= IssueType.infinite_cost_call ~kind ; top_and_bottom } diff --git a/infer/src/base/CostIssues.mli b/infer/src/base/CostIssues.mli index 83ddfcc64..ef35e062e 100644 --- a/infer/src/base/CostIssues.mli +++ b/infer/src/base/CostIssues.mli @@ -11,8 +11,8 @@ type issue_spec = { extract_cost_f: Jsonbug_t.cost_item -> Jsonbug_t.cost_info ; name: string ; threshold: int option - ; complexity_increase_issue: is_on_cold_start:bool -> IssueType.t - ; expensive_issue: is_on_cold_start:bool -> IssueType.t + ; complexity_increase_issue: is_on_cold_start:bool -> is_on_ui_thread:bool -> IssueType.t + ; expensive_issue: is_on_cold_start:bool -> is_on_ui_thread:bool -> IssueType.t ; zero_issue: IssueType.t ; infinite_issue: IssueType.t ; top_and_bottom: bool } diff --git a/infer/src/base/IssueType.ml b/infer/src/base/IssueType.ml index 6ec187012..843ce0d9d 100644 --- a/infer/src/base/IssueType.ml +++ b/infer/src/base/IssueType.ml @@ -25,6 +25,7 @@ module Unsafe : sig val register_from_cost_string : ?enabled:bool -> ?is_on_cold_start:bool + -> ?is_on_ui_thread:bool -> kind:CostKind.t -> (string -> string, Format.formatter, unit, string) format4 -> t @@ -86,11 +87,13 @@ end = struct (** cost issues are already registered below.*) - let register_from_cost_string ?(enabled = true) ?(is_on_cold_start = false) ~(kind : CostKind.t) - s = + let register_from_cost_string ?(enabled = true) ?(is_on_cold_start = false) + ?(is_on_ui_thread = false) ~(kind : CostKind.t) s = let issue_type_base = Format.asprintf s (CostKind.to_issue_string kind) in let issue_type = - if is_on_cold_start then issue_type_base ^ "_COLD_START" else issue_type_base + if is_on_ui_thread then issue_type_base ^ "_UI_THREAD" + else if is_on_cold_start then issue_type_base ^ "_COLD_START" + else issue_type_base in register_from_string ~enabled issue_type @@ -266,8 +269,8 @@ let eradicate_return_over_annotated = register_from_string "ERADICATE_RETURN_OVER_ANNOTATED" ~hum:"Return Over Annotated" -let expensive_cost_call ~kind ~is_on_cold_start = - register_from_cost_string ~enabled:false ~kind ~is_on_cold_start "EXPENSIVE_%s" +let expensive_cost_call ~kind ~is_on_cold_start ~is_on_ui_thread = + register_from_cost_string ~enabled:false ~kind ~is_on_cold_start ~is_on_ui_thread "EXPENSIVE_%s" let exposed_insecure_intent_handling = register_from_string "EXPOSED_INSECURE_INTENT_HANDLING" @@ -423,8 +426,8 @@ let tainted_memory_allocation = register_from_string "TAINTED_MEMORY_ALLOCATION" let thread_safety_violation = register_from_string "THREAD_SAFETY_VIOLATION" -let complexity_increase ~kind ~is_on_cold_start = - register_from_cost_string ~kind ~is_on_cold_start "%s_COMPLEXITY_INCREASE" +let complexity_increase ~kind ~is_on_cold_start ~is_on_ui_thread = + register_from_cost_string ~kind ~is_on_cold_start ~is_on_ui_thread "%s_COMPLEXITY_INCREASE" let topl_error = register_from_string "TOPL_ERROR" @@ -483,8 +486,9 @@ let zero_cost_call ~kind = register_from_cost_string ~enabled:false ~kind "ZERO_ let () = List.iter CostKind.enabled_cost_kinds ~f:(fun CostKind.{kind} -> List.iter [true; false] ~f:(fun is_on_cold_start -> - let _ = zero_cost_call ~kind in - let _ = expensive_cost_call ~kind ~is_on_cold_start in - let _ = infinite_cost_call ~kind in - let _ = complexity_increase ~kind ~is_on_cold_start in - () ) ) + List.iter [true; false] ~f:(fun is_on_ui_thread -> + let _ = zero_cost_call ~kind in + let _ = expensive_cost_call ~kind ~is_on_cold_start ~is_on_ui_thread in + let _ = infinite_cost_call ~kind in + let _ = complexity_increase ~kind ~is_on_cold_start ~is_on_ui_thread in + () ) ) ) diff --git a/infer/src/base/IssueType.mli b/infer/src/base/IssueType.mli index cdf30c895..6f84e4565 100644 --- a/infer/src/base/IssueType.mli +++ b/infer/src/base/IssueType.mli @@ -101,7 +101,7 @@ val codequery : t val comparing_floats_for_equality : t -val complexity_increase : kind:CostKind.t -> is_on_cold_start:bool -> t +val complexity_increase : kind:CostKind.t -> is_on_cold_start:bool -> is_on_ui_thread:bool -> t val component_factory_function : t @@ -162,7 +162,7 @@ val eradicate_return_not_nullable : t val eradicate_return_over_annotated : t -val expensive_cost_call : kind:CostKind.t -> is_on_cold_start:bool -> t +val expensive_cost_call : kind:CostKind.t -> is_on_cold_start:bool -> is_on_ui_thread:bool -> t val exposed_insecure_intent_handling : t diff --git a/infer/src/checkers/cost.ml b/infer/src/checkers/cost.ml index 71ad8e48f..0995d9c23 100644 --- a/infer/src/checkers/cost.ml +++ b/infer/src/checkers/cost.ml @@ -10,6 +10,8 @@ module F = Format module L = Logging module BasicCost = CostDomain.BasicCost +let attrs_of_pname = Summary.OnDisk.proc_resolve_attributes + module Payload = SummaryPayload.Make (struct type t = CostDomain.summary @@ -711,15 +713,15 @@ end module Check = struct let report_threshold proc_desc summary ~name ~location ~cost CostIssues.{expensive_issue} - ~threshold = + ~threshold ~is_on_ui_thread = + let pname = Procdesc.get_proc_name proc_desc in let report_issue_type = L.(debug Analysis Medium) - "@\n\n++++++ Checking error type for %a **** @\n" Typ.Procname.pp - (Procdesc.get_proc_name proc_desc) ; + "@\n\n++++++ Checking error type for %a **** @\n" Typ.Procname.pp pname ; let is_on_cold_start = ExternalPerfData.in_profiler_data_map (Procdesc.get_proc_name proc_desc) in - expensive_issue ~is_on_cold_start + expensive_issue ~is_on_cold_start ~is_on_ui_thread in let bigO_str = Format.asprintf ", %a" @@ -760,7 +762,7 @@ module Check = struct else if BasicCost.is_zero cost then report zero_issue "is zero" - let check_and_report WorstCaseCost.{costs; reports} proc_desc summary = + let check_and_report ~is_on_ui_thread WorstCaseCost.{costs; reports} proc_desc summary = let pname = Procdesc.get_proc_name proc_desc in if not (Typ.Procname.is_java_access_method pname) then ( CostIssues.CostKindMap.iter2 CostIssues.enabled_cost_map reports @@ -769,7 +771,7 @@ module Check = struct () | ThresholdReports.ReportOn {location; cost} -> report_threshold proc_desc summary ~name ~location ~cost kind_spec - ~threshold:(Option.value_exn threshold) ) ; + ~threshold:(Option.value_exn threshold) ~is_on_ui_thread ) ; CostIssues.CostKindMap.iter2 CostIssues.enabled_cost_map costs ~f:(fun _kind (CostIssues.{name; top_and_bottom} as issue_spec) cost -> if top_and_bottom then report_top_and_bottom proc_desc summary ~name ~cost issue_spec ) ) @@ -811,9 +813,13 @@ let compute_worst_case_cost tenv integer_type_widths get_callee_summary_and_form WorstCaseCost.compute tenv extras instr_cfg_wto -let get_cost_summary astate = CostDomain.{post= astate.WorstCaseCost.costs} +let get_cost_summary ~is_on_ui_thread astate = + CostDomain.{post= astate.WorstCaseCost.costs; is_on_ui_thread} + + +let report_errors ~is_on_ui_thread proc_desc astate summary = + Check.check_and_report ~is_on_ui_thread astate proc_desc summary -let report_errors proc_desc astate summary = Check.check_and_report astate proc_desc summary let checker {Callbacks.exe_env; summary} : Summary.t = let proc_name = Summary.get_proc_name summary in @@ -846,6 +852,7 @@ let checker {Callbacks.exe_env; summary} : Summary.t = let bound_map = compute_bound_map node_cfg inferbo_invariant_map control_dep_invariant_map loop_inv_map in + let is_on_ui_thread = ConcurrencyModels.runs_on_ui_thread ~attrs_of_pname tenv proc_name in let get_node_nb_exec = compute_get_node_nb_exec node_cfg bound_map in let astate = let get_callee_summary_and_formals callee_pname = @@ -867,5 +874,5 @@ let checker {Callbacks.exe_env; summary} : Summary.t = (Container.length ~fold:NodeCFG.fold_nodes node_cfg) CostDomain.VariantCostMap.pp exit_cost_record in - report_errors proc_desc astate summary ; - Payload.update_summary (get_cost_summary astate) summary + report_errors ~is_on_ui_thread proc_desc astate summary ; + Payload.update_summary (get_cost_summary ~is_on_ui_thread astate) summary diff --git a/infer/src/checkers/costDomain.ml b/infer/src/checkers/costDomain.ml index 43ff1dfbf..de686962b 100644 --- a/infer/src/checkers/costDomain.ml +++ b/infer/src/checkers/costDomain.ml @@ -40,7 +40,7 @@ end type t = VariantCostMap.t -type summary = {post: t} +type summary = {post: t; is_on_ui_thread: bool} let pp_summary fmt {post} = F.fprintf fmt "@\n Post: %a @\n" VariantCostMap.pp post diff --git a/infer/tests/build_systems/differential_of_costs_report/Makefile b/infer/tests/build_systems/differential_of_costs_report/Makefile index 0dedb01df..ecd59c5db 100644 --- a/infer/tests/build_systems/differential_of_costs_report/Makefile +++ b/infer/tests/build_systems/differential_of_costs_report/Makefile @@ -6,9 +6,10 @@ # E2E test for differential of costs TESTS_DIR = ../.. -SOURCES = src/DiffExample.java.current src/DiffExample.java.previous src/DiffExampleColdStart.java.current src/DiffExampleColdStart.java.previous +SOURCES = src/DiffExample.java.current src/DiffExample.java.previous src/DiffExampleColdStart.java.current src/DiffExampleColdStart.java.previous src/DiffExampleUIThread.java.current src/DiffExampleUIThread.java.previous CLEAN_EXTRA = src/Diff*.java *.class include $(TESTS_DIR)/differential.make +include $(TESTS_DIR)/java.make INFERPRINT_ISSUES_FIELDS = \ "bug_type,bucket,file,procedure,line_offset,bug_trace" @@ -17,11 +18,13 @@ $(CURRENT_REPORT) $(PREVIOUS_REPORT): $(JAVA_DEPS) $(CURRENT_REPORT): $(QUIET)$(COPY) src/DiffExample.java.current src/DiffExample.java $(QUIET)$(COPY) src/DiffExampleColdStart.java.current src/DiffExampleColdStart.java + $(QUIET)$(COPY) src/DiffExampleUIThread.java.current src/DiffExampleUIThread.java $(QUIET)$(call silent_on_success,Testing Cost Differential: current,\ - $(INFER_BIN) --enable-issue-type INFINITE_EXECUTION_TIME --cost-only -o $(CURRENT_DIR) -- $(JAVAC) src/*.java) + $(INFER_BIN) --enable-issue-type INFINITE_EXECUTION_TIME --cost-only -o $(CURRENT_DIR) -- $(JAVAC) -cp $(CLASSPATH) src/*.java) $(PREVIOUS_REPORT): $(QUIET)$(COPY) src/DiffExample.java.previous src/DiffExample.java $(QUIET)$(COPY) src/DiffExampleColdStart.java.previous src/DiffExampleColdStart.java + $(QUIET)$(COPY) src/DiffExampleUIThread.java.previous src/DiffExampleUIThread.java $(QUIET)$(call silent_on_success,Testing Cost Differential: previous,\ - $(INFER_BIN) --debug --enable-issue-type INFINITE_EXECUTION_TIME --cost-only -o $(PREVIOUS_DIR) -- $(JAVAC) src/*.java) + $(INFER_BIN) --debug --enable-issue-type INFINITE_EXECUTION_TIME --cost-only -o $(PREVIOUS_DIR) -- $(JAVAC) -cp $(CLASSPATH) src/*.java) diff --git a/infer/tests/build_systems/differential_of_costs_report/costs_summary.json.exp b/infer/tests/build_systems/differential_of_costs_report/costs_summary.json.exp index 677a4c779..d5e759de7 100644 --- a/infer/tests/build_systems/differential_of_costs_report/costs_summary.json.exp +++ b/infer/tests/build_systems/differential_of_costs_report/costs_summary.json.exp @@ -1 +1 @@ -{"top":{"current":4,"previous":2},"zero":{"current":17,"previous":15},"degrees":[{"degree":0,"current":8,"previous":7},{"degree":100,"current":3,"previous":4},{"degree":101,"current":4,"previous":0},{"degree":200,"current":2,"previous":4}]} \ No newline at end of file +{"top":{"current":4,"previous":2},"zero":{"current":21,"previous":19},"degrees":[{"degree":0,"current":11,"previous":10},{"degree":100,"current":4,"previous":5},{"degree":101,"current":4,"previous":0},{"degree":200,"current":2,"previous":4}]} \ No newline at end of file diff --git a/infer/tests/build_systems/differential_of_costs_report/fixed.exp b/infer/tests/build_systems/differential_of_costs_report/fixed.exp index 0815f119b..53f0874d8 100644 --- a/infer/tests/build_systems/differential_of_costs_report/fixed.exp +++ b/infer/tests/build_systems/differential_of_costs_report/fixed.exp @@ -1,2 +1,3 @@ EXECUTION_TIME_COMPLEXITY_INCREASE, no_bucket, src/DiffExample.java, DiffExample.f6(java.util.ArrayList):void, 0, [Updated Cost is 5 + list.length × log(list.length) (degree is 1 + 1⋅log),{list.length},call to void DiffExample.f5(ArrayList),Modeled call to Collections.sort,{list.length},call to void DiffExample.f5(ArrayList),Modeled call to Collections.sort] EXECUTION_TIME_COMPLEXITY_INCREASE_COLD_START, no_bucket, src/DiffExampleColdStart.java, DiffExampleColdStart.f6(java.util.ArrayList):void, 0, [Updated Cost is 5 + list.length × log(list.length) (degree is 1 + 1⋅log),{list.length},call to void DiffExampleColdStart.f5(ArrayList),Modeled call to Collections.sort,{list.length},call to void DiffExampleColdStart.f5(ArrayList),Modeled call to Collections.sort] +EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD, no_bucket, src/DiffExampleUIThread.java, DiffExampleUIThread.f1(int):void, 0, [Updated Cost is 3 (degree is 0)] diff --git a/infer/tests/build_systems/differential_of_costs_report/introduced.exp b/infer/tests/build_systems/differential_of_costs_report/introduced.exp index 340976c51..ab8deeca8 100644 --- a/infer/tests/build_systems/differential_of_costs_report/introduced.exp +++ b/infer/tests/build_systems/differential_of_costs_report/introduced.exp @@ -6,3 +6,4 @@ INFINITE_EXECUTION_TIME, no_bucket, src/DiffExampleColdStart.java, DiffExampleCo ALLOCATION_COMPLEXITY_INCREASE_COLD_START, no_bucket, src/DiffExampleColdStart.java, DiffExampleColdStart.f4(int):int, 0, [Updated Cost is k (degree is 1),{k},Loop at line 45] EXECUTION_TIME_COMPLEXITY_INCREASE_COLD_START, no_bucket, src/DiffExampleColdStart.java, DiffExampleColdStart.f4(int):int, 0, [Updated Cost is 6 + 7 ⋅ k (degree is 1),{k},Loop at line 45] EXECUTION_TIME_COMPLEXITY_INCREASE, no_bucket, src/DiffExampleColdStart.java, DiffExampleColdStart.f5(java.util.ArrayList):void, 0, [Updated Cost is 2 + list.length × log(list.length) (degree is 1 + 1⋅log),{list.length},Modeled call to Collections.sort,{list.length},Modeled call to Collections.sort] +EXECUTION_TIME_COMPLEXITY_INCREASE_UI_THREAD, no_bucket, src/DiffExampleUIThread.java, DiffExampleUIThread.f2(int):void, 0, [Updated Cost is 5 + 5 ⋅ x (degree is 1),{x},Loop at line 27] diff --git a/infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.current b/infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.current new file mode 100644 index 000000000..f89e6756e --- /dev/null +++ b/infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.current @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import android.support.annotation.UiThread; + +// This class has the following costs: +// 3 constant, 1 linear +// constructor: constant +// f1: constant +// f2: linear +// f3: constant + +public class DiffExampleUIThread { + + @UiThread + public void f1(int x) { + x++; + } + + + @UiThread + public void f2(int x) { + for (int i = 0; i < x; i++) {} + } + + + @UiThread + public void f3(int x) { + f1(x); +} + +} \ No newline at end of file diff --git a/infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.previous b/infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.previous new file mode 100644 index 000000000..05e642909 --- /dev/null +++ b/infer/tests/build_systems/differential_of_costs_report/src/DiffExampleUIThread.java.previous @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import android.support.annotation.UiThread; + +// This class has the following costs: +// 3 constant, 1 linear +// constructor: constant +// f1: linear +// f2: constant +// f3: constant + +public class DiffExampleUIThread { + + @UiThread + public void f1(int x) { + for (int i = 0; i < x; i++) {} + } + + + @UiThread + public int f2(int x) { + return x+1; + } + + + @UiThread + public int f3(int x) { + return f2(x); +} + +} \ No newline at end of file diff --git a/infer/tests/codetoanalyze/java/performance/UIAnnotationTest.java b/infer/tests/codetoanalyze/java/performance/UIAnnotationTest.java new file mode 100644 index 000000000..1b4f66305 --- /dev/null +++ b/infer/tests/codetoanalyze/java/performance/UIAnnotationTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package codetoanalyze.java.checkers; + +import android.support.annotation.UiThread; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +@interface OnBind {} + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +@interface OnEvent {} + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +@interface OnMount {} + +@UiThread +class AllMethodsOnUiThread { + int f; + + void foo_UIThread_constant() { + f = 0; + } + + int bar_UIThread_linear() { + for (int i = 0; i < f; i++) { + foo_UIThread_constant(); + } + return f; + } +} + +class ExtendsClassOnUiThread extends AllMethodsOnUiThread { + @Override + void foo_UIThread_constant() { + f = 9; + } + + @Override + int bar_UIThread_linear() { + return super.bar_UIThread_linear(); + } +} + +class UIAnnotationTest { + + // NOT All annotations that start with "On" are on the main thread + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.CLASS) + @interface OnXYZ {} + + class WeirdAnnotation { + int f; + + @OnXYZ + void foo_linear() { + for (int i = 0; i < f; i++) {} + } + } + + class Annotations { + + @UiThread + public void loop_UIThread_linear(int x) { + for (int i = 0; i < x; i++) {} + } + + public void constant() { + // not on UI thread + } + + public void loop_linear(int x) { + for (int i = 0; i < x; i++) {} + } + + // anything annotated with OnEvent is modeled as running on the UIThread + @OnEvent + public void onClick_linear(int x) { + for (int i = 0; i < x; i++) {} + } + + @OnBind + public void onBindMethod_linear(int x) { + loop_linear(x); + } + } +} diff --git a/infer/tests/codetoanalyze/java/performance/issues.exp b/infer/tests/codetoanalyze/java/performance/issues.exp index 5f07481cc..852aab56d 100644 --- a/infer/tests/codetoanalyze/java/performance/issues.exp +++ b/infer/tests/codetoanalyze/java/performance/issues.exp @@ -194,6 +194,13 @@ codetoanalyze/java/performance/StringTest.java, StringTest.indexof_quadratic(jav codetoanalyze/java/performance/Switch.java, codetoanalyze.java.performance.Switch.test_switch():int, 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] codetoanalyze/java/performance/Switch.java, codetoanalyze.java.performance.Switch.test_switch():int, 3, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 798, O(1), degree = 0] codetoanalyze/java/performance/Switch.java, codetoanalyze.java.performance.Switch.vanilla_switch(int):void, 2, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/java/performance/UIAnnotationTest.java, codetoanalyze.java.checkers.AllMethodsOnUiThread.bar_UIThread_linear():int, 1, EXPENSIVE_EXECUTION_TIME_UI_THREAD, no_bucket, ERROR, [with estimated cost 2 + 10 ⋅ (max(0, this.f)), O(this.f), degree = 1,{max(0, this.f)},Loop at line 37] +codetoanalyze/java/performance/UIAnnotationTest.java, codetoanalyze.java.checkers.ExtendsClassOnUiThread.bar_UIThread_linear():int, 0, EXPENSIVE_EXECUTION_TIME_UI_THREAD, no_bucket, ERROR, [with estimated cost 11 + 10 ⋅ (max(0, this.f)), O(this.f), degree = 1,{max(0, this.f)},call to int AllMethodsOnUiThread.bar_UIThread_linear(),Loop at line 37] +codetoanalyze/java/performance/UIAnnotationTest.java, codetoanalyze.java.checkers.UIAnnotationTest$Annotations.loop_UIThread_linear(int):void, 1, EXPENSIVE_EXECUTION_TIME_UI_THREAD, no_bucket, ERROR, [with estimated cost 2 + 5 ⋅ x, O(x), degree = 1,{x},Loop at line 76] +codetoanalyze/java/performance/UIAnnotationTest.java, codetoanalyze.java.checkers.UIAnnotationTest$Annotations.loop_linear(int):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 2 + 5 ⋅ x, O(x), degree = 1,{x},Loop at line 84] +codetoanalyze/java/performance/UIAnnotationTest.java, codetoanalyze.java.checkers.UIAnnotationTest$Annotations.onBindMethod_linear(int):void, 1, EXPENSIVE_EXECUTION_TIME_UI_THREAD, no_bucket, ERROR, [with estimated cost 8 + 5 ⋅ x, O(x), degree = 1,{x},call to void UIAnnotationTest$Annotations.loop_linear(int),Loop at line 84] +codetoanalyze/java/performance/UIAnnotationTest.java, codetoanalyze.java.checkers.UIAnnotationTest$Annotations.onClick_linear(int):void, 1, EXPENSIVE_EXECUTION_TIME_UI_THREAD, no_bucket, ERROR, [with estimated cost 2 + 5 ⋅ x, O(x), degree = 1,{x},Loop at line 90] +codetoanalyze/java/performance/UIAnnotationTest.java, codetoanalyze.java.checkers.UIAnnotationTest$WeirdAnnotation.foo_linear():void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 2 + 6 ⋅ this.f, O(this.f), degree = 1,{this.f},Loop at line 68] codetoanalyze/java/performance/UnknownCallsTest.java, UnknownCallsTest.call_concrete_func_linear(UnknownCallsTest$AbstractC):void, 2, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 5 + 6 ⋅ UnknownCallsTest$AbstractC.abstract_func().length.ub, O(UnknownCallsTest$AbstractC.abstract_func().length.ub), degree = 1,{UnknownCallsTest$AbstractC.abstract_func().length.ub},Loop at line 94] codetoanalyze/java/performance/UnknownCallsTest.java, UnknownCallsTest.call_loop_over_charArray(java.lang.StringBuilder,java.lang.String):void, 1, EXPENSIVE_EXECUTION_TIME, no_bucket, ERROR, [with estimated cost 14 + 12 ⋅ String.toCharArray().length.ub, O(String.toCharArray().length.ub), degree = 1,{String.toCharArray().length.ub},call to void UnknownCallsTest.loop_over_charArray(StringBuilder,String),Loop at line 51] codetoanalyze/java/performance/UnknownCallsTest.java, UnknownCallsTest.call_may_throw_exception_constant():void, 1, EXPENSIVE_ALLOCATION, no_bucket, ERROR, [with estimated cost 11, O(1), degree = 0]