From a6da208e9df3dc2e10b8e58c02c4a9b0b2d5d80f Mon Sep 17 00:00:00 2001 From: Nikos Gorogiannis Date: Thu, 6 Feb 2020 09:54:24 -0800 Subject: [PATCH] [starvation] use access expressions instead of access paths Summary: The goals are: - Increase precision in C-languages by ditching access paths. - Help with eventually sharing the abstract address module with RacerD. - Reports are now language-mode specific (eg `->` in clang vs `.` in Java). It's not exactly access expressions used here. Instead the pattern `(base, access list)` is used where `access` is `HilExp.Access.t`. This is done to ease the way `deriving` is used for creating two comparison functions, one that cares about the root variable and one that doesn't; and also because the main function that recurses over accesses (`normalise_access_list`) visits the accesses from innermost to outermost. Also, kill some dead code. Reviewed By: skcho Differential Revision: D19741545 fbshipit-source-id: 013bf1a89 --- infer/src/IR/AccessPath.ml | 51 ----- infer/src/IR/AccessPath.mli | 21 +-- infer/src/IR/Typ.ml | 9 - infer/src/IR/Typ.mli | 4 - infer/src/concurrency/AbstractAddress.ml | 174 ++++++++++++++---- .../codetoanalyze/cpp/starvation/issues.exp | 26 +-- 6 files changed, 152 insertions(+), 133 deletions(-) diff --git a/infer/src/IR/AccessPath.ml b/infer/src/IR/AccessPath.ml index 100511531..a5b66d6fa 100644 --- a/infer/src/IR/AccessPath.ml +++ b/infer/src/IR/AccessPath.ml @@ -215,13 +215,6 @@ module Raw = struct let is_prefix ap1 ap2 = chop_prefix ~prefix:ap1 ap2 |> Option.is_some - - let replace_prefix ~prefix access_path replace_with = - match chop_prefix ~prefix access_path with - | Some remaining_accesses -> - Some (append replace_with remaining_accesses) - | None -> - None end module Abs = struct @@ -281,47 +274,3 @@ module BaseMap = PrettyPrintable.MakePPMap (struct let pp = pp_base end) - -(* transform an access path that starts on "this" of an inner class but which breaks out to - access outer class fields to the outermost one *) -let inner_class_normalize p = - let open Typ in - let is_synthetic_this pvar = Pvar.get_simplified_name pvar |> String.is_prefix ~prefix:"this$" in - let mk_pvar_as name pvar = Pvar.get_declaring_function pvar |> Option.map ~f:(Pvar.mk name) in - let aux = function - (* (this:InnerClass* ).(this$n:OuterClassAccessor).f. ... -> (this:OuterClass* ).f . ... *) - | Some - ( ( (Var.ProgramVar pvar as root) - , ({desc= Tptr (({desc= Tstruct name} as cls), pkind)} as ptr) ) - , FieldAccess first :: accesses ) - when Pvar.is_this pvar && Fieldname.is_java_outer_instance first -> - Name.Java.get_outer_class name - |> Option.map ~f:(fun outer_name -> - let outer_class = mk ~default:cls (Tstruct outer_name) in - let outer_ptr = mk ~default:ptr (Tptr (outer_class, pkind)) in - ((root, outer_ptr), accesses) ) - (* this$n.(this$m:OuterClassAccessor).f ... -> (this$m:OuterClass* ).f . ... *) - (* happens in ctrs only *) - | Some - ( (Var.ProgramVar pvar, ({desc= Tptr (({desc= Tstruct name} as cls), pkind)} as ptr)) - , FieldAccess first :: accesses ) - when is_synthetic_this pvar && Fieldname.is_java_outer_instance first -> - Name.Java.get_outer_class name - |> Option.bind ~f:(fun outer_name -> - let outer_class = mk ~default:cls (Tstruct outer_name) in - let outer_ptr = mk ~default:ptr (Tptr (outer_class, pkind)) in - let varname = Fieldname.get_field_name first |> Mangled.from_string in - mk_pvar_as varname pvar - |> Option.map ~f:(fun new_pvar -> - let base = base_of_pvar new_pvar outer_ptr in - (base, accesses) ) ) - (* this$n.f ... -> this.f . ... *) - (* happens in ctrs only *) - | Some ((Var.ProgramVar pvar, typ), all_accesses) when is_synthetic_this pvar -> - mk_pvar_as Mangled.this pvar - |> Option.map ~f:(fun new_pvar -> (base_of_pvar new_pvar typ, all_accesses)) - | _ -> - None - in - let rec loop path_opt = match aux path_opt with None -> path_opt | res -> loop res in - loop (Some p) |> Option.value ~default:p diff --git a/infer/src/IR/AccessPath.mli b/infer/src/IR/AccessPath.mli index bd0510156..7a49a3d1e 100644 --- a/infer/src/IR/AccessPath.mli +++ b/infer/src/IR/AccessPath.mli @@ -16,17 +16,14 @@ type access = | FieldAccess of Fieldname.t (** field name *) [@@deriving compare, equal] -(** root var, and a list of accesses. closest to the root var is first that is, x.f.g is - representedas (x, [f; g]) *) +(** root var, and a list of accesses. closest to the root var is first that is, x.f.g is represented + as (x, [f; g]) *) and t = base * access list [@@deriving compare] val truncate : t -> t * access option (** remove and return the last access of the access path if the access list is non-empty. returns the original access path * None if the access list is empty *) -val get_access_type : Tenv.t -> Typ.t -> access -> Typ.t option -(** Get the type of an access, or None if the type cannot be determined *) - val get_last_access : t -> access option (** get the last access in the list. returns None if the list is empty *) @@ -66,18 +63,6 @@ val append : t -> access list -> t val is_prefix : t -> t -> bool (** return true if [ap1] is a prefix of [ap2]. returns true for equal access paths *) -val replace_prefix : prefix:t -> t -> t -> t option [@@warning "-32"] - -val inner_class_normalize : t -> t - [@@warning "-32"] -(** transform an access path that starts on "this" of an inner class but which breaks out to access - outer class fields to the outermost one. Cases handled (recursively): - - - (this:InnerClass* ).(this$n:OuterClassAccessor).f. ... -> (this:OuterClass* ).f . ... - - this$n.(this$m:OuterClassAccessor).f ... -> (this$m:OuterClass* ).f . ... (happens in ctrs - only) - - this$n.f ... -> this.f . ... (happens in ctrs only) *) - val equal : t -> t -> bool val equal_base : base -> base -> bool @@ -88,8 +73,6 @@ val pp_base : Format.formatter -> base -> unit val pp_access : Format.formatter -> access -> unit -val pp_access_list : Format.formatter -> access list -> unit [@@warning "-32"] - module Abs : sig type raw = t diff --git a/infer/src/IR/Typ.ml b/infer/src/IR/Typ.ml index 120aa99f7..b7a3ef469 100644 --- a/infer/src/IR/Typ.ml +++ b/infer/src/IR/Typ.ml @@ -499,15 +499,6 @@ module Name = struct let split_typename typename = Split.of_string (name typename) - let get_outer_class class_name = - let {Split.package; type_name} = split_typename class_name in - match String.rsplit2 ~on:'$' type_name with - | Some (parent_class, _) -> - Some (from_package_class (Option.value ~default:"" package) parent_class) - | None -> - None - - let is_anonymous_inner_class_name class_name = let class_name_no_package = Split.type_name (split_typename class_name) in match String.rsplit2 class_name_no_package ~on:'$' with diff --git a/infer/src/IR/Typ.mli b/infer/src/IR/Typ.mli index 71dd65d63..eeee8225f 100644 --- a/infer/src/IR/Typ.mli +++ b/infer/src/IR/Typ.mli @@ -249,10 +249,6 @@ module Name : sig val is_external : t -> bool (** return true if the typename is in the .inferconfig list of external classes *) - val get_outer_class : t -> t option - (** Given an inner classname like C$Inner1$Inner2, return Some C$Inner1. If the class is not an - inner class, return None *) - val is_anonymous_inner_class_name : t -> bool val split_typename : t -> Split.t diff --git a/infer/src/concurrency/AbstractAddress.ml b/infer/src/concurrency/AbstractAddress.ml index 880612986..92cc7ee57 100644 --- a/infer/src/concurrency/AbstractAddress.ml +++ b/infer/src/concurrency/AbstractAddress.ml @@ -9,6 +9,70 @@ module F = Format module L = Logging module MF = MarkupFormatter +type access = HilExp.t option HilExp.Access.t [@@deriving compare] + +let get_access_typ tenv prev_typ (access : access) = + let lookup tn = Tenv.lookup tenv tn in + match access with + | FieldAccess field_name -> + Struct.get_field_type_and_annotation ~lookup field_name prev_typ |> Option.map ~f:fst + | ArrayAccess (typ, _) -> + Some typ + | TakeAddress -> + Some (Typ.mk (Tptr (prev_typ, Pk_pointer))) + | Dereference -> ( + match prev_typ with {Typ.desc= Tptr (typ, _)} -> Some typ | _ -> None ) + + +type access_list = access list [@@deriving compare] + +let equal_access_list = [%compare.equal: access_list] + +let get_typ tenv ((_, base_typ), accesses) = + let f acc access = match acc with Some typ -> get_access_typ tenv typ access | None -> None in + List.fold accesses ~init:(Some base_typ) ~f + + +let normalise_access_list (accesses : access_list) = + let exception NormalisationFailure in + let rec normalise_access_list_inner (accesses : access_list) = + match accesses with + | [] -> + [] + | TakeAddress :: Dereference :: rest -> + normalise_access_list_inner rest + | TakeAddress :: _ :: _ -> + (* an address can only be dereferenced *) + raise NormalisationFailure + | access :: rest -> + access :: normalise_access_list_inner rest + in + try Some (normalise_access_list_inner accesses) with NormalisationFailure -> None + + +let pp_with_base pp_base fmt (base, accesses) = + let rec pp_rev_accesses fmt (accesses : access_list) = + match (accesses, !Language.curr_language) with + | [], _ -> + pp_base fmt base + | ArrayAccess _ :: rest, _ -> + F.fprintf fmt "%a[]" pp_rev_accesses rest + | FieldAccess field_name :: Dereference :: rest, _ -> + let op = match !Language.curr_language with Clang -> "->" | Java -> "." in + F.fprintf fmt "%a%s%a" pp_rev_accesses rest op Fieldname.pp field_name + | FieldAccess field_name :: rest, Clang -> + F.fprintf fmt "%a.%a" pp_rev_accesses rest Fieldname.pp field_name + | Dereference :: rest, Clang -> + F.fprintf fmt "*(%a)" pp_rev_accesses rest + | TakeAddress :: rest, Clang -> + F.fprintf fmt "&(%a)" pp_rev_accesses rest + | access :: rest, Java -> + L.internal_error "Asked to print %a in Java mode@" (HilExp.Access.pp (fun _ _ -> ())) access ; + pp_rev_accesses fmt rest + in + pp_rev_accesses fmt (List.rev accesses) + + (** var type used only for printing, not comparisons *) module IgnoreVar = struct type t = Var.t @@ -18,33 +82,49 @@ module IgnoreVar = struct 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. *) -type path = (IgnoreVar.t * Typ.t) * AccessPath.access list [@@deriving compare, equal] +type raw_path = (Var.t * Typ.t) * access_list [@@deriving compare, equal] + +(** path-like type using [HilExp.Access] steps instead of [AccessPath.access]. It does not ignore + the root variable type (like the original [AccessPath.t]) but instead ignores the root variable + for comparisons. *) +type unrooted_path = (IgnoreVar.t * Typ.t) * access_list [@@deriving compare, equal] type t = - | Global of {path: AccessPath.t} (** [AccessPath] so as to include root var in comparison *) + | Global of {path: raw_path} (** root var is included in comparison *) | Class of {typename: Typ.Name.t} (** Java-only class object identified by typename *) - | Parameter of {index: int; path: path} - (** method parameter represented by its 0-indexed position *) + | Parameter of {index: int; path: unrooted_path} + (** method parameter represented by its 0-indexed position, root var is not used in comparison *) [@@deriving compare, equal] let get_typ tenv = let class_type = Typ.(mk (Tstruct Name.Java.java_lang_class)) in let some_ptr_to_class_type = Some Typ.(mk (Tptr (class_type, Pk_pointer))) in function - | Class _ -> - some_ptr_to_class_type - | Global {path} | Parameter {path} -> - AccessPath.get_typ path tenv + | Class _ -> some_ptr_to_class_type | Global {path} | Parameter {path} -> get_typ tenv path + + +let append ~on_to:(base, accesses) (_, accesses') = + match normalise_access_list (accesses @ accesses') with + | Some accesses'' -> + Some (base, accesses'') + | None -> + None -let rec norm_path tenv ((typ, (accesses : AccessPath.access list)) as path) = +(* Remove initial prefix of synthetic java fields giving access to outer-class fields. + This allows comparing as equal two paths generated by two different inner classes of the + same outer class. *) +let rec inner_class_normalise tenv ((typ, (accesses : 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) + | (Dereference as access) :: (FieldAccess fieldname as access') :: rest + when Fieldname.is_java_outer_instance fieldname -> ( + match get_access_typ tenv typ access with + | Some typ' -> ( + match get_access_typ tenv typ' access' with + | Some typ'' -> + inner_class_normalise tenv (typ'', rest) + | None -> + path ) | None -> path ) | _ -> @@ -55,8 +135,9 @@ let equal_across_threads tenv t1 t2 = match (t1, t2) with | Parameter {path= (_, typ1), accesses1}, Parameter {path= (_, typ2), accesses2} -> (* 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 + let path1 = inner_class_normalise tenv (typ1, accesses1) in + let path2 = inner_class_normalise tenv (typ2, accesses2) in + [%equal: Typ.t * access_list] path1 path2 | _, _ -> (* globals and class objects must be identical across threads *) equal t1 t2 @@ -65,19 +146,30 @@ let equal_across_threads tenv t1 t2 = let is_class_object = function Class _ -> true | _ -> false let rec make formal_map (hilexp : HilExp.t) = + let make_from_acc_exp acc_exp = + match HilExp.AccessExpression.to_accesses acc_exp with + | HilExp.AccessExpression.Base ((var, _) as base), accesses -> ( + match normalise_access_list accesses with + | Some accesses -> ( + let path = (base, accesses) in + match var with + | Var.LogicalVar _ -> + (* ignore logical variables *) + None + | Var.ProgramVar pvar when Pvar.is_global pvar -> + Some (Global {path}) + | Var.ProgramVar _ -> + FormalMap.get_formal_index base formal_map + (* ignores non-formals *) + |> Option.map ~f:(fun index -> Parameter {index; path}) ) + | _ -> + None ) + | _ -> + None + in 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 (Global {path}) - | Var.ProgramVar _ -> - FormalMap.get_formal_index (fst path) formal_map - (* ignores non-formals *) - |> Option.map ~f:(fun index -> Parameter {index; path}) ) + | AccessExpression access_exp -> + make_from_acc_exp access_exp | Constant (Cclass class_id) -> (* this is a synchronized(CLASSNAME.class) or class object construct *) let typename = Ident.name_to_string class_id |> Typ.Name.Java.from_string in @@ -89,9 +181,9 @@ let rec make formal_map (hilexp : HilExp.t) = let pp fmt t = - 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 + let pp_path fmt path = + let pp_base fmt (var, typ) = F.fprintf fmt "(%a:%a)" Var.pp var (Typ.pp_full Pp.text) typ in + pp_with_base pp_base fmt path in match t with | Global {path} -> @@ -114,6 +206,10 @@ let root_class = function let describe fmt t = + let describe_path fmt path = + let describe_base fmt (var, _) = Var.pp fmt var in + pp_with_base describe_base fmt path + in let describe_root fmt t = root_class t |> Option.iter ~f:(F.fprintf fmt " in %a" (MF.wrap_monospaced Typ.Name.pp)) in @@ -122,7 +218,7 @@ let describe fmt t = | Class {typename} -> MF.wrap_monospaced describe_class_object fmt typename | Global {path} | Parameter {path} -> - F.fprintf fmt "%a%a" (MF.wrap_monospaced AccessPath.pp) path describe_root t + F.fprintf fmt "%a%a" (MF.wrap_monospaced describe_path) path describe_root t type subst = t option Array.t @@ -163,8 +259,12 @@ let apply_subst (subst : subst) t = | Some (Class _ as t') as c -> L.internal_error "Cannot dereference class object %a in path %a@." pp t' pp t ; c - | Some (Parameter param) -> - Some (Parameter {param with path= AccessPath.append param.path (snd path)}) - | Some (Global global) -> - Some (Global {path= AccessPath.append global.path (snd path)}) + | Some (Parameter param) -> ( + match append ~on_to:param.path path with + | Some path -> + Some (Parameter {param with path}) + | None -> + None ) + | Some (Global global) -> ( + match append ~on_to:global.path path with Some path -> Some (Global {path}) | None -> None ) with Invalid_argument _ -> None ) diff --git a/infer/tests/codetoanalyze/cpp/starvation/issues.exp b/infer/tests/codetoanalyze/cpp/starvation/issues.exp index b48bde864..9437f08d8 100644 --- a/infer/tests/codetoanalyze/cpp/starvation/issues.exp +++ b/infer/tests/codetoanalyze/cpp/starvation/issues.exp @@ -1,13 +1,13 @@ -codetoanalyze/cpp/starvation/basics.cpp, basics::Basic::thread1_bad, 18, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::Basic::thread1_bad`, locks `this.mutex_1` in `class basics::Basic`, locks `this.mutex_2` in `class basics::Basic`,[Trace 2] `basics::Basic::thread2_bad`, locks `this.mutex_2` in `class basics::Basic`, locks `this.mutex_1` in `class basics::Basic`] -codetoanalyze/cpp/starvation/basics.cpp, basics::Basic::thread2_bad, 26, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::Basic::thread2_bad`, locks `this.mutex_2` in `class basics::Basic`, locks `this.mutex_1` in `class basics::Basic`,[Trace 2] `basics::Basic::thread1_bad`, locks `this.mutex_1` in `class basics::Basic`, locks `this.mutex_2` in `class basics::Basic`] -codetoanalyze/cpp/starvation/basics.cpp, basics::PathSensitive::FP_ok, 142, DEADLOCK, no_bucket, ERROR, [In method `basics::PathSensitive::FP_ok`, locks `this.mutex_` in `class basics::PathSensitive`, locks `this.mutex_` in `class basics::PathSensitive`] -codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock::complicated_bad, 131, DEADLOCK, no_bucket, ERROR, [In method `basics::SelfDeadlock::complicated_bad`, locks `this.mutex_` in `class basics::SelfDeadlock`, locks `this.mutex_` in `class basics::SelfDeadlock`] -codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock::interproc1_bad, 114, DEADLOCK, no_bucket, ERROR, [In method `basics::SelfDeadlock::interproc1_bad`, locks `this.mutex_` in `class basics::SelfDeadlock`,Method call: `basics::SelfDeadlock::interproc2_bad`, locks `this.mutex_` in `class basics::SelfDeadlock`] -codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock::thread_bad, 105, DEADLOCK, no_bucket, ERROR, [In method `basics::SelfDeadlock::thread_bad`, locks `this.mutex_` in `class basics::SelfDeadlock`, locks `this.mutex_` in `class basics::SelfDeadlock`] -codetoanalyze/cpp/starvation/basics.cpp, basics::WithGuard::thread1_bad, 44, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::WithGuard::thread1_bad`, locks `this.mutex_1` in `class basics::WithGuard`, locks `this.mutex_2` in `class basics::WithGuard`,[Trace 2] `basics::WithGuard::thread2_bad`, locks `this.mutex_2` in `class basics::WithGuard`, locks `this.mutex_1` in `class basics::WithGuard`] -codetoanalyze/cpp/starvation/basics.cpp, basics::WithGuard::thread2_bad, 49, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::WithGuard::thread2_bad`, locks `this.mutex_2` in `class basics::WithGuard`, locks `this.mutex_1` in `class basics::WithGuard`,[Trace 2] `basics::WithGuard::thread1_bad`, locks `this.mutex_1` in `class basics::WithGuard`, locks `this.mutex_2` in `class basics::WithGuard`] -codetoanalyze/cpp/starvation/crossfile-1.cpp, CrossFileOne::lock_my_mutex_first_then_the_other, 12, DEADLOCK, no_bucket, ERROR, [[Trace 1] `CrossFileOne::lock_my_mutex_first_then_the_other`, locks `this._mutex` in `class CrossFileOne`,Method call: `CrossFileTwo::just_lock_my_mutex`, locks `other._mutex` in `class CrossFileTwo`,[Trace 2] `CrossFileTwo::lock_my_mutex_first_then_the_other`, locks `this._mutex` in `class CrossFileTwo`,Method call: `CrossFileOne::just_lock_my_mutex`, locks `other._mutex` in `class CrossFileOne`] -codetoanalyze/cpp/starvation/crossfile-2.cpp, CrossFileTwo::lock_my_mutex_first_then_the_other, 12, DEADLOCK, no_bucket, ERROR, [[Trace 1] `CrossFileTwo::lock_my_mutex_first_then_the_other`, locks `this._mutex` in `class CrossFileTwo`,Method call: `CrossFileOne::just_lock_my_mutex`, locks `other._mutex` in `class CrossFileOne`,[Trace 2] `CrossFileOne::lock_my_mutex_first_then_the_other`, locks `this._mutex` in `class CrossFileOne`,Method call: `CrossFileTwo::just_lock_my_mutex`, locks `other._mutex` in `class CrossFileTwo`] -codetoanalyze/cpp/starvation/skip.cpp, skipped::Skip::not_skipped_bad, 19, DEADLOCK, no_bucket, ERROR, [In method `skipped::Skip::not_skipped_bad`,Method call: `skipped::Skip::private_deadlock`, locks `this.mutex_` in `class skipped::Skip`, locks `this.mutex_` in `class skipped::Skip`] -codetoanalyze/cpp/starvation/skip.cpp, skipped::SkipTemplate::not_skipped_bad, 44, DEADLOCK, no_bucket, ERROR, [In method `skipped::SkipTemplate::not_skipped_bad`,Method call: `skipped::SkipTemplate::private_deadlock`, locks `this.mutex_` in `class skipped::SkipTemplate`, locks `this.mutex_` in `class skipped::SkipTemplate`] -codetoanalyze/cpp/starvation/skip.cpp, skipped::UseTemplate::foo, 51, DEADLOCK, no_bucket, ERROR, [In method `skipped::UseTemplate::foo`,Method call: `skipped::SkipTemplate::not_skipped_bad`,Method call: `skipped::SkipTemplate::private_deadlock`, locks `this.x.mutex_` in `class skipped::UseTemplate`, locks `this.x.mutex_` in `class skipped::UseTemplate`] +codetoanalyze/cpp/starvation/basics.cpp, basics::Basic::thread1_bad, 18, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::Basic::thread1_bad`, locks `&(this->mutex_1)` in `class basics::Basic`, locks `&(this->mutex_2)` in `class basics::Basic`,[Trace 2] `basics::Basic::thread2_bad`, locks `&(this->mutex_2)` in `class basics::Basic`, locks `&(this->mutex_1)` in `class basics::Basic`] +codetoanalyze/cpp/starvation/basics.cpp, basics::Basic::thread2_bad, 26, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::Basic::thread2_bad`, locks `&(this->mutex_2)` in `class basics::Basic`, locks `&(this->mutex_1)` in `class basics::Basic`,[Trace 2] `basics::Basic::thread1_bad`, locks `&(this->mutex_1)` in `class basics::Basic`, locks `&(this->mutex_2)` in `class basics::Basic`] +codetoanalyze/cpp/starvation/basics.cpp, basics::PathSensitive::FP_ok, 142, DEADLOCK, no_bucket, ERROR, [In method `basics::PathSensitive::FP_ok`, locks `&(this->mutex_)` in `class basics::PathSensitive`, locks `&(this->mutex_)` in `class basics::PathSensitive`] +codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock::complicated_bad, 131, DEADLOCK, no_bucket, ERROR, [In method `basics::SelfDeadlock::complicated_bad`, locks `&(this->mutex_)` in `class basics::SelfDeadlock`, locks `&(this->mutex_)` in `class basics::SelfDeadlock`] +codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock::interproc1_bad, 114, DEADLOCK, no_bucket, ERROR, [In method `basics::SelfDeadlock::interproc1_bad`, locks `&(this->mutex_)` in `class basics::SelfDeadlock`,Method call: `basics::SelfDeadlock::interproc2_bad`, locks `&(this->mutex_)` in `class basics::SelfDeadlock`] +codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock::thread_bad, 105, DEADLOCK, no_bucket, ERROR, [In method `basics::SelfDeadlock::thread_bad`, locks `&(this->mutex_)` in `class basics::SelfDeadlock`, locks `&(this->mutex_)` in `class basics::SelfDeadlock`] +codetoanalyze/cpp/starvation/basics.cpp, basics::WithGuard::thread1_bad, 44, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::WithGuard::thread1_bad`, locks `&(this->mutex_1)` in `class basics::WithGuard`, locks `&(this->mutex_2)` in `class basics::WithGuard`,[Trace 2] `basics::WithGuard::thread2_bad`, locks `&(this->mutex_2)` in `class basics::WithGuard`, locks `&(this->mutex_1)` in `class basics::WithGuard`] +codetoanalyze/cpp/starvation/basics.cpp, basics::WithGuard::thread2_bad, 49, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::WithGuard::thread2_bad`, locks `&(this->mutex_2)` in `class basics::WithGuard`, locks `&(this->mutex_1)` in `class basics::WithGuard`,[Trace 2] `basics::WithGuard::thread1_bad`, locks `&(this->mutex_1)` in `class basics::WithGuard`, locks `&(this->mutex_2)` in `class basics::WithGuard`] +codetoanalyze/cpp/starvation/crossfile-1.cpp, CrossFileOne::lock_my_mutex_first_then_the_other, 12, DEADLOCK, no_bucket, ERROR, [[Trace 1] `CrossFileOne::lock_my_mutex_first_then_the_other`, locks `&(this->_mutex)` in `class CrossFileOne`,Method call: `CrossFileTwo::just_lock_my_mutex`, locks `&(other->_mutex)` in `class CrossFileTwo`,[Trace 2] `CrossFileTwo::lock_my_mutex_first_then_the_other`, locks `&(this->_mutex)` in `class CrossFileTwo`,Method call: `CrossFileOne::just_lock_my_mutex`, locks `&(other->_mutex)` in `class CrossFileOne`] +codetoanalyze/cpp/starvation/crossfile-2.cpp, CrossFileTwo::lock_my_mutex_first_then_the_other, 12, DEADLOCK, no_bucket, ERROR, [[Trace 1] `CrossFileTwo::lock_my_mutex_first_then_the_other`, locks `&(this->_mutex)` in `class CrossFileTwo`,Method call: `CrossFileOne::just_lock_my_mutex`, locks `&(other->_mutex)` in `class CrossFileOne`,[Trace 2] `CrossFileOne::lock_my_mutex_first_then_the_other`, locks `&(this->_mutex)` in `class CrossFileOne`,Method call: `CrossFileTwo::just_lock_my_mutex`, locks `&(other->_mutex)` in `class CrossFileTwo`] +codetoanalyze/cpp/starvation/skip.cpp, skipped::Skip::not_skipped_bad, 19, DEADLOCK, no_bucket, ERROR, [In method `skipped::Skip::not_skipped_bad`,Method call: `skipped::Skip::private_deadlock`, locks `&(this->mutex_)` in `class skipped::Skip`, locks `&(this->mutex_)` in `class skipped::Skip`] +codetoanalyze/cpp/starvation/skip.cpp, skipped::SkipTemplate::not_skipped_bad, 44, DEADLOCK, no_bucket, ERROR, [In method `skipped::SkipTemplate::not_skipped_bad`,Method call: `skipped::SkipTemplate::private_deadlock`, locks `&(this->mutex_)` in `class skipped::SkipTemplate`, locks `&(this->mutex_)` in `class skipped::SkipTemplate`] +codetoanalyze/cpp/starvation/skip.cpp, skipped::UseTemplate::foo, 51, DEADLOCK, no_bucket, ERROR, [In method `skipped::UseTemplate::foo`,Method call: `skipped::SkipTemplate::not_skipped_bad`,Method call: `skipped::SkipTemplate::private_deadlock`, locks `&(this->x.mutex_)` in `class skipped::UseTemplate`, locks `&(this->x.mutex_)` in `class skipped::UseTemplate`]