log_decoder_c_bindings/
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_log_encoding;
6use diagnostics_log_encoding::parse::ParseError;
7use diagnostics_message::error::MessageError;
8use diagnostics_message::ffi::{CPPArray, LogMessage};
9use diagnostics_message::{self as message, MonikerWithUrl};
10use std::ffi::CString;
11use std::os::raw::c_char;
12use thiserror::Error;
13
14/// # Safety
15///
16/// Same as for `std::slice::from_raw_parts`. Summarizing in terms of this API:
17///
18/// - `msg` must be valid for reads for `size`, and it must be properly aligned.
19/// - `msg` must point to `size` consecutive u8 values.
20/// - The `size` of the slice must be no larger than `isize::MAX`, and adding
21///   that size to data must not "wrap around" the address space. See the safety
22///   documentation of pointer::offset.
23#[unsafe(no_mangle)]
24pub unsafe extern "C" fn fuchsia_decode_log_message_to_json(
25    msg: *const u8,
26    size: usize,
27) -> *mut c_char {
28    let managed_ptr = unsafe { std::slice::from_raw_parts(msg, size) };
29    let data = &message::from_structured(
30        MonikerWithUrl { moniker: "test_moniker".try_into().unwrap(), url: "".into() },
31        managed_ptr,
32    )
33    .unwrap();
34    let item = serde_json::to_string(&data).unwrap();
35    CString::new(format!("[{}]", item)).unwrap().into_raw()
36}
37
38/// Memory-managed state to be free'd on the Rust side
39/// when the log messages are destroyed.
40pub struct ManagedState<'a> {
41    allocator: Bump,
42    message_array: Vec<*mut LogMessage<'a>>,
43}
44
45impl Drop for LogMessages<'_> {
46    fn drop(&mut self) {
47        unsafe {
48            // SAFETY: All pointers in message_array are assumed to be valid.
49            // Other unsafe code in this file and in C++ ensures this invariant.
50
51            // Free all managed state in the log messages.
52            // The log messages themselves don't need to be explicitly free'd
53            // as they are owned by the Bump allocator.
54            let state = Box::from_raw(self.state);
55            for msg in &state.message_array {
56                std::ptr::drop_in_place(*msg);
57            }
58        }
59    }
60}
61
62/// LogMessages struct containing log messages
63/// It is created by calling fuchsia_decode_log_messages_to_struct,
64/// and freed by calling fuchsia_free_log_messages.
65/// Log messages contain embedded pointers to the bytes from
66/// which they were created, so the memory referred to
67/// by the LogMessages must not be modified or free'd until
68/// the LogMessages are free'd.
69#[repr(C)]
70pub struct LogMessages<'a> {
71    messages: CPPArray<*mut LogMessage<'a>>,
72    state: *mut ManagedState<'a>,
73    error_str: *mut c_char,
74}
75
76#[derive(Error, Debug)]
77pub enum DecodeError {
78    #[error(transparent)]
79    Message(#[from] MessageError),
80    #[error(transparent)]
81    ParserError(#[from] ParseError),
82}
83
84/// # Safety
85///
86/// Same as for `std::slice::from_raw_parts`. Summarizing in terms of this API:
87///
88/// - `msg` must be valid for reads for `size`, and it must be properly aligned.
89/// - `msg` must point to `size` consecutive u8 values.
90/// - 'msg' must outlive the returned LogMessages struct, and must not be free'd
91///   until fuchsia_free_log_messages has been called.
92/// - The `size` of the slice must be no larger than `isize::MAX`, and adding
93///   that size to data must not "wrap around" the address space. See the safety
94///   documentation of pointer::offset.
95/// If identity is provided, it must contain a valid moniker and URL.
96///
97/// The returned LogMessages may be free'd with fuchsia_free_log_messages(log_messages).
98/// Free'ing the LogMessages struct does the following, in this order:
99/// * Frees memory associated with each individual log message
100/// * Frees the bump allocator itself (and everything allocated from it), as well as
101/// the message array itself.
102/// If a malformed message is passed, returns nullptr.
103#[unsafe(no_mangle)]
104pub unsafe extern "C" fn fuchsia_decode_log_messages_to_struct(
105    msg: *const u8,
106    size: usize,
107    expect_extended_attribution: bool,
108) -> LogMessages<'static> {
109    unsafe {
110        fuchsia_decode_log_messages_to_struct_internal(msg, size, expect_extended_attribution)
111    }
112    .unwrap_or_else(|err| LogMessages {
113        messages: (&vec![]).into(),
114        state: std::ptr::null_mut(),
115        error_str: CString::new(err.to_string())
116            .map(|value| value.into_raw())
117            .unwrap_or(std::ptr::null_mut()),
118    })
119}
120
121/// # Safety
122///
123/// Same as for `std::slice::from_raw_parts`. Summarizing in terms of this API:
124///
125/// - `msg` must be valid for reads for `size`, and it must be properly aligned.
126/// - `msg` must point to `size` consecutive u8 values.
127/// - 'msg' must outlive the returned LogMessages struct, and must not be free'd
128///   until fuchsia_free_log_messages has been called.
129/// - The `size` of the slice must be no larger than `isize::MAX`, and adding
130///   that size to data must not "wrap around" the address space. See the safety
131///   documentation of pointer::offset.
132/// If identity is provided, it must contain a valid moniker and URL.
133///
134/// The returned LogMessages may be free'd with fuchsia_free_log_messages(log_messages).
135/// Free'ing the LogMessages struct does the following, in this order:
136/// * Frees memory associated with each individual log message
137/// * Frees the bump allocator itself (and everything allocated from it), as well as
138/// the message array itself.
139unsafe fn fuchsia_decode_log_messages_to_struct_internal(
140    msg: *const u8,
141    size: usize,
142    expect_extended_attribution: bool,
143) -> Result<LogMessages<'static>, DecodeError> {
144    let mut state = Box::new(ManagedState { allocator: Bump::new(), message_array: vec![] });
145    let buf = unsafe { std::slice::from_raw_parts(msg, size) };
146    let mut current_slice = buf.as_ref();
147    loop {
148        let (data, remaining) = if expect_extended_attribution {
149            message::ffi::ffi_from_extended_record(
150                current_slice,
151                // SAFETY: The returned LogMessage must NOT outlive the bump allocator.
152                // This is ensured by the allocator living in the heap-allocated ManagedState
153                // struct which frees the LogMessages first when dropped, before allowing the bump
154                // allocator itself to be freed.
155                unsafe { &*(&state.allocator as *const Bump) },
156            )?
157        } else {
158            let (_, remaining_after_parse) =
159                diagnostics_log_encoding::parse::parse_record(current_slice)?;
160            let record_len = current_slice.len() - remaining_after_parse.len();
161            let record_slice = &current_slice[..record_len];
162            let (data, _) = message::ffi::ffi_from_extended_record(
163                record_slice,
164                // SAFETY: The returned LogMessage must NOT outlive the bump allocator.
165                // This is ensured by the allocator living in the heap-allocated ManagedState
166                // struct which frees the LogMessages first when dropped, before allowing the bump
167                // allocator itself to be freed.
168                unsafe { &*(&state.allocator as *const Bump) },
169            )?;
170            (data, remaining_after_parse)
171        };
172        state.message_array.push(data as *mut LogMessage<'static>);
173        if remaining.is_empty() {
174            break;
175        }
176        current_slice = remaining;
177    }
178
179    Ok(LogMessages {
180        messages: (&state.message_array).into(),
181        state: Box::into_raw(state),
182        error_str: std::ptr::null_mut(),
183    })
184}
185
186/// # Safety
187///
188/// This should only be called with a pointer obtained through
189/// `fuchsia_decode_log_message_to_json`.
190#[unsafe(no_mangle)]
191pub unsafe extern "C" fn fuchsia_free_decoded_log_message(msg: *mut c_char) {
192    let str_to_free = unsafe { CString::from_raw(msg) };
193    let _freer = str_to_free;
194}
195
196/// # Safety
197///
198/// This should only be called with a pointer obtained through
199/// `fuchsia_decode_log_messages_to_struct`. This method
200/// should not be called if state is nullptr.
201#[unsafe(no_mangle)]
202pub unsafe extern "C" fn fuchsia_free_log_messages(input: LogMessages<'_>) {
203    drop(input);
204}