Remote Development and Debugging of Rust with CLion
DEVELOPERS
March 10, 2019
Rust, being a relatively new language, is still on its path to gaining wide support by IDEs. Most in our team use CLion for Rust development which is especially great for local debugging, alas it is not free. Since we are developing a blockchain it requires careful orchestration of the nodes running on separate machines, and occasionally we need to debug some corner case on a remotely running node. CLion and other JetBrains products have great support of the remote development and debugging. However, unfortunately, Rust is not a primary language of CLion which makes configuration tricky. In this post we walk through the configuration of CLion for remote Rust development and debugging, our goal is to be able to:

Synchronize source code between local and remote machines, so that local modifications are automatically copied over;
Compile and run on two different architectures: local Mac OS and remote Ubuntu;
Interactively debug in local IDE the code running on the remote machine;
Remote machine setup
We will be using an AWS instance running an Ubuntu image as a remote machine. The following ports would need to be allowed: TCP 22 for ssh and TCP 2345 for gdbserver. Our team also likes using mosh because it tolerates interrupted connections and allows moving between WiFi hotspots, which needs UDP 60000–61000 ports.

After launching an AWS instance and connecting to it through ssh or mosh, run the following commands on it:

apt-get update && apt-get install -y curl gdb g++-multilib lib32stdc++6 libssl-dev libncurses5-dev
curl https://sh.rustup.rs -sSf | sh -s — -y — default-toolchain nightly
Local machine setup
If you do not have Rust, run the following command to install it:

curl https://sh.rustup.rs -sSf | sh -s — -y — default-toolchain nightly
You can download CLion from here, and upon its launch, you would also need to install the Rust plugin which can be found in Preferences→Plugins.

CLion deployment configuration
We will be using Actix repository as a debugging example, so you would need to download it from here: https://github.com/actix/actix and clone it locally, e.g. to /Users/<username>/Repos/actix, if you are on Mac OS. Open this project in CLion, but note that IDE might need time to update its indices and cache.

CLion deployment feature allows us to keep in sync the files on the local and the remote machines. It can also be used for a large number of other special cases used mostly for the web servers which we are going to omit in this post. We will use CLion deployment for copying the following files:

Source code from the local machine to the remote machine;
Debug symbols from the remote machine to the local machine.
In CLion open Preferences→Build, Execution, Deployment→Deployment. Click on + to add the new deployment configuration and fill it in:

Since we are using the standard AWS Ubuntu image, the default username is ubuntu. You would also need to go to the Mappings tab and add local to remote mappings of the project files, e.g.:
Local path: /Users/<username>/Repos/actix;
Deployment path: /home/ubuntu/Repos/actix.

If you already have more than one deployment configuration in CLion, you might want to right-click on the newly created configuration and select Set As Default.

Since we want to compile and run on two different architectures, we would need two folders for the build files. We will be using /Users/<username>/Repos/actix/target for the Mac OS build files, and /Users/<username>/Repos/actix/target_remote for the Linux build files. We want Linux build files to be synced to the local machine since we need the debug symbols, but we do not need to sync Mac OS build files to the remote machine. So go to Excluded Paths tab and add /Users/<username>/Repos/actix/target local path.

We also recommend going to Preferences→Build, Execution, Deployment→Deployment→Options and switching Upload changed files automatically to the default server to Always which will allow CLion to automatically copy locally modified source files to the remote machine.

Building on the remote machine
Before we can build the source on the remote machine, we need to copy it. Select the root folder in the Project tree (you might need to open it with View→Tool Windows→Project), in our case it is actix. Right click →Deployment→Upload to remote machine. Wait until the files are copied. Then SSH/Mosh into the remote machine and run:

cd /home/ubuntu/Repos/actix
cargo build — package actix — example ping — target-dir ./target_remote
gdbserver 0.0.0.0:2345 ./target_remote/debug/examples/ping
CLion does not automatically synchronize new files, it only does it with the changed files, so we need to manually download target_remote to the local machine. Navigate to Tools→Deployment→Browse Remote Host, which will open a window with the remote file system view. Locate /home/ubuntu/Repos/actix/target_remote, then right-click→Download from here.

Debug configuration
Now it is time to connect local CLion to the remote machine. Navigate to Run→Edit Configurations, click on + and select GDB Remote Debug. Configure it like this:

Start the debugging and observe that the execution stops at the breakpoints and we can observe the values of the variables.

Compiling on several architectures
Recall that we used target_remote folder for remote build files from Ubuntu. If we now execute the standard Rust build command from CLion, by e.g. clicking on the green arrow next to the main function or unit tests, CLion will use a different target folder for Mac OS build files.

Missing features
In general, JetBrains IDEs provide several ways of doing remote debugging for each language, but since Rust is not the primary language of CLion and requires a plug-in, remote debugging of Rust is currently limited to using gdbserver. It has several disadvantages from a UX perspective:

There is a need to copy symbol files. Build directory can get quite heavy, especially since cargo accumulates old build files until they are cleaned with a manual command;
Some interactive UI features of CLion do not work with gdbserver. For instance with local debugging one can click on a specific unit test and run it, with gdbserver that would require restarting it on the remote machine with special arguments;
Need to start gdbserver.
The solution to all this is using remote host toolchains that would allow the local IDE to access the remote cargo to build and run remote programs. This feature is available in PyCharm using “SSH Interpreter” and for C++ in CLion, but unfortunately it is not yet supported with Rust.

For those interested in Near Protocol: we build a sharded general purpose blockchain with a huge emphasis on usability. If you like our write-ups, follow us:

http://twitter.com/nearprotocol
https://discord.gg/nqAXT7h
https://github.com/nearprotocol/nearcore
 

https://upscri.be/633436/