tracers: queue-levels: Add a signal to force writing log file

This way applications can write the queue levels info whenever it wants
and does not need to wait for the application to quite.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1912>
This commit is contained in:
Thibault Saunier 2024-11-02 09:24:17 -03:00
parent def8a29cc6
commit 153d4c7ac5

View file

@ -44,7 +44,7 @@
* *
* By default this is not set. * By default this is not set.
*/ */
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -179,6 +179,7 @@ struct State {
queues: HashMap<usize, Arc<glib::GString>>, queues: HashMap<usize, Arc<glib::GString>>,
log: Vec<LogLine>, log: Vec<LogLine>,
settings: Settings, settings: Settings,
logs_written: HashSet<PathBuf>,
} }
struct LogLine { struct LogLine {
@ -239,49 +240,26 @@ impl ObjectImpl for QueueLevels {
self.register_hook(TracerHook::PadPushEventPre); self.register_hook(TracerHook::PadPushEventPre);
} }
fn signals() -> &'static [glib::subclass::Signal] {
static SIGNALS: LazyLock<Vec<glib::subclass::Signal>> = LazyLock::new(|| {
vec![glib::subclass::Signal::builder("write-log")
.action()
.param_types([Option::<String>::static_type()])
.class_handler(|_, args| {
let obj = args[0].get::<super::QueueLevels>().unwrap();
obj.imp().write_log(args[1].get::<Option<&str>>().unwrap());
None
})
.build()]
});
SIGNALS.as_ref()
}
fn dispose(&self) { fn dispose(&self) {
use std::io::prelude::*; self.write_log(None);
let state = self.state.lock().unwrap();
let mut file = match std::fs::File::create(&state.settings.file) {
Ok(file) => file,
Err(err) => {
gst::error!(CAT, imp = self, "Failed to create file: {err}");
return;
}
};
gst::debug!(
CAT,
imp = self,
"Writing file {}",
state.settings.file.display()
);
for LogLine {
timestamp,
name,
idx,
ptr,
cur_level_bytes,
cur_level_time,
cur_level_buffers,
max_size_bytes,
max_size_time,
max_size_buffers,
} in &state.log
{
let res = if let Some(idx) = idx {
writeln!(&mut file, "{timestamp},{name}:{idx},0x{ptr:08x},{cur_level_bytes},{cur_level_time},{cur_level_buffers},{max_size_bytes},{max_size_time},{max_size_buffers}")
} else {
writeln!(&mut file, "{timestamp},{name},0x{ptr:08x},{cur_level_bytes},{cur_level_time},{cur_level_buffers},{max_size_bytes},{max_size_time},{max_size_buffers}")
};
if let Err(err) = res {
gst::error!(CAT, imp = self, "Failed to write to file: {err}");
return;
}
}
} }
} }
@ -545,4 +523,54 @@ impl QueueLevels {
}); });
} }
} }
fn write_log(&self, file_path: Option<&str>) {
use std::io::prelude::*;
let mut state = self.state.lock().unwrap();
let path = file_path.map_or_else(|| state.settings.file.clone(), PathBuf::from);
let first_write = state.logs_written.contains(&path);
let mut file = match std::fs::OpenOptions::new()
.append(!first_write)
.create(true)
.open(path.clone())
{
Ok(file) => file,
Err(err) => {
gst::error!(CAT, imp = self, "Failed to create file: {err}");
return;
}
};
let log = std::mem::take(&mut state.log);
state.logs_written.insert(path);
drop(state);
gst::debug!(CAT, imp = self, "Writing file {:?}", file);
for LogLine {
timestamp,
name,
idx,
ptr,
cur_level_bytes,
cur_level_time,
cur_level_buffers,
max_size_bytes,
max_size_time,
max_size_buffers,
} in &log
{
let res = if let Some(idx) = idx {
writeln!(&mut file, "{timestamp},{name}:{idx},0x{ptr:08x},{cur_level_bytes},{cur_level_time},{cur_level_buffers},{max_size_bytes},{max_size_time},{max_size_buffers}")
} else {
writeln!(&mut file, "{timestamp},{name},0x{ptr:08x},{cur_level_bytes},{cur_level_time},{cur_level_buffers},{max_size_bytes},{max_size_time},{max_size_buffers}")
};
if let Err(err) = res {
gst::error!(CAT, imp = self, "Failed to write to file: {err}");
return;
}
}
}
} }