summaryrefslogtreecommitdiff
path: root/source/util/Diag.mc
blob: a09d3f873a50d7f85005ded2c27add063ba4487c (plain) (blame)
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
// Author:  cirakg & claude ai
// Summary: Debug-only diagnostics: start-up time, memory, screen markers for UI scripts, and the performance overlay.
//
// Debug builds print one machine-readable line per event to the simulator
// console: "DIAG", the event name, then key=value pairs, for example
//     DIAG first_frame startup_ms=1 mem_used=6984 mem_total=781888
// tools/check_budgets.py reads these lines and compares them with
// tools/budgets.json. Plain key=value pairs are used instead of JSON because
// the simulator console escapes quotes. Release builds compile the empty
// (:release) versions instead, so a real watch running a release build never
// logs anything.
//
// UI scripts (docker/scripts/screenshots.sh) wait for "DIAG shown <screen>"
// lines before taking a screenshot, and read the PIN keypad's key positions
// from "DIAG pin_keys" to tap them.
//
// Security rule: only timings and memory counters ever go through here, never
// secrets, codes or entry names. This is the only file allowed to call
// System.println outside tests (enforced by tools/lint_project.py).

import Toybox.Graphics;
import Toybox.Lang;
import Toybox.System;

// (:glance): the app object calls markStart() before the non-glance code is
// loaded (apps with a glance load the rest only for the full app).
(:glance)
module Diag {

    // System.getTimer() value when the app object was created.
    (:debug)
    var startMs as Number = 0;

    // Start the start-up timer. Called from OtpWalletApp.initialize().
    (:debug)
    function markStart() as Void {
        startMs = System.getTimer();
    }

    (:release)
    function markStart() as Void {
    }

    // Report start-up time and memory use after the first complete frame.
    // startup_ms covers app object creation to the end of the first draw. The
    // time the system needs to load the program before that is invisible to
    // the app; app-diag.sh measures it from outside (README section 18).
    (:debug)
    function firstFrame() as Void {
        var stats = System.getSystemStats();
        System.println("DIAG first_frame startup_ms=" + (System.getTimer() - startMs)
            + " mem_used=" + stats.usedMemory + " mem_total=" + stats.totalMemory);
    }

    (:release)
    function firstFrame() as Void {
    }

    // A screen finished its first draw since it was shown.
    (:debug)
    function shown(screen as String) as Void {
        System.println("DIAG shown " + screen);
    }

    (:release)
    function shown(screen as String) as Void {
    }

    // Free-form key=value data for UI scripts (never secrets).
    (:debug)
    function data(event as String, values as String) as Void {
        System.println("DIAG " + event + " " + values);
    }

    (:release)
    function data(event as String, values as String) as Void {
    }

    // The performance overlay (toggled in the debug menu): draw time of the
    // frame that started at startMs, and memory use.
    (:debug)
    var overlayOn as Boolean = false;

    (:debug)
    function overlay(dc as Graphics.Dc, frameStartMs as Number) as Void {
        if (!overlayOn) {
            return;
        }
        var stats = System.getSystemStats();
        var text = (System.getTimer() - frameStartMs) + " ms  " + (stats.usedMemory * 100 / stats.totalMemory) + "% mem";
        dc.setColor(Graphics.COLOR_YELLOW, Graphics.COLOR_BLACK);
        dc.drawText(dc.getWidth() / 2, dc.getHeight() - dc.getFontHeight(Graphics.FONT_XTINY) - 2, Graphics.FONT_XTINY,
            text, Graphics.TEXT_JUSTIFY_CENTER);
    }

    (:release)
    function overlay(dc as Graphics.Dc, frameStartMs as Number) as Void {
    }
}