@ -6,42 +6,88 @@
* )
* )
open ! IStd
open ! IStd
module L = Logging
module L = Logging
module IdMap = Procname . Hash
let iter_captured_procs_and_callees f =
let build_from_captured_procs g =
let hashcons_pname =
let pname_tbl : Procname . t IdMap . t = IdMap . create 1001 in
fun pname ->
match IdMap . find_opt pname_tbl pname with
| Some pname' ->
pname'
| None ->
IdMap . add pname_tbl pname pname ; pname
in
let db = ResultsDatabase . get_database () in
let db = ResultsDatabase . get_database () in
(* only load procedure info for those we have a CFG *)
(* only load procedure info for those we have a CFG *)
let stmt =
let stmt =
Sqlite3 . prepare db
Sqlite3 . prepare db
" SELECT proc_name, callees FROM procedures WHERE cfg IS NOT NULL and attr_kind != 0 "
" SELECT proc_name, callees FROM procedures WHERE cfg IS NOT NULL and attr_kind != 0 "
in
in
SqliteUtils . result_fold_rows db ~ log : " creating call graph " stmt ~ init : () ~ f : ( fun () stmt ->
SqliteUtils . result_fold_rows db ~ log : " loading captured procs " stmt ~ init : () ~ f : ( fun () stmt ->
let proc_name = Sqlite3 . column stmt 0 | > Procname . SQLite . deserialize | > hashcons_pname in
let proc_name = Sqlite3 . column stmt 0 | > Procname . SQLite . deserialize in
let callees =
let callees : Procname . t list = Sqlite3 . column stmt 1 | > Procname . SQLiteList . deserialize in
Sqlite3 . column stmt 1 | > Procname . SQLiteList . deserialize | > List . map ~ f : hashcons_pname
f proc_name callees )
in
CallGraph . create_node g proc_name callees )
type hashconsed_procname_info =
{ name : Procname . t ; mutable defined : bool ; mutable callees : Procname . t list }
let hashcons_pname pname_info pname =
match Procname . Hash . find_opt pname_info pname with
| Some { name } ->
name
| None ->
Procname . Hash . add pname_info pname { name = pname ; defined = false ; callees = [] } ;
pname
let hashcons_and_update_pname pname_info pname callees =
let callees = List . map ~ f : ( hashcons_pname pname_info ) callees in
match Procname . Hash . find_opt pname_info pname with
| Some info when info . defined ->
L . die InternalError " SyntacticCallGraph: Tried to define %a twice.@. " Procname . pp pname
| Some info ->
info . callees <- callees ;
info . defined <- true
| None ->
Procname . Hash . add pname_info pname { name = pname ; defined = true ; callees }
(* load and hashcons all captured procs and their callees ; return also number of defined procs *)
let pname_info_from_captured_procs () =
let pname_info = Procname . Hash . create 1009 in
let n_captured = ref 0 in
iter_captured_procs_and_callees ( fun pname callees ->
incr n_captured ;
hashcons_and_update_pname pname_info pname callees ) ;
( pname_info , ! n_captured )
let enqueue q pname = Procname . HashQueue . enqueue_back q pname pname | > ignore
let dequeue q = Procname . HashQueue . dequeue_front q
let queue_from_sources pname_info sources =
let q = Procname . HashQueue . create () in
List . iter sources ~ f : ( fun sf ->
SourceFiles . proc_names_of_source sf
| > List . iter ~ f : ( fun pname -> hashcons_pname pname_info pname | > enqueue q ) ) ;
q
let rec bfs pname_info g q =
match dequeue q with
| Some pname ->
( match Procname . Hash . find_opt pname_info pname with
| Some { defined = true ; callees } ->
CallGraph . create_node g pname callees ;
List . iter callees ~ f : ( fun pname ->
if not ( CallGraph . mem_procname g pname ) then enqueue q pname )
| _ ->
() ) ;
bfs pname_info g q
| _ ->
()
let build_from_sources sources =
let build_from_sources sources =
let g = CallGraph . create CallGraph . default_initial_capacity in
let g = CallGraph . create CallGraph . default_initial_capacity in
let time0 = Mtime_clock . counter () in
let time0 = Mtime_clock . counter () in
L . progress " Building call graph...@ \n %! " ;
L . progress " Building call graph...@ \n %! " ;
build_from_captured_procs g ;
let pname_info , n_captured = pname_info_from_captured_procs () in
let n_captured = CallGraph . n_procs g in
let q = queue_from_sources pname_info sources in
List . iter sources ~ f : ( fun sf ->
bfs pname_info g q ;
SourceFiles . proc_names_of_source sf | > List . iter ~ f : ( CallGraph . flag_reachable g ) ) ;
CallGraph . remove_unflagged_and_unflag_all g ;
CallGraph . trim_id_map g ;
if Config . debug_level_analysis > 0 then CallGraph . to_dotty g " syntactic_callgraph.dot " ;
if Config . debug_level_analysis > 0 then CallGraph . to_dotty g " syntactic_callgraph.dot " ;
L . progress
L . progress
" Built call graph in %a, from %d total procs, %d reachable defined procs and takes %d bytes@. "
" Built call graph in %a, from %d total procs, %d reachable defined procs and takes %d bytes@. "