Remote cross debugging: Windows to Raspberry-Pi

In the past few days I’ve spent quite a lot of time on my new shining Raspberry-Pi. It’s an amazing little devil, worth every Euro I spent! Since I’ve planned to deploy a software component of my augmented reality system on this board, I needed to figure out how to remotely debug it in order to troubleshoot any issue I would run into. It took me some time, but thanks to the friends on Raspberry Pi Forum and to this awesome tutorial I finally made it: I can deploy my software to the raspberry-pi from Eclipse CDT and debug from my comfortable Windows 7 machine.

Step 1. The first thing you need to do is to setup a working Cygwin environment on your machine. I setup mine by following the advices from IanLinsdell and CopperPhil on the Raspberry Pi forum. Simply copy Cygwin installer to a folder (es. c:\cygwin) and execute it. Be sure to install the python package, the shell and the make tool.
Step 2. The next thing you need to do is to install the required cross-compilation toolchain. That is needed since we are on Windows and we need to compile and debug a software which will run on an ARM machine (our Raspberry Pi). Instead of building your own toolchain, you can download the ones built by a Raspberry Pi Forum user, IanLinsdell, available from his github account here. Since we want to cross-debug, download the hardfp version. Unpack the archive in c:/cygwin/opt/cross/x-tools so that the full path will look like this one c:/cygwin/opt/cross/x-tools/arm-unknown-linux-gnueabi.

Step 3. In order to upload the binaries we will compile to the Raspberry Pi, we need to enable an SSH server on the board. If you installed the Raspbian image, this operation is as easy as running the raspi-config command and selecting the ssh row. Once the server is running, you can use your username and password (default pi/raspberry) to access the Raspberry Pi from your Windows machine, using an SSH terminal.

Step 4. Download and install PUTTY (the full package), which is needed to talk to our Raspberry Pi using an SSH terminal. In order to upload our binaries to the Raspberry Pi and start the debugger, I thought of a little hack: I created a script which run as a post-build step in Eclipse (i.e. gets executed when the compilation successfully produces an artifact/binary) and uploads the file on the board using an SSH connection and executes a few commands after that (chmod and gdbserver). The following is the content of the BAT file, which you can save in a file and path of your choice. In my case I saved it to C:/Users/Dexter/Desktop/RaspberryPi/deploy_debug.bat, but any path will do.

@echo off

rem Check for a valid filename
IF "%1"=="" (
	ECHO You have to provide a valid source file.
	EXIT /b
)

IF "%2"=="" (
	ECHO You have to provide a valid destination path.
	EXIT /b
)

SET PUTTYSCP_BIN="C:\Program Files (x86)\PuTTY\pscp.exe"
SET PUTTY_BIN="C:\Program Files (x86)\PuTTY\putty.exe"
SET RASPBERRYPI_ADDR=192.168.0.123
SET USERNAME=pi
SET PASSWORD=raspberry
SET CMD_FILENAME=commands.sh

rem Upload the file to raspberry pi
%PUTTYSCP_BIN% -pw %PASSWORD% "%1" %USERNAME%@%RASPBERRYPI_ADDR%:"%2"

rem Build a list of actions to do on the pi (chmod, execute GDB server)
if exist %~dp0%CMD_FILENAME% del %~dp0%CMD_FILENAME%
rem echo rm "%2" >> %~dp0%CMD_FILENAME%
echo chmod +x "%2" >> %~dp0%CMD_FILENAME%
echo gdbserver :3785 "%2" >> %~dp0%CMD_FILENAME%

rem Execute the action list on the raspberry pi
%PUTTY_BIN% -pw %PASSWORD% -m %~dp0%CMD_FILENAME% %USERNAME%@%RASPBERRYPI_ADDR%

exit /b %ERRORLEVEL%

