_log_decoder_c_bindings_rustc_static/
lib.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3
4use bumpalo::Bump;
5use diagnostics_message::ffi::{CPPArray, LogMessage};
6use diagnostics_message::{self as message, MonikerWithUrl};
7use std::ffi::CString;
8use std::os::raw::c_char;
9
10/// # Safety
11///
12/// Same as for `std::slice::from_raw_parts`. Summarizing in terms of this API:
13///
14/// - `msg` must be valid for reads for `size`, and it must be properly aligned.
15/// - `msg` must point to `size` consecutive u8 values.
16/// - The `size` of the slice must be no larger than `isize::MAX`, and adding
17///   that size to data must not "wrap around" the address space. See the safety
18///   documentation of pointer::offset.
19#[no_mangle]
20pub unsafe extern "C" fn fuchsia_decode_log_message_to_json(
21    msg: *const u8,
22    size: usize,
23) -> *mut c_char {
24    let managed_ptr = std::slice::from_raw_parts(msg, size);
25    let data = &message::from_structured(
26        MonikerWithUrl { moniker: "test_moniker".try_into().unwrap(), url: "".into() },
27        managed_ptr,
28    )
29    .unwrap();
30    let item = serde_json::to_string(&data).unwrap();
31    CString::new(format!("[{}]", item)).unwrap().into_raw()
32}
33
34/// Memory-managed state to be free'd on the Rust side
35/// when the log messages are destroyed.
36pub struct ManagedState<'a> {
37    allocator: Bump,
38    message_array: Vec<*mut LogMessage<'a>>,
39}
40
41impl Drop for LogMessages<'_> {
42    fn drop(&mut self) {
43        unsafe {
44            // SAFETY: All pointers in message_array are assumed to be valid.
45            // Other unsafe code in this file and in C++ ensures this invariant.
46
47            // Free all managed state in the log messages.
48            // The log messages themselves don't need to be explicitly free'd
49            // as they are owned by the Bump allocator.
50            let state = Box::from_raw(self.state);
51            for msg in &state.message_array {
52                std::ptr::drop_in_place(*msg);
53            }
54        }
55    }
56}
57
58/// LogMessages struct containing log messages
59/// It is created by calling fuchsia_decode_log_messages_to_struct,
60/// and freed by calling fuchsia_free_log_messages.
61/// Log messages contain embedded pointers to the bytes from
62/// which they were created, so the memory referred to
63/// by the LogMessages must not be modified or free'd until
64/// the LogMessages are free'd.
65#[repr(C)]
66pub struct LogMessages<'a> {
67    messages: CPPArray<*mut LogMessage<'a>>,
68    state: *mut ManagedState<'a>,
69}
70
71/// # Safety
72///
73/// Same as for `std::slice::from_raw_parts`. Summarizing in terms of this API:
74///
75/// - `msg` must be valid for reads for `size`, and it must be properly aligned.
76/// - `msg` must point to `size` consecutive u8 values.
77/// - 'msg' must outlive the returned LogMessages struct, and must not be free'd
78///   until fuchsia_free_log_messages has been called.
79/// - The `size` of the slice must be no larger than `isize::MAX`, and adding
80///   that size to data must not "wrap around" the address space. See the safety
81///   documentation of pointer::offset.
82/// If identity is provided, it must contain a valid moniker and URL.
83///
84/// The returned LogMessages may be free'd with fuchsia_free_log_messages(log_messages).
85/// Free'ing the LogMessages struct does the following, in this order:
86/// * Frees memory associated with each individual log message
87/// * Frees the bump allocator itself (and everything allocated from it), as well as
88/// the message array itself.
89#[no_mangle]
90pub unsafe extern "C" fn fuchsia_decode_log_messages_to_struct(
91    msg: *const u8,
92    size: usize,
93    expect_extended_attribution: bool,
94) -> LogMessages<'static> {
95    let mut state = Box::new(ManagedState { allocator: Bump::new(), message_array: vec![] });
96    let buf = std::slice::from_raw_parts(msg, size);
97    let mut current_slice = buf.as_ref();
98    loop {
99        let (data, remaining) = message::ffi::ffi_from_extended_record(
100            current_slice,
101            // SAFETY: The returned LogMessage must NOT outlive the bump allocator.
102            // This is ensured by the allocator living in the heap-allocated ManagedState
103            // struct which frees the LogMessages first when dropped, before allowing the bump
104            // allocator itself to be freed.
105            &*(&state.allocator as *const Bump),
106            None,
107            expect_extended_attribution,
108        )
109        .unwrap();
110        state.message_array.push(data as *mut LogMessage<'static>);
111        if remaining.is_empty() {
112            break;
113        }
114        current_slice = remaining;
115    }
116
117    LogMessages { messages: (&state.message_array).into(), state: Box::into_raw(state) }
118}
119
120/// # Safety
121///
122/// This should only be called with a pointer obtained through
123/// `fuchsia_decode_log_message_to_json`.
124#[no_mangle]
125pub unsafe extern "C" fn fuchsia_free_decoded_log_message(msg: *mut c_char) {
126    let str_to_free = CString::from_raw(msg);
127    let _freer = str_to_free;
128}
129
130/// # Safety
131///
132/// This should only be called with a pointer obtained through
133/// `fuchsia_decode_log_messages_to_struct`.
134#[no_mangle]
135pub unsafe extern "C" fn fuchsia_free_log_messages(input: LogMessages<'_>) {
136    drop(input);
137}