snprintf with floating point numbers

Asked by David Regan

System Details:
 RTOS - FreeRTOSV8.1.2
 Compiler - "GNU Tools ARM Embedded" toolchain version 4.8 2014q3
 IDE - Eclipse Luna Service Release 1 (4.4.1)
 Controller - STM32F429i Discovery board.

Problem:
 2 of the several tasks running in my application use snprintf to format floating point numbers
 into independent buffers. One formats a single float, the other a list of about 14 floats.

If both tasks are allowed to call snprintf I receive corruption in the larger buffer.
 A null is embedded. Interestingly, snprintf returns the correct string length processed but
 strlen obviously reports a shorter string.

Is snprintf thread safe or can you suggest what may be wrong.

Many thanks.

Question information

Language:
English Edit question
Status:
Answered
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:

This question was reopened

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#1

Hi David, are you using newlib or newlib-nano?

Revision history for this message
David Regan (david-regan) said :
#2

Hi Thomas

Thanks for replying. Sorry about the delay - meetings!

I have only just taken over this project so I'm still familiarising myself.

As far as I can tell neither variant of newlib is used.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#3

Hi David,

Then where does newlib come from?

Best regards.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#4

Sorry, I meant where does snprintf come from?

Revision history for this message
David Regan (david-regan) said :
#5

Hi Thomas

I'm using Eclipse as the IDE. I did a file search for newlib and found lots of tests of newlib type defines in FreeRTOS but none
in the application code.

Looking at the map file there are numerous references to snprintf of the type

 .text.snprintf
                0x0804e2c8 0x68 c:/program files (x86)/gnu tools arm embedded/4.8 2014q3/bin/../lib/gcc/arm-none-eabi/4.8.4/../../../../arm-none-eabi/lib/armv7e-m/fpu\libg.a(lib_a-snprintf.o)
                0x0804e2c8 snprintf

I can find no matches of newlib in this map file.

This led me to believe that newlib is not used and it is just compiled against the gnu arm package.

Revision history for this message
Xinxin19871118 (755087287-qq) said :
#6
Revision history for this message
David Regan (david-regan) said :
#7

Thanks Xinxin19871118, that solved my question.

Revision history for this message
David Regan (david-regan) said :
#8

Sorry.

XinXin your message was blank.

I clicked on the This Solved my problem button as if it was a link to your answer.

Sorry

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#9

David, libg means it's newlib. It would be libg_nano.a if it was newlib-nano. Is your question really solved? I cannot see any text in Xinxin's post.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#10

Hi David,

Can you show an equivalent code of what is happening? From your description I understand that both snprintf write to the same buffer, can you show us how?

Best regards.

Revision history for this message
David Regan (david-regan) said :
#11

Hi Thomas

No it is not solved I clicked on the 'This solved my problem' button when I saw the blank message thinking it woull lead me to
XinXin's answer.

Sorry I'm new to this.

Revision history for this message
David Regan (david-regan) said :
#12

Hi Thomas

The tasks write to separate buffers

in graphicsTasks I have :-

        char ratioVal[RATIO_STRLEN];
        snprintf(ratioVal, RATIO_STRLEN, "R:%05.2f", data->freq[uFreqBin].ratio);

in usbTask I have :-

