-
-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow override ARRAY_CAPACITY in variable array #189
Allow override ARRAY_CAPACITY in variable array #189
Conversation
…ze publisher memory usage
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nice, thanks Peter.
Note that in the serialization function we have a check that the target buffer is large enough:
static inline int8_t reg_drone_service_battery_Status_0_2_serialize_(
const reg_drone_service_battery_Status_0_2* const obj, uint8_t* const buffer, size_t* const inout_buffer_size_bytes)
{
if ((obj == NULL) || (buffer == NULL) || (inout_buffer_size_bytes == NULL))
{
return -NUNAVUT_ERROR_INVALID_ARGUMENT;
}
const size_t capacity_bytes = *inout_buffer_size_bytes;
if ((8U * (size_t) capacity_bytes) < 4272UL)
{
return -NUNAVUT_ERROR_SERIALIZATION_BUFFER_TOO_SMALL;
}
So if you pass a smaller buffer here with the aim of conserving memory it will error out. Still, your change makes sense because even if you keep the original buffer size you can still save on the memory footprint of the struct itself. Do you want to keep this behavior unchanged or should we consider a way of disabling the serialization buffer size check? Perhaps a new code generation option would help?
Another question is that of MISRA/AUTOSAR compliance. We take great care to keep violations to the minimum which is why the generated code is virtually devoid of conditional compilation statements, but your added #ifdef
breaks that. A possible way to address this is to add another code generation option, which could be actually merged with the previously mentioned one. Such that if the option is enabled, the buffer size checks are not emitted and the ability to override the array capacity is supported. Do you think it makes sense?
Co-authored-by: Pavel Kirienko <[email protected]>
Indeed we could apply this to the serialization output buffer to save memory there as well, but then indeed we've to change the checks as well. But we don't have to disable the serialization buffer check entirely right, we could make it a define that recalculates the value based on other defines? Or is this problematic. So the correct way would be adding a new code generation option that enables
|
It's much harder than it may seem at first, I wouldn't venture there. The serialization buffer size and the footprint of your C structure are completely unrelated, because one is defined by the DSDL memory model and the other comes from the C memory model. You would have to trace back your array capacity changes back to the DSDL model and then recompute the size from that. |
Indeed you're right it can become very complex due to bit padding. I've added enable_override_variable_array_capacity option in nunavut where you can override the define and the capacity bytes check is then removed. I'm not that familiar with MISRA but we might want to add the specific rules we're violating when this option is enabled. Furthermore I've added a check when you override the variable array length you can't make it greater then the maximum specified size in the DSDL definition. |
34c9f49
to
61dbbf3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not bad. Let's extend the verification suite to cover your changes and I think we're good to merge, assuming that @thirtytwobits has no objections. He was considering addressing this issue differently by providing an option to use dynamic storage but I don't think it invalidates this approach.
Extending the test suite is easy but a bit tedious:
-
Mirror your new option in
Findnnvg.cmake
: https://github.com/UAVCAN/nunavut/blob/b85dc862ba8c4ff0a3b958ea993a7ba028b411d0/verification/cmake/modules/Findnnvg.cmake#L35-L44 -
Propagate it in
verification/CMakeLists.txt
: https://github.com/UAVCAN/nunavut/blob/main/verification/CMakeLists.txt#L144-L184 -
Mirror it in
verify.py
as well: https://github.com/UAVCAN/nunavut/blob/main/.buildkite/verify.py#L22-L194 -
Add a new line at the end of
verify_c_*.sh
(both platforms) that invokes the verification suite with your option activated: https://github.com/UAVCAN/nunavut/blob/main/.buildkite/verify_c_amd64.sh#L13
Phew. Maybe in the future, we could just export specific environment variables from verify_c_*.sh
and have nnvg consume them at the bottom of the call stack, kind of like Click does.
src/nunavut/cli.py
Outdated
help=textwrap.dedent(''' | ||
|
||
Instruct support header generators to add the possiblitiy to override max capacity of a variable length array | ||
in serialization routines. This will option will disable serialization buffer checks and add conditional compilation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in serialization routines. This will option will disable serialization buffer checks and add conditional compilation | |
in serialization routines. This option will disable serialization buffer checks and add conditional compilation |
#define {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ {{ f.data_type.capacity }}U | ||
{%- if options.enable_override_variable_array_capacity and f.data_type is VariableLengthArrayType %} | ||
#elif {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ > {{ f.data_type.capacity }}U | ||
#error {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ > {{ f.data_type.capacity }}U |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#error {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ > {{ f.data_type.capacity }}U | |
# error {{ t | full_reference_name }}_{{ f.name }}_ARRAY_CAPACITY_ > {{ f.data_type.capacity }}U |
The changes I described above would break this test: https://github.com/UAVCAN/nunavut/blob/33864a509eaaa9a9a7cdfe06ff611106f791a894/verification/c/suite/test_serialization.c#L462-L467 We probably need to propagate the value of your flag into the generated code. For instance, you could add a new preprocessor definition that resolves to true if your new option is enabled, and check it in the verification suite to conditionally disable this check. Come to think of it, it makes sense to expose all code generation flags, not just the new one -- |
Might fail though
I've captured that with this, which doesn't disable the serialization buffer check if you don't override the variable array capacity. I've added test option definition to test suite as you described above, however we still need to make a separate test where override the variable array capacity and test if it actually works. Hm I'm breaking CI with this change but not the way how expected it, any idea what this error means? |
Only @thirtytwobits can save us now. |
Fixed enable override variable array naming
Turns out I had to modify the arguments here below as well CI is now successful, but I need to make a separate test to see if override is working correctly. |
This is great, thanks Peter. Re testing, you can just copy-paste that Status message (or define something similar) somwhere to https://github.com/UAVCAN/nunavut/tree/main/verification/nunavut_test_types/test0/regulated and use that. |
3970517
to
8e8b9ee
Compare
Okay I've made a simple test case, is this okay or did I miss some test cases? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also test that reg_drone_service_battery_Status_0_2_DISABLE_SERIALIZATION_BUFFER_CHECK
is handled properly. Other than that it should be good.
#elif !defined(reg_drone_service_battery_Status_0_2_DISABLE_SERIALIZATION_BUFFER_CHECK) | ||
# define reg_drone_service_battery_Status_0_2_DISABLE_SERIALIZATION_BUFFER_CHECK |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#elif !defined(reg_drone_service_battery_Status_0_2_DISABLE_SERIALIZATION_BUFFER_CHECK) | |
# define reg_drone_service_battery_Status_0_2_DISABLE_SERIALIZATION_BUFFER_CHECK | |
#elif !defined(reg_drone_service_battery_Status_0_2_DISABLE_SERIALIZATION_BUFFER_CHECK_) | |
# define reg_drone_service_battery_Status_0_2_DISABLE_SERIALIZATION_BUFFER_CHECK_ |
By convention documented in the header comment, we add a single underscore at the end to reduce the chance of conflict with DSDL entities (what would happen if you have a constant named DISABLE_SERIALIZATION_BUFFER_CHECK
in your DSDL)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having trouble with this change: why do we have reg_drone_service_battery_Status_0_2_
anything in the templates? This is a single datatype in one UAVCAN profile. We can't pollute these templates with specific knowledge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The objective is to disable the check for this type only, keeping this on for other types. This is supposed to be safer than disabling the check for all generated code completely.
I understand the feature (although using a variable-length array type is a more elegant solution) but the implementation seems unacceptable. Creating a single override for all arrays breaks the design of Nunavut and the need to add a single "battery status" ifdef demonstrates why. The two ways forward I see are:
|
You're right it applies to all variable length array's in all DSDL files but in the following manner.
If there's a cleaner way I'm happy to adapt to that
Would this mean changing the array allocation from |
3a4ab3f
to
d2c5008
Compare
Yeah. Here, let's discuss better options here -> #191 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pending further discussion, I would like to register my opinion about this PR. Ultimately the decision is up to Scott.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not super-thrilled with this approach but it is pragmatic. My main concern is that, when we do add a variable-length data structure, this becomes a vestigial feature that we need a major revision to purge.
Regardless, I approve.
@PetervdPerk-NXP the CD is going to explode now because we forgot to bump the version number. Can you please do that now? |
Allow override ARRAY_CAPACITY in variable array to optimize publisher memory usage at compile time.
As a publisher you might already know the maximum size of variable length array. This commit allow to override the XXXX_ARRAY_CAPACITY_ define for variable length arrays.
A good example would be the reg_drone_service_battery_Status_0_2_cell_voltages_ARRAY_CAPACITY_ define. At default the maximum is 255, but for instance your BMS only supports atmost 6 cells.
By default the reg.drone.service.battery.Status_0.2 struct is 1056 bytes
By allowing to overrule the cell_voltages array capacity we can decrease the struct size tremendously.
We only need 56 bytes now to allocate this struct on our stack