use ark_std::vec::Vec;
use core::{
any::{type_name, TypeId},
fmt,
marker::PhantomData,
};
use tracing::{span, Dispatch, Metadata, Subscriber};
use tracing_subscriber::{
layer::{self, Layer},
registry::LookupSpan,
};
pub struct ConstraintLayer<S> {
pub mode: TracingMode,
get_context: WithContext,
_subscriber: PhantomData<fn(S)>,
}
#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Debug)]
pub enum TracingMode {
OnlyConstraints,
NoConstraints,
All,
}
pub(crate) struct WithContext(
fn(&Dispatch, &span::Id, f: &mut dyn FnMut(&'static Metadata<'static>, &str) -> bool),
);
impl<S> Layer<S> for ConstraintLayer<S>
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
fn enabled(&self, metadata: &Metadata<'_>, _ctx: layer::Context<'_, S>) -> bool {
match self.mode {
TracingMode::OnlyConstraints => metadata.target() == "gr1cs",
TracingMode::NoConstraints => metadata.target() != "gr1cs",
TracingMode::All => true,
}
}
fn on_new_span(
&self,
_attrs: &span::Attributes<'_>,
_id: &span::Id,
_ctx: layer::Context<'_, S>,
) {
}
#[allow(unsafe_code, trivial_casts)]
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
match id {
id if id == TypeId::of::<Self>() => Some(self as *const _ as *const ()),
id if id == TypeId::of::<WithContext>() => {
Some(&self.get_context as *const _ as *const ())
},
_ => None,
}
}
}
impl<S> ConstraintLayer<S>
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
pub fn new(mode: TracingMode) -> Self {
Self {
mode,
get_context: WithContext(Self::get_context),
_subscriber: PhantomData,
}
}
fn get_context(
dispatch: &Dispatch,
id: &span::Id,
f: &mut dyn FnMut(&'static Metadata<'static>, &str) -> bool,
) {
let subscriber = dispatch
.downcast_ref::<S>()
.expect("subscriber should downcast to expected type; this is a bug!");
let span = subscriber
.span(id)
.expect("registry should have a span for the current ID");
for span in span.scope() {
let cont = f(span.metadata(), "");
if !cont {
break;
}
}
}
}
impl WithContext {
pub(crate) fn with_context(
&self,
dispatch: &Dispatch,
id: &span::Id,
mut f: impl FnMut(&'static Metadata<'static>, &str) -> bool,
) {
(self.0)(dispatch, id, &mut f)
}
}
impl<S> Default for ConstraintLayer<S>
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
fn default() -> Self {
Self::new(TracingMode::All)
}
}
impl<S> fmt::Debug for ConstraintLayer<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConstraintLayer")
.field("subscriber", &format_args!("{}", type_name::<S>()))
.finish()
}
}
macro_rules! try_bool {
($e:expr, $dest:ident) => {{
let ret = $e.unwrap_or_else(|e| $dest = Err(e));
if $dest.is_err() {
return false;
}
ret
}};
}
#[derive(Clone, Debug)]
pub struct ConstraintTrace {
span: span::Span,
}
impl ConstraintTrace {
pub fn capture() -> Option<Self> {
let span = span::Span::current();
if span.is_none() {
None
} else {
let trace = Self { span };
Some(trace)
}
}
fn with_spans(&self, f: impl FnMut(&'static Metadata<'static>, &str) -> bool) {
self.span.with_subscriber(|(id, s)| {
if let Some(getcx) = s.downcast_ref::<WithContext>() {
getcx.with_context(s, id, f);
}
});
}
pub fn path(&self) -> Vec<TraceStep> {
let mut path = Vec::new();
self.with_spans(|metadata, _| {
if metadata.target() == "gr1cs" {
let n = metadata.name();
let step = metadata
.module_path()
.map(|m| (n, m))
.and_then(|(n, m)| metadata.file().map(|f| (n, m, f)))
.and_then(|(n, m, f)| metadata.line().map(|l| (n, m, f, l)));
if let Some((name, module_path, file, line)) = step {
let step = TraceStep {
name,
module_path,
file,
line,
};
path.push(step);
} else {
return false;
}
}
true
});
path.reverse(); path
}
}
impl fmt::Display for ConstraintTrace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut err = Ok(());
let mut span = 0;
self.with_spans(|metadata, _| {
if metadata.target() != "gr1cs" {
return true;
}
if span > 0 {
try_bool!(write!(f, "\n",), err);
}
try_bool!(
write!(
f,
"{:>4}: {}::{}",
span,
metadata.module_path().unwrap(),
metadata.name()
),
err
);
if let Some((file, line)) = metadata
.file()
.and_then(|file| metadata.line().map(|line| (file, line)))
{
try_bool!(write!(f, "\n at {}:{}", file, line), err);
}
span += 1;
true
});
err
}
}
#[derive(Debug, Clone, Copy)]
pub struct TraceStep {
pub name: &'static str,
pub module_path: &'static str,
pub file: &'static str,
pub line: u32,
}