Skip to main content

lmflow/ffi/graph/
diagnostics.rs

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/// 显式选择 Graphviz DOT 的详细程度。返回值同 `dump`:线程局部缓冲,调用方不得 free。
152#[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        // 未初始化图 → 合法的空 digraph(而非文本占位符),便于直接喂 graphviz。
171        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/// 导出当前 trace 环内容为 Chrome Trace Event Format 的 JSON(chrome://tracing / perfetto
183/// 可直接打开)。需在建图时设 `trace_capacity > 0`;未开启时返回合法的空 trace。返回值同
184/// `dump`:**线程局部缓冲**,下次在本线程调用即失效,调用方不得 free(需要就先拷走)。
185#[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            // 空句柄:仍给一个合法的空 trace。走同一个导出器而非硬编码字面量,
194            // 免得将来给格式加字段时两处漂移。
195            || 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}