Summary: This will be eventually used in RacerD. Further diffs up the stack will migrate away the starvation-specific aspects. Reviewed By: ezgicicek Differential Revision: D19623392 fbshipit-source-id: a72650718master
							parent
							
								
									9f4098ea1a
								
							
						
					
					
						commit
						b2dc55ce0a
					
				| @ -0,0 +1,183 @@ | ||||
| (* | ||||
|  * Copyright (c) Facebook, Inc. and its affiliates. | ||||
|  * | ||||
|  * This source code is licensed under the MIT license found in the | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  *) | ||||
| open! IStd | ||||
| module F = Format | ||||
| module L = Logging | ||||
| module MF = MarkupFormatter | ||||
| 
 | ||||
| (** var type used only for printing, not comparisons *) | ||||
| module IgnoreVar = struct | ||||
|   type t = Var.t | ||||
| 
 | ||||
|   let compare _x _y = 0 | ||||
| 
 | ||||
|   let equal _x _y = true | ||||
| end | ||||
| 
 | ||||
| (** access path that does not ignore the type (like the original AccessPath.t) but which instead | ||||
|     ignores the root variable for comparisons; this is taken care of by the root type *) | ||||
| type path = (IgnoreVar.t * Typ.t) * AccessPath.access list [@@deriving compare, equal] | ||||
| 
 | ||||
| type root = | ||||
|   | Global of Mangled.t | ||||
|   | Class of Typ.name | ||||
|   | Parameter of int  (** method parameter represented by its 0-indexed position *) | ||||
| [@@deriving compare, equal] | ||||
| 
 | ||||
| type t = {root: root; path: path} [@@deriving compare, equal] | ||||
| 
 | ||||
| let get_typ tenv t = AccessPath.get_typ t.path tenv | ||||
| 
 | ||||
