[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 05:29:25 EST 2015

p.s. add a separate regular old (provide ...)  for module interface
routines that are not traceable ...

On Tue, Jan 20, 2015 at 5:59 PM, Thomas Lynch <
thomas.lynch at reasoningtechnology.com> wrote:

> 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/46c87a32/attachment-0001.html>

Posted on the users mailing list.