123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)openDive_typesletfresh_key=letnext_key=ref0infun()->incrnext_key;!next_keyletnew_node?(node_kind=Error"no kind")?(node_locality={loc_file="";loc_callstack=[]})()={node_key=fresh_key();node_kind;node_locality;node_is_root=false;node_hidden=false;node_values=None;node_range=Empty;node_taint=None;node_writes_computation=NotDone;node_reads_computation=NotDone;node_writes=[];}moduleNode=Datatype.Make_with_collections(structtypet=nodeincludeDatatype.Serializable_undefinedletname="Dive.Node"letreprs=[new_node()]letcomparen1n2=Datatype.Int.comparen1.node_keyn2.node_keylethashn=n.node_keyletequaln1n2=n1.node_key=n2.node_keyletprettyfmtn=Format.pp_print_intfmtn.node_keyend)moduleDependency=structtypet=dependencyletcomparee1e2=e1.dependency_key-e2.dependency_keylethashe=e.dependency_keyletequale1e2=e1.dependency_key=e2.dependency_keyletdefault={dependency_key=-1;dependency_kind=Data;dependency_origins=[]}endmoduleG=Graph.Imperative.Digraph.ConcreteBidirectionalLabeled(Node)(Dependency)includeGletcreate_node~node_kind~node_localityg=letnode=new_node~node_kind~node_locality()inadd_vertexgnode;nodeletremove_node=remove_vertexletcreate_dependencyg~origin~kindv1v2=letsame_kind(_,e,_)=e.dependency_kind=kindinletmatching_edge=trySome(List.findsame_kind(G.find_all_edgesgv1v2))withNot_found->Noneinlete=matchmatching_edgewith|Some(_,e,_)->e|None->lete={dependency_key=fresh_key();dependency_kind=kind;dependency_origins=[]}inadd_edge_eg(v1,e,v2);ein(* Add origin *)letadd_uniqlx=List.sort_uniqStudia.Writes.compare(x::l)ine.dependency_origins<-add_uniqe.dependency_originsorigin;(v1,e,v2)letremove_dependencygedge=remove_edge_egedgeletremove_dependenciesgnode=iter_pred_e(remove_dependencyg)gnodeletupdate_node_valuesnode~typ~cvalue~taint=letjoin_taintt1t2=letopenEva.Resultsinmatcht1,t2with|Direct,_|_,Direct->Direct|Indirect,_|_,Indirect->Indirect|Untainted,Untainted->Untaintedinnode.node_values<-Some(Option.fold~some:(Cvalue.V.joincvalue)~none:cvaluenode.node_values);node.node_range<-Node_range.(upper_boundnode.node_range(evaluatecvaluetyp));Option.iter(funtaint->node.node_taint<-Some(Option.fold~some:(join_tainttaint)~none:taintnode.node_taint))taintletfind_independant_nodesgroots=letmoduleDfs=Graph.Traverse.Dfs(structincludeG(* Consider the graph as unoriented *)letiter_succfgn=iter_predfgn;iter_succfgnletfold_succfgnacc=letacc=fold_predfgnaccinletacc=fold_succfgnaccinaccend)inletmoduleTable=Hashtbl.Make(Node)inlettable=Table.create13inList.iter(Dfs.prefix_component(funn->Table.addtablentrue)g)roots;fold_vertex(funnacc->ifTable.memtablenthenaccelsen::acc)g[]letbfs?(iter_succ=iter_succ)?(limit=max_int)groots=letmoduleTable=Hashtbl.Make(Node)inletexplored:intTable.t=Table.create13andqueue:(node*int)Queue.t=Queue.create()in(* Add roots to queue *)List.iter(funroot->Queue.add(root,0)queue)roots;(* Iterate over the queue *)whilenot(Queue.is_emptyqueue)dolet(n,d)=Queue.takequeueinifd<=limit&¬(Table.memexploredn)thenbeginTable.addexplorednd;iter_succ(funn'->Queue.add(n',d+1)queue)gnenddone;(* Convert the result to list *)Table.fold(funn_l->n::l)explored[]letoutput_to_dotout_channelg=letopenGraph.Graphviz.DotAttributesin(* let g = add_dummy_nodes g in *)letbuild_labels=`HtmlLabel(Extlib.html_escapes)inletmoduleFileTable=Datatype.String.HashtblinletmoduleCallstackTable=Callstack.Hashtblinletfile_table=FileTable.create13andcallstack_table=CallstackTable.create13inletfile_counter=ref0inletcallstack_counter=ref0inletrecbuild_file_subgraphfilename=incrfile_counter;{sg_name="file_"^(string_of_int!file_counter);sg_attributes=[build_labelfilename];sg_parent=None;}andbuild_callstack_subgraph=function|[]->None|(kf,_kinstr)::stack->letparent=get_callstack_subgraphstackinincrcallstack_counter;Some{sg_name="cs_"^(string_of_int!callstack_counter);sg_attributes=[build_label(Kernel_function.get_namekf)];sg_parent=Option.map(funsg->sg.sg_name)parent;}andget_file_subgraphfilename=FileTable.memofile_tablefilenamebuild_file_subgraphandget_callstack_subgraphcs=CallstackTable.memocallstack_tablecsbuild_callstack_subgraphinletmoduleDot=Graph.Graphviz.Dot(structincludeGletgraph_attributes_g=[]letdefault_vertex_attributes_g=[]letvertex_namev="cp"^(string_of_intv.node_key)letvertex_attributesv=letl=ref[]inlettext=Pretty_utils.to_stringNode_kind.prettyv.node_kindiniftext<>""thenl:=build_labeltext::!l;letkind=matchv.node_kindwith|Scalar_->[`Shape`Box]|Composite_->[`Shape`Box3d]|Scattered_->[`Shape`Parallelogram]|Unknown_->[`Shape`Diamond;`Color0xff0000]|Alarm_->[`Shape`Doubleoctagon;`Style`Bold;`Color0xff0000;`Style`Filled;`Fillcolor0xff0000]|AbsoluteMemory|String_->[`Shape`Box3d]|Const_->[`Shape`Ellipse]|Error_->[`Color0xff0000]andrange=matchv.node_rangewith|Empty->[]|Singleton->[`Color0x88aaff;`Style`Filled;`Fillcolor0xaaccff]|Normal_->[`Color0x004400;`Style`Filled;`Fillcolor0xeeffee]|Wide->[`Color0xff0000;`Style`Filled;`Fillcolor0xffbbbb]inl:=range@kind@!l;ifv.node_writes_computation<>Donethenl:=[`Style`Dotted]@!l;ifv.node_is_rootthenl:=[`Style`Bold]@!l;!lletget_subgraphv=let{loc_file;loc_callstack}=v.node_localityinmatchloc_callstackwith|[]->Some(get_file_subgraphloc_file)|cs->get_callstack_subgraphcsletdefault_edge_attributes_g=[]letedge_attributes(_v1,e,_v2)=letkind_attribute=matche.dependency_kindwith|Callee->[`Color0x00ff00]|_->[]andfolding_attribute=matche.dependency_originswith|[]|[_]->[]|_->[`Style`Bold]inkind_attribute@folding_attributeend)inDot.output_graphout_channelg