USB CDC project scanf() redirection fails

Sysprogs forums Forums VisualGDB USB CDC project scanf() redirection fails

Tagged: , ,

Viewing 8 posts - 1 through 8 (of 8 total)
  • Author
    Posts
  • #21644
    Vlad
    Participant

    Hi, I got USB CDC tutorial working fine up until a point of a full system IO redirection to include scanf(). I have my STM32F407 redirecting printf() to PC without semihosting. But after I made changes to redirect all “read” and “write” functions and then run it – terminal runs at full speed non-stop the same printout:

    Enter a number: 0 = 0x0

    ……………………………………….

    Enter a number: 0 = 0x0

    making the terminal locked out.  Apparently scanf() returns without waiting for input. Debugging is impossible because when debug session stops at the breakpoint, there is no terminal communication with target whence no input possible and that means USB CDC firmware does not even start -nothing to debug. Again printf() works fine but can somebody suggest what is wrong with “read” in scanf()  ? Is the code provided in that tutorial enough for the CDC OUT communication ?  I don’t understand how is it possible to run a debugger (F5) as the tutorial suggests, and still use terminal for communication with target when it is stopped at break?

    Please help

    #21651
    support
    Keymaster

    Hi,

    It is hard to say why scanf() would not work with USB, but generally stepping through the _read() function shown in the tutorial should help.

    Debugging the USB devices is a bit tricker than the regular embedded code, but is still totally possible. Windows will consider your device “not responding” if it fails to respond to USB requests within a certain time-out (usually in hundreds of milliseconds). Stopping at a breakpoint to examine the program state would trigger this, however adding and removing breakpoints while the program is running would not (it involves a very short stop that VisualGDB handles automatically).

    Hence we would advise starting the debug session without breakpoints, getting to the point where the code loops indefinitely and then setting a breakpoint and stepping through it. Stepping through the code will likely time out the USB communication, but it should provide sufficient clues about the internal state of the USB-related logic. Once it breaks, simply disable/remove your breakpoints, restart the device, wait for Windows to discover it and set the breakpoint(s) again.

    #21751
    Vlad
    Participant

    Hello, – why am I not getting replies to my posts in email? I don’t see a possibility of setup email in this forum…
    Anyways, thank you for the advice how to debug USB code. I did this and here are my findings:
    scanf() never calls redefined _read()
    in fact _read() is indeed redefined in code and I did verified this fact but scanf() goes running without calling it.
    I put breakpoint in _read() – it never triggered . Breakpoint put in redefined _write() indeed gets called when doing printf().
    I did yet another test: I replaced scanf() with the following

    char val;   _read(NULL, &val, sizeof(val));

    this changed code works: waits for input and shows each character printed in dec and hex. Again – scanf() does not call any of redefined functions (neither those original) . Stepping into scanf() and following all commands is daunting task – steps forever  into many functions inside scanf() never coming to _read()….

    I see two probabilities here making my run different from yours: either you are not using newlib-nano “C” library  or, if you do use newlib-nano, then some of your compilation flags are different from mine… for ex. my setup uses _STDC_ and _HAVE_STDC  flags, do you ?  In other words the “C” library string input functions (probably all input formatting functions) in my setup don’t use _read() and therefore they are not overwritten. Also another flag – I did not choose semihosting option for obvious reasons – I don’t use semihosting…. What other flags could be different and what function should be overwritten additionally to those provided in your code?

    Can you please verify this?

     

    • This reply was modified 5 years, 7 months ago by Vlad.
    #21753
    Vlad
    Participant

    Interesting thoughts brought about from stepping through inside scanf() function…

    This function implementation is a huge bulk of the code, most of it has nothing to do with formatting or data transfer but rather with multitude of macros and #ifdef’s trying to reconcile different versions of “C”… (and “C++” just adds another layer on the top), making the code practically non-readable unless you dedicate many hours or days on just digging scanf() alone 🙂 And this makes setup compilation flags are kind of high art….

    I wonder how much benefits programmers gain from introducing one more layer of compiler’s complexity when they choose “C++” compiler (to the weel overloaded versions of “C”)? Even considering the benefits of the OOP … Notice I am not even taking into consideration the additional complexity of correctly using “C++” to eliminate many bugs programmers get from messing with C++ peculiarities, I am only talking about possibility of messing with compilation flags and compiler-related code peculiarities while using both C and C++ ….

    I doubt there are so many benefits to justify so many more chances to mess up… this is yet one more example how we, trying to make our lives easier, make it much harder instead 🙂

    • This reply was modified 5 years, 7 months ago by Vlad.
    #21766
    support
    Keymaster

    Hi,

    Could it be that your implementation of _read() was not declared with extern “C” and hence would not replace the original one? We have done a quick test with newlib-nano and scanf() did call the redefined _read(). If nothing helps, please try checking if your version of _read() is actually compiled in (e.g. set a breakpoint in _read by function name and see whether it resolves to your function).

    Regarding email notifications, unfortunately we had to disable it some email servers classified those notifications as spam and prevented the delivery of regular emails from our domain as well.

    With C++, there are a few cases where it could be beneficial to embedded projects, (e.g. using RAII to avoid “forgot to call release() before return” errors, or using templates as a more IntelliSense- and debugger-friendly alternative to bulky plain C macros), although whether it pays off depends on the project size, style and many other factors. VisualGDB is designed to support both Plain C and C++ projects, so it should provide comfortable development experience for both languages.

    #21802
    Vlad
    Participant

    Thank you for reply.

    As I reported, when I replace scanf() with _read() , it calls my new version of _read() so it is compiled in (it cannot be that with the same compilation flags  one function calls one version of _read and another function would call another version…)   apparently _read()  is not called by scanf() at all …  but I could see from stepping through scanf() that it goes through different functions depending on the flags…. this I believe is different between our environments.

    #21807
    Vlad
    Participant

    Can you show your compiler properties entry with all C/C++ compiler flags ? What file is it in ?

    For ex. I am curious to see “_STDC_” and similar flags related to the version of “C”… those define the actual flow of scanf() …

    #21808
    support
    Keymaster

    Hi,

    Our newlib-nano build (gcc-arm-none-eabi-6-2017-q2-update) is configured with the following flags:

    ../newlib/configure --target=arm-eabi --prefix=/q/gnu/newlib-nano/newlib-nano-install --disable-newlib-supplied-syscalls --enable-newlib-reent-small --disable-newlib-fvwrite-in-streamio --disable-newlib-fseek-optimization --disable-newlib-wide-orient --enable-newlib-nano-malloc --disable-newlib-unbuf-stream-opt --enable-lite-exit --enable-newlib-global-atexit --disable-nls --enable-newlib-nano-formatted-io

    The configuration process creates many header files with generated flags, so it’s hard to give a definitive answer. If you would like to look deeper into it, please consider building it from sources on your side and verify which version of the code is built by injecting #error directives into the source files.

    Another (easier) option would be to try setting breakpoints in other functions that should be called (see the call stack below) until you pinpoint the one that is not called:

    > _read() C++ (gdb)
     _read_r() C++ (gdb)
     __sread() C++ (gdb)
     __srefill_r() C++ (gdb)
     __svfscanf_r() C++ (gdb)
     scanf() C++ (gdb)

    Here’s the program we tested it with:

    extern "C" int _read()
    {
     asm("bkpt 255");
    }
    
    int main(void)
    {
     int x;
     scanf("%x", &x);
    }
Viewing 8 posts - 1 through 8 (of 8 total)
  • You must be logged in to reply to this topic.