[racket] trace: set!: cannot mutate module-required identifier - alright .. how do you do this?

From: Thomas Lynch (thomas.lynch at reasoningtechnology.com)
Date: Tue Jan 20 04:59:52 EST 2015

Alright, here is one solution to the trace of libraries.

The following is macro definition for a type of model provide statement
that allows toggling on and off trace for the provided routines.  Instead
of using (provide x y z)  you use (provide-with-trace "my-lib" x y z).
Then the functions (my-lib-trace) will turn on tracing for x y and z,  and
(my-lib-untrace) turns them off.  Use provide-with-trace only once per
module.

(provide-with-trace ...)  must be at the bottom of the module because the
interface routines must be defined before trace can be turned on for them.
  Note the provide statement must be at module top level scope - not the
top of the file, not the same thing of course, and the macro handles this.

Matthias would probably tell you to use a case match instead of pulling the
args out of stx directly, while the docs tell you to use syntax-parse
instead, so that the args can be put into syntax classes; however grabbing
the args was the least of my worries here .. perhaps when I'm better at
this ..

;;--------------------------------------------------------------------------------
;; defines a trace-able module interface
;;
;;  (interface "lib-name" provide-functions ...)
;;
;;   lib-name-trace to turn on tracing
;;   lib-name-untrace to turn off tracing
;;
  (define-syntax (provide-with-trace stx)
    (let(
           [datum  (syntax->datum stx)]
          )
      (let(
            [prefix              (cadr datum)]
            [interface-functions (cddr datum)]
           )
        (let(
              [name-trace-fun   (string->symbol (string-append prefix
"-trace"))]
              [name-untrace-fun (string->symbol (string-append prefix
"-untrace"))]
              )
           #|
          (displayln name-trace-fun)
          (displayln name-untrace-fun)
           |#
          (let(
                [code-block `(begin)]
                [trace-require '(require racket/trace)]
                [trace-fun
                  (append
                    '(define)
                    (list (list name-trace-fun))
                    (map (λ(e)`(trace ,e)) interface-functions)
                    )
                  ]
                [untrace-fun
                  (append
                    '(define)
                    (list (list name-untrace-fun))
                    (map (λ(e)`(untrace ,e)) interface-functions)
                    )
                  ]
                [provide-calls (map (λ(e)`(provide ,e))
interface-functions)]
                )
            #|
            (displayln code-block)
            (displayln trace-require)
            (displayln trace-fun)
            (displayln untrace-fun)
            (displayln provide-calls)
            |#
            (let*(
                   [program
                     (append
                       code-block
                       (list trace-require)
                       (list trace-fun)
                       (list untrace-fun)
                       provide-calls
                       )
                     ]
                   )
              ;;(displayln program)
              (datum->syntax stx program)
              ))))))



As an example for using this, we have removed all of the (provide f)
statements from the top of our db-lib module (which is a variation of the
standard one) and now instead at the bottom of the file we have:

;;--------------------------------------------------------------------------------
;; module interface
;;
  (provide-with-trace "db-lib"
    db-init
    db-exec
    db-exec*
    db-value
    db-value*
    db-maybe-value
    db-maybe-value*
    db-row
    db-row*
    db-rows
    db-rows*
    db-list
    db-list*
    db-lib-trace
    db-lib-untrace
    )


Now when debugging,  we type (db-lib-trace)  to turn on tracing of calls to
the db-lib interface.  We call (db-lib-untrace) to turn the tracing off.

We plan to use this approach for all modules.

Seems to be working.  Hope someone finds this useful.

-Thomas



On Mon, Jan 19, 2015 at 11:31 PM, Thomas Lynch <
thomas.lynch at reasoningtechnology.com> wrote:

