1use crate::file_metadata::SessionId;
7use crate::rc::{Ref, RefCountable};
8use crate::string::{raw_to_string, BnString, IntoCStr};
9use binaryninjacore_sys::*;
10use std::ffi::CString;
11use std::os::raw::{c_char, c_void};
12use std::ptr::NonNull;
13
14#[allow(unused_imports)]
16use crate::binary_view::BinaryView;
17
18pub use binaryninjacore_sys::BNLogLevel as BnLogLevel;
19
20pub const LOGGER_DEFAULT_SESSION_ID: SessionId = SessionId(0);
21
22pub fn bn_log(logger: &str, level: BnLogLevel, msg: &str) {
26 bn_log_with_session(LOGGER_DEFAULT_SESSION_ID, logger, level, msg);
27}
28
29pub fn bn_log_with_session(session_id: SessionId, logger: &str, level: BnLogLevel, msg: &str) {
34 if let Ok(msg) = CString::new(msg) {
35 let logger_name = logger.to_cstr();
36 unsafe {
37 BNLog(
38 session_id.0,
39 level,
40 logger_name.as_ptr(),
41 0,
42 c"%s".as_ptr(),
43 msg.as_ptr(),
44 )
45 }
46 }
47}
48
49#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub struct Logger {
51 handle: NonNull<BNLogger>,
52}
53
54impl Logger {
55 pub fn new(name: &str) -> Ref<Logger> {
57 Self::new_with_session(name, LOGGER_DEFAULT_SESSION_ID)
58 }
59
60 pub fn ref_from_raw(handle: *mut BNLogger) -> Ref<Logger> {
61 unsafe {
62 Ref::new(Logger {
63 handle: NonNull::new(handle).unwrap(),
64 })
65 }
66 }
67
68 pub fn new_with_session(name: &str, session_id: SessionId) -> Ref<Logger> {
82 let name_raw = CString::new(name).unwrap();
83 let handle = unsafe { BNLogCreateLogger(name_raw.as_ptr(), session_id.0) };
84 unsafe {
85 Ref::new(Logger {
86 handle: NonNull::new(handle).unwrap(),
87 })
88 }
89 }
90
91 pub fn name(&self) -> String {
93 unsafe { BnString::into_string(BNLoggerGetName(self.handle.as_ptr())) }
94 }
95
96 pub fn session_id(&self) -> Option<SessionId> {
101 let raw = unsafe { BNLoggerGetSessionId(self.handle.as_ptr()) };
102 match raw {
103 0 => None,
104 _ => Some(SessionId(raw)),
105 }
106 }
107
108 pub fn log(&self, level: BnLogLevel, msg: &str) {
112 let session = self.session_id().unwrap_or(LOGGER_DEFAULT_SESSION_ID);
113 bn_log_with_session(session, &self.name(), level, msg);
114 }
115}
116
117impl Default for Ref<Logger> {
118 fn default() -> Self {
119 Logger::new("Default")
120 }
121}
122
123impl ToOwned for Logger {
124 type Owned = Ref<Self>;
125
126 fn to_owned(&self) -> Self::Owned {
127 unsafe { RefCountable::inc_ref(self) }
128 }
129}
130
131unsafe impl RefCountable for Logger {
132 unsafe fn inc_ref(logger: &Self) -> Ref<Self> {
133 Ref::new(Self {
134 handle: NonNull::new(BNNewLoggerReference(logger.handle.as_ptr())).unwrap(),
135 })
136 }
137
138 unsafe fn dec_ref(logger: &Self) {
139 BNFreeLogger(logger.handle.as_ptr());
140 }
141}
142
143unsafe impl Send for Logger {}
144unsafe impl Sync for Logger {}
145
146#[must_use]
152pub fn register_log_listener<L: LogListener>(listener: L) -> LogGuard<L> {
153 use binaryninjacore_sys::BNRegisterLogListener;
154
155 let raw = Box::into_raw(Box::new(listener));
156 let mut bn_obj = BNLogListener {
157 context: raw as *mut _,
158 log: Some(cb_log::<L>),
159 logWithStackTrace: Some(cb_log_with_stack_trace::<L>),
160 close: Some(cb_close::<L>),
161 getLogLevel: Some(cb_level::<L>),
162 };
163
164 unsafe {
165 BNRegisterLogListener(&mut bn_obj);
166 BNUpdateLogListeners();
167 }
168
169 LogGuard { ctxt: raw }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
174pub struct LogContext<'a> {
175 pub session_id: Option<SessionId>,
179 pub thread_id: usize,
181 pub stack_trace: Option<&'a str>,
183 pub logger_name: &'a str,
185}
186
187pub trait LogListener: 'static + Sync {
192 fn log(&self, ctx: &LogContext, lvl: BnLogLevel, msg: &str);
196
197 fn level(&self) -> BnLogLevel;
202
203 fn close(&self) {}
205}
206
207pub struct LogGuard<L: LogListener> {
208 ctxt: *mut L,
209}
210
211impl<L: LogListener> Drop for LogGuard<L> {
212 fn drop(&mut self) {
213 use binaryninjacore_sys::BNUnregisterLogListener;
214
215 let mut bn_obj = BNLogListener {
216 context: self.ctxt as *mut _,
217 log: Some(cb_log::<L>),
218 logWithStackTrace: Some(cb_log_with_stack_trace::<L>),
219 close: Some(cb_close::<L>),
220 getLogLevel: Some(cb_level::<L>),
221 };
222
223 unsafe {
224 BNUnregisterLogListener(&mut bn_obj);
225 BNUpdateLogListeners();
226
227 let _listener = Box::from_raw(self.ctxt);
228 }
229 }
230}
231
232extern "C" fn cb_log<L>(
233 ctxt: *mut c_void,
234 session: usize,
235 level: BnLogLevel,
236 msg: *const c_char,
237 logger_name: *const c_char,
238 tid: usize,
239) where
240 L: LogListener,
241{
242 ffi_wrap!("LogListener::log", unsafe {
243 let listener = &*(ctxt as *const L);
244 let msg_str = raw_to_string(msg).unwrap();
245 let logger_name_str = raw_to_string(logger_name).unwrap();
246 let session_id = match session {
247 0 => None,
248 _ => Some(SessionId(session)),
249 };
250 let ctx = LogContext {
251 session_id,
252 thread_id: tid,
253 stack_trace: None,
254 logger_name: &logger_name_str,
255 };
256 listener.log(&ctx, level, &msg_str);
257 })
258}
259
260extern "C" fn cb_log_with_stack_trace<L>(
261 ctxt: *mut c_void,
262 session: usize,
263 level: BnLogLevel,
264 stack_trace: *const c_char,
265 msg: *const c_char,
266 logger_name: *const c_char,
267 tid: usize,
268) where
269 L: LogListener,
270{
271 ffi_wrap!("LogListener::log_with_stack_trace", unsafe {
272 let listener = &*(ctxt as *const L);
273 let stack_trace_str = raw_to_string(stack_trace).unwrap();
274 let msg_str = raw_to_string(msg).unwrap();
275 let logger_name_str = raw_to_string(logger_name).unwrap();
276 let session_id = match session {
277 0 => None,
278 _ => Some(SessionId(session)),
279 };
280 let ctx = LogContext {
281 session_id,
282 thread_id: tid,
283 stack_trace: Some(&stack_trace_str),
284 logger_name: &logger_name_str,
285 };
286 listener.log(&ctx, level, &msg_str);
287 })
288}
289
290extern "C" fn cb_close<L>(ctxt: *mut c_void)
291where
292 L: LogListener,
293{
294 ffi_wrap!("LogListener::close", unsafe {
295 let listener = &*(ctxt as *const L);
296 listener.close();
297 })
298}
299
300extern "C" fn cb_level<L>(ctxt: *mut c_void) -> BnLogLevel
301where
302 L: LogListener,
303{
304 ffi_wrap!("LogListener::log", unsafe {
305 let listener = &*(ctxt as *const L);
306 listener.level()
307 })
308}