Skip to content

Commit

Permalink
Stop building higher order call graph when reaching a max number of i…
Browse files Browse the repository at this point in the history
…terations

Summary:
This would help the higher order call graph building algorithm to terminate,
when there are too many parameterized targets that we need to build higher
order call graphs for.

Currently, we raise an exception when reaching a max number of iterations.

Reviewed By: arthaud

Differential Revision: D66312154

fbshipit-source-id: a2dd3701848bec485e61f4b629b86f1d2c5002bc
  • Loading branch information
Tianhan Lu authored and facebook-github-bot committed Nov 22, 2024
1 parent 7bd0813 commit f4a9af8
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 11 deletions.
1 change: 1 addition & 0 deletions source/interprocedural/callGraphFixpoint.ml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ let compute
}
~callables_to_analyze
~max_iterations
~error_on_max_iterations:false
~epoch:Fixpoint.Epoch.initial
~shared_models

Expand Down
40 changes: 31 additions & 9 deletions source/interprocedural/fixpointAnalysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ module type LOGGER = sig
callables_to_analyze:Target.t list ->
exn

val reached_maximum_iteration_exit : iteration:int -> callables_to_analyze:Target.t list -> unit

(** This is called at the beginning of each iteration. *)
val iteration_start
: iteration:int ->
Expand Down Expand Up @@ -753,6 +755,7 @@ module Make (Analysis : ANALYSIS) = struct
~context
~callables_to_analyze:initial_callables_to_analyze
~max_iterations
~error_on_max_iterations
~epoch
~shared_models:shared_models_handle
=
Expand All @@ -761,7 +764,11 @@ module Make (Analysis : ANALYSIS) = struct
if number_of_callables = 0 then (* Fixpoint. *)
iteration
else if iteration >= max_iterations then
raise (Logger.reached_maximum_iteration_exception ~iteration ~callables_to_analyze)
if error_on_max_iterations then
raise (Logger.reached_maximum_iteration_exception ~iteration ~callables_to_analyze)
else
let () = Logger.reached_maximum_iteration_exit ~iteration ~callables_to_analyze in
iteration
else
let () = Logger.iteration_start ~iteration ~callables_to_analyze ~number_of_callables in
let timer = Timer.start () in
Expand Down Expand Up @@ -856,6 +863,10 @@ module WithoutLogging = struct
Format.asprintf "Failed to reach a fixpoint after %d iterations" iteration |> failwith


let reached_maximum_iteration_exit ~iteration ~callables_to_analyze:_ =
Log.info "Failed to reach a fixpoint after %d iterations. Terminate now." iteration


let iteration_start ~iteration:_ ~callables_to_analyze:_ ~number_of_callables:_ = ()

let iteration_end ~iteration:_ ~expensive_callables:_ ~number_of_callables:_ ~timer:_ = ()
Expand Down Expand Up @@ -884,21 +895,32 @@ struct
()


let reached_maximum_iteration_exception ~iteration ~callables_to_analyze =
let max_to_show = 15 in
let bucket =
callables_to_analyze |> List.map ~f:Target.show_pretty |> List.sort ~compare:String.compare
in
let show_callables ~max_to_show callables =
let bucket = callables |> List.map ~f:Target.show_pretty |> List.sort ~compare:String.compare in
let bucket_len = List.length bucket in
Format.sprintf
"Failed to reach a fixpoint after %d iterations (%d callables: %s%s)"
iteration
(List.length callables_to_analyze)
"%s%s"
(String.concat ~sep:", " (List.take bucket max_to_show))
(if bucket_len > max_to_show then "..." else "")


let reached_maximum_iteration_exception ~iteration ~callables_to_analyze =
Format.sprintf
"Failed to reach a fixpoint after %d iterations (%d callables: %s)"
iteration
(List.length callables_to_analyze)
(show_callables ~max_to_show:15 callables_to_analyze)
|> failwith


let reached_maximum_iteration_exit ~iteration ~callables_to_analyze =
Log.info
"Failed to reach a fixpoint after %d iterations. Terminate now (%d callables: %s)"
iteration
(List.length callables_to_analyze)
(show_callables ~max_to_show:15 callables_to_analyze)


