Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::tracing::color::{AnsiEscapes, Color, SetColor};
9use core::cell::UnsafeCell;
10use core::fmt::{Arguments, Write};
11use core::{cmp, fmt};
12use spin::{ReentrantMutex, ReentrantMutexGuard};
13use tracing_core::Metadata;
14
15pub trait MakeWriter<'a> {
16 type Writer: fmt::Write;
17 fn make_writer(&'a self) -> Self::Writer;
18
19 fn enabled(&self, meta: &Metadata<'_>) -> bool {
20 let _ = meta;
21 true
22 }
23
24 #[inline]
25 fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Option<Self::Writer> {
26 if self.enabled(meta) {
27 return Some(self.make_writer());
28 }
29
30 None
31 }
32
33 fn line_len(&self) -> usize {
34 120
35 }
36}
37
38#[derive(Debug, Copy, Clone, Eq, PartialEq)]
39pub enum IndentKind {
40 Event,
41 NewSpan,
42 Indent,
43}
44
45pub struct Writer<W: Write> {
46 pub(super) writer: W,
47 pub(super) indent: usize,
48 pub(super) max_line_len: usize,
49 pub(super) current_line: usize,
50}
51
52impl<W: Write> Writer<W> {
53 pub fn indent(&mut self, kind: IndentKind) -> fmt::Result {
54 self.write_indent(" ")?;
55
56 for i in 1..=self.indent {
57 let indent_str = match (i, kind) {
58 (i, IndentKind::Event) if i == self.indent => "├",
59 _ => "│",
60 };
61 self.write_indent(indent_str)?;
62 }
63
64 if kind == IndentKind::NewSpan {
65 self.write_indent("┌")?;
66 }
67
68 Ok(())
69 }
70
71 fn write_indent(&mut self, chars: &'static str) -> fmt::Result {
72 self.writer.write_str(chars)?;
73 self.current_line += chars.len();
74 Ok(())
75 }
76
77 fn write_newline(&mut self) -> fmt::Result {
78 // including width of the 16-character timestamp bit
79 self.writer.write_str(" ")?;
80 self.current_line = 3;
81 self.indent(IndentKind::Indent)
82 }
83
84 pub fn finish(&mut self) -> fmt::Result {
85 self.current_line = 0;
86 self.writer.write_char('\n')
87 }
88}
89
90impl<W> Write for Writer<W>
91where
92 W: Write,
93{
94 fn write_str(&mut self, s: &str) -> fmt::Result {
95 let lines = s.split_inclusive('\n');
96 for line in lines {
97 let mut line = line;
98 while self.current_line + line.len() >= self.max_line_len {
99 let offset = if let Some(last_ws) = line[..self.max_line_len - self.current_line]
100 .chars()
101 .rev()
102 .position(|c| c.is_whitespace())
103 {
104 // found a nice whitespace to break on!
105 self.writer.write_str(&line[..last_ws])?;
106 last_ws
107 } else {
108 let offset = cmp::min(line.len(), self.max_line_len);
109 self.writer.write_str(&line[..offset])?;
110 offset
111 };
112
113 self.writer.write_char('\n')?;
114 self.write_newline()?;
115 self.writer.write_char(' ')?;
116 self.current_line += 1;
117 line = &line[offset..];
118 }
119
120 self.writer.write_str(line)?;
121 if line.ends_with('\n') {
122 self.write_newline()?;
123 self.writer.write_char(' ')?;
124 }
125 self.current_line += line.len();
126 }
127
128 Ok(())
129 }
130
131 fn write_char(&mut self, ch: char) -> fmt::Result {
132 self.writer.write_char(ch)?;
133 if ch == '\n' {
134 self.write_newline()
135 } else {
136 Ok(())
137 }
138 }
139}
140
141impl<W> SetColor for Writer<W>
142where
143 W: Write + SetColor,
144{
145 fn set_fg_color(&mut self, color: Color) {
146 self.writer.set_fg_color(color);
147 }
148
149 fn fg_color(&self) -> Color {
150 self.writer.fg_color()
151 }
152
153 fn set_bold(&mut self, bold: bool) {
154 self.writer.set_bold(bold);
155 }
156}
157
158impl<W: Write> Drop for Writer<W> {
159 fn drop(&mut self) {
160 let _ = self.finish();
161 }
162}
163
164// Architecture-specific debug output implementation
165cfg_if::cfg_if! {
166 if #[cfg(target_arch = "riscv64")] {
167 type DebugStream = riscv::hio::HostStream;
168
169 fn new_debug_stream() -> DebugStream {
170 riscv::hio::HostStream::new_stdout()
171 }
172 } else {
173 compile_error!("Unsupported architecture for debug output");
174 }
175}
176
177pub struct Semihosting(ReentrantMutex<UnsafeCell<DebugStream>>);
178pub struct SemihostingWriter<'a>(ReentrantMutexGuard<'a, UnsafeCell<DebugStream>>);
179
180impl Semihosting {
181 pub fn new() -> Self {
182 Self(ReentrantMutex::new(UnsafeCell::new(new_debug_stream())))
183 }
184}
185
186impl<'a> MakeWriter<'a> for Semihosting {
187 type Writer = AnsiEscapes<SemihostingWriter<'a>>;
188
189 fn make_writer(&'a self) -> Self::Writer {
190 AnsiEscapes::new(SemihostingWriter(self.0.lock()))
191 }
192}
193
194impl Write for SemihostingWriter<'_> {
195 fn write_str(&mut self, s: &str) -> fmt::Result {
196 // Safety: Racy access to the HostStream is safe, at worst this produces interleaved debug output
197 let this = unsafe { &mut *self.0.get() };
198 this.write_str(s)
199 }
200
201 fn write_char(&mut self, c: char) -> fmt::Result {
202 // Safety: Racy access to the HostStream is safe, at worst this produces interleaved debug output
203 let this = unsafe { &mut *self.0.get() };
204 this.write_char(c)
205 }
206
207 fn write_fmt(&mut self, args: Arguments<'_>) -> fmt::Result {
208 // Safety: Racy access to the HostStream is safe, at worst this produces interleaved debug output
209 let this = unsafe { &mut *self.0.get() };
210 this.write_fmt(args)
211 }
212}