static char usbStreamOutputString[USB_STREAM_STRING_LEN];
...

          for (i = 0; i < NUMBER_OF_FREQUENCIES; i++)
          {

             Frequency* frequency = &data->freq[i];
            uint32_t len = snprintf(usbStreamOutputString, USB_STREAM_STRING_LEN,
                    "%c" //0
                    "f"
                    ",%d"//1
                    ",%07.2f"//2
                    ",%07.2f"//3
                    ",%07.2f"//4
                    ",%07.2f"//5
                    ",%07.2f"//6
                    ",%u"//7
                    ",%07.2f"//8
                    ",%03d"//9
                    ",%07.2f"//10
                    ",%07.2f"//11
                    ",%03d"//12
                    ",%03d"//13
                    ",%01d"//14
                    ",%01d"//15
                    ",%01d"//16
                    ",%03d"//17
                    ",%01d"//18
                    "%c"//19
                    "\r\n",
                    (unsigned char)0x02, //0
                    (unsigned int)i, //1
                    frequency->SSIValue,//2
                    frequency->antenna[0].magnitude,//3
                    frequency->antenna[0].angle,//4
                    frequency->antenna[1].magnitude,//5
                    frequency->antenna[1].angle,//6
                    (unsigned int)frequency->fieldDistortion,//7
                    frequency->measuredDepth.measuredDepth,//8
                    (unsigned int)frequency->measuredDepth.quality,//9
                    frequency->ratio,//10
                    frequency->current.current,//11
                    (unsigned int)frequency->current.quality,//12
                    (unsigned int)frequency->detectionConfidence,//13
                    (unsigned int)frequency->freqDetected,//14
                    (unsigned int)frequency->strikeDetected,//15
                    (unsigned int)frequency->transmitterSetting.transmitterQuality,//16
                    (unsigned int)frequency->transmitterSetting.transmitterType,//17
                    (unsigned int)frequency->detectedServicetype,//18
                    (unsigned char)0x03); //19

/*

            uint32_t len = snprintf(usbStreamOutputString, USB_STREAM_STRING_LEN, "%c" //0
                    "f"
                    ",%d"//1
                    ",%07.2f"//2
                    ",%07.2f"//3
                    ",%07.2f"//4
                    ",%07.2f"//5
                    ",%07.2f"//6
                    ",%u"//7
                    ",%07.2f"//8
                    ",%03d"//9
                    ",%07.2f"//10
                    ",%07.2f"//11
                    ",%03d"//12
                    ",%03d"//13
                    ",%d"//14
                    ",%d"//15
                    ",%01d"//16
                    ",%03d"//17
                    ",%0d"//18
                    "%c"//19
                    "\r\n", (unsigned char)0x02, //0
                (unsigned int)i, //1
                123.45f, //2 frequency->SSIValue,
                337.8f, //3 frequency->antenna[0].magnitude,
                22.89f, //4 frequency->antenna[0].angle,
                554.21f, //5 frequency->antenna[1].magnitude,
                76.8f, //6 frequency->antenna[1].angle,
                1, //7 (unsigned int)frequency->fieldDistortion,
                114.7f, //8 frequency->measuredDepth.measuredDepth,
                229, //9 (unsigned int)frequency->measuredDepth.quality,
                207.5f, //10 frequency->ratio,
                34.9f, //11 frequency->current.current,
                76, //12 (unsigned int)frequency->current.quality,
                208, //13 (unsigned int)frequency->detectionConfidence,
                1, //14 (unsigned int)frequency->freqDetected,
                0, //15 (unsigned int)frequency->strikeDetected,
                222, //16 (unsigned int)frequency->transmitterSetting.transmitterQuality,
                4, //17 (unsigned int)frequency->transmitterSetting.transmitterType,
                0, //18 (unsigned int)frequency->detectedServicetype,
                (unsigned char)0x03); //19

*/
            uint32_t checkLen = strlen(usbStreamOutputString);

            if (checkLen != 97)
            {
              len += 1;
              len -= 1;
              bUSBBadLength = true;
            }

 I provided the checkLen test to break on so that I could examine the buffers.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#13

Hi David,

Where is the null embedded? Is it always at the same place in usbStreamOutputString? The printf family of function, especially when processing a float, can be quite time consuming and often cause a difference of behavior in concurrent code. Looking at the code snprintf in newlib uses a reentrant snprintf_r so I think newlib shouldn't be in cause here. Of course there could be a bug but I would look first in whether it could be a concurrency bug.

Best regards.

Revision history for this message
David Regan (david-regan) said :
#14

Hi Thomas

The null position is random.

Thanks for your time. It's much appreciated.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#15

Hi David,

Are the values after the null correct or random? If the value tend to stay the same, first zero the array to make sure it's not correct because you still have the old values.

Best regards,

Thomas

Revision history for this message
David Regan (david-regan) said :
#16

Hi Thomas

If you look back at the extracts I sent I tested it with fixed data.
Therefore the buffer should have been consistent but when
the fault occurred different values preceded the null for 2 or 3 bytes.

Revision history for this message
Launchpad Janitor (janitor) said :
#17

This question was expired because it remained in the 'Open' state without activity for the last 15 days.

Revision history for this message
David Regan (david-regan) said :
#18

This question has not been answered.

I have implemented a mutex version of snprintf to overcome the situation but it would appear that
snprintf is not re-entrant.

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#19

Hi David,

If that is indeed the case would you mind reporting it to newlib community on its mailing list [1]?

[1] https://sourceware.org/newlib/mailing.html

Best regards.

Can you help with this problem?

Provide an answer of your own, or ask David Regan for more information if necessary.

To post a message you must log in.