1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

//! Logging APIs

#![allow(clippy::not_unsafe_ptr_arg_deref)]

use crate::sys::{
    VT_log_critical, VT_log_error, VT_log_info, VT_log_spec_violation, VT_log_unimplemented,
};
use crate::{
    simics_exception,
    sys::{SIM_log_level, SIM_log_register_groups, SIM_set_log_level},
    ConfClass, ConfObject, Error, Result,
};
use std::{ffi::CString, ptr::null};

/// The default log group
pub const LOG_GROUP: i32 = 0;

#[repr(i32)]
/// A log level as defined by SIMICS
pub enum LogLevel {
    /// Error log level
    Error = 0,
    /// Warning log level
    Warn = 1,
    /// Informational log level
    Info = 2,
    /// Debug log level
    Debug = 3,
    /// Trace log level
    Trace = 4,
}

/// Sanitize a string for logging (i.e. as if with printf)
fn sanitize<S>(s: S) -> String
where
    S: AsRef<str>,
{
    s.as_ref().replace('%', "%%")
}

#[simics_exception]
/// Log an info-level message through the SIMICS logging functions
///
/// # Arguments
///
/// * `level` - The level to emit this log message at
/// * `device` - The device to emit this log message through
/// * `msg` - The message to log
///
/// # Notes
///
/// The macros [`simics::error`], [`simics::warn`], [`simics::info`], [`simics::debug`],
/// and [`simics::trace`] are more flexible and user friendly. They should be used instead.
///
/// # Context
///
/// All Contexts
pub fn log_info<S>(level: LogLevel, device: *mut ConfObject, msg: S) -> Result<()>
where
    S: AsRef<str>,
{
    let msg_cstring = CString::new(msg.as_ref())?;

    unsafe {
        VT_log_info(level as i32, device, LOG_GROUP as u64, msg_cstring.as_ptr());
    }

    Ok(())
}

#[simics_exception]
/// Log an info-level message through the SIMICS logging functions
///
/// # Arguments
///
/// * `device` - The device to emit this log message through
/// * `msg` - The message to log
///
/// # Notes
///
/// The macros [`simics::error`], [`simics::warn`], [`simics::info`], [`simics::debug`],
/// and [`simics::trace`] are more flexible and user friendly. They should be used instead.
///
/// # Context
///
/// All Contexts
pub fn log_error<S>(device: *mut ConfObject, msg: S) -> Result<()>
where
    S: AsRef<str>,
{
    let msg_cstring = CString::new(sanitize(msg.as_ref()))?;

    unsafe {
        VT_log_error(device, LOG_GROUP as u64, msg_cstring.as_ptr());
    };

    Ok(())
}

#[simics_exception]
/// Log an info-level message through the SIMICS logging functions
///
/// # Arguments
///
/// * `device` - The device to emit this log message through
/// * `msg` - The message to log
///
/// # Notes
///
/// This function causes a frontend exception. Only use it if the error is truly critical.
///
/// # Context
///
/// All Contexts
pub fn log_critical<S>(device: *mut ConfObject, msg: S) -> Result<()>
where
    S: AsRef<str>,
{
    let msg_cstring = CString::new(sanitize(msg.as_ref()))?;

    unsafe {
        VT_log_critical(device, LOG_GROUP as u64, msg_cstring.as_ptr());
    };

    Ok(())
}

#[simics_exception]
/// Log an error-level message through the SIMICS logging functions
///
/// # Context
///
/// All Contexts
pub fn log_spec_violation(level: LogLevel, device: *mut ConfObject, msg: String) -> Result<()> {
    let msg_cstring = CString::new(sanitize(msg))?;

    unsafe {
        VT_log_spec_violation(level as i32, device, LOG_GROUP as u64, msg_cstring.as_ptr());
    };

    Ok(())
}

#[simics_exception]
/// Log an error-level message through the SIMICS logging functions
///
/// # Context
///
/// All Contexts
pub fn log_unimplemented(level: LogLevel, device: *mut ConfObject, msg: String) -> Result<()> {
    let msg_cstring = CString::new(sanitize(msg))?;

    unsafe {
        VT_log_unimplemented(level as i32, device, LOG_GROUP as u64, msg_cstring.as_ptr());
    };

    Ok(())
}

#[simics_exception]
/// Get the current log level of an object
///
/// # Arguments
///
/// * `obj` - The object to get the log level for
///
/// # Context
///
/// Cell Context
pub fn log_level(obj: *mut ConfObject) -> u32 {
    unsafe { SIM_log_level(obj as *const ConfObject) }
}

#[simics_exception]
/// Set the SIMICS log level for an object
///
/// # Arguments
///
/// * `obj` - The object to set the log level for
/// * `level` - The level to set the log level to
///
/// # Context
///
/// Cell Context
pub fn set_log_level(obj: *mut ConfObject, level: LogLevel) {
    unsafe { SIM_set_log_level(obj, level as u32) };
}

#[simics_exception]
/// Register one or more groups for the class
///
/// # Context
///
/// Global Context
pub fn log_register_groups<S>(cls: *mut ConfClass, names: &[S]) -> Result<()>
where
    S: AsRef<str>,
{
    let name_cstrs = names
        .iter()
        .map(|n| CString::new(n.as_ref()).map_err(Error::from))
        .collect::<Result<Vec<CString>>>()?;
    let mut name_ptrs = name_cstrs.iter().map(|n| n.as_ptr()).collect::<Vec<_>>();
    name_ptrs.push(null());
    unsafe { SIM_log_register_groups(cls, name_ptrs.as_ptr()) };

    Ok(())
}