_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}