Skip to content

Commit 626b487

Browse files
committed
lua api: add en_filter and en_filter_range
1 parent e2aff10 commit 626b487

File tree

2 files changed

+207
-4
lines changed

2 files changed

+207
-4
lines changed

example_query.lua

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,28 @@ function query6()
4545
end
4646
return ids
4747
end
48+
49+
function query7()
50+
local r_start, r_end = en_span_range()
51+
local ids = {}
52+
for i = r_start, r_end do
53+
local node_value = en_attr_by_name(i, "node_value")
54+
if node_value and node_value >= "d3" then
55+
table.insert(ids, i)
56+
end
57+
end
58+
return ids
59+
end
60+
61+
-- equivalent to query7(), but about 2x faster.
62+
function query8()
63+
local r_start, r_end = en_span_range()
64+
65+
filter_settings = {
66+
target = "node_value",
67+
relation = "GT",
68+
value = "d3",
69+
}
70+
filtered = en_filter_range(r_start, r_end, filter_settings)
71+
return filtered
72+
end

gui/src/search/lua_api.rs

Lines changed: 182 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
use std::{
22
cell::RefCell,
3+
cmp::Ordering,
34
collections::HashMap,
45
ops::RangeInclusive,
56
rc::Rc,
6-
sync::{Arc, RwLock},
7+
sync::{Arc, RwLock, RwLockReadGuard},
78
};
89

9-
use entrace_core::{EnValueRef, LogProviderError, MetadataRefContainer};
10+
use anyhow::bail;
11+
use egui::Order;
12+
use entrace_core::{EnValue, EnValueRef, LogProviderError, MetadataRefContainer};
1013
use memchr::memmem::Finder;
11-
use mlua::{IntoLua, Lua, Table, Value};
14+
use mlua::{ExternalError, IntoLua, Lua, Table, Value};
15+
use tracing::span;
1216

1317
use crate::{
1418
LevelRepr, TraceProvider,
15-
search::{LuaValueRef, LuaValueRefRef, QueryError},
19+
search::{LuaValue, LuaValueRef, LuaValueRefRef, QueryError},
1620
};
1721

1822
fn make_oob_error(index: u32, len: usize) -> mlua::Error {
@@ -280,6 +284,178 @@ pub fn en_contains_anywhere(
280284
}
281285
}
282286

