--- # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. title: Introducing App Debugging author: katharine tags: - Freshly Baked --- Happy leap day! Today is a once-every-four-years day of bizarre date-related bugs, and thus an opportune moment for us to introduce our latest developer feature: app debugging in the emulator! This gives you a powerful new way to hunt down errors in your apps. A new command is available in our preview release of pebble tool 4.2 and SDK 3.10: `pebble gdb`. Running this command from a project directory on an emulator with your app installed will pause the emulator and attach a gdb debugger instance to it: ```nc|text katharine@scootaloo ~/p/pebble-caltrain (master)> pebble gdb --emulator basalt Reading symbols from /Users/katharine/Library/Application Support/Pebble SDK/SDKs/3.10-beta4/sdk-core/pebble/basalt/qemu/basalt_sdk_debug.elf...(no debugging symbols found)...done. Remote debugging using :49229 0x0801cd8c in ?? () add symbol table from file "/Users/katharine/projects/pebble-caltrain/build/basalt/pebble-app.elf" at .text_addr = 0x200200a8 .data_addr = 0x20023968 .bss_addr = 0x200239b8 Reading symbols from /Users/katharine/projects/pebble-caltrain/build/basalt/pebble-app.elf...done. Breakpoint 1 at 0x804aebc Press ctrl-D or type 'quit' to exit. Try `pebble gdb --help` for a short cheat sheet. Note that the emulator does not yet crash on memory access violations. (gdb) ``` Do note that once we pause execution by launching gdb, emulator buttons and any pebble tool command that interacts with the emulator won't work until we continue execution (using `continue` or `c`) or quit gdb. Once we're here, we can add some breakpoints to the app in interesting places. Here we are debugging my [Caltrain app](https://github.com/Katharine/pebble-caltrain). Let's say I think there's a bug in the search for the next train: I probably want to poke around in [`find_next_train`](https://github.com/Katharine/pebble-caltrain/blob/f4983c748429127a8af85911cb123bd8c3bacb73/src/planning.c#L4). We can run `b find_next_train` to add a breakpoint there: ```nc|text (gdb) b find_next_train Breakpoint 2 at 0x20020f1e: file ../src/planning.c, line 5. (gdb) ``` Now we can use the `c` or `continue` command to set my app running again, until it stops at `find_next_train`: ```nc|text (gdb) c Continuing. ``` The app runs as usual until we open a station, which causes it to look up a train, where it hits the breakpoint and pauses the app so we can inspect it: ```nc|text Breakpoint 2, find_next_train (count=184, times=0x2002f414, nb=0x2001873c, sb=0x20018738) at ../src/planning.c:5 5 TrainTime *best[2] = {NULL, NULL}; (gdb) ``` Now we can see how we got here using the `backtrace` or `bt` command: ```nc|text (gdb) bt #0 find_next_train (count=184, times=0x2002f414, nb=0x2001873c, sb=0x20018738) at ../src/planning.c:7 #1 0x200211b2 in next_train_at_station (station=13 '\r', northbound=0x20025a0c , southbound=0x20025a14 ) at ../src/planning.c:76 #2 0x200215c8 in prv_update_times () at ../src/stop_info.c:106 #3 0x200216f8 in show_stop_info (stop_id=13 '\r') at ../src/stop_info.c:174 #4 0x200219f0 in prv_handle_menu_click (menu_layer=0x2002fe3c, cell_index=0x2002ff0c, context=0x2002fe3c) at ../src/stop_list.c:57 #5 0x0805cb1c in ?? () #6 0x0805a962 in ?? () #7 0x0801ebca in ?? () #8 0x0801e1fa in ?? () #9 0x200202d6 in main () at ../src/main.c:23 #10 0x080079de in ?? () #11 0x00000000 in ?? () ``` The `??` entries are inside the pebble firmware; the rest are in the Caltrain app. We can step forward a few times to get to an interesting point using the `step` or `s` command: ```nc|text (gdb) s 7 const time_t timestamp = time(NULL); (gdb) s 8 const uint16_t minute = current_minute(); (gdb) s current_minute () at ../src/model.c:183 183 const time_t timestamp = time(NULL); (gdb) ``` Now we've stepped into another function, `current_minute`. Let's say we're confident in the implementation of this (maybe we wrote unit tests), so we can jump back up to `find_next_train` using the `finish` command: ```nc|text (gdb) finish Run till exit from #0 current_minute () at ../src/model.c:183 0x20020f38 in find_next_train (count=184, times=0x2002f414, nb=0x2001873c, sb=0x20018738) at ../src/planning.c:8 8 const uint16_t minute = current_minute(); Value returned is $2 = 738 (gdb) ``` When we step to the next line, we see it has a similar `current_day` that we don't need to inspect closely, so we jump over it using the `next` or `n` command: ```nc|text (gdb) s 9 const uint8_t day = current_day(); (gdb) n 11 for(int i = 0; i < count; ++i) { (gdb) ``` Now we can double check our current state by using `info locals` to look at all our local variables, and `info args` to look at what was originally passed in: ```nc|text (gdb) info locals i = 184 best = {0x0 <__pbl_app_info>, 0x0 <__pbl_app_info>} timestamp = 1456776942 minute = 738 day = 1 '\001' (gdb) info args count = 184 times = 0x2002f414 nb = 0x2001873c sb = 0x20018738 (gdb) ``` `timestamp`, `minute` and `day` all have the values they gained from our last few function calls. `best` is still a pair of NULL pointers, and `i` hasn't been assigned yet, so its value is garbage. Once we step another line it'll be filled in, which we can check using the `print` or `p` command: ```nc|text (gdb) s 12 TrainTime *train_time = &times[i]; (gdb) p i $3 = 0 ``` Now let's step forward and have it fill in `train_time`, and see what we get: ```nc|text (gdb) s 14 trip_get(train_time->trip, &trip); (gdb) p train_time $4 = (TrainTime *) 0x2002f414 (gdb) ``` This is unenlightening — it's just the same pointer as `times`, which is what we expect when referencing `×[0]`. Fortunately, `print`/`p` will evaluate arbitrary expressions, so we can dereference the pointer to see what it actually points at: ```nc|text (gdb) p *train_time $5 = {trip = 189, time = 309, stop = 13 '\r', sequence = 10 '\n'} (gdb) ``` Better! It might be more interesting to just print that out for each loop iteration, so let's set a breakpoint here and have it print `*train_time` and continue: ```nc|text (gdb) b Breakpoint 3 at 0x20020f62: file ../src/planning.c, line 14. (gdb) commands Type commands for breakpoint(s) 3, one per line. End with a line saying just "end". >p *train_time >c >end (gdb) c Continuing. Breakpoint 3, find_next_train (count=184, times=0x2002f414, nb=0x2001873c, sb=0x20018738) at ../src/planning.c:14 14 trip_get(train_time->trip, &trip); $6 = {trip = 209, time = 344, stop = 13 '\r', sequence = 11 '\v'} Breakpoint 3, find_next_train (count=184, times=0x2002f414, nb=0x2001873c, sb=0x20018738) at ../src/planning.c:14 14 trip_get(train_time->trip, &trip); $7 = {trip = 199, time = 345, stop = 13 '\r', sequence = 13 '\r'} ``` …and so on. A bit noisy, so let's remove that breakpoint now: ```nc|text (gdb) delete 3 (gdb) ``` Finally, let's have our program continue on its way by running `c` again: ```nc|text (gdb) c Continuing ``` When we want to get out of gdb we'll need our `(gdb)` prompt back, so press ctrl-C to pause the app again: ```nc|text ^C Program received signal SIGINT, Interrupt. 0x08007072 in ?? () (gdb) ``` This will most likely pause execution inside some firmware code, as we did when we initially launched gdb. We can now do anything we've done before, but we're just going to quit: ```nc|text (gdb) quit A debugging session is active. Inferior 1 [Remote target] will be killed. Quit anyway? (y or n) y katharine@scootaloo ~/p/pebble-caltrain (master)> ``` Hopefully this has given you some ideas as to how you might be able to use gdb to debug your own apps. If you'd like to know more about gdb, [here is a Q&A-style tutorial](http://www.unknownroad.com/rtfm/gdbtut/) that will answer many questions you might have. Good luck and happy debugging!