[traces] add matches function for extra flexibility in expanding traces

Summary:
Expanding traces currently works in the following way:
Given a `TraceElem.Kind` `k` we want to report in `foo`, we look for a callee `C` of `foo` that has a `TraceElem.Kind` equal to `k` in its summary, grab the summary for `C`, then repeat until we bottom out.
This isn't very flexible: it insists on equality between `TraceElem.Kind`'s as the criteria for expanding a trace.

This diff introduces a new `matches` function for deciding when to expand a trace from a caller into a callee.
Clients that don't want strict equality can implement a fuzzier kind of equality inside this function.
I've gone ahead and done this for the trace elemes of thread-safety.
In the near future, equivalent access paths won't always compare equal from caller to callee, so we want to match their suffixes instead.

Reviewed By: jvillard

Differential Revision: D5914118

fbshipit-source-id: 233c603
master
Sam Blackshear 7 years ago committed by Facebook Github Bot
parent ca23ed5f5f
commit 983bcbbae7

@ -15,6 +15,8 @@ module L = Logging
module GlobalVar = struct
include Pvar
let matches ~caller ~callee = Pvar.equal caller callee
let pp fmt v =
F.fprintf fmt "%a|%a" Mangled.pp (Pvar.get_name v) Pvar.pp_translation_unit
(Pvar.get_translation_unit v)

@ -96,6 +96,8 @@ module Dummy = struct
let compare = compare
let matches ~caller ~callee = Int.equal 0 (compare caller callee)
let pp = pp
end

@ -1108,7 +1108,7 @@ let trace_of_pname orig_sink orig_pdesc callee_pname =
match Summary.read_summary orig_pdesc callee_pname with
| Some {accesses}
-> get_all_accesses
(fun access -> Int.equal (Access.compare (PathDomain.Sink.kind access) orig_access) 0)
(fun access -> Access.matches ~caller:orig_access ~callee:(PathDomain.Sink.kind access))
accesses
| _
-> PathDomain.empty

@ -19,6 +19,25 @@ module Access = struct
| InterfaceCall of Typ.Procname.t
[@@deriving compare]
let suffix_matches (_, accesses1) (_, accesses2) =
match (List.rev accesses1, List.rev accesses2) with
| access1 :: _, access2 :: _
-> AccessPath.equal_access access1 access2
| _
-> false
let matches ~caller ~callee =
match (caller, callee) with
| Read ap1, Read ap2 | Write ap1, Write ap2
-> suffix_matches ap1 ap2
| ContainerRead (ap1, pname1), ContainerRead (ap2, pname2)
| ContainerWrite (ap1, pname1), ContainerWrite (ap2, pname2)
-> Typ.Procname.equal pname1 pname2 && suffix_matches ap1 ap2
| InterfaceCall pname1, InterfaceCall pname2
-> Typ.Procname.equal pname1 pname2
| _
-> false
let make_field_access access_path ~is_write =
if is_write then Write access_path else Read access_path

@ -20,6 +20,10 @@ module Access : sig
(** Call to method of interface not annotated with @ThreadSafe *)
[@@deriving compare]
val matches : caller:t -> callee:t -> bool
(** returns true if the caller access matches the callee access after accounting for mismatch
between the formals and actuals *)
val get_access_path : t -> AccessPath.t option
val equal : t -> t -> bool

@ -128,10 +128,10 @@ end
module Expander (TraceElem : TraceElem.S) = struct
let expand elem0 ~elems_passthroughs_of_pname ~filter_passthroughs =
let rec expand_ elem (elems_passthroughs_acc, seen_acc) =
let elem_site = TraceElem.call_site elem in
let elem_kind = TraceElem.kind elem in
let seen_acc' = CallSite.Set.add elem_site seen_acc in
let elems, passthroughs = elems_passthroughs_of_pname (CallSite.pname elem_site) in
let caller_elem_site = TraceElem.call_site elem in
let caller_elem_kind = TraceElem.kind elem in
let seen_acc' = CallSite.Set.add caller_elem_site seen_acc in
let elems, passthroughs = elems_passthroughs_of_pname (CallSite.pname caller_elem_site) in
let is_recursive callee_elem seen =
CallSite.Set.mem (TraceElem.call_site callee_elem) seen
in
@ -139,7 +139,7 @@ module Expander (TraceElem : TraceElem.S) = struct
let matching_elems =
List.filter
~f:(fun callee_elem ->
[%compare.equal : TraceElem.Kind.t] (TraceElem.kind callee_elem) elem_kind
TraceElem.Kind.matches ~caller:caller_elem_kind ~callee:(TraceElem.kind callee_elem)
&& not (is_recursive callee_elem seen_acc'))
elems
in
@ -148,7 +148,7 @@ module Expander (TraceElem : TraceElem.S) = struct
| callee_elem :: _
-> (* TODO: pick the shortest path to a sink here instead (t14242809) *)
let filtered_passthroughs =
filter_passthroughs elem_site (TraceElem.call_site callee_elem) passthroughs
filter_passthroughs caller_elem_site (TraceElem.call_site callee_elem) passthroughs
in
expand_ callee_elem ((elem, filtered_passthroughs) :: elems_passthroughs_acc, seen_acc')
| _

@ -13,6 +13,12 @@ module F = Format
module type Kind = sig
type t [@@deriving compare]
val matches : caller:t -> callee:t -> bool
(** Return true if the [caller] element kind matches the [callee] element kind. Used during trace
expansion; we will only consider expanding the trace from caller into callee if this
evaluates to true. This can normally just be [equal], but something fuzzier may be required
if [t] is a type that contains identifiers from the caller/callee *)
val pp : F.formatter -> t -> unit
end

@ -20,6 +20,8 @@ module SourceKind = struct
| Other (** for testing or uncategorized sources *)
[@@deriving compare]
let matches ~caller ~callee = Int.equal 0 (compare caller callee)
let of_string = function
| "CommandLineFlag"
-> L.die UserError "User-specified CommandLineFlag sources are not supported"
@ -146,6 +148,8 @@ module SinkKind = struct
| Other (** for testing or uncategorized sinks *)
[@@deriving compare]
let matches ~caller ~callee = Int.equal 0 (compare caller callee)
let of_string = function
| "Allocation"
-> Allocation

@ -20,6 +20,8 @@ module SourceKind = struct
| UserControlledURI (** resource locator from the browser bar *)
[@@deriving compare]
let matches ~caller ~callee = Int.equal 0 (compare caller callee)
let of_string = function
| "Intent"
-> Intent
@ -182,6 +184,8 @@ module SinkKind = struct
| Other (** for testing or uncategorized sinks *)
[@@deriving compare]
let matches ~caller ~callee = Int.equal 0 (compare caller callee)
let of_string = function
| "CreateFile"
-> CreateFile

@ -11,7 +11,11 @@ open! IStd
module F = Format
module MockTrace = Trace.Make (struct
module MockTraceElem = CallSite
module MockTraceElem = struct
include CallSite
let matches ~caller ~callee = equal caller callee
end
module Source = Source.Make (struct
include MockTraceElem

@ -14,6 +14,8 @@ module F = Format
module MockTraceElem = struct
type t = Kind1 | Kind2 | Footprint [@@deriving compare]
let matches ~caller ~callee = Int.equal 0 (compare caller callee)
let call_site _ = CallSite.dummy
let kind t = t
@ -33,6 +35,8 @@ module MockTraceElem = struct
let compare = compare
let matches = matches
let pp = pp
end

Loading…
Cancel
Save