287+
fn meta_matches(
288+
meta: &MetadataRefContainer, target: &str, comparator: Ordering, value: &EnValue,
289+
) -> anyhow::Result<bool> {
290+
fn string_eq(a: &str, value: &EnValue, comparator: std::cmp::Ordering) -> bool {
291+
match value {
292+
EnValue::String(b) => a.cmp(b) == comparator,
293+
_ => false,
294+
}
295+
}
296+
fn opt_string_eq(a: Option<&str>, value: &EnValue, comparator: Ordering) -> bool {
297+
let Some(a) = a else { return false };
298+
match value {
299+
EnValue::String(b) => a.cmp(b) == comparator,
300+
_ => false,
301+
}
302+
}
303+
match target {
304+
"name" => Ok(string_eq(meta.name, value, comparator)),
305+
"target" => Ok(string_eq(meta.target, value, comparator)),
306+
"level" => {
307+
let asu8 = match value {
308+
EnValue::U64(x) => *x as u8,
309+
EnValue::I64(x) => *x as u8,
310+
_ => return Ok(false),
311+
};
312+
Ok((meta.level as u8).cmp(&asu8) == comparator)
313+
}
314+
"module_path" => Ok(opt_string_eq(meta.module_path, value, comparator)),
315+
"file" => Ok(opt_string_eq(meta.file, value, comparator)),
316+
"line" => {
317+
let converted = match value {
318+
EnValue::Float(a) => *a as u32,
319+
EnValue::U64(a) => *a as u32,
320+
EnValue::I64(a) => *a as u32,
321+
_ => return Ok(false),
322+
};
323+
let Some(line) = meta.line else { return Ok(false) };
324+
Ok(line.cmp(&converted) == comparator)
325+
}
326+
x => bail!("Bad meta field {x}"),
327+
}
328+
}
329+
330+
fn values_match(comparator: std::cmp::Ordering, span_value: &EnValueRef, value: &EnValue) -> bool {
331+
match value {
332+
EnValue::String(a) => match span_value {
333+
EnValueRef::String(b) => b.cmp(&a.as_str()) == comparator,
334+
_ => false,
335+
},
336+
EnValue::Bool(a) => match span_value {
337+
EnValueRef::Bool(b) => b.cmp(&a) == comparator,
338+
_ => false,
339+
},
340+
EnValue::Float(a) => match span_value {
341+
EnValueRef::Float(b) => b.total_cmp(&a) == comparator,
342+
_ => false,
343+
},
344+
EnValue::U64(a) => {
345+
let span_value_converted = match span_value {
346+
EnValueRef::U64(x) => *x,
347+
EnValueRef::I64(x) => *x as u64,
348+
EnValueRef::U128(x) => *x as u64,
349+
EnValueRef::I128(x) => *x as u64,
350+
_ => return false,
351+
};
352+
span_value_converted.cmp(a) == comparator
353+
}
354+
EnValue::I64(a) => {
355+
let span_value_converted = match span_value {
356+
EnValueRef::U64(x) => *x as i64,
357+
EnValueRef::I64(x) => *x,
358+
EnValueRef::U128(x) => *x as i64,
359+
EnValueRef::I128(x) => *x as i64,
360+
_ => return false,
361+
};
362+
span_value_converted.cmp(&a) == comparator
363+
}
364+
// we explicitly don't construct these
365+
EnValue::U128(_) => false,
366+
EnValue::I128(_) => false,
367+
// table->bytes is not handled for now
368+
EnValue::Bytes(_) => false,
369+
}
370+
}
371+
372+
pub fn filter_inner(
373+
tcc: RwLockReadGuard<TraceProvider>, ids: impl Iterator<Item = u32>, target: &str,
374+
target_is_meta: bool, relation: Ordering, en_value: EnValue,
375+
) -> mlua::Result<Vec<u32>> {
376+
let mut returned = Vec::with_capacity(128);
377+
for id in ids {
378+
if target_is_meta {
379+
let meta = tcc.meta(id).unwrap();
380+
let matches_here =
381+
meta_matches(&meta, target, relation, &en_value).map_err(|x| x.into_lua_err())?;
382+
if matches_here {
383+
returned.push(id);
384+
}
385+
} else {
386+
let attrs = tcc.attrs(id).unwrap();
387+
let Some((_name, target_here)) = attrs.iter().find(|(name, _)| *name == target) else {
388+
continue;
389+
};
390+
let matches_here = values_match(relation, target_here, &en_value);
391+
if matches_here {
392+
returned.push(id);
393+
}
394+
}
395+
}
396+
Ok(returned)
397+
}
398+
399+
fn parse_filter_desc(desc: mlua::Table) -> mlua::Result<(String, bool, Ordering, EnValue)> {
400+
use anyhow::anyhow;
401+
let Ok(raw_target): mlua::Result<String> = desc.get("target") else {
402+
return Err(anyhow!("Filter target must be a string").into_lua_err());
403+
};
404+
let mut target: &str = raw_target.as_str();
405+
let mut target_is_meta = false;
406+
if let Some(stripped) = raw_target.strip_prefix("meta.") {
407+
target = stripped;
408+
target_is_meta = true;
409+
}
410+
let Ok(rel): mlua::Result<String> = desc.get("relation") else {
411+
return Err(anyhow!("Filter relation must be a string").into_lua_err());
412+
};
413+
let relation = match rel.as_str() {
414+
"GT" => Ordering::Greater,
415+
"LT" => Ordering::Less,
416+
"EQ" => Ordering::Equal,
417+
x => return Err(anyhow::anyhow!("Bad filter relation {x}").into_lua_err()),
418+
};
419+
420+
let value: Value = desc.get("value").unwrap();
421+
let en_value = match value {
422+
Value::Boolean(f) => EnValue::Bool(f),
423+
Value::Integer(k) => EnValue::I64(k),
424+
Value::Number(z) => EnValue::Float(z),
425+
Value::String(ref q) => EnValue::String(q.to_string_lossy()),
426+
x => {
427+
return Err(anyhow!("Cannot convert value {x:?} to EnValue").into_lua_err());
428+
}
429+
};
430+
Ok((target.into(), target_is_meta, relation, en_value))
431+
}
432+
433+
/// en_filter(list<id>, compare_desc)
434+
/// where compare_desc is a table:
435+
/// target: name of variable eg. "message" or "meta.filename"
436+
/// relation: a relation, one of "EQ", "LT", "GT"
437+
/// value: a constant to compare with
438+
/// Returns the ids of the spans matching the compare_desc.
439+
pub fn en_filter(
440+
trace_provider: Arc<RwLock<TraceProvider>>,
441+
) -> impl Fn(&Lua, (Vec<u32>, Table)) -> mlua::Result<Vec<u32>> {
442+
move |_lua: &Lua, (ids, desc): (Vec<u32>, Table)| {
443+
let tcc = trace_provider.read().unwrap();
444+
let (target, target_is_meta, relation, en_value) = parse_filter_desc(desc)?;
445+
filter_inner(tcc, ids.iter().copied(), &target, target_is_meta, relation, en_value)
446+
}
447+
}
448+
/// Same as en_filter, but accets a range start and range end instead
449+
pub fn en_filter_range(
450+
trace_provider: Arc<RwLock<TraceProvider>>,
451+
) -> impl Fn(&Lua, (u32, u32, Table)) -> mlua::Result<Vec<u32>> {
452+
move |_lua: &Lua, (start, end, desc): (u32, u32, Table)| {
453+
let tcc = trace_provider.read().unwrap();
454+
let (target, target_is_meta, relation, en_value) = parse_filter_desc(desc)?;
455+
filter_inner(tcc, start..=end, &target, target_is_meta, relation, en_value)
456+
}
457+
}
458+
283459
pub fn setup_lua(
284460
lua: &mut Lua, range: RangeInclusive<u32>, trace: Arc<RwLock<TraceProvider>>,
285461
finder_cache: Rc<RefCell<HashMap<String, Finder<'static>>>>,
@@ -314,6 +490,8 @@ pub fn setup_lua(
314490
"en_contains_anywhere",
315491
lua.create_function(en_contains_anywhere(trace.clone(), fc))?,
316492
)?;
493+
globals.set("en_filter", lua.create_function(en_filter(trace.clone()))?)?;
494+
globals.set("en_filter_range", lua.create_function(en_filter_range(trace.clone()))?)?;
317495

318496
Ok(())
319497
}

0 commit comments

Comments
 (0)