GCC Code Coverage Report


Directory: Svc/FpySequencer/
File: FpySequencerValidationState.cpp
Date: 2026-09-03 21:17:55
Exec Total Coverage
Lines: 126 139 90.6%
Functions: 7 7 100.0%
Branches: 108 127 85.0%

Line Branch Exec Source
1 #include <Utils/Hash/Hash.hpp>
2 #include "Svc/FpySequencer/FppConstantsAc.hpp"
3 #include "Svc/FpySequencer/FpySequencer.hpp"
4
5 namespace Svc {
6
7 1 void FpySequencer::allocateBuffer(FwEnumStoreType identifier, Fw::MemAllocator& allocator, FwSizeType bytes) {
8 // if this assertion fails, you aren't allocating enough bytes for the
9 // FpySequencer. this is because you must have a buffer big enough to fit the
10 // header of a sequence
11 1 FW_ASSERT(bytes >= Fpy::Header::SERIALIZED_SIZE, static_cast<FwAssertArgType>(bytes));
12 1 FwSizeType originalBytes = bytes;
13 1 bool recoverable = false;
14 1 this->m_allocatorId = identifier;
15
1/1
✓ Branch 6 taken 1 times.
1 U8* allocatedMemory = static_cast<U8*>(allocator.allocate(identifier, bytes, recoverable));
16 // if this fails, unable to allocate the requested amount of money
17 1 FW_ASSERT(bytes >= originalBytes, static_cast<FwAssertArgType>(bytes));
18
1/1
✓ Branch 6 taken 1 times.
1 this->m_sequenceBuffer.setExtBuffer(allocatedMemory, bytes);
19 1 }
20
21 1 void FpySequencer::deallocateBuffer(Fw::MemAllocator& allocator) {
22 1 allocator.deallocate(this->m_allocatorId, this->m_sequenceBuffer.getBuffAddr());
23 1 this->m_sequenceBuffer.clear();
24 1 }
25
26 // loads the sequence in memory, and does header/crc/integrity checks.
27 // return SUCCESS if sequence is valid, FAILURE otherwise
28 67 Fw::Success FpySequencer::validate() {
29
3/3
✓ Branch 6 taken 67 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 66 times.
67 if (this->m_sequenceFilePath.length() == 0) {
30
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_FileOpenError(this->m_sequenceFilePath, static_cast<I32>(Os::File::INVALID_ARGUMENT));
31
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
32 }
33
34 // crc needs to be initialized with a particular value
35 // for the calculation to work
36
1/1
✓ Branch 4 taken 66 times.
66 this->m_computedCRC.init();
37
38
1/1
✓ Branch 2 taken 66 times.
66 Os::File sequenceFile;
39
1/1
✓ Branch 8 taken 66 times.
66 Os::File::Status openStatus = sequenceFile.open(this->m_sequenceFilePath.toChar(), Os::File::OPEN_READ);
40
41
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 62 times.
66 if (openStatus != Os::File::Status::OP_OK) {
42
1/1
✓ Branch 8 taken 4 times.
4 this->log_WARNING_HI_FileOpenError(this->m_sequenceFilePath, static_cast<I32>(openStatus));
43
1/1
✓ Branch 1 taken 4 times.
4 return Fw::Success::FAILURE;
44 }
45
46 62 Fw::Success readStatus =
47
2/2
✓ Branch 2 taken 62 times.
✓ Branch 6 taken 62 times.
62 this->readBytes(sequenceFile, Fpy::Header::SERIALIZED_SIZE, FpySequencer_FileReadStage::HEADER);
48
49
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 61 times.
62 if (readStatus != Fw::Success::SUCCESS) {
50
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
51 }
52
53
2/2
✓ Branch 2 taken 61 times.
✓ Branch 7 taken 61 times.
61 readStatus = this->readHeader();
54
55
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 60 times.
61 if (readStatus != Fw::Success::SUCCESS) {
56
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
57 }
58
59 readStatus =
60
3/3
✓ Branch 3 taken 60 times.
✓ Branch 17 taken 60 times.
✓ Branch 22 taken 60 times.
60 readBytes(sequenceFile, this->m_sequenceObj.get_header().get_bodySize(), FpySequencer_FileReadStage::BODY);
61
62
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 59 times.
60 if (readStatus != Fw::Success::SUCCESS) {
63
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
64 }
65
66
2/2
✓ Branch 2 taken 59 times.
✓ Branch 7 taken 59 times.
59 readStatus = this->readBody();
67
68
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 55 times.
59 if (readStatus != Fw::Success::SUCCESS) {
69
1/1
✓ Branch 1 taken 4 times.
4 return Fw::Success::FAILURE;
70 }
71
72 // read footer bytes but don't include in CRC
73
3/3
✓ Branch 3 taken 55 times.
✓ Branch 7 taken 55 times.
✓ Branch 12 taken 55 times.
55 readStatus = this->readBytes(sequenceFile, Fpy::Footer::SERIALIZED_SIZE, FpySequencer_FileReadStage::FOOTER, false);
74
75
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 55 times.
55 if (readStatus != Fw::Success::SUCCESS) {
76 return Fw::Success::FAILURE;
77 }
78
79
2/2
✓ Branch 2 taken 55 times.
✓ Branch 7 taken 55 times.
55 readStatus = this->readFooter();
80
81
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 55 times.
55 if (readStatus != Fw::Success::SUCCESS) {
82 return Fw::Success::FAILURE;
83 }
84
85 // make sure we're at EOF. The size() and position() OS calls can fail
86 // on filesystem errors or if the file was concurrently modified by
87 // another FileManager command (e.g. RemoveFile or AppendFile) between
88 // the file open and this point. Treat the failure as a validation
89 // failure rather than aborting the FSW process.
90 55 FwSizeType sequenceFileSize;
91
1/1
✓ Branch 2 taken 55 times.
55 Os::File::Status sizeStatus = sequenceFile.size(sequenceFileSize);
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
55 if (sizeStatus != Os::File::Status::OP_OK) {
93 this->log_WARNING_HI_FileApiError(this->m_sequenceFilePath, static_cast<I32>(sizeStatus));
94 return Fw::Success::FAILURE;
95 }
96
97 55 FwSizeType sequenceFilePosition;
98
1/1
✓ Branch 2 taken 55 times.
55 Os::File::Status positionStatus = sequenceFile.position(sequenceFilePosition);
99
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
55 if (positionStatus != Os::File::Status::OP_OK) {
100 this->log_WARNING_HI_FileApiError(this->m_sequenceFilePath, static_cast<I32>(positionStatus));
101 return Fw::Success::FAILURE;
102 }
103
104
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 54 times.
55 if (sequenceFileSize != sequenceFilePosition) {
105
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_ExtraBytesInSequence(static_cast<FwSizeType>(sequenceFileSize - sequenceFilePosition));
106
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
107 }
108
109
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 54 times.
54 if (this->m_sequenceArgs.get_size() > Fpy::MAX_STACK_SIZE) {
110 this->log_WARNING_HI_ArgTotalSizeExceedsStackLimit(
111 static_cast<Fpy::StackSizeType>(this->m_sequenceArgs.get_size()));
112 return Fw::Success::FAILURE;
113 }
114
115 // The argument size arrives separately from the argument buffer, so it can describe more bytes
116 // than that buffer holds. Reject before anything reads the buffer by that size.
117 54 const FwSizeType argCapacity = static_cast<FwSizeType>(sizeof(this->m_sequenceArgs.get_buffer()));
118
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 54 times.
54 if (this->m_sequenceArgs.get_size() > argCapacity) {
119 this->log_WARNING_HI_ArgSizeExceedsCapacity(this->m_sequenceArgs.get_size(), argCapacity);
120 return Fw::Success::FAILURE;
121 }
122
123
1/1
✓ Branch 1 taken 54 times.
54 return Fw::Success::SUCCESS;
124 66 }
125
126 // reads and validates the header from the m_sequenceBuffer
127 // return SUCCESS if sequence is valid, FAILURE otherwise
128 66 Fw::Success FpySequencer::readHeader() {
129 // deser header
130 66 Fw::SerializeStatus deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_header());
131
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 65 times.
66 if (deserStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
132
2/2
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
3 this->log_WARNING_HI_FileReadDeserializeError(
133 FpySequencer_FileReadStage::HEADER, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
134 2 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
135 1 return Fw::Success::FAILURE;
136 }
137
138 // check matching schema version
139
2/2
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 63 times.
65 if (this->m_sequenceObj.get_header().get_schemaVersion() != Fpy::SCHEMA_VERSION) {
140 4 this->log_WARNING_HI_WrongSchemaVersion(Fpy::SCHEMA_VERSION,
141 2 this->m_sequenceObj.get_header().get_schemaVersion());
142 2 return Fw::Success::FAILURE;
143 }
144
145
2/2
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 62 times.
63 if (this->m_sequenceObj.get_header().get_argumentCount() > Fpy::MAX_SEQUENCE_ARG_COUNT) {
146 1 this->log_WARNING_HI_TooManySequenceArgs(m_sequenceObj.get_header().get_argumentCount(),
147 Fpy::MAX_SEQUENCE_ARG_COUNT);
148 1 return Fw::Success::FAILURE;
149 }
150
151
2/2
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 61 times.
62 if (this->m_sequenceObj.get_header().get_statementCount() > Fpy::MAX_SEQUENCE_STATEMENT_COUNT) {
152 1 this->log_WARNING_HI_TooManySequenceDirectives(this->m_sequenceObj.get_header().get_statementCount(),
153 Fpy::MAX_SEQUENCE_STATEMENT_COUNT);
154 1 return Fw::Success::FAILURE;
155 }
156 61 return Fw::Success::SUCCESS;
157 }
158
159 // reads and validates the body from the m_sequenceBuffer
160 // return SUCCESS if sequence is valid, FAILURE otherwise
161 64 Fw::Success FpySequencer::readBody() {
162 Fw::SerializeStatus deserStatus;
163
164 64 const U8 argumentCount = this->m_sequenceObj.get_header().get_argumentCount();
165 64 this->m_totalExpectedArgSize = 0;
166
167 // deser arguments
168 // Read and deserialize each arg_spec incrementally since they're variable-length
169
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 63 times.
95 for (U8 i = 0; i < argumentCount; i++) {
170 32 Fpy::ArgSpec& argSpec = this->m_sequenceObj.get_args()[i];
171 32 deserStatus = this->m_sequenceBuffer.deserializeTo(argSpec);
172
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 31 times.
32 if (deserStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
173
2/2
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
3 this->log_WARNING_HI_FileReadDeserializeError(
174 FpySequencer_FileReadStage::BODY, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
175 2 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
176 1 return Fw::Success::FAILURE;
177 }
178
179 31 m_totalExpectedArgSize += argSpec.get_argSize();
180 }
181
182 // Check for overflow
183
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 62 times.
63 if (m_totalExpectedArgSize > Fpy::MAX_STACK_SIZE) {
184 1 this->log_WARNING_HI_ArgTotalSizeExceedsStackLimit(m_totalExpectedArgSize);
185 1 return Fw::Success::FAILURE;
186 }
187
188 // Validate total argument size
189
2/2
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 59 times.
62 if (this->m_totalExpectedArgSize != this->m_sequenceArgs.get_size()) {
190 3 this->log_WARNING_HI_ArgSizeMismatch(this->m_totalExpectedArgSize, this->m_sequenceArgs.get_size(),
191 this->m_sequenceFilePath);
192 3 return Fw::Success::FAILURE;
193 }
194
195 // deser statements
196 59 const U16 statementCount = this->m_sequenceObj.get_header().get_statementCount();
197
2/2
✓ Branch 0 taken 6309 times.
✓ Branch 1 taken 57 times.
6366 for (U16 statementIdx = 0; statementIdx < statementCount; statementIdx++) {
198 // deser statement
199 6309 deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_statements()[statementIdx]);
200
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6307 times.
6309 if (deserStatus != Fw::FW_SERIALIZE_OK) {
201
2/2
✓ Branch 11 taken 2 times.
✓ Branch 15 taken 2 times.
6 this->log_WARNING_HI_FileReadDeserializeError(
202 FpySequencer_FileReadStage::BODY, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
203 4 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
204 2 return Fw::Success::FAILURE;
205 }
206 }
207 57 return Fw::Success::SUCCESS;
208 }
209
210 // reads and validates the footer from the m_sequenceBuffer
211 // return SUCCESS if sequence is valid, FAILURE otherwise
212 58 Fw::Success FpySequencer::readFooter() {
213
1/1
✓ Branch 14 taken 58 times.
58 Fw::SerializeStatus deserStatus = this->m_sequenceBuffer.deserializeTo(this->m_sequenceObj.get_footer());
214
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 57 times.
58 if (deserStatus != Fw::FW_SERIALIZE_OK) {
215
4/4
✓ Branch 5 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
3 this->log_WARNING_HI_FileReadDeserializeError(
216 FpySequencer_FileReadStage::FOOTER, this->m_sequenceFilePath, static_cast<I32>(deserStatus),
217 2 this->m_sequenceBuffer.getDeserializeSizeLeft(), this->m_sequenceBuffer.getSize());
218
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
219 }
220
221 // need this for some reason to "finalize" the crc TODO get an explanation on this
222 57 U32 computedCRC = 0;
223
1/1
✓ Branch 4 taken 57 times.
57 this->m_computedCRC.finalize(computedCRC);
224
225
2/2
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 56 times.
57 if (computedCRC != this->m_sequenceObj.get_footer().get_crc()) {
226
1/1
✓ Branch 15 taken 1 times.
1 this->log_WARNING_HI_WrongCRC(this->m_sequenceObj.get_footer().get_crc(), computedCRC);
227
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
228 }
229
230
1/1
✓ Branch 1 taken 56 times.
56 return Fw::Success::SUCCESS;
231 }
232
233 // reads some bytes from the open file into the m_sequenceBuffer.
234 // return success if successful
235 180 Fw::Success FpySequencer::readBytes(Os::File& file,
236 FwSizeType expectedReadLen,
237 const FpySequencer_FileReadStage& readStage,
238 bool updateCrc) {
239 180 FW_ASSERT(file.isOpen());
240 // this has to be declared a var because file.read must take a ref
241 180 FwSizeType actualReadLen = expectedReadLen;
242
243 180 const FwSizeType capacity = this->m_sequenceBuffer.getCapacity();
244
245 // if this fails, then you need to give the sequencer more buffer memory. pass in a bigger number
246 // to fpySeq.allocateBuffer(). This is usually done in topology setup CPP
247
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 179 times.
180 if (expectedReadLen > capacity) {
248
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_InsufficientBufferSpace(static_cast<U64>(capacity), this->m_sequenceFilePath);
249
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
250 }
251
252
1/1
✓ Branch 10 taken 179 times.
179 Os::File::Status fileStatus = file.read(this->m_sequenceBuffer.getBuffAddr(), actualReadLen);
253
254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 179 times.
179 if (fileStatus != Os::File::OP_OK) {
255 this->log_WARNING_HI_FileReadError(readStage, this->m_sequenceFilePath, static_cast<I32>(fileStatus));
256 return Fw::Success::FAILURE;
257 }
258
259
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 176 times.
179 if (actualReadLen < expectedReadLen) {
260
1/1
✓ Branch 8 taken 3 times.
3 this->log_WARNING_HI_EndOfFileError(readStage, this->m_sequenceFilePath);
261
1/1
✓ Branch 1 taken 3 times.
3 return Fw::Success::FAILURE;
262 }
263
264 // should probably fail if we read in MORE bytes than we ask for
265 176 FW_ASSERT(expectedReadLen == actualReadLen, static_cast<FwAssertArgType>(expectedReadLen),
266 static_cast<FwAssertArgType>(actualReadLen));
267
268 Fw::SerializeStatus serializeStatus =
269
1/1
✓ Branch 6 taken 176 times.
176 this->m_sequenceBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(expectedReadLen));
270 176 FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, serializeStatus);
271
272
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 55 times.
176 if (updateCrc) {
273
1/1
✓ Branch 10 taken 121 times.
121 this->m_computedCRC.update(this->m_sequenceBuffer.getBuffAddr(), expectedReadLen);
274 }
275
276
1/1
✓ Branch 1 taken 176 times.
176 return Fw::Success::SUCCESS;
277 }
278
279 } // namespace Svc
280