> please note, the simple example here is to facilitate discussion.  trace
> is being used very broadly for an entire development cycle.  This isn't a
> one time fix issue in general.
>
> I apologize if my original post wasn't clear, but I did modify the file
> that included "db-lib.rkt" as such:
>
>
> Saw this in another thread here, but the proposed solution didnt work,
>
> ...
>
> so I add this to "db-citation.rkt"
>
>     (require racket/trace)
>
>     (trace db-exec)
>
>
> And then at the prompt I get the following:
>
> Welcome to Racket v5.2.1.
>
> racket@> (enter! "db-citation.rkt")
>
> db-citation.rkt:26:11: set!: cannot mutate module-required identifier in:
> db-exec
>
>
> Alright, so what is the magic incantation here that allows one to
> debug/trace calls in included modules?
>
> And was trying to implement Vince's solution and was asking why the above
> didn't work.
>
> I also tried going to the library source and adding at the top:
>
>     (require racket/trace)
>     (trace db-exec)
>
> Running this again just now, I see that this gives me an error of
>
> racket@> (enter! "db-citation.rkt")
>
> ... and own in the regression tests ..>
>
> hooking test: drop-end-chars-test-0
> link: reference (phase 0) to a variable in module
> "/home/deep/src/db-lib.rkt" that is uninitialized (phase level 0);
> reference appears in module: "/home/deep/src/db-citation.rkt" in: db-exec
>
>  === context ===
> /home/deep/src/db-citation.rkt: [running body]
> /usr/share/racket/collects/racket/enter.rkt:33:0: do-enter!
> /usr/share/racket/collects/racket/private/misc.rkt:87:7
>
>
> Which is a big hint,  I moved the traces to the bottom of the file, yes
> and now they work!  Thanks.
>
> Top of the library file:
>
> (require racket/trace)
>
>
> Bottom of the library I copy all of the provides from the top, and then do
> a text replace with the "provide"-> "trace" and thus:
>
> (trace db-exec)
> (trace db-rows)
> ...
>
> Yes, and now calls to the library are tracing.   So to trace calls to a
> library, one edits the library, puts the require at the top, copies all the
> provides to the bottom, edits that to change the provides to trace, and
> then off it goes.  .. could wrap the trace calls in a parameter switch
> (current-db-trace) or some such...
>
>
> On Mon, Jan 19, 2015 at 11:03 PM, J. Ian Johnson <ianj at ccs.neu.edu> wrote:
>
>> To be fair, trace is more than that.
>>
>> Vincent's solution does work, but you have to modify the file - you can't
>> do this from the REPL.
>> You can modify the file containing the identifier you want to trace, if
>> changing call sites is not palatable to you. Add `(require racket/trace)`
>> at the top, and after the function definition `(trace function)`.
>> Alternatively, if you only want to trace at given points in time, define
>> and provide a function `(define (add-trace) (trace function))` so you can
>> call `add-trace` when you want to turn on tracing.
>>
>> A missing feature for racket/trace is to provide a setter function so
>> that modules that export a mutating function can be traced without
>> depending on racket/trace. If a module contains a `set!` for an identifier,
>> it disables any inlining for that identifier.
>> e.g.
>> (trace #:setter set-function!)
>>
>> -Ian
>>
>> ----- Original Message -----
>> From: "Thomas Lynch" <thomas.lynch at reasoningtechnology.com>
>> To: "J. Ian Johnson" <ianj at ccs.neu.edu>
>> Cc: "users" <users at racket-lang.org>
>> Sent: Monday, January 19, 2015 9:49:51 AM
>> Subject: Re: [racket] trace: set!: cannot mutate module-required
>> identifier - alright .. how do you do this?
>>
>> So to trace calls to the interface functions of a library I am to write a
>> gasket for the library.  I may as well put a display of the args in the
>> gasket functions as at this point it is the same amount of code as hooking
>> the trace.   Of course then I have implemented trace.
>>
>> So to use trace in racket all you have to do is implement trace ..
>> really??
>>
>> On Mon, Jan 19, 2015 at 10:06 PM, J. Ian Johnson <ianj at ccs.neu.edu>
>> wrote:
>>
>> > Yes, racket 101 (305?) is that identifiers can only be set!'d in their
>> > defining module. This is to allow necessary optimizations such as
>> inlining.
>> >
>> > If you want to trace `f` from a different module. you can `(define (g .
>> > args) (apply f args)) (trace g)`, use `g` wherever you use `f`. You
>> won't
>> > get any calls the library makes internally, but if you're just tracing
>> for
>> > your own code, you'll be golden.
>> >
>> > -Ian
>> >
>> > ----- Original Message -----
>> > From: "Thomas Lynch" <thomas.lynch at reasoningtechnology.com>
>> > To: "users" <users at racket-lang.org>
>> > Sent: Monday, January 19, 2015 8:59:55 AM
>> > Subject: [racket] trace: set!: cannot mutate module-required identifier
>> -
>> > alright .. how do you do this?
>> >
>> > Saw this in another thread here, but the proposed solution didnt work,
>> > well at least the way I understood it. So ..
>> >
>> > In file db-lib.rkt I have a definition:
>> >
>> >
>> >
>> >
>> > (provide db-exec)
>> > (define (db-exec a) ...)
>> >
>> > Then in racket
>> >
>> >
>> >
>> >
>> > > (enter! "db-user.rkt")
>> >
>> > Where db-citation.rkt has
>> >
>> >
>> >
>> >
>> > (require "db-lib.rkt")
>> >
>> > Now at the read-eval-print prompt I try
>> >
>> >
>> >
>> >
>> > (enter! "db-citation.rkt")
>> >
>> > And when I do (trace db-exec) I get the cannot mutate error. (Surely
>> this
>> > must be a racket 101 type problem.) So I try to include the module at
>> the
>> > current scope..
>> >
>> >
>> >
>> >
>> > racket at db-citation.rkt> (require "db-lib.rkt")
>> > racket at db-citation.rkt> (require racket/trace)
>> > racket at db-citation.rkt> (trace db-exec)
>> >
>> > racket at db-citation.rkt> stdin::1130: set!: cannot mutate
>> module-required
>> > identifier in: db-exec
>> > ok, so that doesn't work, so I start over again. This time after reading
>> > the thread on this list
>> >
>> >
>> >
>> >
>> > From: Vincent St-Amour (stamourv at ccs.neu.edu )
>> > Date: Mon Oct 10 13:49:46 EDT 2011
>> >
>> > which concludes by saying 'put the trace in your program that should
>> > work', so I add this to "db-citation.rkt"
>> >
>> >
>> >
>> >
>> > (require racket/trace)
>> > (trace db-exec)
>> >
>> > And then at the prompt I get the following:
>> >
>> >
>> >
>> >
>> > Welcome to Racket v5.2.1.
>> > racket@> (enter! "db-citation.rkt")
>> > db-citation.rkt:26:11: set!: cannot mutate module-required identifier
>> in:
>> > db-exec
>> >
>> > Alright, so what is the magic incantation here that allows one to
>> > debug/trace calls in included modules?
>> >
>> > Thanks!
>> >
>> > Thomas
>> >
>> > ____________________
>> >   Racket Users list:
>> >   http://lists.racket-lang.org/users
>> >
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20150120/721fcfc7/attachment-0001.html>

Posted on the users mailing list.