GCC Code Coverage Report


Directory: ./
File: Svc/FpySequencer/FpySequencerValidationState.cpp
Date: 2026-11-03 21:15:54
Exec Total Coverage
Lines: 128 141 90.8%
Functions: 7 7 100.0%
Branches: 112 131 85.5%

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