lkml.org 
[lkml]   [2009]   [Mar]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    From
    SubjectRe: [RFC][PATCH 1/2] tracing/ftrace: syscall tracing infrastructure
    Date
    The utrace API itself is not a good fit for global tracing, since its
    purpose is tracing and control of individual user threads. There is
    no reason to allocate its per-task data structures when you are going
    to treat all tasks the same anyway. The points that I think are being
    missed are about the possibilities of overloading TIF_SYSCALL_TRACE.

    It's true that ptrace uses TIF_SYSCALL_TRACE as a flag for whether you are
    in the middle of a PTRACE_SYSCALL, so it can be confused by setting it for
    other purposes on a task that is also ptrace'd (but not with PTRACE_SYSCALL).
    Until we are able to do away with these parts of the old ptrace innards,
    you can't overload TIF_SYSCALL_TRACE without perturbing ptrace behavior.

    The utrace code does not have this problem. It keeps its own state bits,
    so for it, TIF_SYSCALL_TRACE means exactly "the task will call
    tracehook_report_syscall_*" and no more. To use TIF_SYSCALL_TRACE for
    another purpose, just set it on all the tasks you like (and/or set it on
    new tasks in fork.c) and add your code (tracepoints, whatever) to
    tracehook_report_syscall_* alongside the calls there into utrace. There is
    exactly one place in utrace code that clears TIF_SYSCALL_TRACE, and you
    just add "&& !global_syscall_tracing_enabled" to the condition there. You
    don't need to bother clearing TIF_SYSCALL_TRACE on any task when you're
    done. If your "global_syscall_tracing_enabled" (or whatever it is) is
    clear, each task will lazily fall into utrace at its next syscall
    entry/exit and then utrace will reset TIF_SYSCALL_TRACE when it finds no
    reason left to have it on.

    I'm not really going to delve into utrace internals in this thread. Please
    raise those questions in review of the utrace patches when current code is
    actually posted, where they belong. Here I'll just mention the relevant
    things that relate to the underlying issue you raised about synchronization.
    As thoroughly documented, utrace_set_events() is a quick, asynchronous call
    that itself makes no guarantees about how quickly a running task will start
    to report the newly-requested events. For purposes relevant here, it just
    sets TIF_SYSCALL_TRACE and nothing else. In utrace, if you want synchronous
    assurance that a task misses no events you ask for, then you must first use
    utrace_control (et al) to stop it and synchronize. That is not something
    that makes much sense at all for a "flip on global tracing" operation, which
    is not generally especially synchronous with anything else. If you want
    best effort that a task will pick up newly-requested events Real Soon Now,
    you can use utrace_control with just UTRACE_REPORT. For purposes relevant
    here, this just does set_notify_resume(). That will send an IPI if the task
    is running, and then it will start noticing before it returns to user mode.
    So:
    set_tsk_thread_flag(task, TIF_SYSCALL_TRACE);
    set_notify_resume(task);
    is what I would expect you to do on each task if you want to quickly get it
    to start hitting tracehook_report_syscall_*. (I'm a bit dubious that there
    is really any need to speed it up with set_notify_resume, but that's just me.)

    Finally, some broader points about TIF_SYSCALL_TRACE that I think
    have been overlooked. The key special feature of TIF_SYSCALL_TRACE
    is that it gets you to a place where full user_regset access is
    available. Debuggers need this to read (and write) the full user
    register state arbitrarily, which they also need to do user
    backtraces and the like. If you do not need user_regset to work,
    then you don't need to be on this (slowest) code path.

    If you are only interested in reading syscall arguments and results
    (or even in changing syscall results in exit tracing) then you do
    not need user_regset and you do not need to take the slowest syscall
    path. (If you are doing backtraces but already rely on full kernel
    stack unwinding to do it, you also do not need user_regset.) From
    anywhere inside the kernel, you can use the asm/syscall.h calls to
    read syscall args, whichever entry path the task took.

    The other mechanism to hook into every syscall entry/exit is
    TIF_SYSCALL_AUDIT. On some machines (like x86), this takes a third,
    "warm" code path that is faster than the TIF_SYSCALL_TRACE path
    (though obviously still off the fastest direct code path). It can
    be faster precisely because it doesn't need to allow for user_regset
    access, nor for modification of syscall arguments in entry tracing.
    For normal read-only tracing of just the actual syscall details,
    it has all you need.

    Unfortunately the arch code all looks like:

    if (unlikely(current->audit_context))
    audit_syscall_{entry,exit}(...);

    So we need to change that to:

    if (unlikely(test_thread_flag(TIF_SYSCALL_AUDIT)))
    audit_syscall_{entry,exit}(...);

    But that is pretty easy to get right, even doing it blind on arch's
    you can't test. Far better than adding new asm hackery for each arch
    that's almost identical to TIF_SYSCALL_TRACE or TIF_SYSCALL_AUDIT (and
    finding out that some are fresh out of TIF bits in the range that
    their asm code can handle).

    TIF_SYSCALL_AUDIT is only set when allocating audit_context, and its
    paths already have !context tests so overloading is harmless today.
    (Whereas with TIF_SYSCALL_TRACE, you have to wait for later ptrace
    cleanups or write off using ptrace simultaneously.)

    Then you can do the lazy disable in audit_syscall_{entry,exit} with:

    if (unlikely(!context)) {
    if (unlikely(!global_syscall_tracing_enabled))
    clear_thread_flag(TIF_SYSCALL_AUDIT);
    return;
    }

    Plus add there your tracepoint or whatnot.

    Unless you really plan to use user_regset in your tracepoints, then
    I think this is a better plan for global syscall tracing than either
    fiddling with TIF_SYSCALL_TRACE or adding new arch asm requirements.
    (IMHO, the latter is the worst idea on the table.)


    Thanks,
    Roland


    \
     
     \ /
      Last update: 2009-03-19 11:41    [W:0.030 / U:1.180 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site