[racerd] refactor analysis driver function

Summary: Move state to summary conversion into the domain file, move a model matcher into the models file and simplify.

Reviewed By: skcho

Differential Revision: D21064351

fbshipit-source-id: bb5b07b6b
master
Nikos Gorogiannis 5 years ago committed by Facebook GitHub Bot
parent 572af451a9
commit 3b46c3b4da

@ -425,57 +425,53 @@ end
module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (ProcCfg.Normal)) module Analyzer = LowerHil.MakeAbstractInterpreter (TransferFunctions (ProcCfg.Normal))
let analyze_procedure {Callbacks.exe_env; summary} = let analyze_procedure {Callbacks.exe_env; summary} =
let open RacerDDomain in
let proc_desc = Summary.get_proc_desc summary in let proc_desc = Summary.get_proc_desc summary in
let proc_name = Summary.get_proc_name summary in let proc_name = Summary.get_proc_name summary in
let tenv = Exe_env.get_tenv exe_env proc_name in let tenv = Exe_env.get_tenv exe_env proc_name in
let open RacerDModels in
let open ConcurrencyModels in let open ConcurrencyModels in
let method_annotation = (Procdesc.get_attributes proc_desc).method_annotation in let method_annotation = (Procdesc.get_attributes proc_desc).method_annotation in
let is_initializer tenv proc_name = if RacerDModels.should_analyze_proc tenv proc_name then
Procname.is_constructor proc_name || FbThreadSafety.is_custom_init tenv proc_name
in
let open RacerDDomain in
if should_analyze_proc tenv proc_name then
let formal_map = FormalMap.make proc_desc in let formal_map = FormalMap.make proc_desc in
let proc_data = ProcData.make summary tenv (FormalMap.make proc_desc) in let locks =
let initial = if Procdesc.is_java_synchronized proc_desc then LockDomain.(acquire_lock bottom)
let locks = else LockDomain.bottom
if Procdesc.is_java_synchronized proc_desc then LockDomain.(acquire_lock bottom) in
else LockDomain.bottom let threads =
in if
let threads = runs_on_ui_thread ~attrs_of_pname tenv proc_name
if || RacerDModels.is_thread_confined_method tenv proc_name
runs_on_ui_thread ~attrs_of_pname tenv proc_name then ThreadsDomain.AnyThreadButSelf
|| is_thread_confined_method tenv proc_name else if
then ThreadsDomain.AnyThreadButSelf Procdesc.is_java_synchronized proc_desc || RacerDModels.is_marked_thread_safe proc_name tenv
else if Procdesc.is_java_synchronized proc_desc || is_marked_thread_safe proc_name tenv then then ThreadsDomain.AnyThread
ThreadsDomain.AnyThread else ThreadsDomain.NoThread
else ThreadsDomain.NoThread in
in let add_owned_local acc (var_data : ProcAttributes.var_data) =
let add_owned_local acc (var_data : ProcAttributes.var_data) = let pvar = Pvar.mk var_data.name proc_name in
let pvar = Pvar.mk var_data.name proc_name in let base = AccessPath.base_of_pvar pvar var_data.typ in
let base = AccessPath.base_of_pvar pvar var_data.typ in OwnershipDomain.add (AccessExpression.base base) OwnershipAbstractValue.owned acc
OwnershipDomain.add (AccessExpression.base base) OwnershipAbstractValue.owned acc in
in (* Add ownership to local variables. In cpp, stack-allocated local
(* Add ownership to local variables. In cpp, stack-allocated local variables cannot be raced on as every thread has its own stack.
variables cannot be raced on as every thread has its own stack. More generally, we will never be confident that a race exists on a local/temp. *)
More generally, we will never be confident that a race exists on a local/temp. *) let own_locals =
let own_locals = List.fold ~f:add_owned_local (Procdesc.get_locals proc_desc) ~init:OwnershipDomain.empty
List.fold ~f:add_owned_local (Procdesc.get_locals proc_desc) ~init:OwnershipDomain.empty in
in let is_owned_formal {Annot.class_name} =
let is_owned_formal {Annot.class_name} = (* @InjectProp allocates a fresh object to bind to the parameter *)
(* @InjectProp allocates a fresh object to bind to the parameter *) String.is_suffix ~suffix:Annotations.inject_prop class_name
String.is_suffix ~suffix:Annotations.inject_prop class_name in
in let add_conditional_owned_formal acc (formal, formal_index) =
let add_conditional_owned_formal acc (formal, formal_index) = let ownership_value =
let ownership_value = if Annotations.ma_has_annotation_with method_annotation is_owned_formal then
if Annotations.ma_has_annotation_with method_annotation is_owned_formal then OwnershipAbstractValue.owned
OwnershipAbstractValue.owned else OwnershipAbstractValue.make_owned_if formal_index
else OwnershipAbstractValue.make_owned_if formal_index
in
OwnershipDomain.add (AccessExpression.base formal) ownership_value acc
in in
if is_initializer tenv proc_name then OwnershipDomain.add (AccessExpression.base formal) ownership_value acc
in
let ownership =
if RacerDModels.is_initializer tenv proc_name then
let add_owned_formal acc formal_index = let add_owned_formal acc formal_index =
match FormalMap.get_formal_base formal_index formal_map with match FormalMap.get_formal_base formal_index formal_map with
| Some base -> | Some base ->
@ -483,46 +479,29 @@ let analyze_procedure {Callbacks.exe_env; summary} =
| None -> | None ->
acc acc
in in
let ownership = (* if a constructor is called via DI, all of its formals will be freshly allocated and
(* if a constructor is called via DI, all of its formals will be freshly allocated and therefore owned. we assume that constructors annotated with @Inject will only be
therefore owned. we assume that constructors annotated with @Inject will only be called via DI or using fresh parameters. *)
called via DI or using fresh parameters. *) if Annotations.pdesc_has_return_annot proc_desc Annotations.ia_is_inject then
if Annotations.pdesc_has_return_annot proc_desc Annotations.ia_is_inject then List.mapi ~f:(fun i _ -> i) (Procdesc.get_formals proc_desc)
List.mapi ~f:(fun i _ -> i) (Procdesc.get_formals proc_desc) |> List.fold ~f:add_owned_formal ~init:own_locals
|> List.fold ~f:add_owned_formal ~init:own_locals else
else (* express that the constructor owns [this] *)
(* express that the constructor owns [this] *) let init = add_owned_formal own_locals 0 in
let init = add_owned_formal own_locals 0 in FormalMap.get_formals_indexes formal_map
FormalMap.get_formals_indexes formal_map |> List.filter ~f:(fun (_, index) -> not (Int.equal 0 index))
|> List.filter ~f:(fun (_, index) -> not (Int.equal 0 index)) |> List.fold ~init ~f:add_conditional_owned_formal
|> List.fold ~init ~f:add_conditional_owned_formal
in
{RacerDDomain.bottom with ownership; threads; locks}
else else
(* add Owned(formal_index) predicates for each formal to indicate that each one is owned if (* add Owned(formal_index) predicates for each formal to indicate that each one is owned if
it is owned in the caller *) it is owned in the caller *)
let ownership = List.fold ~init:own_locals ~f:add_conditional_owned_formal
List.fold ~init:own_locals ~f:add_conditional_owned_formal (FormalMap.get_formals_indexes formal_map)
(FormalMap.get_formals_indexes formal_map)
in
{RacerDDomain.bottom with ownership; threads; locks}
in in
match Analyzer.compute_post proc_data ~initial with let initial = {bottom with ownership; threads; locks} in
| Some {threads; locks; accesses; ownership; attribute_map} -> let proc_data = ProcData.make summary tenv formal_map in
let return_var_exp = Analyzer.compute_post proc_data ~initial
AccessExpression.base |> Option.map ~f:(astate_to_summary proc_desc)
(Var.of_pvar (Pvar.get_ret_pvar proc_name), Procdesc.get_ret_type proc_desc) |> Option.value_map ~default:summary ~f:(fun post -> Payload.update_summary post summary)
in
let return_ownership = OwnershipDomain.get_owned return_var_exp ownership in
let return_attribute = AttributeMapDomain.find return_var_exp attribute_map in
let locks =
(* if method is [synchronized] released the lock once. *)
if Procdesc.is_java_synchronized proc_desc then LockDomain.release_lock locks else locks
in
let post = {threads; locks; accesses; return_ownership; return_attribute} in
Payload.update_summary post summary
| None ->
summary
else Payload.update_summary empty_summary summary else Payload.update_summary empty_summary summary

