1#![allow(clippy::missing_safety_doc)]
2use super::*;
3
4#[no_mangle]
5pub unsafe extern "C" fn lmflow_graph_total_queued(g: *mut LMFlowGraph) -> usize {
6 guard_val(0, || {
7 graph_of(g).map_or(0, |gr| gr.inner().shared.total_queued())
8 })
9}
10#[no_mangle]
11pub unsafe extern "C" fn lmflow_graph_total_queued_bytes(g: *mut LMFlowGraph) -> u64 {
12 guard_val(0, || {
13 graph_of(g).map_or(0, |gr| gr.inner().shared.total_queued_bytes())
14 })
15}
16#[no_mangle]
17pub unsafe extern "C" fn lmflow_graph_queue_depth(
18 g: *mut LMFlowGraph,
19 port: *const c_char,
20) -> usize {
21 guard_val(INVALID_ID, || {
22 graph_of(g)
23 .and_then(|gr| cstr(port).and_then(|p| gr.inner().queue_depth_by_name(p)))
24 .unwrap_or(INVALID_ID)
25 })
26}
27#[no_mangle]
28pub unsafe extern "C" fn lmflow_graph_dropped_count(
29 g: *mut LMFlowGraph,
30 port: *const c_char,
31) -> u64 {
32 guard_val(0, || {
33 graph_of(g)
34 .and_then(|gr| cstr(port).and_then(|p| gr.inner().dropped_by_name(p)))
35 .unwrap_or(0)
36 })
37}
38#[no_mangle]
39pub unsafe extern "C" fn lmflow_graph_num_input_ports(g: *mut LMFlowGraph) -> usize {
40 guard_val(0, || {
41 graph_of(g).map_or(0, |gr| gr.inner().num_input_ports())
42 })
43}
44#[no_mangle]
45pub unsafe extern "C" fn lmflow_graph_num_output_ports(g: *mut LMFlowGraph) -> usize {
46 guard_val(0, || {
47 graph_of(g).map_or(0, |gr| gr.inner().num_output_ports())
48 })
49}
50#[no_mangle]
51pub unsafe extern "C" fn lmflow_graph_num_nodes(g: *mut LMFlowGraph) -> usize {
52 guard_val(0, || graph_of(g).map_or(0, |gr| gr.inner().nodes_len()))
53}
54
55fn graph_arena() -> &'static runtime::CStrArena {
56 static A: std::sync::LazyLock<runtime::CStrArena> =
57 std::sync::LazyLock::new(runtime::CStrArena::default);
58 &A
59}
60
61#[no_mangle]
62pub unsafe extern "C" fn lmflow_graph_input_port_name(
63 g: *mut LMFlowGraph,
64 idx: usize,
65) -> *const c_char {
66 guard_val(c"".as_ptr(), || {
67 graph_of(g)
68 .and_then(|gr| {
69 gr.inner()
70 .input_port_name_at(idx)
71 .map(|s| graph_arena().intern(s))
72 })
73 .unwrap_or(c"".as_ptr())
74 })
75}
76#[no_mangle]
77pub unsafe extern "C" fn lmflow_graph_output_port_name(
78 g: *mut LMFlowGraph,
79 idx: usize,
80) -> *const c_char {
81 guard_val(c"".as_ptr(), || {
82 graph_of(g)
83 .and_then(|gr| {
84 gr.inner()
85 .output_port_name_at(idx)
86 .map(|s| graph_arena().intern(s))
87 })
88 .unwrap_or(c"".as_ptr())
89 })
90}
91#[no_mangle]
92pub unsafe extern "C" fn lmflow_graph_node_name(g: *mut LMFlowGraph, idx: usize) -> *const c_char {
93 guard_val(c"".as_ptr(), || {
94 graph_of(g)
95 .and_then(|gr| {
96 gr.inner()
97 .node_name_at(idx)
98 .map(|s| graph_arena().intern(s))
99 })
100 .unwrap_or(c"".as_ptr())
101 })
102}
103
104#[no_mangle]
105pub unsafe extern "C" fn lmflow_graph_node_num_input_ports(
106 g: *mut LMFlowGraph,
107 node_idx: usize,
108) -> usize {
109 guard_val(0, || {
110 graph_of(g).map_or(0, |graph| graph.inner().node_input_ports_len(node_idx))
111 })
112}
113
114#[no_mangle]
115pub unsafe extern "C" fn lmflow_graph_node_input_port_name(
116 g: *mut LMFlowGraph,
117 node_idx: usize,
118 port_idx: usize,
119) -> *const c_char {
120 guard_val(c"".as_ptr(), || {
121 graph_of(g)
122 .and_then(|graph| {
123 graph
124 .inner()
125 .node_input_port_name_at(node_idx, port_idx)
126 .map(|name| graph_arena().intern(name))
127 })
128 .unwrap_or(c"".as_ptr())
129 })
130}
131
132#[no_mangle]
133pub unsafe extern "C" fn lmflow_graph_dump(g: *mut LMFlowGraph) -> *const c_char {
134 guard_val(c"".as_ptr(), || {
135 thread_local! {
136 static BUF: std::cell::RefCell<std::ffi::CString> =
137 std::cell::RefCell::new(std::ffi::CString::default());
138 }
139 let text = graph_of(g).map_or_else(|| "(uninitialized)".to_string(), |gr| gr.dump());
140 BUF.with(|b| {
141 *b.borrow_mut() = std::ffi::CString::new(text).unwrap_or_default();
142 b.borrow().as_ptr()
143 })
144 })
145}
146
147pub const LMFLOW_DOT_TOPOLOGY: i32 = 0;
148pub const LMFLOW_DOT_COMPACT: i32 = 1;
149pub const LMFLOW_DOT_DIAGNOSTICS: i32 = 2;
150
151#[no_mangle]
153pub unsafe extern "C" fn lmflow_graph_to_dot_view(g: *mut LMFlowGraph, view: i32) -> *const c_char {
154 guard_val(c"".as_ptr(), || {
155 thread_local! {
156 static BUF: std::cell::RefCell<std::ffi::CString> =
157 std::cell::RefCell::new(std::ffi::CString::default());
158 }
159 let view = match view {
160 LMFLOW_DOT_TOPOLOGY => crate::graph::DotView::Topology,
161 LMFLOW_DOT_COMPACT => crate::graph::DotView::Compact,
162 LMFLOW_DOT_DIAGNOSTICS => crate::graph::DotView::Diagnostics,
163 value => {
164 last_error::set(&format!(
165 "invalid DOT view {value}; expected 0 (topology), 1 (compact), or 2 (diagnostics)"
166 ));
167 return c"".as_ptr();
168 }
169 };
170 let text = graph_of(g).map_or_else(
172 || "digraph lmflow {\n}\n".to_string(),
173 |gr| gr.to_dot_with_view(view),
174 );
175 BUF.with(|b| {
176 *b.borrow_mut() = std::ffi::CString::new(text).unwrap_or_default();
177 b.borrow().as_ptr()
178 })
179 })
180}
181
182#[no_mangle]
186pub unsafe extern "C" fn lmflow_graph_to_chrome_trace(g: *mut LMFlowGraph) -> *const c_char {
187 guard_val(c"".as_ptr(), || {
188 thread_local! {
189 static BUF: std::cell::RefCell<std::ffi::CString> =
190 std::cell::RefCell::new(std::ffi::CString::default());
191 }
192 let text = graph_of(g).map_or_else(
193 || crate::trace::to_chrome_trace_json(&[], &[]),
196 |gr| gr.to_chrome_trace(),
197 );
198 BUF.with(|b| {
199 *b.borrow_mut() = std::ffi::CString::new(text).unwrap_or_default();
200 b.borrow().as_ptr()
201 })
202 })
203}
204
205#[repr(C)]
206pub struct LMFlowNodeStats {
207 pub struct_size: u32,
208 pub reserved0: u32,
209 pub node_name: *const c_char,
210 pub kernel_name: *const c_char,
211 pub running: bool,
212 pub running_for_us: i64,
213 pub processed: u64,
214 pub errors: u64,
215 pub total_process_us: i64,
216 pub max_process_us: i64,
217 pub packets_in: u64,
218 pub packets_out: u64,
219 pub peak_queue_depth: usize,
220 pub queued: usize,
221}
222
223#[repr(C)]
224pub struct LMFlowInputQueueStats {
225 pub struct_size: u32,
226 pub reserved0: u32,
227 pub node_name: *const c_char,
228 pub port_name: *const c_char,
229 pub producer_name: *const c_char,
230 pub packet_capacity: usize,
231 pub queued_packets: usize,
232 pub queued_bytes: u64,
233 pub reserved_packets: usize,
234 pub peak_queued_packets: usize,
235 pub peak_queued_bytes: u64,
236 pub blocked: bool,
237 pub reserved1: [u8; 7],
238 pub blocked_for_us: u64,
239 pub block_events: u64,
240 pub total_blocked_us: u64,
241}
242
243#[no_mangle]
244pub unsafe extern "C" fn lmflow_graph_node_stats(
245 g: *mut LMFlowGraph,
246 idx: usize,
247 out: *mut LMFlowNodeStats,
248) -> bool {
249 guard_val(false, || {
250 if out.is_null() {
251 return false;
252 }
253 let declared = (*out).struct_size as usize;
254 if declared < std::mem::size_of::<LMFlowNodeStats>() {
255 last_error::set(
256 "LMFlowNodeStats.struct_size too small -- set it to sizeof(LMFlowNodeStats)",
257 );
258 return false;
259 }
260 let Some(gr) = graph_of(g) else { return false };
261 let Some(s) = gr.node_stats(idx) else {
262 return false;
263 };
264 std::ptr::write(
265 out,
266 LMFlowNodeStats {
267 struct_size: std::mem::size_of::<LMFlowNodeStats>() as u32,
268 reserved0: 0,
269 node_name: graph_arena().intern(&s.node_name),
270 kernel_name: graph_arena().intern(&s.kernel_name),
271 running: s.running,
272 running_for_us: s.running_for_us,
273 processed: s.processed,
274 errors: s.errors,
275 total_process_us: s.total_process_us,
276 max_process_us: s.max_process_us,
277 packets_in: s.packets_in,
278 packets_out: s.packets_out,
279 peak_queue_depth: s.peak_queue_depth,
280 queued: s.queued,
281 },
282 );
283 true
284 })
285}
286
287#[no_mangle]
288pub unsafe extern "C" fn lmflow_graph_input_queue_stats(
289 g: *mut LMFlowGraph,
290 node_idx: usize,
291 port_idx: usize,
292 out: *mut LMFlowInputQueueStats,
293) -> bool {
294 guard_val(false, || {
295 if out.is_null() {
296 return false;
297 }
298 let declared = (*out).struct_size as usize;
299 if declared < std::mem::size_of::<LMFlowInputQueueStats>() {
300 last_error::set(
301 "LMFlowInputQueueStats.struct_size too small -- set it to sizeof(LMFlowInputQueueStats)",
302 );
303 return false;
304 }
305 let Some(graph) = graph_of(g) else {
306 return false;
307 };
308 let Some(stats) = graph.input_queue_stats(node_idx, port_idx) else {
309 return false;
310 };
311 std::ptr::write(
312 out,
313 LMFlowInputQueueStats {
314 struct_size: std::mem::size_of::<LMFlowInputQueueStats>() as u32,
315 reserved0: 0,
316 node_name: graph_arena().intern(&stats.node_name),
317 port_name: graph_arena().intern(&stats.port_name),
318 producer_name: stats
319 .producer_name
320 .as_deref()
321 .map_or(c"".as_ptr(), |name| graph_arena().intern(name)),
322 packet_capacity: stats.packet_capacity.unwrap_or(0),
323 queued_packets: stats.queued_packets,
324 queued_bytes: stats.queued_bytes,
325 reserved_packets: stats.reserved_packets,
326 peak_queued_packets: stats.peak_queued_packets,
327 peak_queued_bytes: stats.peak_queued_bytes,
328 blocked: stats.blocked,
329 reserved1: [0; 7],
330 blocked_for_us: stats.blocked_for_us,
331 block_events: stats.block_events,
332 total_blocked_us: stats.total_blocked_us,
333 },
334 );
335 true
336 })
337}
338
339#[no_mangle]
340pub unsafe extern "C" fn lmflow_graph_counter_value(
341 g: *mut LMFlowGraph,
342 name: *const c_char,
343) -> i64 {
344 guard_val(0, || {
345 graph_of(g)
346 .and_then(|gr| cstr(name).map(|n| gr.inner().shared.counter_value(n)))
347 .unwrap_or(0)
348 })
349}
350#[no_mangle]
351pub unsafe extern "C" fn lmflow_graph_counter_count(g: *mut LMFlowGraph) -> usize {
352 guard_val(0, || {
353 graph_of(g).map_or(0, |gr| gr.inner().shared.counter_names().len())
354 })
355}
356#[no_mangle]
357pub unsafe extern "C" fn lmflow_graph_counter_name(
358 g: *mut LMFlowGraph,
359 idx: usize,
360) -> *const c_char {
361 guard_val(c"".as_ptr(), || {
362 graph_of(g)
363 .and_then(|gr| {
364 gr.inner()
365 .shared
366 .counter_names()
367 .get(idx)
368 .map(|n| graph_arena().intern(n))
369 })
370 .unwrap_or(c"".as_ptr())
371 })
372}