Here is |dig-out-gc-roots-graph|, a routine for digging out the GC roots for a given object.

As potentially the depth to the deepest root can be really deep and each resolve operation takes one full GC to perform which can take seconds, the probing is done one level of depth at a time rather than through ordinary tree recursion, and after each probe the complete result up to that point is printed out.

 ** BETA NOTICE: |dig-out-gc-roots-graph| works well enough for my current use though there is a stability issue about |dig-out-gc-roots-graph| or |##resolve-referencing-objects| addressed in the next email - this version tends to crash Gambit during GC after having worked itself some levels into the graph.

At least if it will become perfectly stable, this will be released on the dumping grounds and then as a module file in a repo or alike at some future point in time.

Usage example 1:

> (dig-out-gc-roots-graph '(1 2 3))
Digging out the GC roots for object #2...

References to object #2:
#2
  (Unknown root)
  #3 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4
      (Unknown root)
      #3 (In work queue)
      #5 (In work queue)
      #6 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4
      (Unknown root)
      #3 (In work queue)
      #5 (In work queue)
      #6
        (Unknown root)
        #4 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4
      (Unknown root)
      #3 (In work queue)
      #5
        (Unknown root)
      #6
        (Unknown root)
        #4 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4
      (Unknown root)
      #3 (This object was already found at a lower depth in the graph.)
      #5
        (Unknown root)
      #6
        (Unknown root)
        #4 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4
      (Unknown root)
      #3 (This object was already found at a lower depth in the graph.)
      #5
        (Unknown root)
      #6
        (Unknown root)
        #4 (This object was already found at a lower depth in the graph.)

Done!
All the objects that refer to this object in total (+ the object itself) are: #5 #4 #6 #2 #3
All the objects that refer to this object closest to the root in total are: #5
> #3
#(#(#f
    #<procedure #4 ##cprc-app1-sub>
    #(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#(#( [...]

"(Unknown root)" should be the interpreter's reference accessible as the # variable in the REPL.

#3 must be some structure created by the interpreter, and so are the objects that reference it in turn (i.e. #4, #5 & #6). Curious what they are about?


Usage example 2:

> (define a "Hello world")
> (define b (list a a))
> (define c (list b b))
> (dig-out-gc-roots-graph a)
Digging out the GC roots for object #2...

References to object #2:
#2
  (Unknown root)
  #3 (In work queue)
  #4 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3 (In work queue)
  #4
    (Unknown root)
    #5 (In work queue)
    #6 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4 (In work queue)
  #4
    (Unknown root)
    #5 (In work queue)
    #6 (In work queue)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4 (In work queue)
  #4
    (Unknown root)
    #5 (In work queue)
    #6
      (Unknown root)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4 (In work queue)
  #4
    (Unknown root)
    #5
      (Unknown root)
      #6 (In work queue)
    #6
      (Unknown root)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4 (This object was already found at a lower depth in the graph.)
  #4
    (Unknown root)
    #5
      (Unknown root)
      #6 (In work queue)
    #6
      (Unknown root)

References to object #2:
#2
  (Unknown root)
  #3
    (Unknown root)
    #4 (This object was already found at a lower depth in the graph.)
  #4
    (Unknown root)
    #5
      (Unknown root)
      #6 (This object was already found at a lower depth in the graph.)
    #6
      (Unknown root)

Done!
All the objects that refer to this object in total (+ the object itself) are: #3 #2 #4 #6 #5
All the objects that refer to this object closest to the root in total are: #6
> (object->serial-number a)
2
> (object->serial-number b)
4
> (object->serial-number (cdr b))
3
> (object->serial-number c)
6
> (object->serial-number (cdr c))
5

"(Unknown root)" are either the global object reference (this is the case for the reports of #2, #4 and #6), or some other reference that Gambit holds - that would be the reports for #3 and #5, would be curious to know what they are!

Usage example 3:

> (define a "Obj")
> (define b (make-will a (lambda (v) (void))))
> (dig-out-gc-roots-graph a)
Digging out the GC roots for object #2...

References to object #2:
#2
  (Unknown root)

Done!
All the objects that refer to this object in total (+ the object itself) are: #2
All the objects that refer to this object closest to the root in total are: #2

Weak references are not covered, as it should be.


Files & notes
First, attached the latest Gambit commit's (ffd839c64f66278cdb4cce5ef86316ee290a4204) files patched with ##resolve-referencing-objects support.

Next, dig-out-gc-roots-graph.scm containing |dig-out-gc-roots-graph| .

For |dig-out-gc-roots-graph| to work, dig-out-gc-roots-graph.scm must be in compiled mode, otherwise it produces an infinite amount of yet-another-referencing-object.