55 * LICENSE file in the root directory of this source tree.
66 *)
77open ! IStd
8- module F = Format
98module L = Logging
10-
11- module type NodeSig = sig
12- type t = private {id : int ; pname : Typ.Procname .t ; successors : int list ; mutable flag : bool }
13-
14- val make : int -> Typ.Procname .t -> int list -> t
15-
16- val set_flag : t -> unit
17-
18- val unset_flag : t -> unit
19-
20- val pp_dot : F .formatter -> t -> unit
21- end
22-
23- module Node : NodeSig = struct
24- type t = {id : int ; pname : Typ.Procname .t ; successors : int list ; mutable flag : bool }
25-
26- let make id pname successors = {id; pname; successors; flag= false }
27-
28- let set_flag n = n.flag < - true
29-
30- let unset_flag n = n.flag < - false
31-
32- let pp_dot fmt {id; pname; successors} =
33- let pp_id fmt id = F. fprintf fmt " N%d" id in
34- let pp_edge fmt src dst = F. fprintf fmt " %a -> %a ;@\n " pp_id src pp_id dst in
35- F. fprintf fmt " %a [ label = %S ];@\n " pp_id id (F. asprintf " %a" Typ.Procname. pp pname) ;
36- List. iter successors ~f: (pp_edge fmt id) ;
37- F. pp_print_newline fmt ()
38- end
39-
409module IdMap = Typ.Procname. Hash
41- module NodeMap = Caml.Hashtbl. Make (Int )
42-
43- (* * [node_map] is a map from ids (unique ints) to nodes corresponding to defined procedures.
44- [id_map] is a map from all encountered (not necessarily defined) procnames to their ids,
45- and thus its image is a superset of the domain of [node_map], and usually a strict superset.
46- [trim_id_map] makes the image equal to the domain of [node_map]. *)
47- type t = {id_map : int IdMap .t ; node_map : Node .t NodeMap .t }
48-
49- let reset {id_map; node_map} = IdMap. reset id_map ; NodeMap. reset node_map
50-
51- let create initial_capacity =
52- {id_map= IdMap. create initial_capacity; node_map= NodeMap. create initial_capacity}
53-
54-
55- let id_of_procname {id_map} pname = IdMap. find_opt id_map pname
56-
57- let node_of_id {node_map} id = NodeMap. find_opt node_map id
58-
59- let mem {node_map} id = NodeMap. mem node_map id
60-
61- (* * [id_map] may contain undefined procedures, so use [node_map] for actual size *)
62- let n_procs {node_map} = NodeMap. length node_map
63-
64- let node_of_procname g pname = id_of_procname g pname |> Option. bind ~f: (node_of_id g)
65-
66- let remove (g : t ) pname id = IdMap. remove g.id_map pname ; NodeMap. remove g.node_map id
67-
68- let add ({id_map; node_map} as graph ) pname successor_pnames =
69- let get_or_set_id procname =
70- match id_of_procname graph procname with
71- | None ->
72- let id = IdMap. length id_map in
73- IdMap. replace id_map procname id ; id
74- | Some id ->
75- id
76- in
77- let id = get_or_set_id pname in
78- let successors = List. map successor_pnames ~f: get_or_set_id in
79- let node = Node. make id pname successors in
80- NodeMap. replace node_map id node
81-
82-
83- let remove_reachable g start_pname =
84- let add_live_successors_and_remove_self init (n : Node.t ) =
85- remove g n.pname n.id ;
86- List. fold n.successors ~init ~f: (fun init succ_id ->
87- node_of_id g succ_id |> Option. fold ~init ~f: (fun acc s -> s :: acc) )
88- in
89- let rec remove_list frontier =
90- if not (List. is_empty frontier) then
91- remove_list (List. fold frontier ~init: [] ~f: add_live_successors_and_remove_self)
92- in
93- node_of_procname g start_pname |> Option. iter ~f: (fun start_node -> remove_list [start_node])
94-
95-
96- let flag_reachable g start_pname =
97- let process_node init (n : Node.t ) =
98- if n.flag then init
99- else (
100- Node. set_flag n ;
101- List. fold n.successors ~init ~f: (fun acc id ->
102- match node_of_id g id with Some n' when not n'.flag -> n' :: acc | _ -> acc ) )
103- in
104- let rec flag_list frontier =
105- if not (List. is_empty frontier) then flag_list (List. fold frontier ~init: [] ~f: process_node)
106- in
107- node_of_procname g start_pname |> Option. iter ~f: (fun start_node -> flag_list [start_node])
108-
109-
110- let remove_unflagged_and_unflag_all {id_map; node_map} =
111- NodeMap. filter_map_inplace
112- (fun _id (n : Node.t ) ->
113- if n.flag then ( Node. unset_flag n ; Some n ) else ( IdMap. remove id_map n.pname ; None ) )
114- node_map
115-
116-
117- (* * remove pnames for all undefined procedures *)
118- let trim_id_map (g : t ) =
119- IdMap. filter_map_inplace (fun _pname id -> Option. some_if (mem g id) id) g.id_map
120-
121-
122- let pp_dot fmt {node_map} =
123- F. fprintf fmt " @\n digraph callgraph {@\n " ;
124- NodeMap. iter (fun _id n -> Node. pp_dot fmt n) node_map ;
125- F. fprintf fmt " }@."
126-
127-
128- let to_dotty g filename =
129- let outc = Filename. concat Config. results_dir filename |> Out_channel. create in
130- let fmt = F. formatter_of_out_channel outc in
131- pp_dot fmt g ; Out_channel. close outc
132-
13310
13411let build_from_captured_procs g =
13512 let hashcons_pname =
@@ -148,27 +25,20 @@ let build_from_captured_procs g =
14825 let callees =
14926 Sqlite3. column stmt 1 |> Typ.Procname.SQLiteList. deserialize |> List. map ~f: hashcons_pname
15027 in
151- add g proc_name callees )
28+ CallGraph. add g proc_name callees )
15229
15330
15431let build_from_sources g sources =
15532 let time0 = Mtime_clock. counter () in
15633 L. progress " Building call graph...@\n %!" ;
15734 build_from_captured_procs g ;
158- let n_captured = n_procs g in
35+ let n_captured = CallGraph. n_procs g in
15936 List. iter sources ~f: (fun sf ->
160- SourceFiles. proc_names_of_source sf |> List. iter ~f: (flag_reachable g) ) ;
161- remove_unflagged_and_unflag_all g ;
162- trim_id_map g ;
163- if Config. debug_level_analysis > 0 then to_dotty g " callgraph.dot" ;
37+ SourceFiles. proc_names_of_source sf |> List. iter ~f: (CallGraph. flag_reachable g) ) ;
38+ CallGraph. remove_unflagged_and_unflag_all g ;
39+ CallGraph. trim_id_map g ;
40+ if Config. debug_level_analysis > 0 then CallGraph. to_dotty g " callgraph.dot" ;
16441 L. progress
16542 " Built call graph in %a, from %d total procs, %d reachable defined procs and takes %d bytes@."
166- Mtime.Span. pp (Mtime_clock. count time0) n_captured (n_procs g)
43+ Mtime.Span. pp (Mtime_clock. count time0) n_captured (CallGraph. n_procs g)
16744 (Obj. (reachable_words (repr g)) * (Sys. word_size / 8 ))
168-
169-
170- let get_unflagged_leaves g =
171- NodeMap. fold
172- (fun _id (n : Node.t ) acc ->
173- if n.flag || List. exists n.successors ~f: (mem g) then acc else n :: acc )
174- g.node_map []
0 commit comments