From eee4c624f92cdcf8d029ec57522c3338bdf7e98d Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 13 Apr 2023 16:01:40 +0200 Subject: [PATCH] feat(core/rust): JSON output from component tracing --- core/embed/rust/Cargo.lock | 30 ++ core/embed/rust/Cargo.toml | 3 + core/embed/rust/src/lib.rs | 1 + core/embed/rust/src/maybe_trace.rs | 15 + core/embed/rust/src/micropython/buffer.rs | 7 - core/embed/rust/src/micropython/list.rs | 14 +- core/embed/rust/src/strutil.rs | 31 ++ core/embed/rust/src/trace.rs | 271 ++++++++++++----- core/embed/rust/src/ui/component/base.rs | 31 +- core/embed/rust/src/ui/component/empty.rs | 3 +- core/embed/rust/src/ui/component/image.rs | 6 +- core/embed/rust/src/ui/component/label.rs | 3 +- core/embed/rust/src/ui/component/marquee.rs | 7 +- core/embed/rust/src/ui/component/painter.rs | 2 +- core/embed/rust/src/ui/component/placed.rs | 34 +-- core/embed/rust/src/ui/component/qr_code.rs | 5 +- .../rust/src/ui/component/text/formatted.rs | 31 +- .../rust/src/ui/component/text/layout.rs | 17 +- .../rust/src/ui/component/text/paragraphs.rs | 35 ++- core/embed/rust/src/ui/component/timeout.rs | 5 +- core/embed/rust/src/ui/layout/obj.rs | 79 +---- .../rust/src/ui/model_tr/component/button.rs | 9 +- .../rust/src/ui/model_tr/component/confirm.rs | 7 +- .../rust/src/ui/model_tr/component/dialog.rs | 13 +- .../rust/src/ui/model_tr/component/frame.rs | 9 +- .../rust/src/ui/model_tr/component/loader.rs | 5 +- .../rust/src/ui/model_tr/component/page.rs | 9 +- .../src/ui/model_tr/component/result_anim.rs | 5 +- .../src/ui/model_tr/component/result_popup.rs | 13 +- core/embed/rust/src/ui/model_tr/layout.rs | 10 +- .../ui/model_tt/component/address_details.rs | 11 +- .../rust/src/ui/model_tt/component/button.rs | 16 +- .../model_tt/component/coinjoin_progress.rs | 30 +- .../rust/src/ui/model_tt/component/dialog.rs | 16 +- .../rust/src/ui/model_tt/component/fido.rs | 3 +- .../rust/src/ui/model_tt/component/frame.rs | 25 +- .../ui/model_tt/component/hold_to_confirm.rs | 11 +- .../ui/model_tt/component/homescreen/mod.rs | 12 +- .../ui/model_tt/component/horizontal_page.rs | 9 +- .../model_tt/component/keyboard/mnemonic.rs | 3 +- .../model_tt/component/keyboard/passphrase.rs | 3 +- .../src/ui/model_tt/component/keyboard/pin.rs | 3 +- .../model_tt/component/keyboard/word_count.rs | 3 +- .../rust/src/ui/model_tt/component/loader.rs | 5 +- .../src/ui/model_tt/component/number_input.rs | 16 +- .../rust/src/ui/model_tt/component/page.rs | 274 +++++++++++++++--- .../src/ui/model_tt/component/progress.rs | 3 +- .../ui/model_tt/component/welcome_screen.rs | 5 +- core/embed/rust/src/ui/model_tt/layout.rs | 46 ++- 49 files changed, 747 insertions(+), 457 deletions(-) create mode 100644 core/embed/rust/src/maybe_trace.rs diff --git a/core/embed/rust/Cargo.lock b/core/embed/rust/Cargo.lock index 34a4c8e76b..5ce4e1d19c 100644 --- a/core/embed/rust/Cargo.lock +++ b/core/embed/rust/Cargo.lock @@ -113,6 +113,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + [[package]] name = "lazy_static" version = "1.4.0" @@ -241,12 +247,35 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "1.1.0" @@ -292,6 +321,7 @@ dependencies = [ "num-derive", "num-traits", "qrcodegen", + "serde_json", ] [[package]] diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml index b473f3caaf..f8fe0d0921 100644 --- a/core/embed/rust/Cargo.toml +++ b/core/embed/rust/Cargo.toml @@ -88,3 +88,6 @@ version = "1.0.69" [build-dependencies.glob] optional = true version = "0.3.0" + +[dev-dependencies] +serde_json = "1.0.96" diff --git a/core/embed/rust/src/lib.rs b/core/embed/rust/src/lib.rs index 0edae7d190..1ff0a5dd3a 100644 --- a/core/embed/rust/src/lib.rs +++ b/core/embed/rust/src/lib.rs @@ -13,6 +13,7 @@ mod error; // use trezorhal for its macros early #[macro_use] mod trezorhal; +mod maybe_trace; #[cfg(feature = "micropython")] #[macro_use] mod micropython; diff --git a/core/embed/rust/src/maybe_trace.rs b/core/embed/rust/src/maybe_trace.rs new file mode 100644 index 0000000000..8519aabdfc --- /dev/null +++ b/core/embed/rust/src/maybe_trace.rs @@ -0,0 +1,15 @@ +#[cfg(feature = "ui_debug")] +mod maybe_trace { + use crate::trace::Trace; + + pub trait MaybeTrace: Trace {} + impl MaybeTrace for T where T: Trace {} +} + +#[cfg(not(feature = "ui_debug"))] +mod maybe_trace { + pub trait MaybeTrace {} + impl MaybeTrace for T {} +} + +pub use maybe_trace::MaybeTrace; diff --git a/core/embed/rust/src/micropython/buffer.rs b/core/embed/rust/src/micropython/buffer.rs index 59ee9f19d2..debd6e4319 100644 --- a/core/embed/rust/src/micropython/buffer.rs +++ b/core/embed/rust/src/micropython/buffer.rs @@ -253,10 +253,3 @@ pub fn hexlify_bytes(obj: Obj, offset: usize, max_len: usize) -> Result::try_into(list.get(i).unwrap()).unwrap() + ); list.set(i, Obj::from(value + 1)).unwrap(); - assert_eq!(value + 1, list.get(i).unwrap().try_into().unwrap()); + assert_eq!( + value + 1, + TryInto::::try_into(list.get(i).unwrap()).unwrap() + ); } let mut buf = IterBuf::new(); @@ -215,7 +221,7 @@ mod tests { let slice = unsafe { list.as_slice() }; assert_eq!(slice.len(), vec.len()); for i in 0..slice.len() { - assert_eq!(vec[i], slice[i].try_into().unwrap()); + assert_eq!(vec[i], TryInto::::try_into(slice[i]).unwrap()); } } @@ -228,7 +234,7 @@ mod tests { let slice = unsafe { Gc::as_mut(&mut list).as_mut_slice() }; assert_eq!(slice.len(), vec.len()); - assert_eq!(vec[0], slice[0].try_into().unwrap()); + assert_eq!(vec[0], TryInto::::try_into(slice[0]).unwrap()); for i in 0..slice.len() { slice[i] = ((i + 10) as u16).into(); diff --git a/core/embed/rust/src/strutil.rs b/core/embed/rust/src/strutil.rs index d1454e8783..5e48f464e4 100644 --- a/core/embed/rust/src/strutil.rs +++ b/core/embed/rust/src/strutil.rs @@ -9,3 +9,34 @@ pub fn hexlify(data: &[u8], buffer: &mut [u8]) { i += 2; } } + +pub fn format_i64(num: i64, buffer: &mut [u8]) -> Option<&str> { + let mut i = 0; + let mut num = num; + let negative = if num < 0 { + num = -num; + true + } else { + false + }; + + while num > 0 && i < buffer.len() { + buffer[i] = b'0' + ((num % 10) as u8); + num /= 10; + i += 1; + } + match i { + 0 => Some("0"), + _ if num > 0 => None, + _ if negative && i == buffer.len() => None, + _ => { + if negative { + buffer[i] = b'-'; + i += 1; + } + let result = &mut buffer[..i]; + result.reverse(); + Some(unsafe { core::str::from_utf8_unchecked(result) }) + } + } +} diff --git a/core/embed/rust/src/trace.rs b/core/embed/rust/src/trace.rs index 6af52b82c9..8c8e2e3ccf 100644 --- a/core/embed/rust/src/trace.rs +++ b/core/embed/rust/src/trace.rs @@ -1,91 +1,212 @@ -/// Visitor passed into `Trace` types. +use crate::strutil::format_i64; + pub trait Tracer { + fn child(&mut self, key: &str, value: &dyn Trace); + fn int(&mut self, key: &str, i: i64); + fn string(&mut self, key: &str, s: &str); + fn bool(&mut self, key: &str, b: bool); + + fn in_child(&mut self, key: &str, block: &dyn Fn(&mut dyn Tracer)); + fn in_list(&mut self, key: &str, block: &dyn Fn(&mut dyn ListTracer)); + + fn component(&mut self, name: &str) { + self.string("component", name); + } +} + +pub trait ListTracer { + fn child(&mut self, value: &dyn Trace); fn int(&mut self, i: i64); - fn bytes(&mut self, b: &[u8]); fn string(&mut self, s: &str); - fn symbol(&mut self, name: &str); - fn open(&mut self, name: &str); - fn field(&mut self, name: &str, value: &dyn Trace); - fn close(&mut self); + fn bool(&mut self, b: bool); + + fn in_child(&mut self, block: &dyn Fn(&mut dyn Tracer)); + fn in_list(&mut self, block: &dyn Fn(&mut dyn ListTracer)); } -/// Value that can describe own structure and data using the `Tracer` interface. -pub trait Trace { - fn trace(&self, d: &mut dyn Tracer); +/// Generic tracer based on a TraceWriter. +pub struct JsonTracer { + write_fn: F, + write_buf: [u8; 32], + buf_pos: usize, + first: bool, } -impl Trace for &[u8] { - fn trace(&self, t: &mut dyn Tracer) { - t.bytes(self); - } -} - -impl Trace for &[u8; N] { - fn trace(&self, t: &mut dyn Tracer) { - t.bytes(&self[..]) - } -} - -impl Trace for &str { - fn trace(&self, t: &mut dyn Tracer) { - t.string(self); - } -} - -impl Trace for usize { - fn trace(&self, t: &mut dyn Tracer) { - t.int(*self as i64); - } -} - -impl Trace for Option -where - T: Trace, -{ - fn trace(&self, d: &mut dyn Tracer) { - match self { - Some(v) => v.trace(d), - None => d.symbol("None"), +impl JsonTracer { + pub fn new(write_fn: F) -> Self { + Self { + write_fn, + write_buf: [0; 32], + buf_pos: 0, + first: true, } } + + fn maybe_comma(&mut self) { + if !self.first { + (self.write_fn)(", "); + } + self.first = false; + } + + fn write_int(&mut self, i: i64) { + let s = format_i64(i, &mut self.write_buf).unwrap_or("\"###NUMERR###\""); + (self.write_fn)(s); + } + + // SAFETY: there must be a valid utf8 string in write_buf[..buf_pos] + unsafe fn flush_buf(&mut self) { + if self.buf_pos == 0 { + return; + } + let substr = &self.write_buf[..self.buf_pos]; + let s = unsafe { core::str::from_utf8_unchecked(substr) }; + (self.write_fn)(s); + self.buf_pos = 0; + } + + // SAFETY: there must be a valid utf8 string in write_buf[..buf_pos]. + // Given that there is valid utf8 at start, there will be valid utf8 when done. + unsafe fn push_or_flush(&mut self, ch: char) { + let size = ch.len_utf8(); + if self.buf_pos + size > self.write_buf.len() { + unsafe { self.flush_buf() }; + } + ch.encode_utf8(&mut self.write_buf[self.buf_pos..]); + self.buf_pos += size; + } + + fn write_str_quoted(&mut self, s: &str) { + self.buf_pos = 0; + // SAFETY: we clear the writebuf by resetting buf_pos to 0, so its contents + // are only affected by push_or_flush, which keeps contents of write_buf + // correct. + unsafe { + self.push_or_flush('"'); + for mut ch in s.chars() { + if ch == '\\' || ch == '"' { + self.push_or_flush('\\'); + } else if ch == '\n' { + self.push_or_flush('\\'); + ch = 'n'; + } + self.push_or_flush(ch); + } + self.push_or_flush('"'); + self.flush_buf(); + } + } + + fn key(&mut self, key: &str) { + self.maybe_comma(); + self.write_str_quoted(key); + (self.write_fn)(": "); + } + + pub fn root(&mut self, block: &dyn Fn(&mut dyn Tracer)) { + (self.write_fn)("{"); + block(self); + (self.write_fn)("}"); + } +} + +impl ListTracer for JsonTracer { + fn child(&mut self, value: &dyn Trace) { + ListTracer::in_child(self, &mut |t| value.trace(t)); + } + + fn int(&mut self, i: i64) { + self.maybe_comma(); + self.write_int(i); + } + + fn string(&mut self, s: &str) { + self.maybe_comma(); + self.write_str_quoted(s); + } + + fn bool(&mut self, b: bool) { + self.maybe_comma(); + (self.write_fn)(if b { "true" } else { "false" }); + } + + fn in_child(&mut self, block: &dyn Fn(&mut dyn Tracer)) { + self.maybe_comma(); + self.first = true; + (self.write_fn)("{"); + block(self); + (self.write_fn)("}"); + self.first = false; + } + + fn in_list(&mut self, block: &dyn Fn(&mut dyn ListTracer)) { + self.maybe_comma(); + self.first = true; + (self.write_fn)("["); + block(self); + (self.write_fn)("]"); + self.first = false; + } +} + +impl Tracer for JsonTracer { + fn child(&mut self, key: &str, value: &dyn Trace) { + Tracer::in_child(self, key, &|t| value.trace(t)); + } + + fn int(&mut self, key: &str, i: i64) { + self.key(key); + self.write_int(i); + } + + fn string(&mut self, key: &str, s: &str) { + self.key(key); + self.write_str_quoted(s); + } + + fn bool(&mut self, key: &str, b: bool) { + self.key(key); + (self.write_fn)(if b { "true" } else { "false" }); + } + + fn in_child(&mut self, key: &str, block: &dyn Fn(&mut dyn Tracer)) { + self.key(key); + (self.write_fn)("{"); + self.first = true; + block(self); + (self.write_fn)("}"); + self.first = false; + } + + fn in_list(&mut self, key: &str, block: &dyn Fn(&mut dyn ListTracer)) { + self.key(key); + (self.write_fn)("["); + self.first = true; + block(self); + (self.write_fn)("]"); + self.first = false; + } +} + +/// Value that can describe own structure and data using the `Tracer` +/// interface. +pub trait Trace { + fn trace(&self, t: &mut dyn Tracer); } #[cfg(test)] -mod tests { +pub mod tests { + extern crate serde_json; + use serde_json::Value; + use super::*; - impl Tracer for Vec { - fn int(&mut self, i: i64) { - self.string(&i.to_string()); - } - - fn bytes(&mut self, b: &[u8]) { - self.extend(b) - } - - fn string(&mut self, s: &str) { - self.extend(s.as_bytes()) - } - - fn symbol(&mut self, name: &str) { - self.extend(name.as_bytes()) - } - - fn open(&mut self, name: &str) { - self.extend(b"<"); - self.extend(name.as_bytes()); - self.extend(b" "); - } - - fn field(&mut self, name: &str, value: &dyn Trace) { - self.extend(name.as_bytes()); - self.extend(b":"); - value.trace(self); - self.extend(b" "); - } - - fn close(&mut self) { - self.extend(b">") - } + pub fn trace(val: &impl Trace) -> Value { + let mut buf = Vec::new(); + let mut tracer = JsonTracer::new(|text| buf.extend_from_slice(text.as_bytes())); + tracer.root(&|t| val.trace(t)); + let s = String::from_utf8(buf).unwrap(); + //crate::micropython::print::print(s.as_str()); + s.parse().unwrap() } } diff --git a/core/embed/rust/src/ui/component/base.rs b/core/embed/rust/src/ui/component/base.rs index de07a4f009..93bc8a6a09 100644 --- a/core/embed/rust/src/ui/component/base.rs +++ b/core/embed/rust/src/ui/component/base.rs @@ -211,16 +211,14 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for (T, U) where - T: Component, T: crate::trace::Trace, - U: Component, U: crate::trace::Trace, { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Tuple"); - d.field("0", &self.0); - d.field("1", &self.1); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.in_list("children", &mut |l| { + l.child(&self.0); + l.child(&self.1); + }); } } @@ -260,25 +258,6 @@ where } } -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for (T, U, V) -where - T: Component, - T: crate::trace::Trace, - U: Component, - U: crate::trace::Trace, - V: Component, - V: crate::trace::Trace, -{ - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Tuple"); - d.field("0", &self.0); - d.field("1", &self.1); - d.field("2", &self.2); - d.close(); - } -} - impl Component for Option where T: Component, diff --git a/core/embed/rust/src/ui/component/empty.rs b/core/embed/rust/src/ui/component/empty.rs index 8c352e1030..ce2a3b8834 100644 --- a/core/embed/rust/src/ui/component/empty.rs +++ b/core/embed/rust/src/ui/component/empty.rs @@ -20,7 +20,6 @@ impl Component for Empty { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Empty { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Empty"); - t.close(); + t.component("Empty"); } } diff --git a/core/embed/rust/src/ui/component/image.rs b/core/embed/rust/src/ui/component/image.rs index 402c432dda..c535453f9c 100644 --- a/core/embed/rust/src/ui/component/image.rs +++ b/core/embed/rust/src/ui/component/image.rs @@ -63,8 +63,7 @@ impl Component for Image { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Image { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Image"); - t.close(); + t.component("Image"); } } @@ -139,7 +138,6 @@ impl Component for BlendedImage { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for BlendedImage { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("BlendedImage"); - t.close(); + t.component("BlendedImage"); } } diff --git a/core/embed/rust/src/ui/component/label.rs b/core/embed/rust/src/ui/component/label.rs index d394c59ae8..f79a9e5f7e 100644 --- a/core/embed/rust/src/ui/component/label.rs +++ b/core/embed/rust/src/ui/component/label.rs @@ -117,6 +117,7 @@ where T: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.string(self.text.as_ref()) + t.component("Label"); + t.string("text", self.text.as_ref()); } } diff --git a/core/embed/rust/src/ui/component/marquee.rs b/core/embed/rust/src/ui/component/marquee.rs index 072fa3a597..ff9cf73a05 100644 --- a/core/embed/rust/src/ui/component/marquee.rs +++ b/core/embed/rust/src/ui/component/marquee.rs @@ -232,9 +232,8 @@ impl crate::trace::Trace for Marquee where T: AsRef, { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Marquee"); - d.field("text", &self.text.as_ref()); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("Marquee"); + t.string("text", self.text.as_ref()); } } diff --git a/core/embed/rust/src/ui/component/painter.rs b/core/embed/rust/src/ui/component/painter.rs index 6887ce5871..6293544153 100644 --- a/core/embed/rust/src/ui/component/painter.rs +++ b/core/embed/rust/src/ui/component/painter.rs @@ -48,7 +48,7 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Painter { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.string("Painter") + t.component("Painter"); } } diff --git a/core/embed/rust/src/ui/component/placed.rs b/core/embed/rust/src/ui/component/placed.rs index ad586a7d7b..06652173ab 100644 --- a/core/embed/rust/src/ui/component/placed.rs +++ b/core/embed/rust/src/ui/component/placed.rs @@ -71,10 +71,9 @@ where T: Component, T: crate::trace::Trace, { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("GridPlaced"); - d.field("inner", &self.inner); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("GridPlaced"); + t.child("inner", &self.inner); } } @@ -115,10 +114,9 @@ where T: Component, T: crate::trace::Trace, { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("FixedHeightBar"); - d.field("inner", &self.inner); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("FixedHeightBar"); + t.child("inner", &self.inner); } } @@ -188,10 +186,9 @@ where T: Component, T: crate::trace::Trace, { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Floating"); - d.field("inner", &self.inner); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("Floating"); + t.child("inner", &self.inner); } } @@ -277,13 +274,12 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Split where - T: Component + crate::trace::Trace, - U: Component + crate::trace::Trace, + T: crate::trace::Trace, + U: crate::trace::Trace, { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Split"); - d.field("first", &self.first); - d.field("second", &self.second); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("Split"); + t.child("first", &self.first); + t.child("second", &self.second); } } diff --git a/core/embed/rust/src/ui/component/qr_code.rs b/core/embed/rust/src/ui/component/qr_code.rs index d660f23686..a688ccff11 100644 --- a/core/embed/rust/src/ui/component/qr_code.rs +++ b/core/embed/rust/src/ui/component/qr_code.rs @@ -150,8 +150,7 @@ impl Component for Qr { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Qr { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Qr"); - t.field("text", &self.text.as_str()); - t.close(); + t.component("Qr"); + t.string("text", self.text.as_ref()); } } diff --git a/core/embed/rust/src/ui/component/text/formatted.rs b/core/embed/rust/src/ui/component/text/formatted.rs index d7096a068e..79a882b62c 100644 --- a/core/embed/rust/src/ui/component/text/formatted.rs +++ b/core/embed/rust/src/ui/component/text/formatted.rs @@ -144,25 +144,6 @@ where } } -#[cfg(feature = "ui_debug")] -pub mod trace { - use crate::ui::component::text::layout::trace::TraceSink; - - use super::*; - - pub struct TraceText<'a, F, T>(pub &'a FormattedText); - - impl<'a, F, T> crate::trace::Trace for TraceText<'a, F, T> - where - F: AsRef, - T: AsRef, - { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - self.0.layout_content(&mut TraceSink(d)); - } - } -} - #[cfg(feature = "ui_debug")] impl crate::trace::Trace for FormattedText where @@ -170,9 +151,15 @@ where T: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Text"); - t.field("content", &trace::TraceText(self)); - t.close(); + use crate::ui::component::text::layout::trace::TraceSink; + use core::cell::Cell; + let fit: Cell> = Cell::new(None); + t.component("FormattedText"); + t.in_list("text", &mut |l| { + let result = self.layout_content(&mut TraceSink(l)); + fit.set(Some(result)); + }); + t.bool("fits", matches!(fit.get(), Some(LayoutFit::Fitting { .. }))); } } diff --git a/core/embed/rust/src/ui/component/text/layout.rs b/core/embed/rust/src/ui/component/text/layout.rs index dadcc13d7b..13c9365ff2 100644 --- a/core/embed/rust/src/ui/component/text/layout.rs +++ b/core/embed/rust/src/ui/component/text/layout.rs @@ -445,31 +445,32 @@ impl LayoutSink for TextRenderer { #[cfg(feature = "ui_debug")] pub mod trace { - use crate::ui::geometry::Point; + use crate::{trace::ListTracer, ui::geometry::Point}; use super::*; - pub struct TraceSink<'a>(pub &'a mut dyn crate::trace::Tracer); + /// `LayoutSink` for debugging purposes. + pub struct TraceSink<'a>(pub &'a mut dyn ListTracer); - impl<'a> LayoutSink for TraceSink<'a> { + impl LayoutSink for TraceSink<'_> { fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &str) { - self.0.string(text); + self.0.string(&text); } fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string("-"); + self.0.string(&"-"); } fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string("..."); + self.0.string(&ELLIPSIS); } fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string("..."); + self.0.string(&ELLIPSIS); } fn line_break(&mut self, _cursor: Point) { - self.0.string("\n"); + self.0.string(&"\n"); } } } diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index 117210739c..54619c3018 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -238,17 +238,23 @@ pub mod trace { impl crate::trace::Trace for Paragraphs { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Paragraphs"); - Self::foreach_visible( - &self.source, - &self.visible, - self.offset, - &mut |layout, content| { - layout.layout_text(content, &mut layout.initial_cursor(), &mut TraceSink(t)); - t.string("\n"); - }, - ); - t.close(); + t.string("component", "Paragraphs"); + t.in_list("paragraphs", &mut |par_list| { + Self::foreach_visible( + &self.source, + &self.visible, + self.offset, + &mut |layout, content| { + par_list.in_list(&mut |par| { + layout.layout_text( + content, + &mut layout.initial_cursor(), + &mut TraceSink(par), + ); + }); + }, + ); + }); } } } @@ -606,10 +612,9 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Checklist { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Checklist"); - t.field("current", &self.current); - t.field("items", &self.paragraphs); - t.close(); + t.component("Checklist"); + t.int("current", self.current as i64); + t.child("items", &self.paragraphs); } } diff --git a/core/embed/rust/src/ui/component/timeout.rs b/core/embed/rust/src/ui/component/timeout.rs index dbd58a42dc..155295694e 100644 --- a/core/embed/rust/src/ui/component/timeout.rs +++ b/core/embed/rust/src/ui/component/timeout.rs @@ -53,8 +53,7 @@ impl Component for Timeout { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Timeout { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Timeout"); - t.int(self.time_ms as i64); - t.close(); + t.component("Timeout"); + t.int("time_ms", self.time_ms as i64); } } diff --git a/core/embed/rust/src/ui/layout/obj.rs b/core/embed/rust/src/ui/layout/obj.rs index b4314d952e..6bec76c7bb 100644 --- a/core/embed/rust/src/ui/layout/obj.rs +++ b/core/embed/rust/src/ui/layout/obj.rs @@ -5,6 +5,7 @@ use core::{ use crate::{ error::Error, + maybe_trace::MaybeTrace, micropython::{ buffer::StrBuffer, gc::Gc, @@ -35,24 +36,6 @@ pub trait ComponentMsgObj: Component { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result; } -#[cfg(feature = "ui_debug")] -mod maybe_trace { - pub trait MaybeTrace: crate::trace::Trace {} - impl MaybeTrace for T where T: crate::trace::Trace {} -} - -#[cfg(not(feature = "ui_debug"))] -mod maybe_trace { - pub trait MaybeTrace {} - impl MaybeTrace for T {} -} - -/// Stand-in for the optionally-compiled trait `Trace`. -/// If UI debugging is enabled, `MaybeTrace` implies `Trace` and is implemented -/// for everything that implements Trace. If disabled, `MaybeTrace` is -/// implemented for everything. -use maybe_trace::MaybeTrace; - /// Object-safe interface between trait `Component` and MicroPython world. It /// converts the result of `Component::event` into `Obj` via the /// `ComponentMsgObj` trait, in order to easily return the value to Python. It @@ -213,55 +196,23 @@ impl LayoutObj { /// raises an exception. #[cfg(feature = "ui_debug")] fn obj_trace(&self, callback: Obj) { - use crate::trace::{Trace, Tracer}; + use crate::trace::JsonTracer; - struct CallbackTracer(Obj); + let mut tracer = JsonTracer::new(|text: &str| { + unwrap!(callback.call_with_n_args(&[unwrap!(text.try_into())])); + }); - impl Tracer for CallbackTracer { - fn int(&mut self, i: i64) { - self.0.call_with_n_args(&[i.try_into().unwrap()]).unwrap(); - } + // For Reasons(tm), we must pass a closure in which we call `root.trace(t)`, + // instead of passing `root` into the tracer. - fn bytes(&mut self, b: &[u8]) { - self.0.call_with_n_args(&[b.try_into().unwrap()]).unwrap(); - } - - fn string(&mut self, s: &str) { - self.0.call_with_n_args(&[s.try_into().unwrap()]).unwrap(); - } - - fn symbol(&mut self, name: &str) { - self.0 - .call_with_n_args(&[ - "<".try_into().unwrap(), - name.try_into().unwrap(), - ">".try_into().unwrap(), - ]) - .unwrap(); - } - - fn open(&mut self, name: &str) { - self.0 - .call_with_n_args(&["<".try_into().unwrap(), name.try_into().unwrap()]) - .unwrap(); - } - - fn field(&mut self, name: &str, value: &dyn Trace) { - self.0 - .call_with_n_args(&[name.try_into().unwrap(), ": ".try_into().unwrap()]) - .unwrap(); - value.trace(self); - } - - fn close(&mut self) { - self.0.call_with_n_args(&[">".try_into().unwrap()]).unwrap(); - } - } - - self.inner - .borrow() - .root - .trace(&mut CallbackTracer(callback)); + // (The Reasons being, root is a `Gc`, and `Gc` does not + // implement `Trace`, and `dyn ObjComponent` is unsized so we can't deref it to + // claim that it implements `Trace`, and we also can't upcast it to `&dyn Trace` + // because trait upcasting is unstable. + // Luckily, calling `root.trace()` works perfectly fine in spite of the above.) + tracer.root(&|t| { + self.inner.borrow().root.trace(t); + }); } fn obj_page_count(&self) -> Obj { diff --git a/core/embed/rust/src/ui/model_tr/component/button.rs b/core/embed/rust/src/ui/model_tr/component/button.rs index a631e188f0..5ba010d25e 100644 --- a/core/embed/rust/src/ui/model_tr/component/button.rs +++ b/core/embed/rust/src/ui/model_tr/component/button.rs @@ -155,15 +155,14 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Button where - T: AsRef + crate::trace::Trace, + T: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Button"); + t.component("Button"); match &self.content { - ButtonContent::Text(text) => t.field("text", text), - ButtonContent::Icon(_) => t.symbol("icon"), + ButtonContent::Text(text) => t.string("text", text.as_ref()), + ButtonContent::Icon(_) => t.bool("icon", true), } - t.close(); } } diff --git a/core/embed/rust/src/ui/model_tr/component/confirm.rs b/core/embed/rust/src/ui/model_tr/component/confirm.rs index aa29c6303b..69d53a591f 100644 --- a/core/embed/rust/src/ui/model_tr/component/confirm.rs +++ b/core/embed/rust/src/ui/model_tr/component/confirm.rs @@ -82,9 +82,8 @@ impl Component for HoldToConfirm { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for HoldToConfirm { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("HoldToConfirm"); - self.loader.trace(d); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("HoldToConfirm"); + t.child("loader", &self.loader); } } diff --git a/core/embed/rust/src/ui/model_tr/component/dialog.rs b/core/embed/rust/src/ui/model_tr/component/dialog.rs index 39820b3b36..d5bbf1336b 100644 --- a/core/embed/rust/src/ui/model_tr/component/dialog.rs +++ b/core/embed/rust/src/ui/model_tr/component/dialog.rs @@ -81,14 +81,9 @@ where U: crate::trace::Trace + AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Dialog"); - t.field("content", &self.content); - if let Some(label) = &self.left_btn { - t.field("left", label); - } - if let Some(label) = &self.right_btn { - t.field("right", label); - } - t.close(); + t.component("Dialog"); + t.child("content", &self.content); + self.left_btn.as_ref().map(|b| t.child("left", b)); + self.right_btn.as_ref().map(|b| t.child("right", b)); } } diff --git a/core/embed/rust/src/ui/model_tr/component/frame.rs b/core/embed/rust/src/ui/model_tr/component/frame.rs index 47e7a489cf..c0a2fc6e36 100644 --- a/core/embed/rust/src/ui/model_tr/component/frame.rs +++ b/core/embed/rust/src/ui/model_tr/component/frame.rs @@ -68,12 +68,11 @@ where impl crate::trace::Trace for Frame where T: crate::trace::Trace, - U: crate::trace::Trace + AsRef, + U: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Frame"); - t.field("title", &self.title); - t.field("content", &self.content); - t.close(); + t.component("Frame"); + t.string("title", self.title.as_ref()); + t.child("content", &self.content); } } diff --git a/core/embed/rust/src/ui/model_tr/component/loader.rs b/core/embed/rust/src/ui/model_tr/component/loader.rs index c334aa94d0..7e16e25818 100644 --- a/core/embed/rust/src/ui/model_tr/component/loader.rs +++ b/core/embed/rust/src/ui/model_tr/component/loader.rs @@ -193,8 +193,7 @@ pub struct LoaderStyle { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Loader { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Loader"); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("Loader"); } } diff --git a/core/embed/rust/src/ui/model_tr/component/page.rs b/core/embed/rust/src/ui/model_tr/component/page.rs index dadb410937..8cb3a45a82 100644 --- a/core/embed/rust/src/ui/model_tr/component/page.rs +++ b/core/embed/rust/src/ui/model_tr/component/page.rs @@ -119,11 +119,10 @@ where T: crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("ButtonPage"); - t.field("active_page", &self.scrollbar.active_page); - t.field("page_count", &self.scrollbar.page_count); - t.field("content", &self.content); - t.close(); + t.component("ButtonPage"); + t.int("active_page", self.scrollbar.active_page as i64); + t.int("page_count", self.scrollbar.page_count as i64); + t.child("content", &self.content); } } diff --git a/core/embed/rust/src/ui/model_tr/component/result_anim.rs b/core/embed/rust/src/ui/model_tr/component/result_anim.rs index 8cde4e309d..d0b1098586 100644 --- a/core/embed/rust/src/ui/model_tr/component/result_anim.rs +++ b/core/embed/rust/src/ui/model_tr/component/result_anim.rs @@ -143,8 +143,7 @@ impl Component for ResultAnim { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for ResultAnim { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("ResultAnim"); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("ResultAnim"); } } diff --git a/core/embed/rust/src/ui/model_tr/component/result_popup.rs b/core/embed/rust/src/ui/model_tr/component/result_popup.rs index 923c254ec5..f7b79390b3 100644 --- a/core/embed/rust/src/ui/model_tr/component/result_popup.rs +++ b/core/embed/rust/src/ui/model_tr/component/result_popup.rs @@ -157,12 +157,11 @@ impl Component for ResultPopup { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for ResultPopup { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("ResultPopup"); - self.text.trace(d); - self.button.trace(d); - self.headline.trace(d); - self.result_anim.trace(d); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("ResultPopup"); + t.child("text", &self.text); + self.button.as_ref().map(|b| t.child("button", b)); + self.headline.as_ref().map(|h| t.child("headline", h)); + t.child("result_anim", &self.result_anim); } } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 1a0d2a25b1..a78fbbee50 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -147,8 +147,10 @@ pub static mp_module_trezorui2: Module = obj_module! { #[cfg(test)] mod tests { + extern crate json; + use crate::{ - trace::Trace, + trace::tests::trace, ui::{ component::Component, model_tr::{ @@ -160,12 +162,6 @@ mod tests { use super::*; - fn trace(val: &impl Trace) -> String { - let mut t = Vec::new(); - val.trace(&mut t); - String::from_utf8(t).unwrap() - } - impl ComponentMsgObj for Dialog where T: ComponentMsgObj, diff --git a/core/embed/rust/src/ui/model_tt/component/address_details.rs b/core/embed/rust/src/ui/model_tt/component/address_details.rs index 7f11e12ad9..2d0e7c01f0 100644 --- a/core/embed/rust/src/ui/model_tt/component/address_details.rs +++ b/core/embed/rust/src/ui/model_tt/component/address_details.rs @@ -194,15 +194,14 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for AddressDetails where - T: ParagraphStrType + crate::trace::Trace, + T: ParagraphStrType, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("AddressDetails"); + t.component("AddressDetails"); match self.current_page { - 0 => self.qr_code.trace(t), - 1 => self.details.trace(t), - _ => self.xpub_view.trace(t), + 0 => t.child("qr_code", &self.qr_code), + 1 => t.child("details", &self.details), + _ => t.child("xpub_view", &self.xpub_view), } - t.close(); } } diff --git a/core/embed/rust/src/ui/model_tt/component/button.rs b/core/embed/rust/src/ui/model_tt/component/button.rs index f65fe1c0ca..5f734d87d2 100644 --- a/core/embed/rust/src/ui/model_tt/component/button.rs +++ b/core/embed/rust/src/ui/model_tt/component/button.rs @@ -323,18 +323,20 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Button where - T: AsRef + crate::trace::Trace, + T: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Button"); + t.component("Button"); match &self.content { ButtonContent::Empty => {} - ButtonContent::Text(text) => t.field("text", text), - ButtonContent::Icon(_) => t.symbol("icon"), - ButtonContent::IconAndText(_) => {} - ButtonContent::IconBlend(_, _, _) => t.symbol("icon"), + ButtonContent::Text(text) => t.string("text", text.as_ref()), + ButtonContent::Icon(_) => t.bool("icon", true), + ButtonContent::IconAndText(content) => { + t.string("text", content.text); + t.bool("icon", true); + } + ButtonContent::IconBlend(_, _, _) => t.bool("icon", true), } - t.close(); } } diff --git a/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs b/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs index 6907f77c49..979cd7f1c2 100644 --- a/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs +++ b/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs @@ -1,12 +1,16 @@ use core::mem; -use crate::ui::{ - component::{ - base::Never, painter, Child, Component, ComponentExt, Empty, Event, EventCtx, Label, Split, +use crate::{ + maybe_trace::MaybeTrace, + ui::{ + component::{ + base::Never, painter, Child, Component, ComponentExt, Empty, Event, EventCtx, Label, + Split, + }, + display::loader::{loader_circular_uncompress, LoaderDimensions}, + geometry::{Alignment, Insets, Rect}, + util::animation_disabled, }, - display::loader::{loader_circular_uncompress, LoaderDimensions}, - geometry::{Alignment, Insets, Rect}, - util::animation_disabled, }; use super::{theme, Frame}; @@ -31,7 +35,10 @@ impl CoinJoinProgress where T: AsRef, { - pub fn new(text: T, indeterminate: bool) -> CoinJoinProgress> + pub fn new( + text: T, + indeterminate: bool, + ) -> CoinJoinProgress + MaybeTrace> where T: AsRef, { @@ -121,11 +128,12 @@ where #[cfg(feature = "ui_debug")] impl crate::trace::Trace for CoinJoinProgress where - T: crate::trace::Trace, - U: Component, + T: AsRef, + U: Component + crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("CoinJoinProgress"); - t.close(); + t.component("CoinJoinProgress"); + t.child("label", &self.label); + t.child("content", &self.content); } } diff --git a/core/embed/rust/src/ui/model_tt/component/dialog.rs b/core/embed/rust/src/ui/model_tt/component/dialog.rs index 833597053d..07b3d3d595 100644 --- a/core/embed/rust/src/ui/model_tt/component/dialog.rs +++ b/core/embed/rust/src/ui/model_tt/component/dialog.rs @@ -85,10 +85,9 @@ where U: crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Dialog"); - t.field("content", &self.content); - t.field("controls", &self.controls); - t.close(); + t.component("Dialog"); + t.child("content", &self.content); + t.child("controls", &self.controls); } } @@ -208,10 +207,9 @@ where U: crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("IconDialog"); - t.field("content", &self.paragraphs); - t.field("image", &self.image); - t.field("controls", &self.controls); - t.close(); + t.component("IconDialog"); + t.child("image", &self.image); + t.child("content", &self.paragraphs); + t.child("controls", &self.controls); } } diff --git a/core/embed/rust/src/ui/model_tt/component/fido.rs b/core/embed/rust/src/ui/model_tt/component/fido.rs index dc336c6da9..c7abce4cf9 100644 --- a/core/embed/rust/src/ui/model_tt/component/fido.rs +++ b/core/embed/rust/src/ui/model_tt/component/fido.rs @@ -205,7 +205,6 @@ where F: Fn(usize) -> T, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("FidoPaginatedPage"); - t.close(); + t.component("FidoConfirm"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/frame.rs b/core/embed/rust/src/ui/model_tt/component/frame.rs index 9df7a7daaf..422a2e42ea 100644 --- a/core/embed/rust/src/ui/model_tt/component/frame.rs +++ b/core/embed/rust/src/ui/model_tt/component/frame.rs @@ -186,19 +186,13 @@ where impl crate::trace::Trace for Frame where T: crate::trace::Trace, - U: crate::trace::Trace + AsRef, + U: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Frame"); - t.field("title", &self.title); - if let Some(s) = &self.subtitle { - t.field("subtitle", s); - } - if let Some(b) = &self.button { - t.field("button", b); - } - t.field("content", &self.content); - t.close(); + t.component("Frame"); + t.child("title", &self.title); + self.subtitle.as_ref().map(|s| t.child("subtitle", s)); + self.button.as_ref().map(|b| t.child("button", b)); } } @@ -279,12 +273,11 @@ where impl crate::trace::Trace for NotificationFrame where T: crate::trace::Trace, - U: crate::trace::Trace + AsRef, + U: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("NotificationFrame"); - t.field("title", &self.title); - t.field("content", &self.content); - t.close(); + t.component("NotificationFrame"); + t.string("title", self.title.as_ref()); + t.child("content", &self.content); } } diff --git a/core/embed/rust/src/ui/model_tt/component/hold_to_confirm.rs b/core/embed/rust/src/ui/model_tt/component/hold_to_confirm.rs index f83919f7e8..f132b32fc1 100644 --- a/core/embed/rust/src/ui/model_tt/component/hold_to_confirm.rs +++ b/core/embed/rust/src/ui/model_tt/component/hold_to_confirm.rs @@ -108,10 +108,9 @@ impl crate::trace::Trace for HoldToConfirm where T: crate::trace::Trace, { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("HoldToConfirm"); - self.content.trace(d); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("HoldToConfirm"); + t.child("content", &self.content); } } @@ -181,8 +180,8 @@ impl Component for CancelHold { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for CancelHold { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.string("CancelHold") + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("CancelHold"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs index facdd9ae3f..4b66719e02 100644 --- a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs +++ b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs @@ -258,10 +258,9 @@ where #[cfg(feature = "ui_debug")] impl> crate::trace::Trace for Homescreen { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Homescreen"); - d.field("label", &self.label.as_ref()); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("Homescreen"); + t.string("label", self.label.as_ref()); } } @@ -392,8 +391,7 @@ fn is_image_toif(buffer: &[u8]) -> bool { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Lockscreen { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Lockscreen"); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("Lockscreen"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/horizontal_page.rs b/core/embed/rust/src/ui/model_tt/component/horizontal_page.rs index 6f5dee02ef..fc0c7bcced 100644 --- a/core/embed/rust/src/ui/model_tt/component/horizontal_page.rs +++ b/core/embed/rust/src/ui/model_tt/component/horizontal_page.rs @@ -138,10 +138,9 @@ where T: crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("HorizontalPage"); - t.field("active_page", &self.scrollbar.active_page); - t.field("page_count", &self.scrollbar.page_count); - t.field("content", &self.content); - t.close(); + t.component("HorizontalPage"); + t.int("active_page", self.scrollbar.active_page as i64); + t.int("page_count", self.scrollbar.page_count as i64); + t.child("content", &self.content); } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs index 9b725b083d..e9511fe613 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs @@ -193,7 +193,6 @@ pub enum MnemonicInputMsg { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for MnemonicKeyboard { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("MnemonicKeyboard"); - t.close(); + t.component("MnemonicKeyboard"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs index dd68aff7de..47c8e51a62 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs @@ -377,7 +377,6 @@ impl Component for Input { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for PassphraseKeyboard { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("PassphraseKeyboard"); - t.close(); + t.component("PassphraseKeyboard"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs index fe4c918eed..7a9ca21978 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs @@ -467,7 +467,6 @@ where T: AsRef, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("PinKeyboard"); - t.close(); + t.component("PinKeyboard"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs index 6d4dad3d8a..d46e472af2 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs @@ -68,7 +68,6 @@ impl Component for SelectWordCount { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for SelectWordCount { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("SelectWordCount"); - t.close(); + t.component("SelectWordCount"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/loader.rs b/core/embed/rust/src/ui/model_tt/component/loader.rs index ef6bb13fbf..7e85bf2094 100644 --- a/core/embed/rust/src/ui/model_tt/component/loader.rs +++ b/core/embed/rust/src/ui/model_tt/component/loader.rs @@ -198,9 +198,8 @@ pub struct LoaderStyle { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for Loader { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - d.open("Loader"); - d.close(); + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.component("Loader"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/number_input.rs b/core/embed/rust/src/ui/model_tt/component/number_input.rs index e0158b5c4b..619583d861 100644 --- a/core/embed/rust/src/ui/model_tt/component/number_input.rs +++ b/core/embed/rust/src/ui/model_tt/component/number_input.rs @@ -135,12 +135,11 @@ where F: Fn(u32) -> T, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("NumberInputDialog"); - t.field("input", &self.input); - t.field("paragraphs", &self.paragraphs); - t.field("info_button", &self.info_button); - t.field("confirm_button", &self.confirm_button); - t.close(); + t.component("NumberInputDialog"); + t.child("input", &self.input); + t.child("paragraphs", &self.paragraphs); + t.child("info_button", &self.info_button); + t.child("confirm_button", &self.confirm_button); } } @@ -238,8 +237,7 @@ impl Component for NumberInput { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for NumberInput { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("NumberInput"); - t.field("value", &(self.value as usize)); - t.close(); + t.component("NumberInput"); + t.int("value", self.value as i64); } } diff --git a/core/embed/rust/src/ui/model_tt/component/page.rs b/core/embed/rust/src/ui/model_tt/component/page.rs index 37be687fb4..dbde4bba7f 100644 --- a/core/embed/rust/src/ui/model_tt/component/page.rs +++ b/core/embed/rust/src/ui/model_tt/component/page.rs @@ -311,12 +311,11 @@ where U: crate::trace::Trace + Component, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("SwipePage"); - t.field("active_page", &self.scrollbar.active_page); - t.field("page_count", &self.scrollbar.page_count); - t.field("content", &self.content); - t.field("controls", &self.controls); - t.close(); + t.component("SwipePage"); + t.int("active_page", self.scrollbar.active_page as i64); + t.int("page_count", self.scrollbar.page_count as i64); + t.child("content", &self.content); + t.child("controls", &self.controls); } } @@ -495,8 +494,10 @@ where #[cfg(test)] mod tests { + extern crate serde_json; + use crate::{ - trace::Trace, + trace::tests::trace, ui::{ component::{ text::paragraphs::{Paragraph, ParagraphStrType, Paragraphs}, @@ -518,12 +519,6 @@ mod tests { } } - fn trace(val: &impl Trace) -> String { - let mut t = Vec::new(); - val.trace(&mut t); - String::from_utf8(t).unwrap() - } - fn swipe(component: &mut impl Component, points: &[(i16, i16)]) { let last = points.len().saturating_sub(1); let mut first = true; @@ -560,8 +555,18 @@ mod tests { ); page.place(SCREEN); - let expected = - " controls: >"; + let expected = serde_json::json!({ + "component": "SwipePage", + "active_page": 0, + "page_count": 1, + "content": { + "component": "Paragraphs", + "paragraphs": [], + }, + "controls": { + "component": "Empty", + }, + }); assert_eq!(trace(&page), expected); swipe_up(&mut page); @@ -588,7 +593,21 @@ mod tests { ); page.place(SCREEN); - let expected = " controls: >"; + let expected = serde_json::json!({ + "component": "SwipePage", + "active_page": 0, + "page_count": 1, + "content": { + "component": "Paragraphs", + "paragraphs": [ + ["This is the first", "\n", "paragraph and it should", "\n", "fit on the screen", "\n", "entirely."], + ["Second, bold, paragraph", "\n", "should also fit on the", "\n", "screen whole I think."], + ], + }, + "controls": { + "component": "Empty", + }, + }); assert_eq!(trace(&page), expected); swipe_up(&mut page); @@ -611,18 +630,61 @@ mod tests { ); page.place(SCREEN); - let expected1 = " controls: > >"; - let expected2 = " controls: > >"; + let first_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 0, + "page_count": 2, + "content": { + "component": "Paragraphs", + "paragraphs": [ + [ + "This is somewhat long", "\n", + "paragraph that goes on", "\n", + "and on and on and on and", "\n", + "on and will definitely not", "\n", + "fit on just a single", "\n", + "screen. You have to", "\n", + "swipe a bit to see all the", "\n", + "text it contains I guess.", "...", + ], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Button", + "text": "NO", + }, + }, + }); + let second_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 1, + "page_count": 2, + "content": { + "component": "Paragraphs", + "paragraphs": [ + ["There's just so much", "\n", "letters in it."], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Button", + "text": "NO", + }, + }, + }); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); swipe_down(&mut page); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected2); + assert_eq!(trace(&page), second_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected2); + assert_eq!(trace(&page), second_page); swipe_down(&mut page); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); } #[test] @@ -647,25 +709,101 @@ mod tests { ); page.place(SCREEN); - let expected1 = " controls: > >"; - let expected2 = " controls: > >"; - let expected3 = " controls: > >"; + let first_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 0, + "page_count": 3, + "content": { + "component": "Paragraphs", + "paragraphs": [ + [ + "This paragraph is using a", "\n", + "bold font. It doesn't need", "\n", + "to be all that long.", + ], + [ + "And this one is u", "\n", + "sing MONO. Monosp", "\n", + "ace is nice for n", "\n", + "umbers, they", "...", + ], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Button", + "text": "IDK", + }, + }, + }); + let second_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 1, + "page_count": 3, + "content": { + "component": "Paragraphs", + "paragraphs": [ + [ + "...", "have the same", "\n", + "width and can be", "\n", + "scanned quickly.", "\n", + "Even if they span", "\n", + "several pages or", "\n", + "something.", + ], + [ + "Let's add another one", "...", + ], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Button", + "text": "IDK", + }, + }, + }); + let third_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 2, + "page_count": 3, + "content": { + "component": "Paragraphs", + "paragraphs": [ + [ + "for a good measure. This", "\n", + "one should overflow all", "\n", + "the way to the third page", "\n", + "with a bit of luck.", + ], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Button", + "text": "IDK", + }, + }, + }); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); swipe_down(&mut page); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected2); + assert_eq!(trace(&page), second_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected3); + assert_eq!(trace(&page), third_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected3); + assert_eq!(trace(&page), third_page); swipe_down(&mut page); - assert_eq!(trace(&page), expected2); + assert_eq!(trace(&page), second_page); swipe_down(&mut page); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); swipe_down(&mut page); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); } #[test] @@ -681,16 +819,70 @@ mod tests { ); page.place(SCREEN); - let expected1 = " controls: > >"; - let expected2 = " controls: > >"; - let expected3 = " controls: > >"; + let first_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 0, + "page_count": 3, + "content": { + "component": "Paragraphs", + "paragraphs": [ + [ + "Short one.", + ], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Empty", + }, + }, + }); + let second_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 1, + "page_count": 3, + "content": { + "component": "Paragraphs", + "paragraphs": [ + [ + "Short two.", + ], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Empty", + }, + }, + }); + let third_page = serde_json::json!({ + "component": "SwipePage", + "active_page": 2, + "page_count": 3, + "content": { + "component": "Paragraphs", + "paragraphs": [ + [ + "Short three.", + ], + ], + }, + "controls": { + "component": "FixedHeightBar", + "inner": { + "component": "Empty", + }, + }, + }); - assert_eq!(trace(&page), expected1); + assert_eq!(trace(&page), first_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected2); + assert_eq!(trace(&page), second_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected3); + assert_eq!(trace(&page), third_page); swipe_up(&mut page); - assert_eq!(trace(&page), expected3); + assert_eq!(trace(&page), third_page); } } diff --git a/core/embed/rust/src/ui/model_tt/component/progress.rs b/core/embed/rust/src/ui/model_tt/component/progress.rs index 758589f8cc..c27c52309e 100644 --- a/core/embed/rust/src/ui/model_tt/component/progress.rs +++ b/core/embed/rust/src/ui/model_tt/component/progress.rs @@ -133,7 +133,6 @@ where T: ParagraphStrType, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Progress"); - t.close(); + t.component("Progress"); } } diff --git a/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs b/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs index ea320ea8b2..11737ec8e1 100644 --- a/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs +++ b/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs @@ -65,8 +65,7 @@ impl Component for WelcomeScreen { #[cfg(feature = "ui_debug")] impl crate::trace::Trace for WelcomeScreen { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("WelcomeScreen"); - t.string(MODEL_NAME); - t.close(); + t.component("WelcomeScreen"); + t.string("model", MODEL_NAME); } } diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index b91ffa2f97..a04a9f1a59 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -1961,25 +1961,17 @@ pub static mp_module_trezorui2: Module = obj_module! { #[cfg(test)] mod tests { + extern crate serde_json; + use crate::{ - trace::Trace, - ui::{ - component::{Component, FormattedText}, - geometry::Rect, - model_tt::constant, - }, + trace::tests::trace, + ui::{geometry::Rect, model_tt::constant}, }; use super::*; const SCREEN: Rect = constant::screen().inset(theme::borders()); - fn trace(val: &impl Trace) -> String { - let mut t = std::vec::Vec::new(); - val.trace(&mut t); - String::from_utf8(t).unwrap() - } - #[test] fn trace_example_layout() { let buttons = @@ -1994,9 +1986,31 @@ mod tests { buttons, ); layout.place(SCREEN); - assert_eq!( - trace(&layout), - " controls: second: