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
master
jrm 9 years ago committed by facebook-github-bot-5
parent 78ffffdeb9
commit a7371d5678

@ -10,17 +10,18 @@
module F = Format module F = Format
type call = Procname.t * Location.t
type t = type t =
| Direct of Procname.t | Direct of call
| Indirect of Procname.t * t list | Indirect of call * t list
let pp fmt tree = let pp fmt tree =
let rec loop stack = function let rec loop stack = function
| Direct pname -> | Direct (pname, _) ->
F.fprintf fmt "%s -> %s" stack (Procname.to_string 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 let stack' = stack ^ " -> " ^ (Procname.to_string pname) in
IList.iter (loop stack') l in IList.iter (loop stack') l in
loop "@" tree loop "@" tree

@ -7,10 +7,12 @@
* of patent rights can be found in the PATENTS file in the same directory. * 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 *) (** data-structure to represent call stacks in a compact tree form *)
type t = type t =
| Direct of Procname.t | Direct of call
| Indirect of Procname.t * t list | Indirect of call * t list
(** print the list of call stacks in the tree *) (** print the list of call stacks in the tree *)
val pp : Format.formatter -> t -> unit val pp : Format.formatter -> t -> unit

@ -102,21 +102,27 @@ let lookup_call_trees pname =
end 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, _) = let collect_expensive_call tenv caller_pdesc checked_pnames call_trees (pname, _) =
if Procname.Set.mem pname !checked_pnames then call_trees if Procname.Set.mem pname !checked_pnames then call_trees
else else
begin begin
Ondemand.do_analysis caller_pdesc pname; Ondemand.do_analysis caller_pdesc pname;
checked_pnames := Procname.Set.add pname !checked_pnames; checked_pnames := Procname.Set.add pname !checked_pnames;
let call_loc = lookup_location pname in
if method_is_expensive tenv pname then if method_is_expensive tenv pname then
(CallTree.Direct pname) :: call_trees (CallTree.Direct (pname, call_loc)) :: call_trees
else else
match lookup_call_trees pname with match lookup_call_trees pname with
| [] -> call_trees | [] -> call_trees
| calls -> (CallTree.Indirect (pname, calls)) :: call_trees | calls -> (CallTree.Indirect ((pname, call_loc), calls)) :: call_trees
end end
let update_summary call_trees pname = let update_summary call_trees pname =
match Specs.get_summary pname with match Specs.get_summary pname with
| None -> () | None -> ()
@ -132,9 +138,20 @@ let update_summary call_trees pname =
let report_expensive_calls pname pdesc loc call_trees = let report_expensive_calls pname pdesc loc call_trees =
let string_of_pname = Procname.to_simplified_string ~withclass:true in 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 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 exp_pname_str = string_of_pname expensive_pname in
let description = let description =
Printf.sprintf Printf.sprintf
@ -145,13 +162,16 @@ let report_expensive_calls pname pdesc loc call_trees =
exp_pname_str exp_pname_str
exp_pname_str exp_pname_str
Annotations.expensive in Annotations.expensive in
Checkers.ST.report_error let exn =
pname pdesc calls_expensive_method loc description Exceptions.Checkers (calls_expensive_method, Localise.verbatim_desc description) in
| CallTree.Indirect (callee_pname, sub_trees) -> 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 callee_pname_str = string_of_pname callee_pname in
let new_stack_str = stack_str ^ callee_pname_str ^ " -> " in let new_stack_str = stack_str ^ callee_pname_str ^ " -> " in
IList.iter (report_call_tree new_stack_str) sub_trees in let new_trace = update_trace trace callee_loc in
IList.iter (report_call_tree "") call_trees 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 = let check_one_procedure tenv pname pdesc =

Loading…
Cancel
Save