aboutsummaryrefslogtreecommitdiff
path: root/source/util/Diag.mc
blob: 0ab121f73ee68e47f1c970d99422dd25f97ad252 (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
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
// 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 {
    }

    // Peak memory while long work runs (sampled by the import job).
    (:debug)
    var peakMemory as Number = 0;

    (:debug)
    function sampleMemory() as Void {
        var used = System.getSystemStats().usedMemory;
        if (used > peakMemory) {
            peakMemory = used;
        }
    }

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

    // Reports an import's size, duration and peak memory for UI scripts.
    (:debug)
    function importDone(entries as Number, wallMs as Number) as Void {
        System.println("DIAG import entries=" + entries + " wall_ms=" + wallMs + " mem_peak=" + peakMemory
            + " mem_total=" + System.getSystemStats().totalMemory);
    }

    (:release)
    function importDone(entries as Number, wallMs as Number) as Void {
    }
}