Redirect debug console to file

cancel
Showing results for 
Search instead for 
Did you mean: 

Redirect debug console to file

389 Views
Martin55
Contributor I

So I want to log some sensor data in MCUXpresso at around 50 Hz. Some simple output format like CSV would be preferrable.

I know this can be done using GBG, I found this guide for instance:
https://mcuoneclipse.com/2020/04/13/dumping-variables-and-arrays-with-gdb-in-eclipse/

However, it is a bit tricky when targeting many values, and also a bit slow? As we have the processing power for it, just printf() to a csv for a quick debug seems easier. The task at hand then would be to redirect the debug console. Sure, we could just copy paste the console contents but that is a bit crude.

There are certainly settings for redirecting output to a file. There seems to be one specific for Jlink:

https://community.nxp.com/t5/CodeWarrior-for-StarCore/Log-Console-Output/m-p/183525/highlight/true


But I do not get the console content from MCUXpresso there, just the message Executed SetRestartOnClose=1 for the whole session.

There is one general debug console redirection for Eclipse and C++ that is possible to set up, similar to:

https://stackoverflow.com/questions/5415260/export-eclipse-console-view-output-to-text-file

However creating a general debug output for C++ makes JLink show an error.

 

I guess there is some setting I am missing somewhere or so. Ideas?

 

 

0 Kudos
13 Replies

224 Views
Martin55
Contributor I

So many great tips here! I decided to go with fprintf() as that is easy enough to use for a CSV log. For some reason fprintf() does not work anymore for me though. Any common causes? I am using LPC-Link2 on Ubuntu 20.04. A file is created correctly by

   #include <stdio.h>
   FILE* file fopen("/home/user_name/test.csv", "w")

However,

    fprintf(file, "Hello world\n");

yields nothing, test.csv is still empty.

0 Kudos

218 Views
frank_meyer
Senior Contributor I

I agree with ErichS.

I used to go the whole 9 yards, i.e. fflush() and fclose() like on a normal host file operation.

0 Kudos

222 Views
ErichS
Senior Contributor III

The written content needs to be flushed (depending on the library you use).

Did you try with fclose(file)?

 

I hope this helps,

Erich

0 Kudos

199 Views
Martin55
Contributor I

Ooh, bummer, thanks. I choose to implement it along these lines, using templates in C++ to easily change around the buffer size and number of columns.

https://pastebin.com/FvMimiv7

Seems to work. There is a trailing comma that could bother some csv readers on the host machine. Could off course template the data type too I guess.

0 Kudos

194 Views
Martin55
Contributor I

Files get corrupted though. For a clean toy example during the init I could write a really nice CSV,  but when the processor is busy with communications, motors etc the whole system goes nuts at file writes and parts of the output file gets corrupted every time. Sure can filter the file afterwards, but so much extra work. An easily accessible UART would have made everything so much easier... I never experienced such a hassle for getting a few plots.

0 Kudos

179 Views
converse
Senior Contributor IV

You probably don't realise what is going on with semihosting... See this for a basic introduction.

https://www.silabs.com/community/mcu/32-bit/knowledge-base.entry.html/2012/02/06/what_is_semihosting...

 

0 Kudos

188 Views
ErichS
Senior Contributor III

Just to add some thoughts for you.

IMHO semihosting is really a bad choice for things like this. Sure it works for a few printf. It is very intrusive and really huge (code size and stack usage). Make sure you have plenty (say >20 KByte heap and plenty (say >5k Byte) stack space available.

The other point would be: is your system re-entrant? If using the standard library malloc/free (which is used inside the standard library depending on your lib/gnu version) and using an RTOS and/or constructors/destructors you have to pay attention to make it reentrant. Have a read at the excellent text by David Nadler on that subject: https://nadler.com/embedded/newlibAndFreeRTOS.html

As for logging, I rather use SEGGER RTT (you get that with using a J-Link probe, see https://mcuoneclipse.com/2015/07/07/using-segger-real-time-terminal-rtt-with-eclipse/ and https://mcuoneclipse.com/2020/06/01/mculog-logging-framework-for-small-embedded-microcontroller-syst... which includes logging to a file. You don't need an UART for this, just a J-Link connection and the RTT module added to your software.

I hope this helps,

Erich

0 Kudos

381 Views
converse
Senior Contributor IV

Why not just write the values directly to a file, using fprintf? Using semihosting allows you to open/read/write files on the host directly. You are using semihosting to printf to the console, so it won’t make any difference to your build. Just use it exactly as you would if running on the host natively.

370 Views
ErichS
Senior Contributor III

Hi @Martin55 ,

semihosting is certainly an option to read from and to the host.

I'm using host file reading/writing for example with gcov (see https://mcuoneclipse.com/2021/02/01/tutorial-gnu-coverage-with-mcuxpresso-ide/ ) and the test function gcov_check() in https://github.com/ErichStyger/mcuoneclipse/blob/master/Examples/MCUXpresso/FRDM-K64F/FRDM-K64F_gcov...

You will need to use a semihosting enabled library (I recommend newlib-nano) and a J-Link. What is always a bit of different is what will be the current directory on the host: this could be inside the IDE installation, inside the project output folder, etc. Otherwise use an absolute path as in the example above.

I hope this helps,

Erich

359 Views
Martin55
Contributor I

Neat! I tried fprintf() and it seemed to work right off the bat for the project I do not know how slow it is, CycleDelta seems to not work properly. Functions fopen() and fclose() seem to be real slow and would cause the program to malfunction when called frequently. Just writing using fprintf() seemed to be working fine.

I had to use POSIX like paths for fopen() like "/path/to/mylog.log", although my host is Windows (I usually use Linux though). I guess that is due to the arm-gcc compiler.

On a side note, CycleDelta does not seem to be working well for me. I get extreme counts like 40000 for an int multiplication. Any common reasons for such issues? I use an FRDMK64 board.

0 Kudos

346 Views
ErichS
Senior Contributor III

Another option is to use the SEGGER RTT to write to a file on the host. This is much more efficient than semihosting. I'm using this e.g. for logging (https://mcuoneclipse.com/2020/06/01/mculog-logging-framework-for-small-embedded-microcontroller-syst...), see about RTT here:

https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/

 

You can load the SEGGER J-Link firmware on the K64F and then you have that functionality.

I hope this helps,

Erich

0 Kudos

350 Views
frank_meyer
Senior Contributor I

I tried the same thing occasionally, with mixed results like you. While it basically worked, it affected the application timing significantly.

Two tricks I tried to improve throughput:

  • simplify and shorten the output strings as much as possible, e.g. hex values without prefix and a minimum of delimiters; interpreting on the host is cheap
  • sample a limited amount into an array in realtime, and transmit afterwards, without realtime requirements (single-shot sample)
0 Kudos

356 Views
converse
Senior Contributor IV

Using semihosting is going to be slow. It works by stopping the program, transferring data to the debugger (which will then perform the operation) and (after the i/o has completed on the host) restart the application. It is not designed for real-time applications! You may want to introduce some buffering in your application so you transfer several results at once - the (relatively) slow part is transferring data to the debugger. I guess you need to experiment to see if it meets your needs.

And, of course, it will only work when a debugger is attached - your application will halt, and not restart, if there is no debugger attached. However, this will be quicker than any of the other methods you suggested!

If you need to transfer large amounts of data, or need it to be quick, it may be best to use a serial port.

 

Regarding CycleDelta - make sure you are measuring what you think you are measuring! It measures every cycle between successive breakpoints. So will include any interrupts, any library calls etc. Take a look at the assembler output and make sure you really are only executing a MUL instruction - there may be a library call involved.

0 Kudos