[cost] log top cost function

Summary:
Following from previous diff.

**Idea** -  80% of functions with Top cost are caused by calling top-costed callees, i.e. callee's Top cost is simply propagated to its transitive callers, so the aim is to investigate such root callees along with the number of their transitive callers.

Consider the following code

```
void bar1() {
   // top cost function
}

void bar2() {
  // another top cost function
}

void baz(){
  // baz have top cost because of bar
   bar1();
}

void foo() {
  // goo have top cost because of baz
   baz();
   bar2()
}

```
Clearly, the root cause of the foo being top cost is `bar1` and `bar2`.
1. When we are analyzing `baz`, we know that it calls `bar1`, which is top cost, so we record that `baz = { T, bar1 } `.
2. Now, say we are analyzing foo.
 When we analyze the call to `baz`, we found out that the top cost of `baz` is caused by `bar1`, so we record `foo = { T, bar1 }`.
When we analyze the call to `bar2`, we know that `bar2` is top cost, but since at this stage we only want to deal with the first top cost function we met, so we ignore it.

Since we are keeping track of top cost function by examining the `Call` instruction, we would expect to see two log of `bar1` in the result. The test plan confirms it.

Reviewed By: ezgicicek

Differential Revision: D22231457

fbshipit-source-id: 45d48e4a7
master
Qianyi Shu 5 years ago committed by Facebook GitHub Bot
parent ba2bad25aa
commit bfe2caf92d

@ -84,10 +84,18 @@ module InstrBasicCostWithReason = struct
CostDomain.of_operation_cost (model model_env ~ret inferbo_mem)
| None -> (
match Tenv.get_summary_formals tenv ~get_summary ~get_formals callee_pname with
| `Found ({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 )
| `Found ({CostDomain.post= callee_cost_record}, callee_formals) -> (
let instantiated_cost =
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 )
in
match CostDomain.get_operation_cost callee_cost_record with
| {cost; top_pname_opt= None} when BasicCost.is_top cost ->
CostDomain.add_top_pname_opt CostKind.OperationCost instantiated_cost
(Some callee_pname)
| _ ->
instantiated_cost )
| `FoundFromSubclass
(callee_pname, {CostDomain.post= callee_cost_record}, callee_formals) ->
(* Note: It ignores top cost of subclass to avoid its propagations. *)
@ -178,8 +186,16 @@ module WorstCaseCost = struct
let compute tenv extras cfg =
let init = CostDomain.zero_record in
InstrCFG.fold_nodes cfg ~init ~f:(fun acc pair ->
exec_node tenv extras pair |> CostDomain.plus acc )
let cost =
InstrCFG.fold_nodes cfg ~init ~f:(fun acc pair ->
exec_node tenv extras pair |> CostDomain.plus acc )
in
Option.iter (CostDomain.get_operation_cost cost).top_pname_opt ~f:(fun top_pname ->
ScubaLogging.log_message ~label:"unmodeled_function_top_cost"
~message:(F.asprintf "Unmodeled Function[Top Cost] : %a" Procname.pp top_pname) ;
Logging.(debug Analysis Verbose)
"@ Unmodeled Function[Top Cost]: %a@\n" Procname.pp top_pname ) ;
cost
end
let is_report_suppressed pname =

@ -31,6 +31,8 @@ module BasicCostWithReason = struct
{record with cost= BasicCost.subst callee_pname location record.cost eval_sym}
(* When we fold the nodes while traversing the cfg,
make sure we only keep the first top cost callee we see *)
let plus record1 record2 =
{ cost= BasicCost.plus record1.cost record2.cost
; top_pname_opt= Option.first_some record1.top_pname_opt record2.top_pname_opt }
@ -78,6 +80,12 @@ let pp_summary fmt {post} = F.fprintf fmt "@\n Post: %a @\n" VariantCostMap.pp p
let get_cost_kind kind cost_record = VariantCostMap.get kind cost_record
let add_top_pname_opt kind cost_record top_pname_opt =
VariantCostMap.update kind
(function Some cost_with_reason -> Some {cost_with_reason with top_pname_opt} | _ -> None)
cost_record
let get_operation_cost cost_record = get_cost_kind CostKind.OperationCost cost_record
let map ~f cost_record = VariantCostMap.map f cost_record

@ -58,6 +58,8 @@ val pp_summary : F.formatter -> summary -> unit
val get_cost_kind : CostKind.t -> t -> BasicCostWithReason.t
val add_top_pname_opt : CostKind.t -> t -> Procname.t option -> t
val get_operation_cost : t -> BasicCostWithReason.t
val map : f:(BasicCostWithReason.t -> BasicCostWithReason.t) -> t -> t

Loading…
Cancel
Save