diff --git a/infer/src/checkers/cost.ml b/infer/src/checkers/cost.ml index 1104fd746..fb408353a 100644 --- a/infer/src/checkers/cost.ml +++ b/infer/src/checkers/cost.ml @@ -28,7 +28,10 @@ module ReportConfig = struct , { name= "The execution time" ; threshold= Option.some_if Config.use_cost_threshold 200 ; top_and_bottom= true } ) - ; (CostDomain.AllocationCost, {name= "The allocations"; threshold= None; top_and_bottom= false}) + ; ( CostDomain.AllocationCost + , { name= "The allocations" + ; threshold= Option.some_if Config.use_cost_threshold 3 + ; top_and_bottom= false } ) ; (CostDomain.IOCost, {name= "The IOs"; threshold= None; top_and_bottom= false}) ] @@ -551,30 +554,41 @@ module InstrBasicCost = struct For example for basic operation we set it to 1 and for function call we take it from the spec of the function. *) + let allocation_functions = [BuiltinDecl.__new] + + let is_allocation_function callee_pname = + List.exists allocation_functions ~f:(fun f -> Typ.Procname.equal callee_pname f) + + let get_instr_cost_record extras instr_node instr = match instr with - | Sil.Call (_, Exp.Const (Const.Cfun callee_pname), params, _, _) -> ( + | Sil.Call (_, Exp.Const (Const.Cfun callee_pname), params, _, _) -> let {inferbo_invariant_map; integer_type_widths; get_callee_summary_and_formals} = extras in - match - BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map - with - | None -> - CostDomain.unit_cost_atomic_operation - | Some inferbo_mem -> ( - let loc = InstrCFG.Node.loc instr_node in - match CostModels.Call.dispatch () callee_pname params with - | Some model -> - CostDomain.of_operation_cost (model loc inferbo_mem) - | None -> ( - match get_callee_summary_and_formals callee_pname with - | Some ({CostDomain.post= callee_cost_record}, callee_formals) -> - 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 ) - | None -> - CostDomain.unit_cost_atomic_operation ) ) ) + let operation_cost = + match + BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map + with + | None -> + CostDomain.unit_cost_atomic_operation + | Some inferbo_mem -> ( + let loc = InstrCFG.Node.loc instr_node in + match CostModels.Call.dispatch () callee_pname params with + | Some model -> + CostDomain.of_operation_cost (model loc inferbo_mem) + | None -> ( + match get_callee_summary_and_formals callee_pname with + | Some ({CostDomain.post= callee_cost_record}, callee_formals) -> + 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 ) + | None -> + CostDomain.unit_cost_atomic_operation ) ) + in + if is_allocation_function callee_pname then + CostDomain.plus CostDomain.unit_cost_allocation operation_cost + else operation_cost | Sil.Load _ | Sil.Store _ | Sil.Call _ | Sil.Prune _ -> CostDomain.unit_cost_atomic_operation | Sil.ExitScope _ -> ( diff --git a/infer/src/checkers/costDomain.ml b/infer/src/checkers/costDomain.ml index dcc181849..7a5ec90e9 100644 --- a/infer/src/checkers/costDomain.ml +++ b/infer/src/checkers/costDomain.ml @@ -91,6 +91,9 @@ let plus cost_record1 cost_record2 = (* Map representing cost record {OperationCost:1; AllocationCost:0; IOCost:0} *) let unit_cost_atomic_operation = VariantCostMap.increment OperationCost zero_record +(* Map representing cost record {OperationCost:0; AllocationCost:1; IOCost:0} *) +let unit_cost_allocation = VariantCostMap.increment AllocationCost zero_record + (* Map representing cost record {OperationCost:operation_cost; AllocationCost:0; IOCost:0} *) let of_operation_cost operation_cost = VariantCostMap.increase_by OperationCost operation_cost zero_record diff --git a/infer/tests/codetoanalyze/java/performance/A.java b/infer/tests/codetoanalyze/java/performance/A.java new file mode 100644 index 000000000..f41fe39dc --- /dev/null +++ b/infer/tests/codetoanalyze/java/performance/A.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2019-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +public class A {} + +class B { + + void error() { + A a1 = new A(); + A a2 = new A(); + A a3 = new A(); + A a4 = new A(); + } + + void ok() { + A a1 = new A(); + } +} diff --git a/infer/tests/codetoanalyze/java/performance/issues.exp b/infer/tests/codetoanalyze/java/performance/issues.exp index 04ae03885..66a5daa7b 100644 --- a/infer/tests/codetoanalyze/java/performance/issues.exp +++ b/infer/tests/codetoanalyze/java/performance/issues.exp @@ -1,3 +1,4 @@ +codetoanalyze/java/performance/A.java, B.error():void, 4, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 4, degree = 0] codetoanalyze/java/performance/Array.java, codetoanalyze.java.performance.Array.array_access_overrun_bad():void, 4, BUFFER_OVERRUN_L2, no_bucket, ERROR, [,Assignment,,Array declaration,Assignment,Array access: Offset: [2, 8] Size: 8] codetoanalyze/java/performance/Array.java, codetoanalyze.java.performance.Array.array_access_weird_ok(long[],int):void, 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 2 + 15 ⋅ length, degree = 1,{length},Loop at line 28] codetoanalyze/java/performance/ArrayCost.java, ArrayCost.ArrayCost(int[]):void, 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 6 + 5 ⋅ mag.length, degree = 1,{mag.length},Loop at line 15]