Skip to content
← Back

src/logging/work_counters.ghul

1
namespace Logging is
2
use Collections.Iterable;
3
use Collections.LIST;
4
5
// The analysis-mode counter vocabulary.
6
//
7
// Analysis mode reports named counters over the STATS request, and a
8
// test asserts against them that no more than the expected amount of
9
// compilation took place. That only works if the names are declared
10
// in one place rather than spelled at each bump site: `declare_all`
11
// creates every counter up front, so a name shows up in the report at
12
// count zero as well as when it fires, and a test can enumerate the
13
// whole vocabulary and check each name is exercised somewhere.
14
//
15
// Three families:
16
//
17
// file-pass:<pass> one file taken through one pass of the
18
// ordinary build loop, or through the
19
// on-demand expression compile
20
// file-rewalk:<pass> one file's bodies taken through one pass by
21
// the incremental body re-walk
22
// subtree-pass:<pass> one changed or appended subtree taken
23
// through one pass
24
//
25
// The distinction is the point: an interface-preserving edit should
26
// show body re-walks and no whole-file passes.
27
class WORK_COUNTERS is
28
EDIT_PATH_BODY_REWALK: string static => "edit-path-body-rewalk";
29
EDIT_PATH_INTERFACE_INCREMENTAL: string static => "edit-path-interface-incremental";
30
EDIT_PATH_FULL_REBUILD: string static => "edit-path-full-rebuild";
31
32
FILE_PASS_PREFIX: string static => "file-pass:";
33
FILE_REWALK_PREFIX: string static => "file-rewalk:";
34
SUBTREE_PASS_PREFIX: string static => "subtree-pass:";
35
DECLINED_PREFIX: string static => "edit-declined-";
36
37
// Why an EDIT was handled by the whole-project rebuild. Exactly
38
// one of these is reported per rebuilt EDIT, so they sum to the
39
// full-rebuild tally and the report says which guard was
40
// responsible rather than only that some guard was.
41
//
42
// A guard inside the narrowing search declines a *route*, not the
43
// edit: the search falls through to member replacement, which may
44
// still succeed. So a reason is latched when its guard fires and
45
// reported only if the edit ends up rebuilding. First latched
46
// wins - the search rejects the narrowest route first and widens
47
// as it falls through, so the first guard to fire is the one that
48
// stopped the best route available.
49
NOT_ELIGIBLE: string static => "not-eligible";
50
NO_RETAINED_PARSE: string static => "no-retained-parse";
51
MISSING_INTERFACE_SIGNATURE: string static => "missing-interface-signature";
52
FILE_ROOT_NOT_A_LIST: string static => "file-root-not-a-list";
53
FUNCTION_PAIRING_DESYNC: string static => "function-pairing-desync";
54
LOCATION_REFRESH_DESYNC: string static => "location-refresh-desync";
55
MULTIPLE_CHANGED_DEFINITIONS: string static => "multiple-changed-definitions";
56
FILE_LEVEL_CHANGE: string static => "file-level-change";
57
FEWER_DEFINITIONS: string static => "fewer-definitions";
58
RETAINED_PREFIX_MISMATCH: string static => "retained-prefix-mismatch";
59
NAMESPACE_NAME_MISMATCH: string static => "namespace-name-mismatch";
60
CLASS_HEADER_MISMATCH: string static => "class-header-mismatch";
61
NO_CLASS_SYMBOL: string static => "no-class-symbol";
62
UNRESETTABLE_CLOSURE: string static => "unresettable-closure";
63
OUTGOING_NOT_A_FUNCTION: string static => "outgoing-not-a-function";
64
OUTGOING_HAS_OVERRIDERS: string static => "outgoing-has-overriders";
65
OUTGOING_HAS_OVERRIDEES: string static => "outgoing-has-overridees";
66
CROSS_FILE_NAMESPACE_REFERENCE: string static => "cross-file-namespace-reference";
67
CROSS_FILE_UNKNOWN_FILE: string static => "cross-file-unknown-file";
68
INCOMING_NOT_A_FUNCTION: string static => "incoming-not-a-function";
69
70
edit_paths: Iterable[string] static =>
71
[
72
EDIT_PATH_BODY_REWALK,
73
EDIT_PATH_INTERFACE_INCREMENTAL,
74
EDIT_PATH_FULL_REBUILD
75
];
76
77
decline_reasons: Iterable[string] static =>
78
[
79
NOT_ELIGIBLE,
80
NO_RETAINED_PARSE,
81
MISSING_INTERFACE_SIGNATURE,
82
FILE_ROOT_NOT_A_LIST,
83
FUNCTION_PAIRING_DESYNC,
84
LOCATION_REFRESH_DESYNC,
85
MULTIPLE_CHANGED_DEFINITIONS,
86
FILE_LEVEL_CHANGE,
87
FEWER_DEFINITIONS,
88
RETAINED_PREFIX_MISMATCH,
89
NAMESPACE_NAME_MISMATCH,
90
CLASS_HEADER_MISMATCH,
91
NO_CLASS_SYMBOL,
92
UNRESETTABLE_CLOSURE,
93
OUTGOING_NOT_A_FUNCTION,
94
OUTGOING_HAS_OVERRIDERS,
95
OUTGOING_HAS_OVERRIDEES,
96
CROSS_FILE_NAMESPACE_REFERENCE,
97
CROSS_FILE_UNKNOWN_FILE,
98
INCOMING_NOT_A_FUNCTION
99
];
100
101
// Create every declared counter so the report carries the whole
102
// vocabulary, including names that have not fired. Called once at
103
// analyser startup.
104
declare_all(timers: TIMERS) static is
105
for name in edit_paths do
106
timers.declare(name);
107
od
108
109
for reason in decline_reasons do
110
timers.declare(declined_name(reason));
111
od
112
si
113
114
declined_name(reason: string) -> string static =>
115
"{DECLINED_PREFIX}{reason}";
116
117
file_pass(timers: TIMERS, pass: string) static is
118
timers.bump("{FILE_PASS_PREFIX}{pass}");
119
si
120
121
file_rewalk(timers: TIMERS, pass: string) static is
122
timers.bump("{FILE_REWALK_PREFIX}{pass}");
123
si
124
125
subtree_pass(timers: TIMERS, pass: string) static is
126
timers.bump("{SUBTREE_PASS_PREFIX}{pass}");
127
si
128
129
declined(timers: TIMERS, reason: string) static is
130
timers.bump(declined_name(reason));
131
si
132
si
133
si