Made spantrace capture optional at the compilation level.

Added `logging` (default) feature for enabling/disabling spantrace captures entirely on build.

Fortune for collect's current commit: Half curse − 半凶
safe-memfd
Avril 2 years ago
parent 3af34dc78f
commit 80a2d65262
Signed by: flanchan
GPG Key ID: 284488987C31F630

41
Cargo.lock generated

@ -26,6 +26,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -70,9 +81,13 @@ name = "collect"
version = "0.1.0"
dependencies = [
"bytes",
"cfg-if",
"color-eyre",
"jemallocator",
"lazy_format",
"libc",
"memchr",
"recolored",
"tracing",
"tracing-error",
"tracing-subscriber",
@ -127,6 +142,15 @@ version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indenter"
version = "0.3.3"
@ -154,6 +178,12 @@ dependencies = [
"libc",
]
[[package]]
name = "lazy_format"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05662be9cd63006934464f935195ae936460edb75de7b9a07e0509795afbdc3"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -245,6 +275,17 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "recolored"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1584c92dd8a87686229f766bb3a62d263a90c47c81e45a49f1a6d684a1b7968d"
dependencies = [
"atty",
"lazy_static",
"winapi",
]
[[package]]
name = "regex"
version = "1.5.5"

@ -6,11 +6,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["jemalloc", "tracing/release_max_level_warn"]
default = ["jemalloc", "logging", "tracing/release_max_level_warn"]
# TODO: mmap, memfd_create() ver
# XXX: without bytes, it completely fails??
# bytes: use `bytes` crate for collecting instead of `std::vec`
# Use jemalloc instead of system malloc.
@ -18,8 +17,15 @@ default = ["jemalloc", "tracing/release_max_level_warn"]
# Decreases memory-handling function calls, resulting in less "used" memory and faster allocation speeds at the cost of mapping a huge amount of virtual memory.
jemalloc = ["jemallocator"]
# Remove all tracing points
no-logging = ["tracing/max_level_off"]
# Remove all runtime logging code.
#
# The capturing of spantraces will still happen if `logging` is enabled.
disable-logging = [] #["tracing/max_level_off"] <-- no longer needed, would enable the `tracing` feature which we don't want.
# Capture spantraces
#
# Will cause a slowdown, but provide more information in the event of an error or when debugging.
logging = ["tracing", "tracing-subscriber", "tracing-error", "color-eyre/capture-spantrace"] #, "recolored" <- XXX doesn't work in tracing output for some reason...]
[profile.release]
opt-level = 3
@ -34,9 +40,13 @@ strip=false
[dependencies]
bytes = { version = "1.1.0", optional = true }
color-eyre = { version = "0.6.1", features = ["capture-spantrace"] }
cfg-if = { version = "1.0.0" }
jemallocator = { version = "0.3.2", optional = true }
libc = "0.2.122"
tracing = { version = "0.1.33", features = ["attributes"] }
tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.11", features = ["tracing", "env-filter"] }
tracing = { version = "0.1.33", features = ["attributes"], optional = true }
tracing-error = {version = "0.2.0", optional = true }
tracing-subscriber = { version = "0.3.11", features = ["tracing", "env-filter"], optional = true }
color-eyre = { version = "0.6.1", default-features=false }#, features = ["capture-spantrace"] }
recolored = { version = "1.9.3", optional = true }
memchr = "2.4.1"
lazy_format = "1.10.0"

@ -85,10 +85,11 @@ const _: () = {
impl<'a, B: ?Sized + Buffer> io::Read for BufferReader<'a, B>
{
#[inline]
#[instrument(level="trace", skip_all, fields(buf = ?buf.len()))]
#[cfg_attr(feature="logging", instrument(level="trace", skip_all, fields(buf = ?buf.len())))]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let adv = self.0.copy_to_slice(self.1, buf);
self.1 += adv;
if_trace!(? trace!(" -> reading one buffer +{adv}"));
Ok(adv)
}
}
@ -96,11 +97,13 @@ impl<'a, B: ?Sized + Buffer> io::Read for BufferReader<'a, B>
impl<'a, B: ?Sized + MutBuffer> io::Write for BufferWriter<'a, B>
{
#[inline]
#[instrument(level="trace", skip_all, fields(buf = ?buf.len()))]
#[cfg_attr(feature="logging", instrument(level="trace", skip_all, fields(buf = ?buf.len())))]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let adv = self.0.copy_from_slice(self.1, buf);
self.1 += adv;
if_trace!(? trace!(" <- writing one buffer {adv}"));
Ok(adv)
}
@ -114,7 +117,7 @@ impl<'a, B: ?Sized + MutBuffer> io::Write for BufferWriter<'a, B>
pub trait Buffer: AsRef<[u8]>
{
#[inline]
#[instrument(level="trace", skip_all, fields(buf = ?slice.len()))]
#[cfg_attr(feature="logging", instrument(level="trace", skip_all, fields(buf = ?slice.len())))]
fn copy_to_slice(&self, st: usize, slice: &mut [u8]) -> usize
{
let by = self.as_ref();
@ -162,7 +165,7 @@ pub trait MutBuffer: AsMut<[u8]>
fn freeze(self) -> Self::Frozen;
#[inline]
#[instrument(level="debug", skip_all, fields(st, buflen = ?slice.len()))]
#[cfg_attr(feature="logging", instrument(level="debug", skip_all, fields(st, buflen = ?slice.len())))]
fn copy_from_slice(&mut self, st: usize, slice: &[u8]) -> usize
{
let by = self.as_mut();
@ -189,10 +192,10 @@ pub trait MutBuffer: AsMut<[u8]>
pub trait MutBufferExt: MutBuffer
{
#[inline(always)]
#[instrument(level="info", skip(self))]
#[cfg_attr(feature="logging", instrument(level="info", skip(self)))]
fn writer_from(&mut self, st: usize) -> BufferWriter<'_, Self>
{
debug!("creating writer at start {st}");
if_trace!(debug!("creating writer at start {st}"));
BufferWriter(self, st)
}
#[inline]
@ -210,7 +213,7 @@ impl MutBuffer for bytes::BytesMut
type Frozen = bytes::Bytes;
#[inline(always)]
#[instrument(level="trace")]
#[cfg_attr(feature="logging", instrument(level="trace"))]
fn freeze(self) -> Self::Frozen {
bytes::BytesMut::freeze(self)
}
@ -222,7 +225,7 @@ impl MutBuffer for bytes::BytesMut
if (st + buf.len()) <= self.len() {
// We can put `buf` in st..buf.len()
self[st..].copy_from_slice(buf);
} else if st <= self.len() {
} else if st < self.len() {
// The start is lower but the end is not
let rem = self.len() - st;
self[st..].copy_from_slice(&buf[..rem]);
@ -235,29 +238,150 @@ impl MutBuffer for bytes::BytesMut
}*/
}
#[cfg(feature="recolored")]
mod perc {
#[deprecated = "this is absolutely retardedly unsafe and unsound... fuck this shit man lole"]
pub(super) unsafe fn gen_perc_boring(low: f64, high: f64) -> std::pin::Pin<&'static (impl std::fmt::Display + ?Sized + 'static)>
{
use std::{
cell::RefCell,
mem::MaybeUninit,
pin::Pin,
};
thread_local! {
static STRING_BUFFER: RefCell<MaybeUninit<[u8; 16]>> = RefCell::new(MaybeUninit::uninit());
}
STRING_BUFFER.try_with(|buffer| -> Result<std::pin::Pin<&'static str>, Box<dyn std::error::Error + 'static>>{
let mut buffer = buffer.try_borrow_mut()?;
use std::io::Write;
write!(unsafe {&mut buffer.assume_init_mut()[..]}, "{:0.2}", (low / high) * 100f64)?;
let s_ref = unsafe {
#[derive(Debug)]
struct FindFailed;
impl std::error::Error for FindFailed{}
impl std::fmt::Display for FindFailed {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
f.write_str("boring perc: failed to write whole string into buffer of size 16")
}
}
let buf = buffer.assume_init_mut();
let spl = memchr::memchr(b'%', &buf[..]).ok_or(FindFailed)?;
std::str::from_utf8_mut(&mut buf[..=spl])?
};
unsafe {
Ok(Pin::new(std::mem::transmute::<_, &'static _>(s_ref)))
}
}).expect("bad static memory access").expect("failed to calc")
}
#[inline]
//XXX::: WHY::: TRACING IGNORES MY COLOURS!!!
#[deprecated(note="my colouring is ignored. we'll have to either: figure out why. or, use a different method to highlight abnormal (above 100) percentages")]
pub(super) fn gen_perc(low: f64, high: f64) -> impl std::fmt::Display
{
use std::fmt;
let f = low / match high {
0f64 => if low != 0f64 {
return Perc::Invalid
} else {
0f64
}
x => x,
};
enum Perc {
Normal(f64),
Goal(String),
High(String),
Zero(String),
Low(String),
Invalid,
}
macro_rules! fmt_str {
(%) => ("{:0.2}%");
() => ("{:0.2}")
}
impl fmt::Display for Perc
{
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
use recolored::Colorize;
write!(f, "{}", match self {
Self::Normal(p) => return write!(f, fmt_str!(%), p),
Self::Goal(p) => p.green(),
Self::High(p) => p.red(),
Self::Zero(p) => p.purple().bold(),
Self::Low(p) => p.on_red().white().underline(),
Self::Invalid => return write!(f, fmt_str!(%), ("0.00%".on_bright_red().white().strikethrough())),
})?;
{
use fmt::Write;
f.write_char('%')
}
}
}
//TODO: StackStr instead of String
(match f {
0f64 => Perc::Zero,
1f64 => Perc::Goal,
0f64..=1f64 => return Perc::Normal(f * 100f64),
1f64.. => Perc::High,
_ => Perc::Low,
})(format!(fmt_str!(), f * 100f64))
}
}
impl MutBuffer for Vec<u8>
{
type Frozen = Box<[u8]>;
#[inline]
#[instrument(level="trace")]
#[cfg_attr(feature="logging", instrument(level="trace"))]
fn freeze(self) -> Self::Frozen {
self.into_boxed_slice()
}
#[instrument(level="trace", skip_all, fields(st, buflen = ?buf.len()))]
#[cfg_attr(feature="logging", instrument(level="trace", skip(buf, self), fields(st = ?st, self = ?self.len(), alloc= ?self.capacity())))]
fn copy_from_slice(&mut self, st: usize, buf: &[u8]) -> usize
{
if (st + buf.len()) <= self.len() {
// We can put `buf` in st..buf.len()
self[st..].copy_from_slice(buf);
} else if st <= self.len() {
} else if st < self.len() {
// The start is lower but the end is not
let rem = self.len() - st;
self[st..].copy_from_slice(&buf[..rem]);
if_trace!(trace!("extending buffer (partial, +{})", buf[rem..].len()));
self.extend_from_slice(&buf[rem..]);
} else {
// it is past the end, extend.
if_trace!(trace!("extending buffer (whole, self + buf = {} / {}: {})"
,self.len() + buf.len()
, self.capacity()
, {
cfg_if! {
if #[cfg(feature="recolored")] {
use perc::*;
(if cfg!(feature="recolored") {
|x,y| -> Box<dyn std::fmt::Display> { Box::new(gen_perc(x,y)) }
} else {
|x,y| -> Box<dyn std::fmt::Display> { Box::new(unsafe {gen_perc_boring(x,y)}.get_ref()) }
})((self.len() + buf.len()) as f64, self.capacity() as f64)
} else {
let t= self.len();
let c= self.capacity();
let b = buf.len();
lazy_format::lazy_format!("{:0.2}", ((t + b) as f64 / c as f64) * 100f64)
}
}
}));
self.extend_from_slice(buf);
}
buf.len()
@ -274,15 +398,15 @@ pub trait WithCapacity: Sized
impl WithCapacity for Box<[u8]>
{
#[inline(always)]
#[instrument(level="info", fields(cap = "(unbound)"))]
#[cfg_attr(feature="logging", instrument(level="info", fields(cap = "(unbound)")))]
fn wc_new() -> Self {
info!("creating new boxed slice with size 0");
if_trace!(debug!("creating new boxed slice with size 0"));
Vec::wc_new().into_boxed_slice()
}
#[inline(always)]
#[instrument(level="info")]
#[cfg_attr(feature="logging", instrument(level="info"))]
fn wc_with_capacity(cap: usize) -> Self {
info!("creating new boxed slice with size {cap}");
if_trace!(debug!("creating new boxed slice with size {cap}"));
Vec::wc_with_capacity(cap).into_boxed_slice()
}
}
@ -337,17 +461,17 @@ macro_rules! cap_buffer {
impl $crate::buffers::WithCapacity for $name
{
#[inline(always)]
#[instrument(level="info", fields(cap = "(unbound)"))]
#[cfg_attr(feature="logging", instrument(level="info", fields(cap = "(unbound)")))]
fn wc_new() -> Self
{
info!("creating {} with no cap", std::any::type_name::<Self>());
if_trace! (debug!("creating {} with no cap", std::any::type_name::<Self>()));
Self::new()
}
#[inline(always)]
#[instrument(level="info")]
#[cfg_attr(feature="logging", instrument(level="info"))]
fn wc_with_capacity(cap: usize) -> Self
{
info!("creating {} with {cap}", std::any::type_name::<Self>());
if_trace!(debug!("creating {} with {cap}", std::any::type_name::<Self>()));
Self::with_capacity(cap)
}
}

@ -1,6 +1,26 @@
#[macro_use] extern crate cfg_if;
#[cfg(feature="logging")]
#[macro_use] extern crate tracing;
/// Run this statement only if `tracing` is enabled
macro_rules! if_trace {
(? $expr:expr) => {
cfg_if! {
if #[cfg(all(feature="logging", debug_assertions))] {
$expr;
}
}
};
($expr:expr) => {
cfg_if! {
if #[cfg(feature="logging")] {
$expr;
}
}
};
}
#[cfg(feature="jemalloc")]
extern crate jemallocator;
@ -37,7 +57,88 @@ use bytes::{
BufMut,
};
#[instrument(level="debug", skip(reader), fields(reader = ?std::any::type_name::<R>()))]
/* TODO: XXX: For colouring buffer::Perc
#[derive(Debug)]
struct StackStr<const MAXLEN: usize>(usize, std::mem::MaybeUninit<[u8; MAXLEN]>);
impl<const SZ: usize> StackStr<SZ>
{
#[inline]
pub const fn new() -> Self
{
Self(0, std::mem::MaybeUninit::uninit())
}
#[inline(always)]
pub const unsafe fn slice_mut(&mut self) -> &mut [u8]
{
&mut self.1[self.0..]
}
#[inline]
pub const fn slice(&self) -> &[u8]
{
&self.1[self.0..]
}
#[inline]
pub const unsafe fn as_str_unchecked(&self) -> &str
{
std::str::from_utf8_unchecked(&self.1[self.0..])
}
#[inline]
pub const unsafe fn as_mut_str_unchecked(&mut self) -> &mut str
{
std::str::from_utf8_unchecked_mut(&mut self.1[..self.0])
}
#[inline]
#[cfg_attr(feature="logging", instrument(level="debug"))]
pub fn as_str(&self) -> &str
{
std::str::from_utf8(self.slice()).expect("Invalid string")
}
#[inline(always)]
const fn left(&self) -> usize {
SZ - self.0
}
#[inline(always)]
pub fn write_bytes(&mut self, s: &[u8]) -> usize {
let b = &s[..std::cmp::min(match self.left() {
0 => return 0,
x => x,
}, s.len())];
unsafe { &mut self.slice_mut() [self.0..] }.copy_from_slice(b);
let v = b.len();
self.0 += v;
v
}
}
impl<const SZ: usize> std::fmt::Write for StackStr<SZ>
{
#[inline]
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.write_bytes(s.as_bytes());
Ok(())
}
#[inline]
fn write_char(&mut self, c: char) -> std::fmt::Result {
let l = c.len_utf8();
if l > self.left() {
return Ok(())
}
self.write_bytes(c.encode_utf8(unsafe { &mut self.slice_mut() [self.0..] }));
self.0 += l;
Ok(())
}
}
*/
#[cfg_attr(feature="logging", instrument(level="info", skip(reader), fields(reader = ?std::any::type_name::<R>())))]
fn try_get_size<R: ?Sized>(reader: &R) -> Option<NonZeroUsize>
where R: AsRawFd
{
@ -63,45 +164,47 @@ where R: AsRawFd
fn init() -> eyre::Result<()>
{
fn install_tracing()
{
//! Install spantrace handling
use tracing_error::ErrorLayer;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
let fmt_layer = fmt::layer()
.with_target(false)
.with_writer(io::stderr);
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new(if cfg!(debug_assertions) {
"info"
} else if cfg!(feature="no-logging") {
"off"
} else {
"warn"
}))
.unwrap();
tracing_subscriber::registry()
.with(fmt_layer)
.with(filter_layer)
.with(ErrorLayer::default())
.init();
}
cfg_if!{ if #[cfg(feature="logging")] {
fn install_tracing()
{
//! Install spantrace handling
use tracing_error::ErrorLayer;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
let fmt_layer = fmt::layer()
.with_target(false)
.with_writer(io::stderr);
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new(if cfg!(debug_assertions) {
"debug"
} else {
"info"
}))
.unwrap();
tracing_subscriber::registry()
.with(fmt_layer)
.with(filter_layer)
.with(ErrorLayer::default())
.init();
}
//if !cfg!(feature="no-logging") {
install_tracing();
//}
if !cfg!(feature="disable-logging") {
install_tracing();
if_trace!(trace!("installed tracing"));
}
} }
color_eyre::install()
}
#[instrument(err)]
#[cfg_attr(tracing, instrument(err))]
fn main() -> eyre::Result<()> {
init()?;
if_trace!(debug!("initialised"));
let (bytes, read) = {
let stdin = io::stdin();
@ -112,9 +215,9 @@ fn main() -> eyre::Result<()> {
.with_section(|| bytes.capacity().header("Buffer cap is"))
.with_section(|| format!("{:?}", bytes).header("Buffer is"))
.wrap_err("Failed to read into buffer")?;
(bytes.freeze(), read as usize)
};
if_trace!(info!("collected {read} from stdin. starting write."));
let written =
io::copy(&mut (&bytes[..read]).reader() , &mut io::stdout().lock())
@ -123,6 +226,7 @@ fn main() -> eyre::Result<()> {
.with_section(|| format!("{:?}", &bytes[..read]).header("Read Buffer"))
.with_section(|| format!("{:?}", bytes).header("Full Buffer"))
.wrap_err("Failed to write from buffer")?;
if_trace!(info!("written {written} to stdout."));
if read != written as usize {
return Err(io::Error::new(io::ErrorKind::BrokenPipe, format!("read {read} bytes, but only wrote {written}")))

Loading…
Cancel
Save