[Openmcl-devel] #_clock_get_time on Darwin returning bogus values

Gary Byers gb at clozure.com
Thu Dec 4 10:15:00 UTC 2008


(RLET ((ptr type)*) &body body)

stack-allocates "an instance of type", binds "ptr" to a pointer to that
stack-allocated instance, and executes "body" with that (dynamic-extent)
binding in effect.

? (defun foo ()
   (rlet ((ptr-to-int :int))
     (print ptr-to-int)
     ;; Don't return the stack-allocated pointer, which becomes invalid
     ;; as soon as we exit the scope of the binding.
     nil))
FOO
? (foo)
#<A Foreign Pointer [stack-allocated] (:* (:SIGNED 32)) #x7FFFFFBFEF10> 
NIL

":int" is shorthand for "signed 32-bit integer" on most platforms.  (I
should have said that RLET also sets some bits in the pointer that describe
the type of object the pointer's pointing to, which might be handy for
debugging.)

"Allocating an instance of the foreign type :int" could also be described
as "reserving 4 bytes on the stack."  We've said nothing about what value
those 4 bytes will have if interpreted as an :int, and they're in fact
pretty much random, e.g., "whatever was last in that particular stack
address".

? (defun bar ()
   (rlet ((ptr-to-int :int))
     (format t "~&the value of the :int that PTR-TO-INT is pointing to is #x~x ."
             (pref ptr-to-int :int))
     nil))
BAR
? (bar)
the value of the :int that PTR-TO-INT is pointing to is #x0 .

Well, in my case it happened to be 0.  YMMV.

Here's another example:

(def-foreign-type :intptr (:* :int)) ; necessary since PREF wants a symbol

(rlet ((ptr-to-ptr-to-int :intptr))
   (format t "~&ptr-to-ptr-to-int = ~s, what it points to = ~s"
     ptr-to-ptr-to-int (pref ptr-to-ptr-to-int :intptr))
   nil)

? (rlet ((ptr-to-ptr-to-int :intptr))
   (format t "~&ptr-to-ptr-to-int = ~s, what it points to = ~s"
     ptr-to-ptr-to-int (pref ptr-to-ptr-to-int :intptr))
   nil)
ptr-to-ptr-to-int = #<A Foreign Pointer [stack-allocated] (:* (:* (:SIGNED 32))) #x7FFFFFBFEF10>, what it points to = #<A Foreign Pointer #x3100000033>
NIL

In that case, we allocated 8 bytes (on a 64-bit machine, would have been 4
bytes in a 32-bit lisp) of uninitialized storage, and bound PTR-TO-PTR-TO-INT
to the address of those 8/4 bytes.  If we interpret those bytes as an
address, that "address" isn't too meaningful.

If we had some foreign code like:

int magic_number = 17;

void
get_address_of_magic_number(int **ref)
{
   *ref = &magic_number;
}

an RLET form like the one in the example above might be useful:

(rlet ((magic-address-ptr :intptr))
   (exernal-call "get_address_of_magic_number" :address magic-address-ptr :void)
   (let* ((magic-address (pref magic-address-ptr :intptr)))
      (format t "magic address = ~s, contents = ~s"
              magic-address (pref magic-address :int))))

After we've called the foreign code (passing it a "pointer to a pointer to
an int" and that code has stored a "pointer to int" where we've asked it
to, the contents of "magic-address-ptr" would be meaningful (as a 
"pointer ot int", and the contents of that "pointer to int" would be 
17.  There -are- widely-used C functions that follow these kinds of
conventions, but they're fortunately rare.


Back to your case:

- a :mach_timespec is a foreign record tyype that has two 32-bit fields
named :tv_sec and :tv_nsec; for reasons best known to Apple, tv_sec
is unsigned and :tv_nsec is signed (though since the :tv_nsec field
is supposed to represent the number of nanoseconds in a time value
or interval, the value of an initialized :tv_nsec field will always
by >= 0 and < 1000000000).

Note that this is a bit different from a POSIX :timespec structure,
where (on 64-bit OSX) the :tv_sec field is 64 bits wide (and signed.)

- a :mach_timespec_t is a "pointer to a :mach_timespec".

- a :clock_serv_t is an unsigned 32-bit integer

The Mach function clock_get_time() takes two arguments; the first
is a clock_serv_t used to identify the clock service, and the second
is a :mach_timepec_t (aka, a pointer to a :mach_timespec.)

So, one way to do this (assuming that we want to ignore the possibility
that the clock service port might change, e.g., if we save an image
and run this code in a different session or different environment):

(rlet ((prealtime-clock-port :clock_serv_t))
   (#_host_get_clock_service (#_mach_host_self) #$REALTIME_CLOCK prealtime-clock-port)
   (let* ((realtime-clock-port (pref prealtime-clock-port :clock_serv_t)))
    (defun read-clock-seconds ()
      ;; Allocate an uninitialized :mach_timespec, bind ts to a pointer to it
      (rlet ((ts :mach_timespec))
        ;; ts is a "pointer to a :mach_timepsec", or "a :mach_timespec_t".
        (let* ((err (#_clock_get_time realtime-clock-port ts)))
          (unless (eql err  #$KERN_SUCCESS)
             (error "Mach error reading time, code = ~s" err))
          ;; No error, access the tv_sec field of the :mach_timespec
          ;; that ts points to and return that value.
          (pref ts :mach_timespec.tv_sec))))))

;;; Unless SLEEP sleeps significantly longer than 1 second, these values
;;; should increase by 1 on each iteration.
? (dotimes (i 10) (print (read-clock-seconds) (sleep 1)))
2939807 
2939808 
2939809 
2939810 
2939811 
2939812 
2939813 
2939814 
2939815 
2939816





(let* ((
(defun read-clock-seconds

On Thu, 4 Dec 2008, Hans Hübner wrote:

> Hi,
>
> I'm trying to fix the problem in the deterministic profiler on Darwin.
> The problem seems to be that the clock reading function does not
> work.  I am using Mach timers in the profiler, and I think I'm reading
> them correctly, but the values returned do not make sense:
>
> PROF> (let ((clock-port (make-record :clock_serv_t)))
>        (#_host_get_clock_service (#_mach_host_self) #$REALTIME_CLOCK
> clock-port)
>        (defun read-clock-seconds ()
>          (ccl:rlet ((ts :mach_timespec_t))
>            (#_clock_get_time (%get-ptr clock-port) ts)
>            (pref ts :timespec.tv_sec))))
> READ-CLOCK-SECONDS
> PROF> (read-clock-seconds)
> 990852764823254945
> PROF> (read-clock-seconds)
> 957862257734252450
> PROF> (read-clock-seconds)
> 4146603814037813155
> PROF> (lisp-implementation-version)
> "Version 1.3-dev-r11396M-trunk  (DarwinX8664)"
>
> I would expect the value to be increasing (and it used to work with
> previous releases).  Maybe something with the header generation went
> wrong?
>
> Thanks,
> Hans
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel
>
>


More information about the Openmcl-devel mailing list