[clang] clang pointer map becomes a clang pointer hashtbl

Summary:
Spacetime profiling showed that this was allocating a lot more than it needs
to. Switching to a `Hashtbl` makes the memory overhead go away, and halves the
memory consumption of the whole frontend on some pathological files.

This also brings the file "clang_ast_main.ml" into the infer repo, renamed to
"ClangPointers.ml" (and given an mli). It's useful to have something like
"clang_ast_main.ml" checked into the facebook-clang-plugins repo to illustrate
how to use the OCaml AST visitor generated by atdgen, but it can be simplified
to be more pedagogical instead of being the visitor used in infer.

Reviewed By: jberdine

Differential Revision: D4884383

fbshipit-source-id: 88f324a
master
Jules Villard 8 years ago committed by Facebook Github Bot
parent aa50d90a7d
commit 1727bd4125

1
.gitignore vendored

@ -137,7 +137,6 @@ buck-out/
/infer/src/clang/clang_ast_v.ml
/infer/src/clang/clang_ast_v.mli
/infer/src/clang/clang_ast_visit.ml
/infer/src/clang/clang_ast_main.ml
/infer/src/clang/clang_ast_types.ml
/infer/annotations/annot_classes/

@ -109,8 +109,7 @@ CLANG_ATDGEN_STUBS = $(addprefix $(CLANG_ATDGEN_STUB_BASE), $(CLANG_ATDGEN_SUFFI
FCP_CLANG_AST_PROJ = $(addprefix $(FCP_CLANG_OCAML_BUILD_DIR)/, \
clang_ast_proj.ml clang_ast_proj.mli)
FCP_CLANG_AST_MAIN = $(addprefix $(FCP_CLANG_OCAML_DIR)/, \
clang_ast_visit.ml clang_ast_main.ml clang_ast_types.ml)
FCP_CLANG_AST_MAIN = $(addprefix $(FCP_CLANG_OCAML_DIR)/, clang_ast_visit.ml clang_ast_types.ml)
FCP_FILES_TO_MIRROR = $(FCP_CLANG_AST_PROJ) $(FCP_CLANG_AST_MAIN)
INFER_CLANG_FCP_MIRRORED_FILES = $(addprefix $(CLANG_SOURCES)/, $(notdir $(FCP_FILES_TO_MIRROR)))

@ -53,7 +53,6 @@ let init_global_state_for_capture_and_linters source_file => {
register_perf_stats_report source_file;
Config.curr_language := Config.Clang;
DB.Results_dir.init source_file;
Clang_ast_main.reset_cache ();
CFrontend_config.reset_global_state ()
};
@ -91,12 +90,7 @@ let run_clang_frontend ast_source => {
| `Pipe _ =>
Format.fprintf fmt "stdin of %a" SourceFile.pp trans_unit_ctx.CFrontend_config.source_file
};
let (decl_index, stmt_index, type_index, ivar_to_property_index) =
Clang_ast_main.index_node_pointers ast_decl;
CFrontend_config.pointer_decl_index := decl_index;
CFrontend_config.pointer_stmt_index := stmt_index;
CFrontend_config.pointer_type_index := type_index;
CFrontend_config.ivar_to_property_index := ivar_to_property_index;
ClangPointers.populate_all_tables ast_decl;
Logging.out "Clang frontend action is %s@\n" Config.clang_frontend_action_string;
Logging.out
"Start %s of AST from %a@\n" Config.clang_frontend_action_string pp_ast_filename ast_source;

@ -0,0 +1,112 @@
(*
* 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! IStd
type t = Clang_ast_t.pointer
module Map = Map.Make(Int)
let ivar_to_property_table = Int.Table.create ~size:256 ()
let pointer_decl_table = Int.Table.create ~size:256 ()
let pointer_stmt_table = Int.Table.create ~size:256 ()
let pointer_type_table = Int.Table.create ~size:256 ()
let empty_v = Clang_ast_visit.empty_visitor
(* This function is not thread-safe *)
let visit_ast
?(visit_decl=empty_v) ?(visit_stmt=empty_v) ?(visit_type=empty_v) ?(visit_src_loc=empty_v)
top_decl =
Clang_ast_visit.decl_visitor := visit_decl;
Clang_ast_visit.stmt_visitor := visit_stmt;
Clang_ast_visit.type_visitor := visit_type;
Clang_ast_visit.source_location_visitor := visit_src_loc;
match Clang_ast_v.validate_decl [] top_decl (* visit *) with
| None ->
()
| Some error ->
failwithf "ERROR: visiting the clang AST failed with error %s"
(Ag_util.Validation.string_of_error error)
let get_ptr_from_node node =
match node with
| `DeclNode decl ->
let decl_info = Clang_ast_proj.get_decl_tuple decl in
decl_info.Clang_ast_t.di_pointer
| `StmtNode stmt ->
let stmt_info,_ = Clang_ast_proj.get_stmt_tuple stmt in
stmt_info.Clang_ast_t.si_pointer
| `TypeNode c_type ->
let type_info = Clang_ast_proj.get_type_tuple c_type in
type_info.Clang_ast_t.ti_pointer
let get_val_from_node node =
match node with
| `DeclNode decl -> decl
| `StmtNode stmt -> stmt
| `TypeNode c_type -> c_type
let add_node_to_cache node cache =
let key = get_ptr_from_node node in
let data = get_val_from_node node in
Int.Table.set cache ~key ~data
let process_decl _ decl =
add_node_to_cache (`DeclNode decl) pointer_decl_table;
match decl with
| Clang_ast_t.ObjCPropertyDecl (_, _, {opdi_ivar_decl=Some decl_ref}) ->
let ivar_pointer = decl_ref.Clang_ast_t.dr_decl_pointer in
Int.Table.set ivar_to_property_table ~key:ivar_pointer ~data:decl
| _ -> ()
let add_stmt_to_cache _ stmt =
add_node_to_cache (`StmtNode stmt) pointer_stmt_table
let add_type_to_cache _ c_type =
add_node_to_cache (`TypeNode c_type) pointer_type_table
let previous_sloc = { Clang_ast_t.sl_file = None; sl_line = None; sl_column = None }
let get_sloc current previous =
match current with
| None -> previous
| Some _ -> current
let mutate_sloc sloc file line column =
let open Clang_ast_t in
sloc.sl_file <- file;
sloc.sl_line <- line;
sloc.sl_column <- column
let reset_sloc sloc = mutate_sloc sloc None None None
let complete_source_location _ source_loc =
let open Clang_ast_t in
let file = get_sloc source_loc.sl_file previous_sloc.sl_file in
let line = get_sloc source_loc.sl_line previous_sloc.sl_line in
let column = get_sloc source_loc.sl_column previous_sloc.sl_column in
mutate_sloc source_loc file line column;
mutate_sloc previous_sloc file line column
let reset_cache () =
Int.Table.clear pointer_decl_table;
Int.Table.clear pointer_stmt_table;
Int.Table.clear pointer_type_table;
Int.Table.clear ivar_to_property_table;
reset_sloc previous_sloc
(* This function is not thread-safe *)
let populate_all_tables top_decl =
reset_cache ();
(* populate caches *)
visit_ast
~visit_decl:process_decl
~visit_stmt:add_stmt_to_cache
~visit_type:add_type_to_cache
~visit_src_loc:complete_source_location top_decl

@ -0,0 +1,30 @@
(*
* Copyright (c) 2017 - 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! IStd
(** pointers produced by the AST exporter to represent sharing in the AST *)
type t = Clang_ast_t.pointer
module Map : module type of Map.Make(Int)
(** maps ivar decl pointer to its decl record *)
val ivar_to_property_table : Clang_ast_t.decl Int.Table.t
(** maps decl pointer to its decl record *)
val pointer_decl_table : Clang_ast_t.decl Int.Table.t
(** maps stmt pointer to its stmt record *)
val pointer_stmt_table : Clang_ast_t.stmt Int.Table.t
(** map pointer to its type *)
val pointer_type_table : Clang_ast_t.c_type Int.Table.t
(** discover what pointers should point to in the tables above; should be run once for the current
toplevel decl *)
val populate_all_tables : Clang_ast_t.decl -> unit

@ -61,9 +61,9 @@ let type_from_unary_expr_or_type_trait_expr_info info =
| None -> None
let get_decl decl_ptr =
try
Some (Clang_ast_main.PointerMap.find decl_ptr !CFrontend_config.pointer_decl_index)
with Not_found -> Logging.out "decl with pointer %d not found\n" decl_ptr; None
let decl = Int.Table.find ClangPointers.pointer_decl_table decl_ptr in
if Option.is_none decl then Logging.out "decl with pointer %d not found\n" decl_ptr;
decl
let get_decl_opt decl_ptr_opt =
match decl_ptr_opt with
@ -71,9 +71,9 @@ let get_decl_opt decl_ptr_opt =
| None -> None
let get_stmt stmt_ptr =
try
Some (Clang_ast_main.PointerMap.find stmt_ptr !CFrontend_config.pointer_stmt_index)
with Not_found -> Logging.out "stmt with pointer %d not found\n" stmt_ptr; None
let stmt = Int.Table.find ClangPointers.pointer_stmt_table stmt_ptr in
if Option.is_none stmt then Logging.out "stmt with pointer %d not found\n" stmt_ptr;
stmt
let get_stmt_opt stmt_ptr_opt =
match stmt_ptr_opt with
@ -86,41 +86,36 @@ let get_decl_opt_with_decl_ref decl_ref_opt =
| None -> None
let get_property_of_ivar decl_ptr =
try
Some (Clang_ast_main.PointerMap.find decl_ptr !CFrontend_config.ivar_to_property_index)
with Not_found -> Logging.out "property with pointer %d not found\n" decl_ptr; None
let decl = Int.Table.find ClangPointers.ivar_to_property_table decl_ptr in
if Option.is_none decl then Logging.out "property with pointer %d not found\n" decl_ptr;
decl
let update_sil_types_map type_ptr sil_type =
CFrontend_config.sil_types_map :=
Clang_ast_extend.TypePointerMap.add type_ptr sil_type !CFrontend_config.sil_types_map
let update_enum_map enum_constant_pointer sil_exp =
let open Clang_ast_main in
let (predecessor_pointer_opt, _) =
try PointerMap.find enum_constant_pointer !CFrontend_config.enum_map
with Not_found -> assert false in
ClangPointers.Map.find_exn !CFrontend_config.enum_map enum_constant_pointer in
let enum_map_value = (predecessor_pointer_opt, Some sil_exp) in
CFrontend_config.enum_map :=
PointerMap.add enum_constant_pointer enum_map_value !CFrontend_config.enum_map
ClangPointers.Map.add !CFrontend_config.enum_map ~key:enum_constant_pointer ~data:enum_map_value
let add_enum_constant enum_constant_pointer predecessor_pointer_opt =
let enum_map_value = (predecessor_pointer_opt, None) in
CFrontend_config.enum_map :=
Clang_ast_main.PointerMap.add enum_constant_pointer enum_map_value !CFrontend_config.enum_map
ClangPointers.Map.add !CFrontend_config.enum_map ~key:enum_constant_pointer ~data:enum_map_value
let get_enum_constant_exp enum_constant_pointer =
Clang_ast_main.PointerMap.find enum_constant_pointer !CFrontend_config.enum_map
ClangPointers.Map.find_exn !CFrontend_config.enum_map enum_constant_pointer
let get_type type_ptr =
(* There is chance for success only if type_ptr is in fact clang pointer *)
match type_ptr with
| Clang_ast_types.TypePtr.Ptr raw_ptr ->
(try
Some (Clang_ast_main.PointerMap.find raw_ptr !CFrontend_config.pointer_type_index)
with Not_found ->
Logging.out "type with pointer %d not found\n" raw_ptr;
None
)
let typ = Int.Table.find ClangPointers.pointer_type_table raw_ptr in
if Option.is_none typ then Logging.out "type with pointer %d not found\n" raw_ptr;
typ
| _ ->
(* otherwise, function fails *)
let type_str = Clang_ast_extend.type_ptr_to_string type_ptr in

@ -93,21 +93,13 @@ let modeled_function_attributes = [replace_with_deref_first_arg_attr]
(** Global state *)
let enum_map = ref Clang_ast_main.PointerMap.empty
let enum_map = ref ClangPointers.Map.empty
let global_translation_unit_decls : Clang_ast_t.decl list ref = ref []
let ivar_to_property_index = ref Clang_ast_main.PointerMap.empty
let log_out = ref Format.std_formatter
let pointer_decl_index = ref Clang_ast_main.PointerMap.empty
let pointer_stmt_index = ref Clang_ast_main.PointerMap.empty
let pointer_type_index = ref Clang_ast_main.PointerMap.empty
let sil_types_map = ref Clang_ast_extend.TypePointerMap.empty
let reset_global_state () =
enum_map := Clang_ast_main.PointerMap.empty;
enum_map := ClangPointers.Map.empty;
global_translation_unit_decls := [];
ivar_to_property_index := Clang_ast_main.PointerMap.empty;
log_out := Format.std_formatter;
pointer_decl_index := Clang_ast_main.PointerMap.empty;
pointer_stmt_index := Clang_ast_main.PointerMap.empty;
pointer_type_index := Clang_ast_main.PointerMap.empty;
sil_types_map := Clang_ast_extend.TypePointerMap.empty;

@ -93,15 +93,9 @@ val modeled_function_attributes : string list
(** Global state *)
(** Map from enum constants pointers to their predecesor and their sil value *)
val enum_map : (Clang_ast_t.pointer option * Exp.t option) Clang_ast_main.PointerMap.t ref
val enum_map : (Clang_ast_t.pointer option * Exp.t option) ClangPointers.Map.t ref
val global_translation_unit_decls : Clang_ast_t.decl list ref
val ivar_to_property_index : Clang_ast_t.decl Clang_ast_main.PointerMap.t ref
val log_out : Format.formatter ref
val pointer_decl_index : Clang_ast_t.decl Clang_ast_main.PointerMap.t ref
val pointer_stmt_index : Clang_ast_t.stmt Clang_ast_main.PointerMap.t ref
(** Map from clang pointers to types produced by ast exporter. Populated once on startup *)
val pointer_type_index : Clang_ast_t.c_type Clang_ast_main.PointerMap.t ref
(** Map from type pointers (clang pointers and types created later by frontend) to sil types
Populated during frontend execution when new type is found *)

@ -1677,8 +1677,8 @@ struct
{ empty_res_trans with root_nodes = trans_state.succ_nodes }
and init_dynamic_array trans_state array_exp_typ array_stmt_info dynlength_stmt_pointer =
let dynlength_stmt = Clang_ast_main.PointerMap.find dynlength_stmt_pointer
!CFrontend_config.pointer_stmt_index in
let dynlength_stmt = Int.Table.find_exn ClangPointers.pointer_stmt_table
dynlength_stmt_pointer in
let dynlength_stmt_info, _ = Clang_ast_proj.get_stmt_tuple dynlength_stmt in
let trans_state_pri = PriorityNode.try_claim_priority_node trans_state array_stmt_info in
let dynlength_trans_result = instruction trans_state_pri dynlength_stmt in

Loading…
Cancel
Save