Skip to content

Commit 8633c1b

Browse files
committed
lua api: add en_filter and en_filter_range
1 parent e2aff10 commit 8633c1b

File tree

2 files changed

+204
-3
lines changed

2 files changed

+204
-3
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: 179 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
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 entrace_core::{EnValue, EnValueRef, LogProviderError, MetadataRefContainer};
1012
use memchr::memmem::Finder;
11-
use mlua::{IntoLua, Lua, Table, Value};
13+
use mlua::{ExternalError, IntoLua, Lua, Table, Value};
1214

1315
use crate::{
1416
LevelRepr, TraceProvider,
@@ -280,6 +282,178 @@ pub fn en_contains_anywhere(
280282
}
281283
}
282284

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

318494
Ok(())
319495
}

0 commit comments

Comments
 (0)