let iteration_start ~iteration ~callables_to_analyze ~number_of_callables =
let witnesses =
if number_of_callables <= 6 then
Expand Down
3 changes: 3 additions & 0 deletions source/interprocedural/fixpointAnalysis.mli
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ module type LOGGER = sig
callables_to_analyze:Target.t list ->
exn

val reached_maximum_iteration_exit : iteration:int -> callables_to_analyze:Target.t list -> unit

(** This is called at the beginning of each iteration. *)
val iteration_start
: iteration:int ->
Expand Down Expand Up @@ -204,6 +206,7 @@ module Make (Analysis : ANALYSIS) : sig
context:Analysis.context ->
callables_to_analyze:Target.t list ->
max_iterations:int ->
error_on_max_iterations:bool ->
epoch:Epoch.t ->
shared_models:shared_models ->
t
Expand Down
59 changes: 57 additions & 2 deletions source/interprocedural/test/callGraphFixpointTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module Expected = struct
}
end

let assert_higher_order_call_graph_fixpoint ~source ~expected () context =
let assert_higher_order_call_graph_fixpoint ?(max_iterations = 10) ~source ~expected () context =
let handle = "test.py" in
let configuration, pyre_api =
initialize_pyre_and_fail_on_errors ~context ~handle ~source_content:source ~models_source:None
Expand Down Expand Up @@ -63,7 +63,7 @@ let assert_higher_order_call_graph_fixpoint ~source ~expected () context =
~dependency_graph
~override_graph_shared_memory
~initial_callables
~max_iterations:10
~max_iterations
in
List.iter expected ~f:(fun { Expected.callable; call_graph; returned_callables } ->
let actual_call_graph =
Expand Down Expand Up @@ -158,6 +158,61 @@ let test_higher_order_call_graph_fixpoint =
]
();
labeled_test_case __FUNCTION__ __LINE__
@@ assert_higher_order_call_graph_fixpoint
~source:
{|
def foo():
return 0
def bar(arg):
return foo
def baz():
return bar(foo)
|}
~max_iterations:1
~expected:
[
{
Expected.callable =
Target.Regular.Function { name = "test.baz"; kind = Normal }
|> Target.from_regular;
call_graph =
[
( "7:9-7:17",
LocationCallees.Singleton
(ExpressionCallees.from_call
(CallCallees.create
~call_targets:
[
CallTarget.create
(create_parameterized_target
~regular:
(Target.Regular.Function
{ name = "test.bar"; kind = Normal })
~parameters:
[
( create_positional_parameter 0 "arg",
Target.Regular.Function
{ name = "test.foo"; kind = Normal }
|> Target.from_regular );
]);
]
())) );
( "7:13-7:16",
LocationCallees.Singleton
(ExpressionCallees.from_attribute_access
(AttributeAccessCallees.create
~callable_targets:
[
CallTarget.create_regular
(Target.Regular.Function { name = "test.foo"; kind = Normal });
]
())) );
];
returned_callables = [];
};
]
();
labeled_test_case __FUNCTION__ __LINE__
@@ assert_higher_order_call_graph_fixpoint
~source:{|
def foo():
Expand Down
1 change: 1 addition & 0 deletions source/interprocedural_analyses/taint/taintAnalysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,7 @@ let run_taint_analysis
}
~callables_to_analyze
~max_iterations:100
~error_on_max_iterations:true
~epoch:Taint.TaintFixpoint.Epoch.initial
~shared_models
in
Expand Down
1 change: 1 addition & 0 deletions source/interprocedural_analyses/taint/test/fixpointTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ let assert_fixpoint
}
~callables_to_analyze
~max_iterations:100
~error_on_max_iterations:true
~epoch:TaintFixpoint.Epoch.initial
~shared_models
in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ let assert_fixpoint
}
~callables_to_analyze
~max_iterations:100
~error_on_max_iterations:true
~epoch:TaintFixpoint.Epoch.initial
~shared_models
in
Expand Down
1 change: 1 addition & 0 deletions source/interprocedural_analyses/taint/test/testHelper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,7 @@ let end_to_end_integration_test path context =
}
~callables_to_analyze
~max_iterations:100
~error_on_max_iterations:true
~epoch:TaintFixpoint.Epoch.initial
~shared_models
in
Expand Down

0 comments on commit f4a9af8

Please sign in to comment.