| let rec norm_path tenv ((typ, (accesses : AccessPath.access list)) as path) = | ||||
|   match accesses with | ||||
|   | (FieldAccess fieldname as access) :: rest when Fieldname.is_java_outer_instance fieldname -> ( | ||||
|     match AccessPath.get_access_type tenv typ access with | ||||
|     | Some typ' -> | ||||
|         norm_path tenv (typ', rest) | ||||
|     | None -> | ||||
|         path ) | ||||
|   | _ -> | ||||
|       path | ||||
| 
 | ||||
| 
 | ||||
| let equal_across_threads tenv t1 t2 = | ||||
|   match (t1.root, t2.root) with | ||||
|   | Global _, Global _ | Class _, Class _ -> | ||||
|       (* globals and class objects must be identical across threads *) | ||||
|       equal t1 t2 | ||||
|   | Parameter _, Parameter _ -> | ||||
|       let ((_, typ1), accesses1), ((_, typ2), accesses2) = (t1.path, t2.path) in | ||||
|       (* parameter position/names can be ignored across threads, if types and accesses are equal *) | ||||
|       let path1, path2 = (norm_path tenv (typ1, accesses1), norm_path tenv (typ2, accesses2)) in | ||||
|       [%equal: Typ.t * AccessPath.access list] path1 path2 | ||||
|   | _, _ -> | ||||
|       false | ||||
| 
 | ||||
| 
 | ||||
| let is_class_object = function {root= Class _} -> true | _ -> false | ||||
| 
 | ||||
| (* using an indentifier for a class object, create an access path representing that lock; | ||||
|    this is for synchronizing on Java class objects only *) | ||||
| let path_of_java_class = | ||||
|   let typ = Typ.(mk (Tstruct Name.Java.java_lang_class)) in | ||||
|   let typ' = Typ.(mk (Tptr (typ, Pk_pointer))) in | ||||
|   fun class_id -> | ||||
|     let ident = Ident.create_normal class_id 0 in | ||||
|     AccessPath.of_id ident typ' | ||||
| 
 | ||||
| 
 | ||||
| let make_global path mangled = {root= Global mangled; path} | ||||
| 
 | ||||
| let make_parameter path index = {root= Parameter index; path} | ||||
| 
 | ||||
| let make_class path typename = {root= Class typename; path} | ||||
| 
 | ||||
| (** convert an expression to a canonical form for a lock identifier *) | ||||
| let rec make formal_map (hilexp : HilExp.t) = | ||||
|   match hilexp with | ||||
|   | AccessExpression access_exp -> ( | ||||
|       let path = HilExp.AccessExpression.to_access_path access_exp in | ||||
|       match fst (fst path) with | ||||
|       | Var.LogicalVar _ -> | ||||
|           (* ignore logical variables *) | ||||
|           None | ||||
|       | Var.ProgramVar pvar when Pvar.is_global pvar -> | ||||
|           Some (make_global path (Pvar.get_name pvar)) | ||||
|       | Var.ProgramVar _ -> | ||||
|           FormalMap.get_formal_index (fst path) formal_map | ||||
|           (* ignores non-formals *) | ||||
|           |> Option.map ~f:(make_parameter path) ) | ||||
|   | Constant (Cclass class_id) -> | ||||
|       (* this is a synchronized/lock(CLASSNAME.class) construct *) | ||||
|       let path = path_of_java_class class_id in | ||||
|       let typename = Ident.name_to_string class_id |> Typ.Name.Java.from_string in | ||||
|       Some (make_class path typename) | ||||
|   | Cast (_, hilexp) | Exception hilexp | UnaryOperator (_, hilexp, _) -> | ||||
|       make formal_map hilexp | ||||
|   | BinaryOperator _ | Closure _ | Constant _ | Sizeof _ -> | ||||
|       None | ||||
| 
 | ||||
| 
 | ||||
| let make_java_synchronized formals procname = | ||||
|   match procname with | ||||
|   | Procname.Java java_pname when Procname.Java.is_static java_pname -> | ||||
|       (* this is crafted so as to match synchronized(CLASSNAME.class) constructs *) | ||||
|       let typename = Procname.Java.get_class_type_name java_pname in | ||||
|       let path = Typ.Name.name typename |> Ident.string_to_name |> path_of_java_class in | ||||
|       Some (make_class path typename) | ||||
|   | Procname.Java _ -> | ||||
|       FormalMap.get_formal_base 0 formals |> Option.map ~f:(fun base -> make_parameter (base, []) 0) | ||||
|   | _ -> | ||||
|       L.die InternalError "Non-Java methods cannot be synchronized.@\n" | ||||
| 
 | ||||
| 
 | ||||
| let pp fmt {root; path} = | ||||
|   let pp_path fmt ((var, typ), accesses) = | ||||
|     F.fprintf fmt "(%a:%a)" Var.pp var (Typ.pp_full Pp.text) typ ; | ||||
|     if not (List.is_empty accesses) then F.fprintf fmt ".%a" AccessPath.pp_access_list accesses | ||||
|   in | ||||
|   match root with | ||||
|   | Global mangled -> | ||||
|       F.fprintf fmt "G<%a>{%a}" Mangled.pp mangled pp_path path | ||||
|   | Class typename -> | ||||
|       F.fprintf fmt "C<%a>{%a}" Typ.Name.pp typename pp_path path | ||||
|   | Parameter idx -> | ||||
|       F.fprintf fmt "P<%i>{%a}" idx pp_path path | ||||
| 
 | ||||
| 
 | ||||
| let root_class {path= (_, {Typ.desc}), _} = | ||||
|   match desc with Typ.Tstruct name | Typ.Tptr ({desc= Tstruct name}, _) -> Some name | _ -> None | ||||
| 
 | ||||
| 
 | ||||
| let describe fmt lock = | ||||
|   let describe_lock fmt lock = (MF.wrap_monospaced AccessPath.pp) fmt lock.path in | ||||
|   let describe_typename = MF.wrap_monospaced Typ.Name.pp in | ||||
|   let describe_owner fmt lock = | ||||
|     root_class lock |> Option.iter ~f:(F.fprintf fmt " in %a" describe_typename) | ||||
|   in | ||||
|   F.fprintf fmt "%a%a" describe_lock lock describe_owner lock | ||||
| 
 | ||||
| 
 | ||||
| let compare_wrt_reporting {path= (_, typ1), _} {path= (_, typ2), _} = | ||||
|   (* use string comparison on types as a stable order to decide whether to report a deadlock *) | ||||
|   String.compare (Typ.to_string typ1) (Typ.to_string typ2) | ||||
| 
 | ||||
| 
 | ||||
| (** A substitution from formal position indices to actuals. Since we only care about locks, use | ||||
|     [None] to denote an argument that cannot be resolved to a lock object. *) | ||||
| type subst = t option Array.t | ||||
| 
 | ||||
| let pp_subst fmt subst = | ||||
|   PrettyPrintable.pp_collection fmt ~pp_item:(Pp.option pp) (Array.to_list subst) | ||||
| 
 | ||||
| 
 | ||||
| let make_subst formal_map actuals = | ||||
|   let actuals = Array.of_list actuals in | ||||
|   let len = | ||||
|     (* deal with var args functions *) | ||||
|     Int.max (FormalMap.cardinal formal_map) (Array.length actuals) | ||||
|   in | ||||
|   let subst = Array.create ~len None in | ||||
|   FormalMap.iter | ||||
|     (fun _base idx -> | ||||
|       if idx < Array.length actuals then subst.(idx) <- make formal_map actuals.(idx) ) | ||||
|     formal_map ; | ||||
|   subst | ||||
| 
 | ||||
| 
 | ||||
| let apply_subst (subst : subst) lock = | ||||
|   match lock.root with | ||||
|   | Global _ | Class _ -> | ||||
|       Some lock | ||||
|   | Parameter index -> ( | ||||
|     try | ||||
|       match subst.(index) with | ||||
|       | None -> | ||||
|           None | ||||
|       | Some actual -> | ||||
|           Some {actual with path= AccessPath.append actual.path (snd lock.path)} | ||||
|     with Invalid_argument _ -> None ) | ||||
| @ -0,0 +1,65 @@ | ||||
| (* | ||||
|  * Copyright (c) Facebook, Inc. and its affiliates. | ||||
|  * | ||||
|  * This source code is licensed under the MIT license found in the | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  *) | ||||
| 
 | ||||
| open! IStd | ||||
| module F = Format | ||||
| 
 | ||||
| (** Type meant to represent abstract addresses based on access paths. It currently distinguishes | ||||
|     between paths | ||||
| 
 | ||||
|     - rooted at formal parameters (these are identified by the parameter index and the path without | ||||
|       the root variable, though that variable is kept for pretty printing); | ||||
|     - rooted at global variables; | ||||
|     - non access-path expressions representing class objects (java only). | ||||
| 
 | ||||
|     Notably, there are no addresses rooted at locals (because proving aliasing between those is | ||||
|     difficult). | ||||
| 
 | ||||
|     There are two notions of equality: | ||||
| 
 | ||||
|     - Equality for comparing two addresses within the same thread/process/trace. Under this, | ||||
|       identical globals and identical class objects compare equal. Parameter-rooted paths compare | ||||
|       equal if their parameter indices, types and lists of accesses are equal. | ||||
|     - Equality for comparing two addresses in two distinct threads/traces. Globals and class objects | ||||
|       are compared in the same way, but parameter-rooted paths need only only have equal access | ||||
|       lists (ie [x.f.g == y.f.g]). This allows demonically aliasing parameters in *distinct* | ||||
|       threads. *) | ||||
| 
 | ||||
| include PrettyPrintable.PrintableOrderedType | ||||
| 
 | ||||
| val describe : F.formatter -> t -> unit | ||||
| (** human readable description *) | ||||
| 
 | ||||
| val equal : t -> t -> bool | ||||
| 
 | ||||
| val equal_across_threads : Tenv.t -> t -> t -> bool | ||||
| 
 | ||||
| val compare_wrt_reporting : t -> t -> int | ||||
| (** a stable order for avoiding reporting deadlocks twice based on the root variable type *) | ||||
| 
 | ||||
| val root_class : t -> Typ.name option | ||||
| (** Class of the root variable of the path representing the lock *) | ||||
| 
 | ||||
| val get_typ : Tenv.t -> t -> Typ.t option | ||||
| 
 | ||||
| val make : FormalMap.t -> HilExp.t -> t option | ||||
| 
 | ||||
| val make_java_synchronized : FormalMap.t -> Procname.t -> t option | ||||
| (** create the monitor locked when entering a synchronized java method *) | ||||
| 
 | ||||
| val is_class_object : t -> bool | ||||
| (** is the lock a class object such as in [synchronized(MyClass.class){}] or | ||||
|     [static synchronized void foo()] *) | ||||
| 
 | ||||
| (** substitution type : a map from (0-based) positional index to lock options *) | ||||
| type subst | ||||
| 
 | ||||
| val pp_subst : F.formatter -> subst -> unit [@@warning "-32"] | ||||
| 
 | ||||
| val make_subst : FormalMap.t -> HilExp.t list -> subst | ||||
| 
 | ||||
| val apply_subst : subst -> t -> t option | ||||
					Loading…
					
					
				
		Reference in new issue