@ -546,3 +546,18 @@ let add_unannotated_call_access formals pname actuals loc (astate : t) =
astate.threads Unowned loc astate.threads Unowned loc
in in
{astate with accesses= AccessDomain.add_opt snapshot astate.accesses} ) {astate with accesses= AccessDomain.add_opt snapshot astate.accesses} )
let astate_to_summary proc_desc {threads; locks; accesses; ownership; attribute_map} =
let proc_name = Procdesc.get_proc_name proc_desc in
let return_var_exp =
AccessExpression.base
(Var.of_pvar (Pvar.get_ret_pvar proc_name), Procdesc.get_ret_type proc_desc)
in
let return_ownership = OwnershipDomain.get_owned return_var_exp ownership in
let return_attribute = AttributeMapDomain.find return_var_exp attribute_map in
let locks =
(* if method is [synchronized] released the lock once. *)
if Procdesc.is_java_synchronized proc_desc then LockDomain.release_lock locks else locks
in
{threads; locks; accesses; return_ownership; return_attribute}

@ -204,3 +204,5 @@ type summary =
val empty_summary : summary val empty_summary : summary
val pp_summary : F.formatter -> summary -> unit val pp_summary : F.formatter -> summary -> unit
val astate_to_summary : Procdesc.t -> t -> summary

@ -495,3 +495,7 @@ let is_builder_passthrough callee =
false ) false )
| _ -> | _ ->
false false
let is_initializer tenv proc_name =
Procname.is_constructor proc_name || FbThreadSafety.is_custom_init tenv proc_name

@ -61,3 +61,6 @@ val creates_builder : Procname.t -> bool
val is_builder_passthrough : Procname.t -> bool val is_builder_passthrough : Procname.t -> bool
(** is the callee a non-static [Builder] method returning the same type as its receiver *) (** is the callee a non-static [Builder] method returning the same type as its receiver *)
val is_initializer : Tenv.t -> Procname.t -> bool
(** should the given procedure be treated as a constructor/initializer? *)

Loading…
Cancel
Save