123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)(** Enriched exceptions have an additional field at the end of their usual data
containing a pair composed of the distinguishing [token] and the backtrace
information. We discriminate the token by pointer equality. *)moduleStore=Store.Make()type'at='aStore.fieldtypeinfo=Store.ttypeiexn=exn*infoletmake=Store.fieldletadd=Store.setletget=Store.getletnull=Store.emptyexceptionUniqueletdummy=(Unique,Store.empty)letcurrent:(int*iexn)listref=ref[](** List associating to each thread id the latest exception raised by an
instrumented raise (i.e. {!raise} from this module). It is shared between
threads, so we must take care of this when modifying it.
Invariants: all index keys are unique in the list.
*)letlock=Mutex.create()letrecremove_assoc(i:int)=function|[]->[]|(j,v)::remasl->ifi=jthenremelseletans=remove_associreminifrem==ansthenlelse(j,v)::ansletrecfind_and_remove_assoc(i:int)=function|[]->dummy,[]|(j,v)::remasl->ifi=jthen(v,rem)elselet(r,ans)=find_and_remove_associreminifrem==ansthen(r,l)else(r,(j,v)::ans)typebacktrace=Printexc.raw_backtraceletbacktrace_to_string=Printexc.raw_backtrace_to_stringletbacktrace_info:backtracet=make"exninfo_backtrace"letis_recording=reffalseletrecord_backtraceb=let()=Printexc.record_backtracebinis_recording:=bletget_backtracee=getebacktrace_infoletiraise(e,i)=CThread.with_locklock~scope:(fun()->letid=Thread.id(Thread.self())incurrent:=(id,(e,i))::remove_associd!current);matchgetibacktrace_infowith|None->raisee|Somebt->Printexc.raise_with_backtraceebtletfind_and_remove()=CThread.with_locklock~scope:(fun()->letid=Thread.id(Thread.self())inlet(v,l)=find_and_remove_associd!currentinlet()=current:=linv)letinfoe=let(src,data)=find_and_remove()inifsrc==ethen(* Slightly unsound, some exceptions may not be unique up to pointer
equality. Though, it should be quite exceptional to be in a situation
where the following holds:
1. An argument-free exception is raised through the enriched {!raise};
2. It is not captured by any enriched with-clause (which would reset
the current data);
3. The same exception is raised through the standard raise, accessing
the wrong data.
. *)dataelse(* Mismatch: the raised exception is not the one stored, either because the
previous raise was not instrumented, or because something went wrong. *)Store.emptyletcapturee=if!is_recordingthen(* This must be the first function call, otherwise the stack may be
destroyed *)letbt=Printexc.get_raw_backtrace()inletinfo=infoeine,addinfobacktrace_infobtelsee,infoeletreify()=if!is_recordingthenletbt=Printexc.get_callstack50inaddnullbacktrace_infobtelsenull