> Enter Renzo Davoli.  

Here I am!

I have spent my time testing the latest version and trying to figure out
how to implement "nested Renzo's engines" with the support you propose.

Comments on the latest version of utrace:
-------------------------------------
1- syscall_entry report reversed.
wonderful, thank you. Now kmview.ko runs on vanilla utrace provided
KMVIEW_NEWSTOP is defined.
KMVIEW_NEWSTOP stops the process inside the syscall report function
so it is a undesirable workaround, not a solution.
Anyway this can be used as a proof-of-concept: the problem related to
the order of callbacks for syscall_entry is solved.
-------------------------------------
2- utrace_control(.., UTRACE_RESUME) can arrive too early, before
ENGINE_STOP is set (in engine->flags by mark_engine_wants_stop).

Let us name p the traced process and vm the tracer.
t=10: p reports a system call. 
     during the report function, p communicates with vm 
     the report function returns UTRACE_STOP
     utrace is unlocked during the report function.
t=20: p records its need to stop: 
      (lock) engine->flags |= ENGINE_STOP; (unlock)

later (time t' > 10) vm calls utrace_control(p, engine, ENGINE_RESUME):
if t' < 20 the request gets lost!
in fact:
t=15:   utrace_control gets the lock
        resume=utrace->stopped IS ZERO!
        clear_engine_wants_stopped clears ENGINE_STOP which has not been
                set yet
        at t=20 ENGINE_STOP is set and the task blocked.

There are two "clean" "non-baroque" approaches to solve this problem:
2A- interface approach: 
long time ago utrace had a utrace_set_flags call to set ENGINE_STOP flag 
before p communicates with vm. In this way ENGINE STOP will always 
be cleared after it has been set.
2B- implementation approach:
use two bits: ENGINE_STOP and ENGINE_RESUME.
before t=10 ENGINE_STOP and ENGINE_RESUME are unset.
utrace_control(p, engine, UTRACE_RESUME) must set ENGINE_RESUME and clear
ENGINE_STOP.
at t=20 p can check if there has been a fast resume request. In this case
ENGINE_STOP is not set.

It is possible to create other workarounds, barriers, fake reports, 
busy wait loops... If we want something effective, we must implement
solutions not workarounds. If a engine say UTRACE_STOP and later
UTRACE_RESUME, the task must be resumed. The simplest, the better.

My patch in:
http://view-os.svn.sourceforge.net/viewvc/view-os/trunk/kmview-kernel-module/kernel_patches/linux-2.6.29-patch1?revision=637&view=markup
implements 2B and works with the latest utrace implementation.
----------------------
Comments on the proposal.

Roland, let me say frankly that the repeated report scan for system call
is just a step towards a solution, but I do not like it so much.

Problem #1: when each engine receives the same syscall_entry report several
times, each engine must discover if:
- a previous engine has already stopped this task
  ( utrace_resume_action(action) == UTRACE_STOP)
- this is a repeated scan and the current engine has already processed this
  report (there is the risk to process it twice). 
- this is a real new report

Maybe I can keep the address of the engine which stopped
the task somewhere (say in a task private variable stopengine).  
During the repeated scan:
        - if stopengine is NULL is a fresh call.
        - else (stopengine != NULL) means that the current engine has 
                already processed this report
                - if stopengine == this engine then set stopengine to NULL.
A more portable approach follows (*) :
Each engine records if it stopped the task.
During the repeated scan:
        - if ! (action & UTRACE_SYSCALL_RESUMED) this is a fresh call
        - else the current engine has already processed this report
                - if this engine stopped the task then clear 
                        UTRACE_SYSCALL_RESUMED in the action returned.

This is not a nice solution: this "protocol" must be consistently applied
by all the modules using utrace otherwise they cannot interoperate.
If a report_syscall_entry does not behave in the same way it may receive
repeated reports or force other engines to skip some reports.

All the programmers of utrace modules should always agree on these 
details: not a good interface for a long term interoperability.

Problem #2: syscall exit may need to modify the return value/errno.
The need for stop&go at each engine applies not only to syscall_entry.


I really do not understand why is so unaccetable to have a UTRACE_STOP_NOW
tag to stop a process *before* reporting to the next engine.
The interface would be clean, interoperability between tracing and virtualizing
guaranteed.

It is not a matter of performance. If your engine need to see the 
system call that is going to be done by the kernel as you say:
        if (utrace_resume_action(action) == UTRACE_STOP)
                return UTRACE_REPORT
it has to wait all the virtualizers to have done their job any way.
On the other hand, this code cannot be used if you want to test which
system call appear to be done after the third virtualizer and before 
the fourth.

If you want to see the syscall arguments before someone else changes 
syscall argument values you propose:
        if (action & UTRACE_SYSCALL_RESUMED)
                return UTRACE_RESUME;
this simply does not work: either this is the last engine inserted or
some other engine may have already changed, or maybe is changing the
arguments concurrently, the result is unpredictable in this latter case.
The code is also foolished by the reset of UTRACE_SYSCALL_RESUMED
as in (*) above.
If you want to see the syscall arguments before someone else changes
simply insert the engine as the last one.
UTRACE_STOP_NOW is a general approach: it is possible to see the syscall 
arguments before the fourth virtualizer changes them but after the third 
virtualizer has already done its work.

Roland, in my opinion you are too concerned to solve the problem to
support the very first and very last engine that you are not 
seeing the problem to support the very fifth of fiftieth.
If hundreds of debuggers can run concurrently, they return UTRACE_STOP,
if a virtualizer must be certain of what happens before and after
it uses UTRACE_STOP_NOW.
In this way utrace provides a support for interoperability between
different modules, and there is no need for programmers to share
the same protocol dealing with nested engines.

ciao.
        renzo

Reply via email to