Debugging a "Symbol Not Found Error" on a specific device (Fenix 5S/X)

Former Member
Former Member

Yesterday I released my very first Garmin watch-face on the CIQ store (Digital Simplicity, https://apps.garmin.com/en-US/apps/97b91745-9287-421d-aa8a-abb437e08eea, the code is open-source: https://github.com/kelnage/digital-simplicity). I'd been able to test my watch-face on my Fenix 5+ and a Forerunner 645 prior to releasing it more widely - but once it was released to the world, I quickly started getting informed by some users it was crashing on the Fenix 5X (but not the Fenix 5).

I eventually managed to get one of these users to send me the contents of their CIQ_LOG.YAML (or YML as they had), and the relevant part of the file to my app was as follows:

Error: Symbol Not Found Error
Details: 'Failed invoking <symbol>'
Time: 2019-04-26T00:30:10Z
Part-Number: 006-B2604-00
Firmware-Version: '13.00'
Language-Code: eng
ConnectIQ-Version: 3.0.10
Store-Id: 97b91745-9287-421d-aa8a-abb437e08eea
Store-Version: 3
Filename: 94Q02834
Appname: Digital Simplicity
Stack:
  - pc: 0x10001c75
  - pc: 0x10001db1

I know from developing the app that this means I am referring to a symbol that does not exist for a specific object - but that debug message is not particularly helpful. Worse, I have been trying to recreate this crash in the simulator, following all of the steps described by my users, and I'm not able to trigger this error myself.

Outside of sending these users a special debug version that does a println before every call to a function, or buying a Fenix 5X to test on (hah, sorry Garmin - but no), is there any sane way I can debug this myself? Can I convert (what I assume is) a program counter in to a line number? One user has seemed to suggest that the very first version I released did not crash in this way - and fortunately, I tag my releases, so I have been trying to bisect the bug using git (https://github.com/kelnage/digital-simplicity/compare/v1.0.0...v1.1.1) - but really nothing is coming to mind!

This was resolved by checking whether the module Toybox.Position has the attribute :getInfo.

  • The thing you don't see in the stack trace from a release build is what the values for "pc:" mean, but you can determine that based on the debug.xml file for the build..  If you've not changed code, you can find this in the bin directory for your project, or you can find it in the .iq file you sent to the store (the .iq is actually a .zip, with folders based on the part number, and you can use the manifest.xml in the .iq to see what part number is which device) 

    So with the debug.xml, you can "translate", the pc:.  The thing here is in the debug.xml, the pc values are in decimal, while in ciq_log, they are hex, so convert the hex to decimal, then look in the debug.xml for a close match.

    One of the things coming in the 3.1.x release is this gets automated on the garmin end, so the pc values will become easier to read.  This is why the debug.xml file is contained in the .iq file.

    As far as getting more info, you can send the user a debug build (see the build for device wizard), and without adding a line of code, get a more detailed stack trace, as the .prg will have the debug symbols.

    But maybe the first thing to look at is your memory usage.  On a f5+, you're max is 92k for a watch face.  Something like this could be you ran out of memory.  In the sim, under "view memory", watch the peak value, and if you're getting close, that could be it.  And try using the "app settings editor" while your app is running, as that could cause an impact in the peak if settings are changed.

  • Former Member
    Former Member over 6 years ago in reply to jim_m_58

    Hi Jim, yes, one of the users mentioned that memory usage might be a problem. However, in the simulator I'm not normally exceeding 30KB of memory at runtime, with a peak of ~65KB according to the memory analyser. But obviously that's the simulator...

    Having followed the rabbit hole that is PCs to line numbers, it appears that the issue was that Position module did not have the getInfo symbol defined. I have no idea why that would occur, and the documentation definitely never suggests that's a possibility, but it seems to be the case. Have added an appropriate check - hopefully it'll fix it going forward! Will update the thread with the final outcome.

  • Former Member
    Former Member over 6 years ago in reply to Former Member

    I have now had two users independently confirm that the changes I made have fixed it. Something to be added/lost in my knowledge-hole I guess.

  • Actually, here's why it happened.  That's not available in watchfaces!  You can't actually do anything with GPS in a watchface.

    I'm guessing you're looking for the current (or really, last known) location for something like sunrise/set.

    Here's how you do that:

    Activity.getActivityInfo().currentLocation;.

    What you get can be null, and will go stale after a while if GPS hasn't been used (and will then become null).

    If non-null, you can get the lat/lon with toDegrees(), and if it's not null, you want to save the data to the ObjectStore/Storage.

    If it is null, you get the location from the ObectStore/Storage and use it if that's not null.

    To get currentLocation from the Activity.Info, you do need the positioning permission, or it will always be null.

    I think there's something on github that uses Position.getInfo() as this has come up before.  This should be caught in the sim, but maybe you weren't taking the code path that makes the call.

  • Former Member
    Former Member over 6 years ago in reply to jim_m_58

    Jim, honestly, thank you for trying to help - but I think you're mistaken there, at least with regards to CIQ 3.0.X. I think that because:

    a) it's working on my wrist, right now, and

    b) because Positioning is explicitly listed as one of the Permissions a watch face may use - and Position.getInfo() states that it requires that permission - and attempting to use the Position.enableLocationEvents() function does throw an exception.

    In addition, this was not at all a problem on my device (a Fenix 5+) nor others - just the Fenix 5S and 5X.

    I based my entire development off the Garmin docs (which I'm slowly coming to the conclusion may have been a mistake in places - some of the information it contains is ambiguous at best) - but this was one part that worked pretty much flawlessly except on those two models. My read of the Activity documentation was that it required an activity to be active in order to work?

  • This has to do with the requirement for the positioning permission to access Activity.Info.currentLocation that was added a while back due to GDPR in some way.  That's the whole reason the permission is allowed for watch faces.

    While it may work on some devices, I don't think it actually should or it should work on all.  Activity.Info is the safe way to do this.