After saving the file, the following settings needs to be tweaked:

  • PUTTYSCP_BIN – The full path to pscp.exe, part of the Putty bundle, used to securely copy files over a SSH connection.
  • PUTTY_BIN – The full path to putty.exe, part of the Putty bundle, used to execute commands on a remote host over a SSH connection.
  • RASPBERRYPI_ADDR – The IP address of the Raspberry Pi
  • USERNAME – The username of the user who will authenticate on the Raspberry Pi
  • PASSWORD – The password :-)
  • CMD_FILENAME – The name of the file which will contains all the commands to be executed on the Raspberry Pi. You don’t want to change that, it’s not really nedded.

Step 5. Download and install Eclipse IDE for C/C++ Developers (CDT). Create a sample “Hello World” project, let’s say its name is “RaspHelloWorld”. Open the project properties by right clicking on the project name inside the Project Explorer. Configure the project as follows.

C/C++ Build -> Environment

  • Add a variable named CYGWIN with value nodosfilewarning.
  • Append c:\cygwin\opt\cross\x-tools\arm-unknown-linux-gnueabi\bin;c:\cygwin\bin; to the beginning of the PATH variable.

C/C++ Build -> Tool Chain Editor

  • Select Cygwin GCC from Current toolchain dropbox.

C/C++ Build -> Settings -> Tool Settings

  • Set the Command field in Cygwin C++ Compiler to arm-unknown-linux-gnueabi-g++.
  • Add an include path in Includes which points to c:\cygwin\opt\cross\x-tools\arm-unknown-linux-gnueabi\arm-unknown-linux-gnueabi\sysroot\usr\include.
  • Set the Command field in Cygwin C Compiler to arm-unknown-linux-gnueabi-g++.
  • Add an include path in Includes which points to c:\cygwin\opt\cross\x-tools\arm-unknown-linux-gnueabi\arm-unknown-linux-gnueabi\sysroot\usr\include.
  • Set the Command field in Cygwin C++ Linker to arm-unknown-linux-gnueabi-g++.
  • Add a library search path in Libraries which points to c:\cygwin\opt\cross\x-tools\arm-unknown-linux-gnueabi\arm-unknown-linux-gnueabi\sysroot\usr\lib.

C/C++ Build -> Settings -> Build Steps

  • Set the Command field in Post-build steps to C:/Users/Dexter/Desktop/RaspberryPi/deploy_debug.bat $(OutDir)${ProjName}.a “/home/pi/projects/${ProjName}.a”.
  • Add a brief description inside the Description field.

C/C++ Build -> Settings -> Build Artifact

  • Set Artifact Extension to .a

Step 6. Compile your sample project. If everything went smoothly, your Console output in Eclipse should also print something about the binary uploading and command execution. Now it’s time to setup a Debug Configuration. Select Run -> Debug configurations from the top menu. Right click on C/C++ Remote Application and choose New to add a new configuration.

Main tab

  • Set the C/C++ Application field to the name of the produced binary (in my case to Debug/RaspHelloWorld.a).
  • Select Disable auto build.
  • Click on Select other…, near the left of the Apply button. From the newly opened window, choose GDB (DSF) Manual Remote Debugging Launcher and click ok.

Debugger tab

  • Set the GDB debugger field to the path of the gdb which is part of our downloaded tool chain, that is to say c:\cygwin\opt\cross\x-tools\arm-unknown-linux-gnueabi\bin\arm-unknown-linux-gnueabi-gdb.exe.
  • In the Connection sub tab, set the Type to TCP, the Host name or IP address field to the IP address of the Rapsberry Pi (the same you specified in the BAT settings) and Port number to 3785.

Click on Apply and then Debug. The debugger should start and Eclipse should switch to the Debug Perspective. We made it ;-) If you have any issue and it doesn’t work as expected, feel free to post a comment below!

