123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: 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, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Abstraction of the heap *)openMopsaopenSig.Abstraction.DomainopenAstmodulePool=Framework.Lattices.Powerset.Make(structtypet=addrletcompare=compare_addrletprint=unformatpp_addrend)type('a,_)query+=|Q_alive_addresses:('a,addrlist)query|Q_alive_addresses_aspset:('a,Pool.t)query|Q_allocated_addresses_aspset:('a,Pool.t)querylet()=register_query{join=(letf:typear.query_pool->(a,r)query->r->r->r=funnextqueryab->matchquerywith|Q_alive_addresses->List.sort_uniqcompare_addr(a@b)|Q_alive_addresses_aspset->Pool.joinab|Q_allocated_addresses_aspset->Pool.joinab|_->next.pool_joinqueryabinf);meet=(letf:typear.query_pool->(a,r)query->r->r->r=funnextqueryab->matchquerywith|Q_alive_addresses->assertfalse|Q_alive_addresses_aspset->Pool.meetab|Q_allocated_addresses_aspset->Pool.meetab|_->next.pool_meetqueryabinf);}letname="universal.heap.recency"letopt_default_allocation_policy:stringref=ref"range_callstack"let()=Policies.register_optionopt_default_allocation_policyname"-default-alloc-pol""by default"(fun_ak->(Policies.of_string!opt_default_allocation_policy)ak)letgc_time=ref0.letgc_nb_collections=ref0letgc_nb_addr_collected=ref0letgc_max_heap_size=ref0(** {2 Domain definition} *)(** ===================== *)typestmt_kind+=|S_perform_gclet()=register_stmt_with_visitor{compare=(funnexts1s2->matchskinds1,skinds2with|_->nexts1s2);print=(fundefaultfmtstmt->matchskindstmtwith|S_perform_gc->Format.fprintffmt"Abstract GC call"|_->defaultfmtstmt);visit=(fundefaultstmt->matchskindstmtwith|S_perform_gc->leafstmt|_->defaultstmt);}moduleDomain=struct(** Domain header *)(** ============= *)typet=Pool.tincludeGenDomainId(structtypenonrect=tletname=nameend)letbottom=Pool.bottomlettop=Pool.topletchecks=[](** Lattice operators *)(** ================= *)letis_bottom_=falseletsubset=Pool.subsetletjoin=Pool.joinletmeet=Pool.meetletwidenctx=Pool.joinletmergepre(a,e)(a',e')=assertfalse(** Initialization *)(** ============== *)letinitprogmanflow=set_envT_curPool.emptymanflow|>Option.some(** Post-conditions *)(** *************** *)letis_recentaddr=addr.addr_mode=STRONGletis_oldaddr=addr.addr_mode=WEAKletexecstmtmanflow=letrange=srangestmtinmatchskindstmtwith(* 𝕊⟦ free(recent); ⟧ *)|S_freeaddrwhenis_recentaddr->letold={addrwithaddr_mode=WEAK}inget_envT_curmanflow>>$?funpoolflow->(* Inform domains to remove addr *)man.exec(mk_remove_addraddrstmt.srange)flow>>%?funflow'->ifnot(Pool.memoldpool)then(* only recent is present : remove it from the pool and return *)map_envT_cur(Pool.removeaddr)manflow'|>OptionExt.returnelse(* old is present : expand it as the new recent *)man.exec(mk_expand_addrold[addr]stmt.srange)flow'|>OptionExt.return(* 𝕊⟦ free(old); ⟧ *)|S_freeaddrwhenis_oldaddr->(* Inform domains to invalidate addr *)map_envT_cur(Pool.removeaddr)manflow>>%?funflow->man.exec(mk_invalidate_addraddrstmt.srange)flow|>OptionExt.return|S_perform_gc->letstartt=Sys.time()inget_envT_curmanflow>>$?funallflow->letalive=ask_and_reduceman.askQ_alive_addresses_aspsetflowinletdead=Pool.diffallaliveindebug"at %a, |dead| = %d@.dead = %a"pp_rangerange(Pool.cardinaldead)(formatPool.print)dead;lettrange=tag_rangerange"agc"in(* let's free weak addresses first, and then the strong ones *)letdead_strong,dead_weak=Pool.partition(funaddr->is_recentaddr)deadinletpost=Pool.fold(funaddracc->debug"free %a"pp_addraddr;acc>>%man.exec(mk_stmt(S_freeaddr)trange))dead_weak(Post.returnflow)inpost>>%(funflow->letpost=Pool.fold(funaddracc->debug"free %a"pp_addraddr;acc>>%man.exec(mk_stmt(S_freeaddr)trange))dead_strong(Post.returnflow)inletdelta=Sys.time()-.starttingc_time:=!gc_time+.delta;incrgc_nb_collections;gc_nb_addr_collected:=!gc_nb_addr_collected+(Pool.cardinaldead);gc_max_heap_size:=max!gc_max_heap_size(Pool.cardinalall);post)|>OptionExt.return|_->None(** Evaluations *)(** *********** *)letevalexprmanflow=letrange=erangeexprinmatchekindexprwith|E_alloc_addr(addr_kind,STRONG)->get_envT_curmanflow>>$?funpoolflow->letrecent_addr=Policies.mk_addraddr_kindSTRONGrange(Flow.get_callstackflow)inifnot(Pool.memrecent_addrpool)then(* first allocation at this site: just add the address to the pool and return it *)map_envT_cur(Pool.addrecent_addr)manflow>>%?funflow->Eval.singleton(mk_addrrecent_addrrange)flow|>OptionExt.returnelseletold_addr=Policies.mk_addraddr_kindWEAKrange(Flow.get_callstackflow)inifnot(Pool.memold_addrpool)then(* old address not present: rename the existing recent as old and return the new recent *)map_envT_cur(Pool.addold_addr)manflow>>%?funflow->man.exec(mk_rename_addrrecent_addrold_addrrange)flow>>%?funflow->Eval.singleton(mk_addrrecent_addrrange)flow|>OptionExt.returnelse(* old present : copy the content of the existing recent to old using `fold` statement *)man.exec(mk_fold_addrold_addr[recent_addr]range)flow>>%?funflow->Eval.singleton(mk_addrrecent_addrrange)flow|>OptionExt.return|E_alloc_addr(addr_kind,WEAK)->get_envT_curmanflow>>$?funpoolflow->letweak_addr=Policies.mk_addraddr_kindWEAKrange(Flow.get_callstackflow)inletpost=ifPool.memweak_addrpoolthenPost.returnflowelsemap_envT_cur(Pool.addweak_addr)manflowinpost>>%Eval.singleton(mk_addrweak_addrrange)|>OptionExt.return|_->None(** Queries *)(** ******* *)letask:typer.('a,r)query->('a,t)man->'aflow->('a,r)casesoption=funquerymanflow->matchquerywith|Q_allocated_addresses->get_envT_curmanflow>>$?funpoolflow->ifPool.is_toppoolthenSome(Cases.singleton[]flow)elseSome(Cases.singleton(Pool.elementspool)flow)|Q_allocated_addresses_aspset->Some(get_envT_curmanflow)|_->None(** Pretty printer *)(** ************** *)letprint_stateprinterpool=pprintprinter~path:[Key"heap"](pboxPool.printpool)letprint_exprmanflowprinterexp=()endlet()=register_standard_domain(moduleDomain)