Skip to content
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

Sort a few python dicts for deterministic builds, even with python 3.5 and earlier. #14119

Merged
merged 2 commits into from
Mar 7, 2019

Conversation

marc-hb
Copy link
Collaborator

@marc-hb marc-hb commented Mar 6, 2019

See commit messages.

To reproduce the issue this addresses, run "cmake -B96b_nitrogen"
twice with Python 3.5. Observe the order of the flash partition table
changing in:
"build{1,2}/zephyr/include/generated/generated_dts_board.conf" and
"generated_dts_board_unfixed.h"

Dictionaries are iterated in a random order by Python 3.5 and before.
This could have caused "Unstable" CI in PR zephyrproject-rtos#13921 and maybe others.
Anyway we want builds to be determimistic by default. Explicit
randomness can be added for better coverage but not by default.

Signed-off-by: Marc Herbert <[email protected]>
for node_path in reduced.keys():
# sorted() otherwise Python < 3.6 randomizes the order of the flash
# partition table
for node_path in sorted(reduced.keys()):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I missed this earlier, but could get rid of the .keys(). x in dict, for x in dict, sorted(dict), etc., always uses the keys.

Copy link
Collaborator Author

@marc-hb marc-hb Mar 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed but I'd like to keep this particular commit as minimal as possible :-) Not just for aesthetic reasons but also cherry-picking for testing / backporting etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, fine with me.

eh = ElfHelper(args.kernel, args.verbose, kobjects, subsystems)
syms = eh.get_symbols()
max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8
objs = eh.find_kobjects(syms)
if len(objs) == 0:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do if not objs:.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

eh = ElfHelper(args.kernel, args.verbose, kobjects, [])
syms = eh.get_symbols()
max_threads = syms["CONFIG_MAX_THREAD_BYTES"] * 8
objs = eh.find_kobjects(syms)
if len(objs) == 0:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

@@ -101,10 +101,14 @@ def parse_args():
def main():
parse_args()

assert args.kernel, "--kernel ELF required for --gperf-output"
Copy link
Collaborator

@ulfalizer ulfalizer Mar 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a required=True on the parser.add_argument() that adds --kernel, so this might not be needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I saw the mostly duplicated main() so I blindly copied the assert from the other script. Will remove.

@@ -269,10 +276,14 @@ def main():
parse_args()

if args.gperf_output:
assert args.kernel, "--kernel ELF required for --gperf-output"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

if not args.kernel:
    sys.exit("--kernel must be passed if --gperf-output is")

would be a bit more purist.

It's more of a usage error than a runtime error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a usage error but... a pretty stupid, "trial-and-error" one so I didn't want to spend too many lines of code on it.

@marc-hb marc-hb requested review from galak, lpereira and chunlinhan March 6, 2019 20:27
@codecov-io
Copy link

codecov-io commented Mar 6, 2019

Codecov Report

Merging #14119 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master   #14119   +/-   ##
=======================================
  Coverage   52.68%   52.68%           
=======================================
  Files         307      307           
  Lines       45473    45473           
  Branches    10530    10530           
=======================================
  Hits        23956    23956           
  Misses      16640    16640           
  Partials     4877     4877

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 97f4265...1d3dff3. Read the comment docs.

Copy link
Contributor

@andrewboie andrewboie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other than the minor issues @ulfalizer noted this LGTM

Dictionaries are iterated in a random order by Python 3.5 and before.
This could have caused "Unstable" CI in PR zephyrproject-rtos#13921 and maybe others.
Anyway we want builds to be determimistic by default. Explicit
randomness can be added for better coverage but not by default.

1. When running "make kobj_types_h_target" repeatedly one can observe
that the following .h files keep changing in
build/zephyr/include/generated/:

- kobj-types-enum.h
- otype-to-str.h
- otype-to-size.h

Switching kobjects to OrderedDict makes these 3 .h files deterministic.

2. When running this test repeatedly with CONFIG_USERSPACE=y:

  rm build/zephyr/*.gperf && make -C build obj_list

... the dict used for --gperf-output seems to be deterministic, probably
because its keys are all integers (memory addresses). However we can't
take that for granted with Python < 3.6 so out of caution also switch
the output of find_objects() in elf_helper.py to a sorted OrderedDict.

PS: I would normally prefer official Python documentation to
StackOverflow however this one is a good summary and has all the
multiple pointers to the... official Python documentation.

Signed-off-by: Marc Herbert <[email protected]>
@marc-hb marc-hb force-pushed the deterministic-builds branch from 0bf4e64 to 1d3dff3 Compare March 6, 2019 23:15
@marc-hb marc-hb requested a review from ulfalizer March 7, 2019 00:59
Copy link
Collaborator

@ulfalizer ulfalizer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still think the assert for reporting a usage error looks confusing, but won't block over it.

@nashif nashif merged commit f78288b into zephyrproject-rtos:master Mar 7, 2019
@marc-hb
Copy link
Collaborator Author

marc-hb commented Mar 7, 2019

Thanks for the very prompt reviews. For the record this is a "screenshot" of a typical diff: https://gist.github.com/nashif/675dd70be922f0200c181d6a65baeafb

There's also one last crypto.mbedtls/zephyr/include/generated/app_smem.ld randomness there which no one has reproduced, maybe addressed elsewhere?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants