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

[TRACKING] buildsystem: introduce "make rawterm" #11099

Closed
wants to merge 9 commits into from

Conversation

jcarrano
Copy link
Contributor

@jcarrano jcarrano commented Mar 4, 2019

Contribution description

This is a tracking issue. It will be split.

Currently, the tests invoke "make term" for interacting with the board. "make term" starts the "pyterm" program which may be nice for a human user but is horrible for a machine: it does line buffering, local echo, inserts lots of unnecessary data in the output stream, has "magical" behaviors (like hitting enter on an empty line repeats the last command, wtf?!?!). Normally, when testing, you want to test the actual thing and try to take out of the equation any external factors. In any case, it's pointless to use any readline-based terminal program when the output is not a pty/tty but a pipe.

Add to this that the tests are done using "dumb matching" with pexpect and the result is that you try to match, say, an integer, and you could be matching your own input, or pyterm's "logging" info.

We are introducing "make rawterm" which gives a "raw" terminal, meaning one that is transparent- whatever you give to the terminal input goes to the serial device, whatever arrives to the serial device- and nothing- else goes to the terminal output.

On the native side, I tried to fix the conceptual error of having TERMPROG be the riot executable and TERMFLAGS be the flags to RIOT.

Other facts to take into account:

  • The CI (murdock) is using picocom. It would be highly desirable that the same tool is used in the developer's machine and in the CI.
  • @MrKevinWeiss's RIOT PAL currently has to REMOVE the superfluous information inserted by pyterm.

Testing procedure

I will provide some tests stolen from #11094 to show the issues with the non-raw terminals and how they interfere with testing.

TODO:

  • Make CI tests use "make rawterm"

Issues/PRs references

Contains commits from: #11156, #11129 (needed for tests)
Would fix: #10952
Part of: #10994
Related to: #11094

@jcarrano jcarrano added Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) Area: tests Area: tests and testing framework Area: build system Area: Build system Area: tools Area: Supplementary tools Type: tracking The issue tracks and organizes the sub-tasks of a larger effort labels Mar 4, 2019
@jcarrano jcarrano requested review from cladmi and MrKevinWeiss March 4, 2019 15:33
@kaspar030
Copy link
Contributor

What's wrong with using picocom (or socat) for tests?
Why does this need another make target?

@cladmi
Copy link
Contributor

cladmi commented Mar 4, 2019

Current picocom configuration is made for a user and does not work for tests as it does local echo.
Local echo could be disabled but it would mean disable also for users.

Also, when using make term you expect to have line rewrite and line buffering. Which you may not want while testing.

@cladmi
Copy link
Contributor

cladmi commented Mar 4, 2019

Also, there are already cases where you cannot directly say "RIOT_TERMINAL = picocom" and expect it to work. native, boards using Jlink and stdio_rtt, tests through IOTLAB_NODE. All these would also need a special configuration to work in a raw mode.

By putting a name on it, it allows doing test specific configurations without breaking the make term user experience.

@jcarrano
Copy link
Contributor Author

jcarrano commented Mar 4, 2019

What's wrong with using picocom (or socat) for tests?

Nothing, quite the contrary, it is GOOD.

Why does this need another make target?

  1. We still want "make term" to have nice readline capabilities.
  2. But we want to make clear the requirements for a "testing grade" terminal.
  3. And doing RIOT_TERMINAL=picocom does not work for boards without a proper tty (like jlink/RTT, boards in IOT-lab)
  4. Having a RAW terminal means that the "nice" terminal can be build on top of this (like I did with rlwrap)

There are not many ways to escape issue (3).

@cladmi
Copy link
Contributor

cladmi commented Mar 5, 2019

To introduce it early without even being implemented, I would like to have an implementation of make rawterm that defaults to term.

$(if $(RAWTERMPROG),$(RAWTERMPROG) $(RAWTERMFLAGS),$(TERMPROG) $(TERMFLAGS))

With a documentation saying the characteristics of rawterm and that it may not be the case because of "reasons" and could default to term if not set.

This way, it could be used in the tests by default. As the first thing would be that it does not change the current behavior, just the target being used.
And then, it could be implemented per board family.

If we manage to have all boards supported, the "backup" could even be removed at the end.

@jcarrano jcarrano added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Mar 12, 2019
@jcarrano
Copy link
Contributor Author

I just ran the tests on a SAMR21 on my machine. Seems to work. I'd like to try a board with jlink rtt.

@jcarrano jcarrano added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Mar 12, 2019
@jcarrano jcarrano added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Mar 20, 2019
@jcarrano
Copy link
Contributor Author

I rebased on top of master.

jcarrano added 7 commits May 28, 2019 15:06
`make rawterm` is the new interface for automated access to the serial
port. It will be used in testing. In contrast with `make term`, which is
intended for human-machine-interaction, rawterm is meant to comminicate
the node with automated scripts running on the host machine.

rawterm must:

- Not have any input or output buffering.
- Not modify input or output in any way (no extra prompts, logging artifacts,
  translations etc.)
- Have local echo disabled.
- Not print anything to stdout that was not generated by the node (i.e. no
  startup banners.)

With this definition it is very easy to implement a generic "nice" terminal
(that is `make term`) as a wrapper around rawterm.
Add socat, picocom and jlink (for rtt) as terminals, and remove
socat from the cooked terminal list.
Rlwrap allows using any raw terminal (if it is really raw) and adding
readline support around it, with history that is even preserved thoughout
sessions.

The -C option is used to create a different history file for each board.
The lobaro-lorabox sets some pyterm-specific TERMFLAGS, so we must
make sure that pyterm is selected.
The msba2 sets some pyterm-specific TERMFLAGS, so we must
make sure that pyterm is selected.
The riot executable is NOT a terminal program. Therefore, to mantain
consistency with the rest of the targets and avoid hacky-hacks the
TERMPROG should not be ELFFILE.

This commit creates a new variables specially for the native platform,
NATIVE_EXEC, NATIVE_FLAGS.

The (raw)terminal program is socat, which wraps NATIVE_EXEC. This also
has the benefit of disabling buffering and echo in the terminal so that
narive behaves more similar to bare-metal RIOT. Doing this terminal
reconfiguration in RIOT is possible, but dangerous as the settings must
be reset after exit or the users terminal emulator will be left broken.

Using socat as RIOT_RAWTERMINAL means rlwrap can be used as RIOT_TERMINAL,
further unifying the behaviour of native and bare-metal.
The native executable is no longer the TERMPROG. Therefore, the flags
are not given by TERMFLAGS, but by NATIVE_FLAGS.
@cladmi cladmi added the State: waiting for other PR State: The PR requires another PR to be merged first label Jun 3, 2019
@@ -19,6 +19,8 @@ PORT_LINUX ?= /dev/ttyUSB0
# This does not make a lot of sense, but it has the same value as the previous code
PORT_DARWIN ?= /dev/tty.usbserial-ARM

# The -tg option is specific to pyterm so we are forced to use it.
RIOT_TERMINAL = pyterm
Copy link
Member

Choose a reason for hiding this comment

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

Can't you also use simply expanded variables := here and in all following cases below? There's no need for recursions as it appears to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At the moment we are trying to do the opposite: remove instances of := where it is not needed.

Copy link
Member

@cgundogan cgundogan Jun 3, 2019

Choose a reason for hiding this comment

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

