From a7371d5678c9450db7b2cc7b74e0be7bae7b38ca Mon Sep 17 00:00:00 2001 From: jrm Date: Mon, 18 Jan 2016 07:46:24 -0800 Subject: [PATCH] Perf critical checker: save the chains of calls to expensive methods in the error traces Summary: public Use Infer traces to follow the chain of calls from methods annotated PerformanceCritical to the methods annotated as Expensive. This contains direct jumps from method definition to method definition. So the traces are of the form: method definition of m1 -> definition of m2 -> definition of m3 -> ... -> definition of mN annotated as Expensive In the next diff, I will make the checker create traces of the form: definition of m1 -> call to m2 -> definition of m2 -> call to m3 -> ... -> call to mN -> definition of mN annotated as Expensive and then simplify the error message whenever the chain of call is longer than, say, 5 calls. Reviewed By: jberdine Differential Revision: D2818398 fb-gh-sync-id: c566a44 --- infer/src/backend/callTree.ml | 9 ++--- infer/src/backend/callTree.mli | 6 ++-- infer/src/checkers/performanceCritical.ml | 40 +++++++++++++++++------ 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/infer/src/backend/callTree.ml b/infer/src/backend/callTree.ml index 2c9b1e4e1..94d3c110d 100644 --- a/infer/src/backend/callTree.ml +++ b/infer/src/backend/callTree.ml @@ -10,17 +10,18 @@ module F = Format +type call = Procname.t * Location.t type t = - | Direct of Procname.t - | Indirect of Procname.t * t list + | Direct of call + | Indirect of call * t list let pp fmt tree = let rec loop stack = function - | Direct pname -> + | Direct (pname, _) -> F.fprintf fmt "%s -> %s" stack (Procname.to_string pname) - | Indirect (pname, l) -> + | Indirect ((pname, _), l) -> let stack' = stack ^ " -> " ^ (Procname.to_string pname) in IList.iter (loop stack') l in loop "@" tree diff --git a/infer/src/backend/callTree.mli b/infer/src/backend/callTree.mli index da91fdb87..611865f4f 100644 --- a/infer/src/backend/callTree.mli +++ b/infer/src/backend/callTree.mli @@ -7,10 +7,12 @@ * of patent rights can be found in the PATENTS file in the same directory. *) +type call = Procname.t * Location.t + (** data-structure to represent call stacks in a compact tree form *) type t = - | Direct of Procname.t - | Indirect of Procname.t * t list + | Direct of call + | Indirect of call * t list (** print the list of call stacks in the tree *) val pp : Format.formatter -> t -> unit diff --git a/infer/src/checkers/performanceCritical.ml b/infer/src/checkers/performanceCritical.ml index 88d43f4dc..343898075 100644 --- a/infer/src/checkers/performanceCritical.ml +++ b/infer/src/checkers/performanceCritical.ml @@ -102,21 +102,27 @@ let lookup_call_trees pname = end +let lookup_location pname = + match Specs.get_summary pname with + | None -> Location.dummy + | Some summary -> summary.Specs.attributes.ProcAttributes.loc + + let collect_expensive_call tenv caller_pdesc checked_pnames call_trees (pname, _) = if Procname.Set.mem pname !checked_pnames then call_trees else begin Ondemand.do_analysis caller_pdesc pname; checked_pnames := Procname.Set.add pname !checked_pnames; + let call_loc = lookup_location pname in if method_is_expensive tenv pname then - (CallTree.Direct pname) :: call_trees + (CallTree.Direct (pname, call_loc)) :: call_trees else match lookup_call_trees pname with | [] -> call_trees - | calls -> (CallTree.Indirect (pname, calls)) :: call_trees + | calls -> (CallTree.Indirect ((pname, call_loc), calls)) :: call_trees end - let update_summary call_trees pname = match Specs.get_summary pname with | None -> () @@ -132,9 +138,20 @@ let update_summary call_trees pname = let report_expensive_calls pname pdesc loc call_trees = let string_of_pname = Procname.to_simplified_string ~withclass:true in - let rec report_call_tree stack_str call_tree = + let update_trace trace loc = + if Location.equal loc Location.dummy then trace + else + let trace_elem = { + Errlog.lt_level = 0; + lt_loc = loc; + lt_description = ""; + lt_node_tags = []; + } in + trace_elem :: trace in + let rec report_call_tree (trace, stack_str) call_tree = match call_tree with - | CallTree.Direct expensive_pname -> + | CallTree.Direct (expensive_pname, callee_loc) -> + let final_trace = IList.rev (update_trace trace callee_loc) in let exp_pname_str = string_of_pname expensive_pname in let description = Printf.sprintf @@ -145,13 +162,16 @@ let report_expensive_calls pname pdesc loc call_trees = exp_pname_str exp_pname_str Annotations.expensive in - Checkers.ST.report_error - pname pdesc calls_expensive_method loc description - | CallTree.Indirect (callee_pname, sub_trees) -> + let exn = + Exceptions.Checkers (calls_expensive_method, Localise.verbatim_desc description) in + Reporting.log_error pname ~loc: (Some loc) ~ltr: (Some final_trace) exn + | CallTree.Indirect ((callee_pname, callee_loc), sub_trees) -> let callee_pname_str = string_of_pname callee_pname in let new_stack_str = stack_str ^ callee_pname_str ^ " -> " in - IList.iter (report_call_tree new_stack_str) sub_trees in - IList.iter (report_call_tree "") call_trees + let new_trace = update_trace trace callee_loc in + IList.iter (report_call_tree (new_trace, new_stack_str)) sub_trees in + let start_trace = update_trace [] (Cfg.Procdesc.get_loc pdesc) in + IList.iter (report_call_tree (start_trace, "")) call_trees let check_one_procedure tenv pname pdesc =