Need help interpreting crash report

Following is an excerpt from a crash report, when my application “quits unexpectedly” in debug mode. The IDE doesn’t catch it. I’ve tried running under the 2016r3 IDE and the 2018r3 IDE’s.

XOJO 2018r3:

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x00005a0d16edbec0

VM Regions Near 0x5a0d16edbec0:
    MALLOC_LARGE           000000011238d000-0000000119a8d000 [119.0M] rw-/rwx SM=PRV  
--> 
    MALLOC_NANO            0000600000000000-0000600000200000 [ 2048K] rw-/rwx SM=COW  

Application Specific Information:
objc_msgSend() selector name: release


Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libobjc.A.dylib               	0x00007fff8ce340dd objc_msgSend + 29
1   com.apple.CoreFoundation      	0x00007fff92753fdd -[__NSDictionaryM dealloc] + 269
2   libobjc.A.dylib               	0x00007fff8ce5489c objc_object::sidetable_release(bool) + 236
3   libobjc.A.dylib               	0x00007fff8ce3ae8f (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 575
4   com.apple.CoreFoundation      	0x00007fff927416f2 _CFAutoreleasePoolPop + 50
5   com.apple.Foundation          	0x00007fff8664b832 -[NSAutoreleasePool drain] + 153
6   com.apple.AppKit              	0x00007fff8dcfdbc1 -[NSApplication run] + 800
7   com.xojo.XojoFramework        	0x00000001088f691d RuntimeRun + 42
8   com.whatever.myExperiment     	0x00000001084ff2f3 REALbasic._RuntimeRun + 19
9   com.whatever.myExperiment     	0x00000001085d4822 _Main + 402 (/#main:134)
10  com.whatever.myExperiment     	0x00000001085d37c3 main + 19
11  libdyld.dylib                 	0x00007fff8d0785c9 start + 1

Here is an excerpt from the crash log when running in 2016r3:


Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000

Application Specific Information:
abort() called
*** error for object 0x2142b40: pointer being freed was not allocated
 

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	0x9aa1969a __pthread_kill + 10
1   libsystem_pthread.dylib       	0x90453634 pthread_kill + 101
2   libsystem_c.dylib             	0x9416ade6 abort + 156
3   libsystem_malloc.dylib        	0x9c31ad28 free + 428
4   libobjc.A.dylib               	0x96e63003 _object_dispose(objc_object*) + 68
5   com.apple.UIFoundation        	0x91a6e50f -[NSParagraphStyle dealloc] + 89
6   com.apple.UIFoundation        	0x91a6e49f -[NSParagraphStyle release] + 301
7   com.apple.CoreFoundation      	0x9779a0cd CFRelease + 989
8   com.apple.CoreFoundation      	0x977d1f6c -[__NSDictionaryM dealloc] + 268
9   libobjc.A.dylib               	0x96e6e650 objc_object::sidetable_release(bool) + 248
10  libobjc.A.dylib               	0x96e5aa59 -[NSObject release] + 25
11  libobjc.A.dylib               	0x96e5a1bc (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 566
12  com.apple.CoreFoundation      	0x977bddaf _CFAutoreleasePoolPop + 47
13  com.apple.Foundation          	0x9a2da30f -[NSAutoreleasePool drain] + 122
14  com.apple.AppKit              	0x9902fb27 -[NSApplication run] + 790
15  com.xojo.XojoFramework        	0x00364bba 0x1b6000 + 1764282
16  com.xojo.XojoFramework        	0x00362d94 RuntimeRun + 49
17  com.whatever.myExperiment     	0x0009d6f5 REALbasic._RuntimeRun + 34
18  com.whatever.myExperiment     	0x001633f9 _Main + 295
19  com.whatever.myExperiment     	0x001625b7 main + 36
20  com.whatever.myExperiment     	0x00163842 start + 53

<>
Additional info:
OS Version: Mac OS X 10.10.5 (14F2009)
<>

A brief explanation of what the program is doing at the time of crash: It’s running a recursive routine searching a small data set for a certain pattern. With most data sets, the routine successfully executes in less than 20 seconds. But with certain data sets, it runs for tens of minutes and then crashes.

I’m trying to figure out what is happening, and why the IDE doesn’t catch it.

The IDE won’t catch this kind of crash, because it’s not something that the Xojo Framework can check for. It’s a CFObject or NSObject that’s already been released, is getting sent the “Release” message.

NSObjects & CFObjects are the underlying elemental objects of macOS, Xojo uses them, as do declares and plugins. So one of those 3 possibilities is not correctly managing the object.

If you use any plugins or declares, strip those from your project first. Once this is pure Xojo code, you can contact Xojo about it. Otherwise if it’s a plugin, then the plugin vendor.

Thanks. I didn’t know which things the IDE can catch and which it can’t.

This program is not using any declares or plug-ins.

I suspect it may be runaway recursion in my program exceeding available memory. Would the IDE be able to detect that situation?

Post the method or methods?

You may regret this. :slight_smile:
Here is the code. See below for explanatory notes.

Public Function SolveAllBranches(currentLevel As Integer) as Integer
  'Recursively solve puzzle
  'return values:
  '  0 = Solved
  '  1 = Exhausted all strategies
  '  2 = Encountered contradiction
  '  3 = Exceeded maximum allowable recursion depth
  '
  'If maxRecursionLevel is zero, then recursion depth is unlimited
  'Otherwise, if the current level exceeds maxRecursionLevel, this routine
  'exits with resultcode = 3.
  '
  'Track deepest recursion in this run
  deepestLevel=max(deepestLevel,currentLevel)
  'Abort if maxRecursionLevel is exceeded
  if maxRecursionLevel<>0 and currentLevel>maxRecursionLevel then
    return 3
  end if
  'Call non-recursive logic based solver 
  dim resultCode As Integer = SolveAllRules
  if resultCode <> 1 then
    'Puzzle solved, or Contradiction, or max depth exceeded
    return resultCode
  Else
    'resultCode = 1 - Logic strategies were exhausted
    'So find empty cells & do trial & error
    trialError=True 'This is used in the notes to indicate T&E was required.
    'Subsequent recursive calls will modify the a(),px(),elimCount properties
    'So they are saved here, so that they can be restored later.
    dim r,c,rTry,cTry,ct As Integer
    dim savePX(8,8) As uint64
    dim saveA(8,8) As Integer
    dim saveClr(8,8) As Color
    dim saveElimCount As Integer = elimCount
    dim cndRow(),cndCol(),cndCount() As Integer 'Empty cell list
    'Save the properties which will be changed
    for r = 0 to 8
      for c= 0 to 8
        savePX(r,c)=px(r,c)
        saveA(r,c)=a(r,c)
        saveClr(r,c)=grid(r,c).TextColor
        'If current cell is empty, add it to the empty cell list
        ct=CountCandidates(r,c)
        if ct>1  then
          'Cell is empty, so add to list
          cndRow.Append(r)
          cndCol.Append(c)
          cndCount.Append(ct)
        end if
      next
    next
    'Sort list so that cells with fewest candidates will be tested first
    cndCount.SortWith(cndRow,cndCol)
    if currentLevel=0 then
      'MsgBox str(UBound(cndRow)+1)+" trial cells."
      NotesOut "## All Logic Strategies exhausted at top level. Starting new branch level "+str(currentLevel+1)+"."+EndOfLine
    Else
      NotesOut "## All Logic Strategies exhausted at level "+str(currentLevel)+". Starting new branch level "+str(currentLevel+1)+"."+EndOfLine
    end if
    'Save the output notes here so that deadend branch notes will be pruned.
    dim saveShortlist As String = shortList
    dim saveLonglist As String = app.solveNotes
    'If maximum recursion depth is unlimited, then there is no need
    'to do more than a single iteration of the emptyCell loop.
    dim LpEnd As Integer = if(maxRecursionLevel=0,0,UBound(cndRow))
    'Try each empty cell in the list
    for emptyCell as Integer = 0 to LpEnd
      rTry=cndRow(emptyCell)
      cTry=cndCol(emptyCell)
      'if currentLevel=0 then
      'MsgBox " Trying cell: "+ CellID(rTry,cTry)+": ["+CandidateLlist(rTry,cTry)+"]"
      'end if
      NotesOut "## Choosing cells starting with those having fewest candidates: "+_
      CellID(rTry,cTry)+": ["+CandidateLlist(rTry,cTry)+"]"+EndOfLine
      '
      'Try each possible candidate for the currently selected cell
      for iCnd as integer = 0 to cndCount(emptyCell)-1
        dim candidate As Integer = NthCandidate(px(rTry,cTry),iCnd)
        saveLonglist = app.solveNotes+"## Level "+str(currentLevel+1)+", trying "+CellID(rTry,cTry)+"="+str(candidate)+": "
        SetCell(rTry,cTry,candidate)
        shortList=""
        app.solveNotes=""
        'Recursive call to solve puzzle with current trial cell value
        resultCode = SolveAllBranches(currentLevel+1)
        'Take appropriate action depending on resultCode
        select case resultCode
        case 0 'Solution found
          saveShortlist=saveShortlist+"*"+CellID(rTry,cTry)+"="+str(candidate)+", "
          'Restore output notes
          shortList=saveShortlist+shortList
          app.solveNotes=saveLonglist+" Leads to solution."+EndOfLine+"* Cell "+CellID(rTry,cTry)+"="+str(candidate)+_
          " by trial & error"+EndOfLine+app.solveNotes
          return resultCode
        case 1 'Exhausted possibilities
          'Restore output notes
          shortList=saveShortlist+shortList
          app.solveNotes=saveLonglist+app.solveNotes
          return resultCode
        case 2 'Contradiction found
          'Branch terminated in Contradiction
          saveLonglist=saveLonglist+" Leads to contradiction. Backtracking..."+EndOfLine
        case 3 'Exhausted all unfilled cells
          saveLonglist=saveLonglist+" Exceeded allowable search depth. Backtracking..."+EndOfLine
        end select
        'Result Codes 2&3 end up here.
        'Restore saved properties and try next candidate
        'Restore puzzle grid to current level
        for r=0 to 8
          for c=0 to 8
            a(r,c)=saveA(r,c)
            grid(r,c).TextColor=saveClr(r,c)
            px(r,c)=savePX(r,c)
            shortList = saveShortlist
            app.solveNotes=saveLonglist
            elimCount=saveElimCount
          next
        next
      next
      'Loop exits here if all candidates in current empty cell have been tried and have failed
      saveLonglist=saveLonglist+ "## Exhausted all candidates for cell "+CellID(rTry,cTry)+" at level "+str(currentLevel+1)+EndOfLine
      'Try next empty cell
    next
    'Loop exits here if all empty cells have been tried and failed to return a solution
    NotesOut "## Exhausted all unfilled cells at level "+str(currentLevel+1)+EndOfLine
    shortList=saveShortlist+shortList
    app.solveNotes=saveLonglist+app.solveNotes
    return resultCode 
  end if
End Function

This is part of my Sudoku puzzle solver. It calls another routine “SolveAllRules” which applies pure logic based rules to solve the puzzle, and returns a result code:
0 = puzzle solved,
1 = unable to solve puzzle using existing rules,
2 = encountered contradiction. Puzzle has no solution.

The routine posted here calls SolveAllRules, and if it returns result code 1 (meaning that the puzzle probably has a solution, but the logic rules were insufficient to find it), then it will successively try different values in currently empty cells, and then call itself recursively until the puzzle is solved.

There are two modes of operation depending on the value of integer property maxRecursionLevel. If its value is zero, then the routine executes with unlimited recursion depth. If maxRecursionLevel>0 then the routine will exit when the recursion depth exceeds the value of maxRecursionLevel.

The unlimited depth mode of operation will typically run 11 levels deep at some point, but always returns a solution. However, that means that the program must correctly guess the values of more cells than necessary in order to get a solution. Typically, the most difficult puzzles can be solved by pure logic after finding only two cell values. That is the reason for the second mode. By setting maxRecursionLevel to 2, it will exit the current level if more than two guesses are needed, and then move on to the next trial.

Strangely, it’s not the unlimited depth mode that causes the problem. That always works (usually no more than 1 second to find a solution). It’s the limited depth mode that fails. The limited depth mode works in most cases (taking about 20 seconds to find a solution with maxRecursionLevel=2). But with certain puzzle data, it will run for 10-20 minutes and then crash. And this is consistent; the same puzzle always causes a crash.

Have you tried breaking into the code after, say, 30 seconds or a minute to see where it is and why it’s not exiting properly?

I think you’re right, the recursion is running wild.

Yes, I tried breaking into the code after several minutes. It didn’t appear to be doing anything that it shouldn’t.

While runaway recursion seems seems plausible on the surface, it’s hard to see how it could happen here, because the second line in the routine checks the recursion depth and aborts if it’s greater than the set value which is normally 2. So, I think it’s more likely a loop that’s not terminating. I’m going to add in a bunch of system.debug lines so I can see what it’s doing when it crashes. I suspect that in the tens of minutes that it’s running, the solvenotes string is growing enormously, and may be what finally causes the crash.

I did learn something interesting. I commented out the array sort line, and then it ran fine with the troublesome data set. However, another data set that had previously worked now causes a crash. So, it seems to have something to do with the order in which cells are processed.