Interesting .. as far as I know you usually remove instances of =, where it is not needed (: maintaining recursively expanded variables is complex and can lead to hard-to-debug problems. Imagine someone replaces pyterm with a function or a variable in the future, but keeps the = ..

What's the motivation for choosing recursive assignments over simple assignments?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:= has some disadvantages:

  • The value of variables defined with := depends on the order they are defined.
  • Once introduced, it is not that easy to tell if := can be replaced by =
  • := will be evaluated even if not needed. This will force evaluation of all called functions, in particular shell calls. This causes unnecessary overhead at minimum, or a failure that should not occur at most (think what happens if a tool is defined with := but you don't have the tool and you don't need it either)

I believe immediate assignments cause harder-to-debug problems because the order of definition starts to matter.

With respect to performance, if some variable is too expensive to evaluate more than once, there is a trick to memoize it (I think there is a PR by @cladmi ).

Finally, recursive variables fit better into the declarative style that good makefiles should follow.

Copy link
Member

Choose a reason for hiding this comment

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

a quote from the docs (:

To avoid all the problems and inconveniences of recursively expanded variables,
there is another flavor: simply expanded variables. 

Kidding aside, I am not very involved in the build system development and I don't know what the desired end result will look like. I was merely stating that I had problems with recursive evaluations in earlier projects and most of the time they lead to performance drops. If you think that recursive evaluations fit better in the picture, then I trust your judgement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reality is that we (and by that I mean @cladmi and me) should probably spend some time writing a proper guide to writing makefiles in RIOT.

That part of the docs is true, but it refers mainly to things like wildcards and shell calls with side-efects. Essentially, I/O. In our case := is most of the times workaround for this. The real solution is to more cleanly separate the functional and I/O parts of our makefiles.

Performance drop in RIOT builds is mainly due to:

  • FORCE and PHONY targets (for example, we are relinking every time).
    • these are ugly hacks because people could not figure out how to do dependencies properly.
  • Recursive make (or more precisely, the fact that the submakes run always)
  • Checking tools all the time / evaluating tool-related variables, even when those tools are not used (:= is involved here).
    • Here is where a memoization function will help, but only after we refactor.

This is probably more info than you asked for, but I feel this should be stated somewhere.

Copy link
Contributor

@cladmi cladmi Jun 3, 2019

Choose a reason for hiding this comment

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

If you are doing recursive definitions you are not defining but doing evaluation.

Here I would say there is no need for evaluation so no :=.

Interesting .. as far as I know you usually remove instances of =, where it is not needed (: maintaining recursively expanded variables is complex and can lead to hard-to-debug problems. Imagine someone replaces pyterm with a function or a variable in the future, but keeps the = ..

usually I would say no, its only in RIOT that I saw this pattern.
If you have INCLUDES that depends on INCLUDES you should introduce a new name and do INCLUDES = $(INCLUDES_TOOLCHAIN) $(INCLUDES_C_LIBRARY) and define INCLUDES_TOOLCHAIN later instead of prepending to the previous value.

If you replace pyterm with a function, it does nothing as you are not evaluating the name with $(pyterm).

I was merely stating that I had problems with recursive evaluations in earlier projects and most of the time they lead to performance drops.

If a shell call is evaluated 100 times it can indeed slow down the build, but then only this one should be optimized. Also, with correct file targets you do not rebuild the target if not neheaderseded so do not re-evaluate the variable for nothing. It may make the clean build slower but makes incremental build faster.
Also, by doing := everywhere you are making make clean slow as it checks the warnings supported for your gcc version even if you do not use it.

Copy link
Contributor

Choose a reason for hiding this comment

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

@cladmi @jcarrano This discussion was quite insightful for me, I think it would be great to have this documented somewhere, kind ok like the advanced-build-system-tricks.md but a build-system-basics.md page :)

Copy link
Contributor

Choose a reason for hiding this comment

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

It is somehow "gnumake" basics, but how it should be applied to RIOT could indeed be good.
I never had any issues before as was not using recursive makefiles.

We still need to export things and so how to counter the issues with it with memoized somehow and target-export-variables.

@jcarrano
Copy link
Contributor Author

jcarrano commented Jun 3, 2019

@cladmi Do you still have the test we did with socat inside jlink.sh (with the automatic retry and all that stuff)?

@cladmi
Copy link
Contributor

cladmi commented Jun 3, 2019

A hardwritten sleep was added

For the retry I had this #10849 but was not using socat retry option


RIOT_RAWTERMINAL ?= socat
ifeq ($(RIOT_RAWTERMINAL),socat)
SOCAT_LOCAL ?= STDIO,icanon=0,echo=0,escape=0x04
Copy link
Contributor Author

Choose a reason for hiding this comment

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

With these options I'm still getting line buffering.

@miri64
Copy link
Member

miri64 commented Jun 25, 2020

#12107 was merged.

@miri64 miri64 closed this Jun 25, 2020
@fjmolinas
Copy link
Contributor

This is not finished, line buffering is still not addressed here with pyterm.

@miri64
Copy link
Member

miri64 commented Jun 25, 2020

Ah sorry, overlooked that.

@miri64 miri64 reopened this Jun 25, 2020
@fjmolinas
Copy link
Contributor

I think missing still

  • native realted commits 8d74a1b, 086480a, 0ad9540: boards/native: Use socat as a termprog.
  • 53800ba that addresses terminals that can work with no line buffering

Unrelated but maybe useful:

@fjmolinas
Copy link
Contributor

I think missing still

* [ ]  native realted commits [8d74a1b](https://github.com/RIOT-OS/RIOT/commit/8d74a1bc54b2fa45eededde88491450cb956a940), [086480a](https://github.com/RIOT-OS/RIOT/commit/086480aa94ef2e7d128c99925e92f040ac7931b7), [0ad9540](https://github.com/RIOT-OS/RIOT/commit/0ad9540ae2dece9a0cc0d4b3f80e8a6a75f1ace9): boards/native: Use socat as a termprog.

* [ ]  [53800ba](https://github.com/RIOT-OS/RIOT/commit/53800ba824c1577c665a8a19acd333998b7f27aa) that addresses terminals that can work with no line buffering

Unrelated but maybe useful:

* [ ]  [3849f8a](https://github.com/RIOT-OS/RIOT/commit/3849f8a680f7179f2b5e1fe95e7934827556afb9)

Maybe I can cherry-pick some of these... I'll add milestone to check later...

@fjmolinas fjmolinas added this to the Release 2022.01 milestone Nov 18, 2021
@OlegHahm
Copy link
Member

OlegHahm commented Mar 9, 2022

@fjmolinas, did you check? ;-)

@fjmolinas
Copy link
Contributor

@fjmolinas, did you check? ;-)

Nope, and I don't think I'll have time, lets postpone to 2022.07

@maribu
Copy link
Member

maribu commented Sep 23, 2022

Stale and apparently no longer needed

@maribu maribu closed this Sep 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: build system Area: Build system Area: tests Area: tests and testing framework Area: tools Area: Supplementary tools CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR State: waiting for other PR State: The PR requires another PR to be merged first State: WIP State: The PR is still work-in-progress and its code is not in its final presentable form yet Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) Type: tracking The issue tracks and organizes the sub-tasks of a larger effort
Projects
None yet
Development

Successfully merging this pull request may close these issues.