GCC Code Coverage Report


Directory: ./
File: firmware/development/engine_sniffer.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 71.3% 62 0 87
Functions: 68.8% 11 0 16
Branches: 46.8% 29 0 62
Decisions: 50.0% 9 - 18

Line Branch Decision Exec Source
1 /**
2 * @file engine_sniffer.cpp
3 * @brief rusEfi console wave sniffer logic
4 *
5 * Here we have our own build-in logic analyzer. The data we aggregate here is sent to the
6 * java UI rusEfi Console so that it can be displayed nicely in the Sniffer tab.
7 *
8 * Both external events (see logic_analyzer.cpp) and internal (see signal executors) are supported
9 *
10 * @date Jun 23, 2013
11 * @author Andrey Belomutskiy, (c) 2012-2020
12 *
13 * This file is part of rusEfi - see http://rusefi.com
14 *
15 * rusEfi is free software; you can redistribute it and/or modify it under the terms of
16 * the GNU General Public License as published by the Free Software Foundation; either
17 * version 3 of the License, or (at your option) any later version.
18 *
19 * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
20 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along with this program.
24 * If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "pch.h"
28
29 #include "engine_sniffer.h"
30
31 // a bit weird because of conditional compilation
32 static char shaft_signal_msg_index[15];
33
34 #if EFI_ENGINE_SNIFFER
35 #define addEngineSnifferEvent(name, msg) { if (getTriggerCentral()->isEngineSnifferEnabled) { waveChart.addEvent3((name), (msg)); } }
36 #else
37 #define addEngineSnifferEvent(name, msg) { UNUSED(name); }
38 #endif /* EFI_ENGINE_SNIFFER */
39
40 #if EFI_ENGINE_SNIFFER
41
42 #include "eficonsole.h"
43 #include "status_loop.h"
44
45 #define CHART_DELIMETER '!'
46 extern WaveChart waveChart;
47
48 /**
49 * This is the number of events in the digital chart which would be displayed
50 * on the 'digital sniffer' pane
51 */
52 #if EFI_PROD_CODE
53 #define WAVE_LOGGING_SIZE 5000
54 #else
55 #define WAVE_LOGGING_SIZE 35000
56 #endif
57
58 static char WAVE_LOGGING_BUFFER[WAVE_LOGGING_SIZE] CCM_OPTIONAL;
59
60 int waveChartUsedSize;
61
62 /**
63 * We want to skip some engine cycles to skip what was scheduled before parameters were changed
64 */
65 static uint32_t skipUntilEngineCycle = 0;
66
67 #if ! EFI_UNIT_TEST
68 extern WaveChart waveChart;
69 static void resetNow() {
70 skipUntilEngineCycle = getRevolutionCounter() + 3;
71 waveChart.reset();
72 }
73 #endif // EFI_UNIT_TEST
74
75 1 WaveChart::WaveChart() : logging("wave chart", WAVE_LOGGING_BUFFER, sizeof(WAVE_LOGGING_BUFFER)) {
76 1 }
77
78 583 void WaveChart::init() {
79 583 isInitialized = true;
80 583 reset();
81 583 }
82
83 583 void WaveChart::reset() {
84 583 logging.reset();
85 583 counter = 0;
86 583 startTimeNt = 0;
87 583 collectingData = false;
88 583 logging.appendPrintf( "%s%s", PROTOCOL_ENGINE_SNIFFER, LOG_DELIMITER);
89 583 }
90
91 1503 void WaveChart::startDataCollection() {
92 1503 collectingData = true;
93 1503 }
94
95 bool WaveChart::isStartedTooLongAgo() const {
96 /**
97 * Say at 300rpm we should get at least four events per revolution.
98 * That's 300/60*4=20 events per second
99 * engineChartSize/20 is the longest meaningful chart.
100 *
101 */
102 efidur_t chartDurationNt = getTimeNowNt() - startTimeNt;
103 return startTimeNt != 0 && NT2US(chartDurationNt) > engineConfiguration->engineChartSize * 1000000 / 20;
104 }
105
106 91033 bool WaveChart::isFull() const {
107 91033 return counter >= engineConfiguration->engineChartSize;
108 }
109
110 1 int WaveChart::getSize() {
111 1 return counter;
112 }
113
114 #if ! EFI_UNIT_TEST
115 static void printStatus() {
116 efiPrintf("engine sniffer: %s", boolToString(getTriggerCentral()->isEngineSnifferEnabled));
117 efiPrintf("engine sniffer size=%lu", engineConfiguration->engineChartSize);
118 }
119
120 void setChartSize(int newSize) {
121 if (newSize < 5) {
122 return;
123 }
124 engineConfiguration->engineChartSize = newSize;
125 printStatus();
126 }
127 #endif // EFI_UNIT_TEST
128
129 void WaveChart::publishIfFull() {
130 if (isFull() || isStartedTooLongAgo()) {
131 publish();
132 reset();
133 }
134 }
135
136 void WaveChart::publish() {
137 #if EFI_ENGINE_SNIFFER
138 logging.appendPrintf( LOG_DELIMITER);
139 waveChartUsedSize = logging.loggingSize();
140
141 if (getTriggerCentral()->isEngineSnifferEnabled) {
142 scheduleLogging(&logging);
143 }
144 #endif /* EFI_ENGINE_SNIFFER */
145 }
146
147 /**
148 * @brief Register an event for digital sniffer
149 */
150 91033 void WaveChart::addEvent3(const char *name, const char * msg) {
151 #if EFI_TEXT_LOGGING
152 91033 ScopePerf perf(PE::EngineSniffer);
153
1/1
✓ Branch 1 taken 91033 times.
91033 efitick_t nowNt = getTimeNowNt();
154
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 91033 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 91033 times.
91033 if (nowNt < pauseEngineSnifferUntilNt) {
156 return;
157 }
158
2/3
✓ Branch 1 taken 91033 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 91033 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 91033 times.
91033 if (!getTriggerCentral()->isEngineSnifferEnabled) {
159 return;
160 }
161
2/7
✗ Branch 0 not taken.
✓ Branch 1 taken 91033 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 91033 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 91033 times.
91033 if (skipUntilEngineCycle != 0 && getRevolutionCounter() < skipUntilEngineCycle)
162 return;
163 #if EFI_SIMULATOR
164 if (!collectingData) {
165 return;
166 }
167 #endif
168
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 91033 times.
✗ Branch 3 not taken.
91033 efiAssertVoid(ObdCode::CUSTOM_ERR_6651, name!=NULL, "WC: NULL name");
169
170 #if EFI_PROD_CODE
171 efiAssertVoid(ObdCode::CUSTOM_ERR_6652, getCurrentRemainingStack() > 32, "lowstck#2c");
172 #endif /* EFI_PROD_CODE */
173
174
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 91033 times.
✗ Branch 3 not taken.
91033 efiAssertVoid(ObdCode::CUSTOM_ERR_6653, isInitialized, "chart not initialized");
175
176
3/3
✓ Branch 1 taken 91033 times.
✓ Branch 3 taken 64452 times.
✓ Branch 4 taken 26581 times.
2/2
✓ Decision 'true' taken 64452 times.
✓ Decision 'false' taken 26581 times.
91033 if (isFull()) {
177 64452 return;
178 }
179
180 // we have multiple threads writing to the same output buffer
181 chibios_rt::CriticalSectionLocker csl;
182
183
2/2
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 26466 times.
2/2
✓ Decision 'true' taken 115 times.
✓ Decision 'false' taken 26466 times.
26581 if (counter == 0) {
184 115 startTimeNt = nowNt;
185 }
186 26581 counter++;
187
188 /**
189 * We want smaller times within a chart in order to reduce packet size.
190 */
191 /**
192 * todo: migrate to binary fractions in order to eliminate
193 * this division? I do not like division
194 *
195 * at least that's 32 bit division now
196 */
197 26581 uint32_t diffNt = nowNt - startTimeNt;
198 26581 uint32_t time100 = NT2US(diffNt / ENGINE_SNIFFER_UNIT_US);
199
200
1/2
✓ Branch 1 taken 26581 times.
✗ Branch 2 not taken.
1/2
✓ Decision 'true' taken 26581 times.
✗ Decision 'false' not taken.
26581 if (logging.remainingSize() > 35) {
201 /**
202 * printf is a heavy method, append is used here as a performance optimization
203 */
204
1/1
✓ Branch 1 taken 26581 times.
26581 logging.appendFast(name);
205 26581 logging.appendChar(CHART_DELIMETER);
206
1/1
✓ Branch 1 taken 26581 times.
26581 logging.appendFast(msg);
207 26581 logging.appendChar(CHART_DELIMETER);
208 // time100 -= startTime100;
209
210
1/1
✓ Branch 1 taken 26581 times.
26581 itoa10(timeBuffer, time100);
211
1/1
✓ Branch 1 taken 26581 times.
26581 logging.appendFast(timeBuffer);
212 26581 logging.appendChar(CHART_DELIMETER);
213 26581 logging.terminate();
214 }
215 #endif /* EFI_TEXT_LOGGING */
216 }
217
218 void initWaveChart(WaveChart *chart) {
219 strcpy((char*) shaft_signal_msg_index, "x_");
220 /**
221 * constructor does not work because we need specific initialization order
222 */
223 chart->init();
224
225 #if EFI_HISTOGRAMS
226 initHistogram(&engineSnifferHisto, "engine sniffer");
227 #endif /* EFI_HISTOGRAMS */
228
229 #if ! EFI_UNIT_TEST
230 printStatus();
231 addConsoleActionI("chartsize", setChartSize);
232 // this is used by HW CI
233 addConsoleAction(CMD_RESET_ENGINE_SNIFFER, resetNow);
234 #endif // EFI_UNIT_TEST
235 }
236
237 #endif /* EFI_ENGINE_SNIFFER */
238
239 18607 void addEngineSnifferOutputPinEvent(NamedOutputPin *pin, FrontDirection frontDirection) {
240
1/2
✓ Branch 0 taken 18607 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 18607 times.
✗ Decision 'false' not taken.
18607 if (!engineConfiguration->engineSnifferFocusOnInputs) {
241
3/4
✓ Branch 1 taken 18607 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8918 times.
✓ Branch 4 taken 9689 times.
18607 addEngineSnifferEvent(pin->getShortName(), frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN);
242 }
243 18607 }
244
245 1503 void addEngineSnifferTdcEvent(int rpm) {
246 static char rpmBuffer[_MAX_FILLER];
247 1503 itoa10(rpmBuffer, rpm);
248 #if EFI_ENGINE_SNIFFER
249 1503 waveChart.startDataCollection();
250 #endif
251
1/2
✓ Branch 1 taken 1503 times.
✗ Branch 2 not taken.
1503 addEngineSnifferEvent(TOP_DEAD_CENTER_MESSAGE, (char* ) rpmBuffer);
252 1503 }
253
254 void addEngineSnifferLogicAnalyzerEvent(int laIndex, FrontDirection frontDirection) {
255 extern const char *laNames[];
256 const char *name = laNames[laIndex];
257
258 addEngineSnifferEvent(name, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN);
259 }
260
261 67212 void addEngineSnifferCrankEvent(int wheelIndex, int triggerEventIndex, FrontDirection frontDirection) {
262 static const char *crankName[2] = { PROTOCOL_CRANK1, PROTOCOL_CRANK2 };
263
264
2/2
✓ Branch 0 taken 33604 times.
✓ Branch 1 taken 33608 times.
67212 shaft_signal_msg_index[0] = frontDirection == FrontDirection::UP ? 'u' : 'd';
265 // shaft_signal_msg_index[1] is assigned once and forever in the init method below
266 67212 itoa10(&shaft_signal_msg_index[2], triggerEventIndex);
267
268
1/2
✓ Branch 1 taken 67212 times.
✗ Branch 2 not taken.
67212 addEngineSnifferEvent(crankName[wheelIndex], (char* ) shaft_signal_msg_index);
269 67212 }
270
271 3711 void addEngineSnifferVvtEvent(int vvtIndex, FrontDirection frontDirection) {
272 extern const char *vvtNames[];
273 3711 const char *vvtName = vvtNames[vvtIndex];
274
275
3/4
✓ Branch 1 taken 3711 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1860 times.
✓ Branch 4 taken 1851 times.
3711 addEngineSnifferEvent(vvtName, frontDirection == FrontDirection::UP ? PROTOCOL_ES_UP : PROTOCOL_ES_DOWN);
276 3711 }
277