Skip to main content

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, CPPMessageFormatter, LogMessage};
9use diagnostics_message::{self as message, MonikerWithUrl};
10use std::ffi::CString;
11use std::ops::{Deref, DerefMut};
12use std::os::raw::c_char;
13use std::ptr::NonNull;
14use thiserror::Error;
15
16/// # Safety
17///
18/// Same as for `std::slice::from_raw_parts`. Summarizing in terms of this API:
19///
20/// - `msg` must be valid for reads for `size`, and it must be properly aligned.
21/// - `msg` must point to `size` consecutive u8 values.
22/// - The `size` of the slice must be no larger than `isize::MAX`, and adding
23///   that size to data must not "wrap around" the address space. See the safety
24///   documentation of pointer::offset.
25#[unsafe(no_mangle)]
26pub unsafe extern "C" fn fuchsia_decode_log_message_to_json(
27    msg: *const u8,
28    size: usize,
29) -> *mut c_char {
30    let managed_ptr = unsafe { std::slice::from_raw_parts(msg, size) };
31    let data = &message::from_structured(
32        MonikerWithUrl { moniker: "test_moniker".try_into().unwrap(), url: "".into() },
33        managed_ptr,
34    )
35    .unwrap();
36    let item = serde_json::to_string(&data).unwrap();
37    CString::new(format!("[{}]", item)).unwrap().into_raw()
38}
39
40/// LogMessages struct containing log messages
41/// It is created by calling fuchsia_decode_log_messages_to_struct,
42/// and freed by calling fuchsia_free_log_messages.
43/// Log messages contain embedded pointers to the bytes from
44/// which they were created, so the memory referred to
45/// by the LogMessages must not be modified or free'd until
46/// the LogMessages are free'd.
47#[repr(C)]
48pub struct LogMessages<'a> {
49    messages: CPPArray<'a, &'a LogMessage<'a>>,
50    error_str: *const c_char,
51    allocator: AliasableBox<Bump>,
52}
53
54#[derive(Error, Debug)]
55pub enum DecodeError {
56    #[error(transparent)]
57    Message(#[from] MessageError),
58    #[error(transparent)]
59    ParserError(#[from] ParseError),
60}
61
62pub type MessageParser = message::MessageParser;
63
64#[unsafe(no_mangle)]
65pub extern "C" fn fuchsia_new_message_parser() -> *mut MessageParser {
66    Box::into_raw(Box::new(MessageParser::default()))
67}
68
69/// # Safety
70///
71/// This should only be called with a pointer obtained through
72/// `fuchsia_new_message_parser`.
73#[unsafe(no_mangle)]
74pub unsafe extern "C" fn fuchsia_free_message_parser(parser: *mut MessageParser) {
75    if !parser.is_null() {
76        // SAFETY: parser must be a valid MessageParser constructed from
77        // fuchsia_new_message_parser.
78        unsafe { drop(Box::from_raw(parser)) };
79    }
80}
81
82/// # Safety
83///
84/// - This function is NOT thread-safe. The caller must ensure that it is not called
85///   concurrently with the same `parser` pointer.
86///
87/// Same as for `std::slice::from_raw_parts`. Summarizing in terms of this API:
88///
89/// - `msg` must be valid for reads for `size`, and it must be properly aligned.
90/// - `msg` must point to `size` consecutive u8 values.
91/// - The `size` of the slice must be no larger than `isize::MAX`, and adding
92///   that size to data must not "wrap around" the address space. See the safety
93///   documentation of pointer::offset.
94/// If identity is provided, it must contain a valid moniker and URL.
95///
96/// The returned LogMessages must be free'd with fuchsia_free_log_messages(log_messages).  Free'ing
97/// the LogMessages struct frees the bump allocator itself (and everything allocated from it).
98#[unsafe(no_mangle)]
99pub unsafe extern "C" fn fuchsia_decode_log_messages_to_struct<'a>(
100    msg: *const u8,
101    size: usize,
102    expect_extended_attribution: bool,
103    parser: *mut MessageParser,
104) -> LogMessages<'a> {
105    let allocator = AliasableBox::new(Bump::new());
106
107    // SAFETY: The C++ side is responsible for managing the lifetime.  We want to return
108    // `LogMessages<'a>`, so we create a reference to the allocator here with a 'a lifetime.  We are
109    // using `AliasableBox` which allows us to move `allocator` without invalidating any of the
110    // data.
111    let allocator_ref: &'a Bump = unsafe { allocator.get_ref() };
112
113    // SAFETY: If `parser` is non-null, it must be valid and the caller guarantees exclusive access
114    // to it.
115    let maybe_parser = unsafe { parser.as_mut() };
116
117    // By using a struct and `Deref` we limit the lifetime to this function.
118    struct Buf(*const u8, usize);
119    impl Deref for Buf {
120        type Target = [u8];
121        fn deref(&self) -> &Self::Target {
122            // SAFETY: Safe because the caller guarantees that `msg` is valid, initialized and
123            // properly aligned.
124            unsafe { std::slice::from_raw_parts(self.0, self.1) }
125        }
126    }
127    let buf = &Buf(msg, size);
128
129    let messages = if let Some(parser) = maybe_parser {
130        fuchsia_decode_log_messages_to_struct_internal(buf, parser, allocator_ref)
131    } else {
132        fuchsia_decode_log_messages_to_struct_internal_legacy(
133            buf,
134            expect_extended_attribution,
135            allocator_ref,
136        )
137    };
138
139    match messages {
140        Ok(messages) => {
141            let messages: &[_] =
142                allocator_ref.alloc_slice_fill_iter(messages.into_iter().map(|m| &*m));
143            LogMessages { messages: messages.into(), error_str: std::ptr::null(), allocator }
144        }
145        Err(err) => LogMessages {
146            messages: CPPArray::default(),
147            error_str: allocator_ref
148                .alloc_slice_copy(CString::new(err.to_string()).unwrap().as_bytes_with_nul())
149                .as_ptr() as *const c_char,
150            allocator,
151        },
152    }
153}
154
155/// Decodes log messages from a FXT stream.
156fn fuchsia_decode_log_messages_to_struct_internal<'a>(
157    buf: &[u8],
158    parser: &mut MessageParser,
159    allocator: &'a Bump,
160) -> Result<Vec<&'a mut LogMessage<'a>>, DecodeError> {
161    let mut messages = vec![];
162    let mut current_slice = buf.as_ref();
163    let formatter = CPPMessageFormatter(allocator);
164    loop {
165        let (data, remaining) = parser.parse_next(current_slice, &formatter)?;
166
167        if let Some(data) = data {
168            messages.push(data);
169        }
170        if remaining.is_empty() {
171            break;
172        }
173        current_slice = remaining;
174    }
175
176    Ok(messages)
177}
178
179/// Decodes log messages from a legacy FXT stream.
180fn fuchsia_decode_log_messages_to_struct_internal_legacy<'a>(
181    buf: &[u8],
182    expect_extended_attribution: bool,
183    allocator: &'a Bump,
184) -> Result<Vec<&'a mut LogMessage<'a>>, DecodeError> {
185    let mut messages = vec![];
186    let mut current_slice = buf.as_ref();
187    loop {
188        let (data, remaining) = if expect_extended_attribution {
189            message::ffi::ffi_from_extended_record(current_slice, allocator)?
190        } else {
191            let (_, remaining_after_parse) =
192                diagnostics_log_encoding::parse::parse_record(current_slice)?;
193            let record_len = current_slice.len() - remaining_after_parse.len();
194            let record_slice = &current_slice[..record_len];
195            let (data, _) = message::ffi::ffi_from_extended_record(record_slice, allocator)?;
196            (data, remaining_after_parse)
197        };
198        messages.push(data);
199        if remaining.is_empty() {
200            break;
201        }
202        current_slice = remaining;
203    }
204
205    Ok(messages)
206}
207
208/// # Safety
209///
210/// This should only be called with a pointer obtained through
211/// `fuchsia_decode_log_message_to_json`.
212#[unsafe(no_mangle)]
213pub unsafe extern "C" fn fuchsia_free_decoded_log_message(msg: *mut c_char) {
214    let str_to_free = unsafe { CString::from_raw(msg) };
215    let _freer = str_to_free;
216}
217
218/// # Safety
219///
220/// This should only be called with `input` obtained through
221/// `fuchsia_decode_log_messages_to_struct`.
222#[unsafe(no_mangle)]
223pub unsafe extern "C" fn fuchsia_free_log_messages(input: LogMessages<'_>) {
224    drop(input);
225}
226
227/// Like `Box` except that it can be moved when there are live pointers.
228#[repr(C)]
229struct AliasableBox<T>(NonNull<T>);
230
231impl<T> AliasableBox<T> {
232    /// Returns a reference with an arbitrary lifetime.
233    unsafe fn get_ref<'a>(&self) -> &'a T {
234        // SAFETY: The caller must make this safe.
235        unsafe { self.0.as_ref() }
236    }
237}
238
239impl<T> AliasableBox<T> {
240    fn new(value: T) -> Self {
241        // SAFETY: `Box::into_raw` won't return null.
242        Self(unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(value))) })
243    }
244}
245
246impl<T> Drop for AliasableBox<T> {
247    fn drop(&mut self) {
248        // SAFETY: We own the pointer.
249        unsafe {
250            let _ = Box::from_raw(self.0.as_ptr());
251        }
252    }
253}
254
255impl<T> Deref for AliasableBox<T> {
256    type Target = T;
257    fn deref(&self) -> &Self::Target {
258        // SAFETY: We own the pointer.
259        unsafe { self.0.as_ref() }
260    }
261}
262
263impl<T> DerefMut for AliasableBox<T> {
264    fn deref_mut(&mut self) -> &mut Self::Target {
265        // SAFETY: We own the pointer.
266        unsafe { self.0.as_mut() }
267    }
268}