|
|
|
/*
|
|
|
|
* vim: set ft=rust:
|
|
|
|
* vim: set ft=reason:
|
|
|
|
*
|
|
|
|
* Copyright (c) 2015 - present Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
open! Utils;
|
|
|
|
|
|
|
|
let module F = Format;
|
|
|
|
|
|
|
|
let module L = Logging;
|
|
|
|
|
|
|
|
|
|
|
|
/** Module to manage the table of attributes. */
|
|
|
|
let serializer: Serialization.serializer ProcAttributes.t = Serialization.create_serializer Serialization.attributes_key;
|
|
|
|
|
|
|
|
let attributes_filename pname => {
|
|
|
|
let pname_file = Procname.to_filename pname;
|
|
|
|
pname_file ^ ".attr"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** path to the .attr file for the given procedure in the current results directory */
|
|
|
|
let res_dir_attr_filename pname => {
|
|
|
|
let attr_fname = attributes_filename pname;
|
|
|
|
let bucket_dir = {
|
|
|
|
let base = Filename.chop_extension attr_fname;
|
|
|
|
let len = String.length base;
|
|
|
|
if (len < 2) {
|
|
|
|
Filename.current_dir_name
|
|
|
|
} else {
|
|
|
|
String.sub base (len - 2) 2
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let filename =
|
|
|
|
DB.Results_dir.path_to_filename
|
|
|
|
DB.Results_dir.Abs_root [Config.attributes_dir_name, bucket_dir, attr_fname];
|
|
|
|
DB.filename_create_dir filename;
|
|
|
|
filename
|
|
|
|
};
|
|
|
|
|
|
|
|
let store_attributes proc_attributes => {
|
|
|
|
let proc_name = proc_attributes.ProcAttributes.proc_name;
|
|
|
|
let attributes_file = res_dir_attr_filename proc_name;
|
|
|
|
let should_write =
|
|
|
|
/* only overwrite defined procedures */
|
|
|
|
proc_attributes.ProcAttributes.is_defined || not (DB.file_exists attributes_file);
|
|
|
|
if should_write {
|
|
|
|
Serialization.to_file serializer attributes_file proc_attributes
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let attr_tbl = Procname.Hash.create 16;
|
|
|
|
|
|
|
|
let load_attributes proc_name =>
|
|
|
|
try (Procname.Hash.find attr_tbl proc_name) {
|
|
|
|
| Not_found =>
|
|
|
|
let attributes_file = res_dir_attr_filename proc_name;
|
|
|
|
let attr = Serialization.from_file serializer attributes_file;
|
|
|
|
if (attr != None) {
|
|
|
|
Procname.Hash.add attr_tbl proc_name attr
|
|
|
|
};
|
|
|
|
attr
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Given a procdesure name, find the file where it is defined and */
|
|
|
|
/** its corresponding type environment */
|
|
|
|
let find_tenv_from_class_of_proc procname =>
|
|
|
|
switch (load_attributes procname) {
|
|
|
|
| None => None
|
|
|
|
| Some attrs =>
|
|
|
|
let source_file = attrs.ProcAttributes.loc.Location.file;
|
|
|
|
let source_dir = DB.source_dir_from_source_file source_file;
|
|
|
|
let tenv_fname = DB.source_dir_get_internal_file source_dir ".tenv";
|
|
|
|
Tenv.load_from_file tenv_fname
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Given an ObjC class c, extract the type from the tenv where the class was */
|
|
|
|
/** defined. We do this by adding a method that is unique to each class, and then */
|
|
|
|
/** finding the tenv that corresponds to the class definition. */
|
|
|
|
let get_correct_type_from_objc_class_name c => {
|
|
|
|
let class_method = Procname.get_default_objc_class_method (Mangled.to_string c);
|
|
|
|
switch (find_tenv_from_class_of_proc class_method) {
|
|
|
|
| None => None
|
|
|
|
| Some tenv =>
|
|
|
|
let type_name = Typename.TN_csu (Csu.Class Csu.Objc) c;
|
|
|
|
Option.map (fun st => Typ.Tstruct st) (Tenv.lookup tenv type_name)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Returns true if the method is defined as a C++ model */
|
|
|
|
let pname_is_cpp_model callee_pname =>
|
|
|
|
switch (load_attributes callee_pname) {
|
|
|
|
| Some attrs =>
|
|
|
|
let file = DB.source_file_to_string attrs.ProcAttributes.loc.Location.file;
|
|
|
|
DB.file_is_in_cpp_model file
|
|
|
|
| None => false
|
|
|
|
};
|
|
|
|
|
|
|
|
let is_whitelisted_cpp_method method_name =>
|
|
|
|
IList.exists
|
|
|
|
(
|
|
|
|
fun whitelisting_class =>
|
|
|
|
IList.for_all
|
|
|
|
(
|
|
|
|
fun whitelisting_class_substring =>
|
|
|
|
Utils.string_contains whitelisting_class_substring method_name
|
|
|
|
)
|
|
|
|
whitelisting_class
|
|
|
|
)
|
|
|
|
Config.whitelisted_cpp_methods;
|
|
|
|
|
|
|
|
type t = {
|
|
|
|
num_bindings: int,
|
|
|
|
num_buckets: int,
|
|
|
|
max_bucket_length: int,
|
|
|
|
serialized_size_kb: option int
|
|
|
|
};
|
|
|
|
|
|
|
|
let to_json at => {
|
|
|
|
let extra_field =
|
|
|
|
switch at.serialized_size_kb {
|
|
|
|
| Some v => [("serialized_size_kb", `Int v)]
|
|
|
|
| None => []
|
|
|
|
};
|
|
|
|
`Assoc (
|
|
|
|
[
|
|
|
|
("num_bindings", `Int at.num_bindings),
|
|
|
|
("num_buckets", `Int at.num_buckets),
|
|
|
|
("max_bucket_length", `Int at.max_bucket_length)
|
|
|
|
]
|
|
|
|
@ extra_field
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
let from_json json => {
|
|
|
|
let open! Yojson.Basic.Util;
|
|
|
|
{
|
|
|
|
num_bindings: json |> member "num_bindings" |> to_int,
|
|
|
|
num_buckets: json |> member "num_buckets" |> to_int,
|
|
|
|
max_bucket_length: json |> member "max_bucket_length" |> to_int,
|
|
|
|
serialized_size_kb: json |> member "serialized_size_kb" |> to_option to_int
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let aggregate s => {
|
|
|
|
let all_num_bindings = IList.map (fun stats => float_of_int stats.num_bindings) s;
|
|
|
|
let all_num_buckets = IList.map (fun stats => float_of_int stats.num_buckets) s;
|
|
|
|
let all_max_bucket_length = IList.map (fun stats => float_of_int stats.max_bucket_length) s;
|
|
|
|
let aggr_num_bindings = StatisticsToolbox.compute_statistics all_num_bindings;
|
|
|
|
let aggr_num_buckets = StatisticsToolbox.compute_statistics all_num_buckets;
|
|
|
|
let aggr_max_bucket_length = StatisticsToolbox.compute_statistics all_max_bucket_length;
|
|
|
|
`Assoc [
|
|
|
|
("num_bindings", StatisticsToolbox.to_json aggr_num_bindings),
|
|
|
|
("num_buckets", StatisticsToolbox.to_json aggr_num_buckets),
|
|
|
|
("max_bucket_length", StatisticsToolbox.to_json aggr_max_bucket_length)
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
let stats () => {
|
|
|
|
let stats = Procname.Hash.stats attr_tbl;
|
|
|
|
let {Hashtbl.num_bindings: num_bindings, num_buckets, max_bucket_length} = stats;
|
|
|
|
let serialized_size_kb =
|
|
|
|
Config.developer_mode ? Some (Marshal.data_size (Marshal.to_bytes attr_tbl []) 0 / 1024) : None;
|
|
|
|
{num_bindings, num_buckets, max_bucket_length, serialized_size_kb}
|
|
|
|
};
|