Kloudless Blog

Tutorials, case studies and how-tos from our experts

A primer on debugging Native Client code in Chrome

This isn’t your father’s your average client-side app.

Chromium NaCl
Native what?

Yesterday, I was faced with an unfamiliar 10k line C program that did custom image manipulation. It takes in two arguments: an input image file and a destination for the resulting output file.

This is straightforward to run on the server-side, but I don’t want to maintain varying compute capacity just for an infrequently run, on-demand image conversion script! Before you mention it, no, I am not about to rewrite the entire program in JavaScript, no matter how fun that sounds.

Enter Chromium Native Client, an “open-source technology for running native compiled code in the browser”. Combined with Pepper.JS, I can run binaries in a browser window! Before you bring out the pitchforks, consider that the C program is almost a perfect fit for this! As long as I write a little code to manage that pesky file I/O, it should be smooth sailing from here, right? Not exactly. Turns out the Pepper.JS docs weren’t kidding about getting your hands dirty.

Moving from JS on the backend to C on the frontend

Downloading the SDK and playing with the File I/O example to get a feel for it showed that simple file I/O operations such as fseek were failing and I had no clue why. Error messages were useless of course, as is often the case:

NaCl I/O Demo

Firing up Developer Tools in Chrome shows that I am faced with a binary .nexe blob to debug, as expectedxkcd. A little searching reveals a few ways to debug Native Client apps, but for the purpose of this post, I am going to detail what I found to be the most straightforward and fruitful approach.

What I would give for a breakpoint

Let’s start with the prerequisites. Instructions on how to set up the Native Client SDK will get you set up with a working environment to build NaCl modules in. I am focusing on NaCl I/O, and working in a Mac OS X environment with Chrome 31, which has PNaCl enabled by default.

First let’s start up our server. I’ve found that the documentation isn’t very clear regarding the various toolchains and config options, but here is the simplest way to get up and running:

This should start a http server at http://localhost:5103/ that you can navigate to and break, as I did above. Now we just need to get a grasp of what exactly broke, causing that vague error.

Enter GDB. GDB is by far the most powerful tool we have in our belt. If you are unfamiliar with how GDB works, I recommend glancing through an overview before proceeding further. As always, the first step is to modify our current Makefile to add debugging symbols to our executable:

We quit out of the server with Ctrl-C and run make serve CONFIG=Debug again.

The next step is to enable GDB debugging in Chome. Although the docs point you to running chrome off the command line with various flags, I’ve found that navigating to chrome:flags, enabling “Native Client GDB-based debugging” and re-launching Chrome is enough.

Navigating to  http://localhost:5103/ should now show the application waiting for us to connect via nacl-gdb.

NaCl I/O Demo

nacl-gdb is located in our SDK:

Recall we are using newlib from earlier. Architecture varies, but uname -a should be able to give you a clue as to what to choose. In my case I am going to run nacl_sdk/pepper_31/toolchain/mac_x86_newlib/bin/x86_64-nacl-gdb.

In addition, there are a couple of GDB commands we need to run first. We will put them all in a file, for easy repeated debugging:

  • nacl-manifest points to our NaCl application’s manifest file. I use a relative path since I will be running gdb from the demo application directory.
  • nacl-irt points to the Native Client Integrated Runtime. You can find it easily:

Just choose the one that corresponds to the version of Chrome you’re running. It’s usually the highest version listed.

  • target points to a TCP port, 4014, being listened on by Chrome that we connect to to debug.
  • Now we can invoke GDB. Make sure you first have the NaCl application server running in a separate terminal, and have navigated to localhost:5103. The order is important here. If all goes well, you should be presented with a prompt.

    Above, I set a breakpoint at line 197 in handlers.c, where an fopen is performed in my application code. I then continue so the process can keep running. Navigating back to the web page, we see that the application is running now:

    NaCl I/O Demo

    I am going to try out the http mount point. Entering in “http/index.html” and clicking fopen results in the break point being triggered.

    NaCl I/O Demo

    But wait! Trying to step in took us right past. Unfortunately, this appears to my untrained eye to be a kernel-level syscall, which is magically intercepted and handled by the SDK. But how do we get in there?

    Stepping through all the things

    The main application file, nacl_io_demo.c, contains headers pointing to a nacl_io library:

    That looks promising. The NaCl SDK source code is located at src/ under the root SDK directory. For me that would be ~/nacl_sdk/pepper_31/src/, with headers in include/. Opening nacl_io.cc reveals more clues, which eventually leads us to a file named suspiciously relevant to our hunt: mount_http.cc. This contains a MountHttp::Open method that appears like a great place to set a breakpoint. But how do we get there?

    Just like we did for our application, we need to include debugging symbols for the Native Client SDK. We modify the Makefile to add the same flags we did above:

    and then compile:

    With 4 am fast approaching, we delve back into our debugging process:

    • Kill GDB with Ctrl-C and then Ctrl-D, and quit.
    • Stop the application server, and start it back up again.
    • Refresh the web page to reload the application.
    • Start GDB back up again.

    This time, we set a break point in mount_http.cc:

    Clicking fopen() on the web page brings us to it:

    By typing in bt, I obtained a backtrace of how in the world we got here. This clears up some of the ambiguity about the black magic that makes NaCl apps work.

    From here on, it’s just a matter of stepping through the code, setting breakpoints and, god forbid, print statements if you prefer the back-to-basics approach, till you know enough to solve the problem. Be aware that unless you turn off the debugging flag at chrome:flags, your NaCl application will always wait for nacl-gdb to connect. So be sure to turn it off after you’re done debugging.

    In my case, the bug turned out to be in the SDK after all and I filed a bug report (here, if you’re curious). A workaround was easy enough to implement. More on what the hell I’m doing with my C script in a future blog post.

    So that was my first day with NaCl… mostly debugging, although I hope to eventually be able to port over legacy desktop apps to the browser for fun and profit.

    Does this kind of silliness interest you? Kloudless is hiring Front-End Engineers! If you know when to get your C++ JavaScript stirred rather than shaken, check out https://kloudless.com/jobs#frontend and shoot us your CoffeeScript/TypeScript/Dart preference at work@kloudless.com.

    Published By

    Vinod Chandru