-2

In reference to SO question: 52164135

The setup:
I have a function which converts many double values into a predefined string. The input is a array of struct from which we concatenate two double values into a string. A double is of size 8 bytes or 64 bits and my MCU of operation is STM32, a 32 bit ARM micro-controller.
An interrupt is also running parallelly.

The data should look like:

[[12.11111111,12.11111111],[12.22222222,12.22222222],...]

But I get (very rarely):

[[12.11111111,12.11111111],[55.01[12.33333333,12.33333333],...]

Note: I missed out [12.22222222,12.22222222]

sprintf is not re-entrant:
According to this discussion, on AVRFreaks, sprintf is not re-entrant. (The discussion is on using sprintf in a interrupt enabled hardware environment.) Which means if an interrupt occurs in-between a sprintf operation the stack fails to continue the operation it was doing.

Since my MCU is a 32 bit one, to perform a 64 bit operation it will take two clock cycles. And if we assume an interrupt occurred in between the sprintf operation according the the above discussion sprintf should fail.

Question
1. Will sprintf fail in case it is interrupted?

Here is the string function, an interrupt routine also runs in the background which deals with other sensor data (local and global)

/* @brief From the array of GPS structs we create a string of the format
 * [[lat,long],[lat,long],..]
 * @param   input   The input array of GPS structs
 * @param   output  The output string which will contain lat, long
 * @param   sz      Size left in the output buffer
 * @return  0       Successfully completed operation
 *          1       Failed / Error
 */
int get_gps60secString(GPS_periodic_t input[GPS_PERIODIC_ARRAY_SIZE], 
                       char *output, size_t sz) 
{
    int cnt = snprintf(output, sz, "[");
    if (cnt < 0 || cnt >= sz)
        return 1;
    output += cnt;
    sz -= cnt;

    int i = 0;
    for (i = 0; i < GPS_PERIODIC_ARRAY_SIZE; i++) {
        cnt = snprintf(output, sz, "[%0.8f,%0.8f]%s", 
                input[i].point.latitude, input[i].point.longitude, 
                i + 1 == GPS_PERIODIC_ARRAY_SIZE ? "" : ",");
        if (cnt < 0 || cnt >= sz)
            return 1;
        output += cnt;
        sz -= cnt;
    }

    cnt = snprintf(output, sz, "]");
    if (cnt < 0 || cnt >= sz)
        return 1;
    return 0; // no error
}

What's happening inside the interrupt routine

void GPS_InterruptHandler(UART_HandleTypeDef *UartHandle)
{
    gps_UART_RxInterrupt_Disable();
    GPS_t l_sGpsInfo;
    memset(&l_sGpsInfo,0,sizeof(GPS_t));
    status=Validate_get_gpsInfo((char*)g_gps_readBuff,&l_sGpsInfo,100);

    MEMS_interruptHandler(); //Inertial sensor ISR
    gps_UART_RxInterrupt_Enable();
}
clamentjohn
  • 3,417
  • 2
  • 18
  • 42
  • In the supplied example, `sprintf` obviously must fail (the last one, at least), because the signal handler ends the program. There is no reentrancy as far as I can see, at least not in the code snippet. The program is simply not done when it's halted. – tofro Sep 18 '18 at 14:28
  • @tofro The supplied example doesn't have a interrupt. I couldn't make a "Minimal, Complete, and Verifiable Example" from the hardware functions and this is just to give an idea of the working. I'll remove it as the error was never recreated in the C program. – clamentjohn Sep 18 '18 at 14:34
  • `sprintf` doesn't necessary need to fail when it is interrupted. Whether that happens depends very much on what you are doing in your interrupt service routine (or signal handler, for that matter). And without you telling us these details, it is impossible to answer your question. – tofro Sep 18 '18 at 14:41
  • `gps_UART_RxInterrupt_Disable(); ... gps_UART_RxInterrupt_Enable();` is a problem as at the end of the routine, the interrupt enable flag is not restored to its original value should it have been initially disabled. I'd expect `save enable state, disable, ..., restore state.` – chux - Reinstate Monica Sep 18 '18 at 16:03
  • It looks like you still have not understood what interrupts are, how to write the handers properly and how to debug you code yourself. Apparently the good advise given with previous questions was completely ignored. Once again: you cannopt learn embedded programming by letting others yo your job or asking questions about particular problems about badly designed code. I'd normally say "orry to tell", but that would be ignored as well. – too honest for this site Sep 19 '18 at 18:58
  • For other users: this is an obvious repost of https://stackoverflow.com/questions/52164135/stray-characters-seen-at-output-of-snprintf – too honest for this site Sep 19 '18 at 19:02
  • @toohonestforthissite I understand my mistakes and will try to improve in the future. In my defense, I had to work on a code base already written. But yes, I have to do most of the debugging and testing and not rush into an answer. Thank you. – clamentjohn Sep 20 '18 at 04:54
  • Why are you using stdio.h and floating point? Are you sure that your STM32 is actually a PC in disguise? In case of floating point "Since my MCU is a 32 bit one, to perform a 64 bit operation it will take two clock cycles" doesn't make much sense. Either you have a FPU that handles this (Cortex M4:ish or bigger), or you don't (M0 to M3), in which case you most likely shouldn't be using floating point in the first place. And if you use sprintf on a buffer shared with an ISR, your program design is completely broken. – Lundin Sep 20 '18 at 09:40
  • @Lundin It's a STM32F429 (with floating point unit). But still a 32 bit MCU, so to my understanding a 64 bit operation should take 2 cycles. – clamentjohn Sep 20 '18 at 09:46
  • @clmno Cycles and instructions are very different things, pretty much every instruction takes several cycles. That being said, re-entrancy of C variables has very little to do with how much data the CPU can read/write in a single instruction. That is still no guarantee of atomicity. See [this](https://stackoverflow.com/a/50236144/584518). – Lundin Sep 20 '18 at 10:45

1 Answers1

0

sprintf will ony fail during an interrupt if it is called again during that interrupt (assuming it uses global variables that are re-used; would it use only stack variables, then it is re-entrant).

So if your interrupt handler is calling sprintf and during that call a new, same or higher priority interrupt occurs then it can fail. However, during the processing of an interrupt, interrupts are normally disabled so there can't (shouldn't!) be another interupt of the same type occurring.

But why convert this raw data during interrupt handling? Why not store/pass this data to the user-level routine via a buffer and have that functionality convert the raw data? That would be consistent with the idea that an interrupt handler should be as short (fast) as possible.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
  • I'm not doing the string operation inside the interrupt routine. I'll modify the question to clear this confusion. – clamentjohn Sep 18 '18 at 14:42
  • As you've mentioned, if the sprintf is called within another interrupt routine it may fail. My scenario is similar - I have a user-level function which has sprintf and an interrupt in the background. My concern is the 64 bit data, 2 cycles, so technically an interrupt routine can occur in between those two cycles. – clamentjohn Sep 18 '18 at 16:20
  • As long as the interrupt does not modify the data that `sprintf` is working on, then everything is fine. An interrupt is completely "invisible" in user-level functionality (if an interrupt occurrs, the registers are saved and later restored) and can occurr between the two "cycles" without any impact. – Paul Ogilvie Sep 19 '18 at 17:21