Update 01/11/2012: As suggested in the comments (thanks Ivo), disabling the “Automate discovery of paths and symbols” option for the C and/or C++ compiler could fix the problem with missing cout or endl. This option can be found in the project properties “c/c++ Build/Discovery Option”. Once disable, click on the “Clear” button to remove all the unwanted include paths introduced into the project.

  • Niccolò

    Thanks for your reply.
    Yes, i did it! I appended this to my “PATH” var: “;c:cygwinoptcrossx-toolsarm-unknown-linux-gnueabibin;c:cygwinbin”.

  • Niccolò

    Sorry for the double reply, but I managed to compile the project, but now I get another error:
    “Error in final launch sequence
    Failed to execute MI command:
    -target-select remote 192.168.0.111:3785
    Error message from debugger back end:
    192.168.0.111:3785: Connection timed out.
    192.168.0.111:3785: Connection timed out.”
    The problem is that he can send the file with puTTY with that ip! Why?

  • http://www.a2p.it/ Alessio Placitelli

    How did you manage to get it compiled? Could you share the solution to your previous problem with the community?

    As for the timeout, you should check if you can manually log in to the RPi using Putty. If you cannot, then the problem could be network related.

  • Niccolò

    Yes, I appended the value to the system var PATH, not to the eclipse one, I don’t know why, but if I add only to eclipse PATH var it don’t work.

    Yes, I created a batch to connect manually to my rpi, and the connection start with no problem. I turned off windows firewall, but nothing change.

  • http://www.a2p.it/ Alessio Placitelli

    Try to manually use the PSCP command in order to copy files from your machine to the RPi. Check if there’s any additional error output

  • Niccolò

    Ok, I have to compile the project every time before debugging also if I modify nothing.
    And also, if I modify nothing, I have to clean the project before start compiling becouse otherwise he don’t open the gdbserver connection.

  • http://www.a2p.it/ Alessio Placitelli

    Yes, that happens because the “debug script” is used in a post-build action in Eclipse. I still have to find a way to work around this issue.

  • Niccolò

    Ah ok, no problem. And also the PuTTY console remains open so I have to cancel the build, but because it is a post build he have already created the .a file and I can debug my app.

  • Bjørn Spockeli

    Hi, great tutorial, but im struggeling to get the bash script to work properly. Somehow it cant find the binary file, even though its there. Has anyone had the same problem?

    18:46:43 **** Build of configuration Debug for project TestCpp ****

    make pre-build main-build

    C:/cygwin/deploy_debug.bat TestCpp.a “/home/pi/projects/TestCpp.a”

    cygwin warning:

    MS-DOS style path detected: C:UsersBjxC3xB8rnworkspaceTestCppDebug

    Preferred POSIX equivalent is: /cygdrive/c/Users/BjxC3xB8rn/workspace/TestCpp/Debug

    CYGWIN environment variable option “nodosfilewarning” turns off this warning.

    Consult the user’s guide for more details about POSIX paths:

    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames

    TestCpp.a: No such file or directory

    Building file: ../test.c

    Invoking: Cygwin C Compiler

    arm-unknown-linux-gnueabi-g++ -I”c:cygwinoptcrossx-toolsarm-unknown-linux-gnueabiarm-unknown-linux-gnueabisysrootusrinclude” -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF”test.d” -MT”test.d” -o “test.o” “../test.c”

    Finished building: ../test.c

    Building target: TestCpp..a

    Invoking: Cygwin C++ Linker

    arm-unknown-linux-gnueabi-g++ -L”c:cygwinoptcrossx-toolsarm-unknown-linux-gnueabiarm-unknown-linux-gnueabisysrootusrlib” -o “TestCpp..a” ./test.o

    Finished building target: TestCpp..a

  • http://www.a2p.it/ Alessio Placitelli

    Judging by your output, it looks like the output the compiler is producing has a different name than the one you are using to launch the debug process:

    -> TestCpp.a: No such file or directory

    (only one point before the suffix “a”).

    -> Building target: TestCpp..a

    (there are two “..” points before the suffix “a”)

  • Bjørn Spockeli

    Could that have something to do with the artifact extension? Should it be .a or just a? I have tried both, but I still get the same error
    -> TestCpp.a: No such file or directory

  • http://www.a2p.it/ Alessio Placitelli

    Can you paste the output of a full rebuild? (Clean + Build)

  • Bjørn Spockeli

    19:38:26 **** Build of configuration Debug for project TestCpp ****

    make pre-build main-build

    C:/cygwin/deploy_debug.bat TestCpp.a “/home/pi/projects/TestCpp.a”

    cygwin warning:

    MS-DOS style path detected: C:UsersBjornworkspaceTestCppDebug

    Preferred POSIX equivalent is: /cygdrive/c/Users/Bjorn/workspace/TestCpp/Debug

    CYGWIN environment variable option “nodosfilewarning” turns off this warning.

    Consult the user’s guide for more details about POSIX paths:

    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames

    TestCpp.a: No such file or directory

    Building file: ../test.c

    Invoking: Cygwin C Compiler

    arm-unknown-linux-gnueabi-g++ -I”c:cygwinoptcrossx-toolsarm-unknown-linux-gnueabiarm-unknown-linux-gnueabisysrootusrinclude” -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF”test.d” -MT”test.d” -o “test.o” “../test.c”

    Finished building: ../test.c

    Building target: TestCpp.a

    Invoking: Cygwin C++ Linker

    arm-unknown-linux-gnueabi-g++ -L”c:cygwinoptcrossx-toolsarm-unknown-linux-gnueabiarm-unknown-linux-gnueabisysrootusrlib” -o “TestCpp.a” ./test.o

    Finished building target: TestCpp.a

    19:38:28 Build Finished (took 1s.986ms)

  • http://www.a2p.it/ Alessio Placitelli

    It looks like you configured the script to be run in the Pre-Build step instead of the Post-Build step, so it looks for the executable before it actually gets built!

    Double check Step 5 of the guide or simply cut the content of the Pre-Build field in Eclipse and paste it into the Post-Build field ;)

  • Bjørn Spockeli

    Copy-paste did the trick!
    Thank you!

  • Bjørn Spockeli

    Hi Alessio, im having trouble with step 6. When try to run the debugger I get the following error:

    Could not determine GDB version after sending: c:cygwinoptcrossx-toolsarm-unknown-linux-gnueabibinarm-unknown-linux-gnueabi-gdb.exe –version

    Any Ideas?

  • http://www.a2p.it/ Alessio Placitelli

    I’m sorry, but I have no idea. I quick search on the Internet determined it could be somehow related to the length of the PATH variable …

  • mimin

    In thsi step C/C++ Build -> Settings -> Build Artifact the ending must be just simple a not .a , it didnt work for me until I changed the ending from .a to a

  • JohnW

    Also getting the Error 127 from Make. It’s also complaining that arm-unknown-linux-gnueabi-g++ can’t be found in ‘PATH’. Have checked the Eclipse PATH and Windows PATH and Path variables and all include the Cygwin64…bin folder where the arm-unknown-Linux-gnu*.exe files reside. If I try to run the compiler (or any of the .exe’s for that matter) from a command window it tells me that the program was unable to start correctly. Trying it through the ‘Compatibility Checker’ comes back with XP SP2 settings but I still get the same result. I’m running Win7 64, could this be where the problem lies?

  • Guest

    Hi,
    I have also same problem as Niccolò. But I didnt solve yet. I followed all of the steps correctly, I can establish connection to RPi over SSH and I can copy files from my PC to RPi with pscp manually. When I start debugger I get this error after 15 second:

    Error in final launch sequence
    Failed to execute MI command:
    -target-select remote 192.168.137.2:3785
    Error message from debugger back end:
    192.168.137.2:3785: Connection timed out.
    192.168.137.2:3785: Connection timed out.

    I copy the outpur file RPiHello.a manually (pscp) to RPi and try to launch it but RPi gives an another error: Segmentation fault.

    Does anybody have an idea? How can I solve this?

  • mimin

    Hi,
    I have also same problem as Niccolò. But I didnt solve yet. I followed all of the steps correctly, I can establish connection to RPi over SSH and I can copy files from my PC to RPi with pscp manually. When I start debugger I get this error after 15 second:

    Error in final launch sequence
    Failed to execute MI command:
    -target-select remote 192.168.137.2:3785
    Error message from debugger back end:
    192.168.137.2:3785: Connection timed out.
    192.168.137.2:3785: Connection timed out.

    I copy the outpur file RPiHello.a manually (pscp) to RPi and try to launch it but RPi gives an another error: Segmentation fault.

    Do you have an idea? How can I solve this?

  • http://www.a2p.it/ Alessio Placitelli

    The segmentation fault error might be the real problem: is the software you are trying a simple hello world? If it is any more complex than it, you could try compiling a simple hello world software.

    You could also try to manually use gdb to check where the software is precisely failing.

  • mimin

    Thanks for reply. Yes, it is a simple Hello World example. It should write “Hello RPi.n” on the console. I launched it with gdb on RPi and as soon as I typed run, gdb says “Program received signal SIGSEGV, Segmentation fault.” and backtrace gives:

    #0 0×00000000 in ?? ()
    #1 0×00000000 in ?? ()

    I have really no idea :(

  • http://www.a2p.it/ Alessio Placitelli

    Sorry but I have no clue :( If you find a solution please post it here as well!

  • mimin

    I finally found something. I compiled the source file manually without any options and copied it to RPi and it worked. I found the option “-shared” in the linker settings, I removed it and it is also working with eclipse :), I am not getting the error I mentioned in the first post anymore.

  • http://www.a2p.it/ Alessio Placitelli

    Thank you very much for sharing this with the community :)

  • mimin

    One question, How can I launch the gdbserver remotely? I am running it manually each time. In the .bat file there is a command echo gdbserver :3785 “%2″ >> %~dp0%CMD_FILENAME%, but it does not launch the gdbserver on RPi, How can I do that? What shall I put in the commands.sh file?

    Thanks.

  • http://www.a2p.it/ Alessio Placitelli

    The echo lines are used to generate a file containing the list of commands to execute on the RPi. This command is then executed on the RPi by issuing the putty -m (see http://the.earth.li/~sgtatham/putty/0.54/htmldoc/Chapter3.html#3.7 )

  • mimin

    Ok, thank you for reply, I am now able to launch it remotely, but it is working only after building the project. If I stop the debugging I have to build the project again in order to start gdbserver again. Is it possible to run the gdbserver before pressing Debug button? Is there any “pre-debug” settings or so like “post-build”? In my eclipse (Eclipse CDT Kepler) there is no such a settings.
    Thanks.

  • http://www.a2p.it/ Alessio Placitelli

    Unfortunately I’ve not found a way to solve this problem yet. Everyone is experiencing the same issue :(

  • Bjorn Spockeli

    Hi, im trying to use libcurl in one of my Raspberry projects. im including the curl header and library from cygwin, but i get undefined references to almost all curl functions. Have you come across this problem?

  • http://www.a2p.it/ Alessio Placitelli

    So you actually get linking errors? Not compilation errors? What’s the command line used to compile and link your project?

  • Pingback: Solving the Error 127 from Make when cross compiling to raspberry pi « dev_religion

  • Vickramjeet Singh

    Hello,
    I am fighting with following error. please help.

    /opt/cross/x-tools/arm-unknown-linux-gnueabi/lib/gcc/arm-unknown-linux-gnueabi/4.6.3/../../../../arm-unknown-linux-gnueabi/bin/ld: cannot find -lc:/cygwin/opt/cross/x-tools/arm-unknown-linux-gnueabi/arm-unknown-linux-gnueabi/sysroot/usr/lib/

  • http://www.a2p.it/ Alessio Placitelli

    Please double check Step 5: did you configure your environment exactly as specified by that step? Which version of Eclipse are you using?

  • NOOB

    **** Build of configuration Debug for project Test4 ****

    **** WARNING: The “Debug” Configuration may not build ****

    **** because it uses the “Cygwin GCC” ****

    **** tool-chain that is unsupported on this system. ****

    **** Attempting to build… ****

    make all

    Building file: ../src/Test4.cpp

    Invoking: Cygwin C++ Compiler

    arm-unknown-linux-gnueabi-g++ -I”C:cygwinoptarm-unknown-linux-gnueabi_hardfparm-unknown-linux-gnueabiarm-unknown-linux-gnueabiinclude” -I”D:WorkC++Test4error” -I”D:WorkC++Test4erroralsa” -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF”src/Test4.d” -MT”src/Test4.d” -o “src/Test4.o” “../src/Test4.cpp”

    cygwin warning:

    MS-DOS style path detected: D:/Work/C++/Test4/Debug

    Preferred POSIX equivalent is: /cygdrive/d/Work/C++/Test4/Debug

    CYGWIN environment variable option “nodosfilewarning” turns off this warning.

    Consult the user’s guide for more details about POSIX paths:

    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames

    Finished building: ../src/Test4.cpp

    Building target: Test4.a

    Invoking: Cygwin C++ Linker

    arm-unknown-linux-gnueabi-g++ -L”C:cygwinoptarm-unknown-linux-gnueabi_hardfparm-unknown-linux-gnueabiarm-unknown-linux-gnueabisysrootusrlib” -L”D:WorkC++Test4errorusrlibarm-linux-gnueabihf” -o “Test4.a” ./src/Test4.o -lasound

    Finished building target: Test4.a

    make –no-print-directory post-build

    src/Test4.d:1: *** multiple target patterns. Stop.

    makefile:42: recipe for target ‘Test4.a’ failed

    make: *** [Test4.a] Error 2

    **** Build Finished ****

    ******************************************************************************

    #include
    #include

    using namespace std;

    int main() {

    cout << " Hello n" << endl;

    return 0;

    }

    Iam trying to compile with asound and the compilation is aborted, the result is attached.If i comment the header inclusion its working.The path is set properly and the .a file is also created but the error comes and the download bat file is not called.
    If i remove the inclusion even with the library for sound configured the compilation works
    Any Idea ?

  • http://www.a2p.it/ Alessio Placitelli

    I’m not quite sure, but I don’t think you can use the ALSA sound library at all under cygwin. Can you compile your software on the RPi?

  • NOOB

    Ok here is what i did, i compiled the code and when the build broke, but there was still a Test4.a file renamed file to Test5 and then downloaded the Test5.a to RPI and it works.

    Added some code to check the number of sound cards it returned a 1 first and with a USB card added returned 2 !

    Still not sure why the build is breaking with warning.
    code below

    //#define SOUND_CHK 1

    #include

    #ifdef SOUND_CHK

    #include

    #endif

    using namespace std;

    #ifndef SOUND_CHK

    int main() {

    cout << " Hellooooo n" << endl;

    return 0;

    }

    #else

    int main() {

    int k;

    register int error;

    int CardNum,TotalCards;

    cout << " Test SoundCards " << endl;

    k=0;

    while(k < 5)

    {

    CardNum =-1;

    TotalCards=0;

    while(1)

    {

    if((error = snd_card_next(&CardNum)) < 0)

    {

    cout << "Can't get the next card number: " << error << endl;

    break;

    }

    // No more cards? ALSA sets "cardNum" to -1 if so

    if (CardNum < 0) break;

    // Another card found, so bump the count

    ++TotalCards;

    }

    cout << "ALSA found = " << TotalCards << endl;

    k++;

    }

    return 0;

    }

    #endif

  • http://www.a2p.it/ Alessio Placitelli

    That’s odd. I mean, if the compilation breaks you shouldn’t get the .a file. Am I missing something?