* [ltt-dev] lttng development plan
[not found] <49646F42.5040003@cn.fujitsu.com>
@ 2009-01-07 17:05 ` Mathieu Desnoyers
2009-01-19 3:43 ` Lai Jiangshan
2009-02-02 4:59 ` Gui Jianfeng
0 siblings, 2 replies; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-01-07 17:05 UTC (permalink / raw)
Hi Gui,
My short-term roadmap is (those are the show stoppers) :
- Get lttng ascii dump to work.
- create periodical buffer flush per-cpu timer for data streaming
- Modify LTTng/lttd/lttv to support variable-sized buffers, so we
don't have to copy the padding. This implies creating an index in
lttv when opening the trace to know where the subbuffer start is.
- Waiting for Lai's channel and event ID management modifications
following my comments. This is needed so the IDs stays valid for the
binary->ascii in-kernel converter.
- Support dynamic frequency scaling on x86.
Other nice-to-have, but not a priority :
- Add support for Performance Monitoring Counters (PMC) so they can be
dumped in the traces.
- Put back support for kernel and userspace stack dump so it can be
connected to any given tracepoint.
- Linux ABI for fast userspace tracing.
- Then, add NPTL instrumentation (mutexes, phtreads).
- Integrate LTTng with LKCD, test with kernel crash extraction,
create tools to simplify extraction of traces from crashed kernel,
integrate those tools to ltt-control.
- Create an in-kernel event filtering module which connects on
LTTng.
- Early boot tracing.
- Virtual machine tracing support (time synchronisation).
- Cluster and distributed computer tracing support (time
synchronisation).
- Port kmemtrace, ftrace, blktrace, kvmtrace and others to LTTng.
Each could have its own channel.
Please ask if you need more information on specific items.
Best regards, and many thanks to Fujitsu for the good work,
Mathieu
* Gui Jianfeng (guijianfeng at cn.fujitsu.com) wrote:
> Hi Mathieu,
>
> I'd like to know whether you have a plan or roadmap
> for lttng's further developping.
> If you have one, would you share it?
>
> --
> Regards
> Gui Jianfeng
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-01-07 17:05 ` [ltt-dev] lttng development plan Mathieu Desnoyers
@ 2009-01-19 3:43 ` Lai Jiangshan
2009-01-20 1:55 ` Mathieu Desnoyers
2009-02-02 4:59 ` Gui Jianfeng
1 sibling, 1 reply; 24+ messages in thread
From: Lai Jiangshan @ 2009-01-19 3:43 UTC (permalink / raw)
Mathieu Desnoyers wrote:
> Hi Gui,
>
> My short-term roadmap is (those are the show stoppers) :
>
> - Get lttng ascii dump to work.
> - create periodical buffer flush per-cpu timer for data streaming
> - Modify LTTng/lttd/lttv to support variable-sized buffers, so we
> don't have to copy the padding. This implies creating an index in
> lttv when opening the trace to know where the subbuffer start is.
> - Waiting for Lai's channel and event ID management modifications
> following my comments. This is needed so the IDs stays valid for the
> binary->ascii in-kernel converter.
> - Support dynamic frequency scaling on x86.
>
> Other nice-to-have, but not a priority :
>
> - Add support for Performance Monitoring Counters (PMC) so they can be
> dumped in the traces.
> - Put back support for kernel and userspace stack dump so it can be
> connected to any given tracepoint.
> - Linux ABI for fast userspace tracing.
> - Then, add NPTL instrumentation (mutexes, phtreads).
> - Integrate LTTng with LKCD, test with kernel crash extraction,
> create tools to simplify extraction of traces from crashed kernel,
> integrate those tools to ltt-control.
We will implement it.
> - Create an in-kernel event filtering module which connects on
> LTTng.
> - Early boot tracing.
What your plan for early boot tracing?
> - Virtual machine tracing support (time synchronisation).
> - Cluster and distributed computer tracing support (time
> synchronisation).
> - Port kmemtrace, ftrace, blktrace, kvmtrace and others to LTTng.
> Each could have its own channel.
>
> Please ask if you need more information on specific items.
>
> Best regards, and many thanks to Fujitsu for the good work,
>
> Mathieu
>
>
> * Gui Jianfeng (guijianfeng at cn.fujitsu.com) wrote:
>> Hi Mathieu,
>>
>> I'd like to know whether you have a plan or roadmap
>> for lttng's further developping.
>> If you have one, would you share it?
>>
>> --
>> Regards
>> Gui Jianfeng
>>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-01-19 3:43 ` Lai Jiangshan
@ 2009-01-20 1:55 ` Mathieu Desnoyers
2009-01-20 6:58 ` Lai Jiangshan
2009-01-23 3:21 ` KOSAKI Motohiro
0 siblings, 2 replies; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-01-20 1:55 UTC (permalink / raw)
* Lai Jiangshan (laijs at cn.fujitsu.com) wrote:
> Mathieu Desnoyers wrote:
> > Hi Gui,
> >
> > My short-term roadmap is (those are the show stoppers) :
> >
> > - Get lttng ascii dump to work.
> > - create periodical buffer flush per-cpu timer for data streaming
> > - Modify LTTng/lttd/lttv to support variable-sized buffers, so we
> > don't have to copy the padding. This implies creating an index in
> > lttv when opening the trace to know where the subbuffer start is.
> > - Waiting for Lai's channel and event ID management modifications
> > following my comments. This is needed so the IDs stays valid for the
> > binary->ascii in-kernel converter.
> > - Support dynamic frequency scaling on x86.
> >
> > Other nice-to-have, but not a priority :
> >
> > - Add support for Performance Monitoring Counters (PMC) so they can be
> > dumped in the traces.
> > - Put back support for kernel and userspace stack dump so it can be
> > connected to any given tracepoint.
> > - Linux ABI for fast userspace tracing.
> > - Then, add NPTL instrumentation (mutexes, phtreads).
> > - Integrate LTTng with LKCD, test with kernel crash extraction,
> > create tools to simplify extraction of traces from crashed kernel,
> > integrate those tools to ltt-control.
>
> We will implement it.
>
Great :) Note that there has already been some work done on this. This
in available as an addition to the crosscrash tool :
http://sourceforge.net/projects/crosscrash/
There is a cross-crash-ltt.patch file available on the project website,
but I think it has not been updated since 2007. Some integration work
will have to be done.
Also outputting the traces in the video card's memory would be a
nice-to-have, because this memory often survives hot reboots.
> > - Create an in-kernel event filtering module which connects on
> > LTTng.
> > - Early boot tracing.
>
> What your plan for early boot tracing?
>
Basically :
- Finding where is the soonest in main.c we can put tracepoints and
start tracing (this is probably after rcu and memory init).
- Figuring out the TSC problems that could arise with tracing a booting
system.
- Allowing kernel command line arguments in a special LTTng module to
control early boot tracing.
- Allowing the kernel to allocate buffers very early at boot time. This
would also make sure that we have large contiguous memory regions
available for allocation.
Please don't hesitate to ask if you need more details.
Best regards,
Mathieu
> > - Virtual machine tracing support (time synchronisation).
> > - Cluster and distributed computer tracing support (time
> > synchronisation).
> > - Port kmemtrace, ftrace, blktrace, kvmtrace and others to LTTng.
> > Each could have its own channel.
> >
> > Please ask if you need more information on specific items.
> >
> > Best regards, and many thanks to Fujitsu for the good work,
> >
> > Mathieu
> >
> >
> > * Gui Jianfeng (guijianfeng at cn.fujitsu.com) wrote:
> >> Hi Mathieu,
> >>
> >> I'd like to know whether you have a plan or roadmap
> >> for lttng's further developping.
> >> If you have one, would you share it?
> >>
> >> --
> >> Regards
> >> Gui Jianfeng
> >>
> >
>
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-01-20 1:55 ` Mathieu Desnoyers
@ 2009-01-20 6:58 ` Lai Jiangshan
2009-01-23 3:21 ` KOSAKI Motohiro
2009-01-23 3:21 ` KOSAKI Motohiro
1 sibling, 1 reply; 24+ messages in thread
From: Lai Jiangshan @ 2009-01-20 6:58 UTC (permalink / raw)
Mathieu Desnoyers wrote:
> * Lai Jiangshan (laijs at cn.fujitsu.com) wrote:
>> Mathieu Desnoyers wrote:
>>> Hi Gui,
>>>
>>> My short-term roadmap is (those are the show stoppers) :
>>>
>>> - Get lttng ascii dump to work.
>>> - create periodical buffer flush per-cpu timer for data streaming
>>> - Modify LTTng/lttd/lttv to support variable-sized buffers, so we
>>> don't have to copy the padding. This implies creating an index in
>>> lttv when opening the trace to know where the subbuffer start is.
>>> - Waiting for Lai's channel and event ID management modifications
>>> following my comments. This is needed so the IDs stays valid for the
>>> binary->ascii in-kernel converter.
>>> - Support dynamic frequency scaling on x86.
>>>
>>> Other nice-to-have, but not a priority :
>>>
>>> - Add support for Performance Monitoring Counters (PMC) so they can be
>>> dumped in the traces.
>>> - Put back support for kernel and userspace stack dump so it can be
>>> connected to any given tracepoint.
>>> - Linux ABI for fast userspace tracing.
>>> - Then, add NPTL instrumentation (mutexes, phtreads).
>>> - Integrate LTTng with LKCD, test with kernel crash extraction,
>>> create tools to simplify extraction of traces from crashed kernel,
>>> integrate those tools to ltt-control.
>> We will implement it.
>>
>
> Great :) Note that there has already been some work done on this. This
> in available as an addition to the crosscrash tool :
>
> http://sourceforge.net/projects/crosscrash/
>
> There is a cross-crash-ltt.patch file available on the project website,
> but I think it has not been updated since 2007. Some integration work
> will have to be done.
>
> Also outputting the traces in the video card's memory would be a
> nice-to-have, because this memory often survives hot reboots.
I will use kdump. kdump is in the mainline, I think kdump is better
than crosscrash & LKCD.
>
>>> - Create an in-kernel event filtering module which connects on
>>> LTTng.
>>> - Early boot tracing.
>> What your plan for early boot tracing?
>>
>
> Basically :
>
> - Finding where is the soonest in main.c we can put tracepoints and
> start tracing (this is probably after rcu and memory init).
> - Figuring out the TSC problems that could arise with tracing a booting
> system.
> - Allowing kernel command line arguments in a special LTTng module to
> control early boot tracing.
> - Allowing the kernel to allocate buffers very early at boot time. This
> would also make sure that we have large contiguous memory regions
> available for allocation.
>
> Please don't hesitate to ask if you need more details.
>
> Best regards,
>
> Mathieu
>
>>> - Virtual machine tracing support (time synchronisation).
>>> - Cluster and distributed computer tracing support (time
>>> synchronisation).
>>> - Port kmemtrace, ftrace, blktrace, kvmtrace and others to LTTng.
>>> Each could have its own channel.
>>>
>>> Please ask if you need more information on specific items.
>>>
>>> Best regards, and many thanks to Fujitsu for the good work,
>>>
>>> Mathieu
>>>
>>>
>>> * Gui Jianfeng (guijianfeng at cn.fujitsu.com) wrote:
>>>> Hi Mathieu,
>>>>
>>>> I'd like to know whether you have a plan or roadmap
>>>> for lttng's further developping.
>>>> If you have one, would you share it?
>>>>
>>>> --
>>>> Regards
>>>> Gui Jianfeng
>>>>
>>
>>
>> _______________________________________________
>> ltt-dev mailing list
>> ltt-dev at lists.casi.polymtl.ca
>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-01-20 6:58 ` Lai Jiangshan
@ 2009-01-23 3:21 ` KOSAKI Motohiro
0 siblings, 0 replies; 24+ messages in thread
From: KOSAKI Motohiro @ 2009-01-23 3:21 UTC (permalink / raw)
> Mathieu Desnoyers wrote:
> > * Lai Jiangshan (laijs at cn.fujitsu.com) wrote:
> >> Mathieu Desnoyers wrote:
> >>> Hi Gui,
> >>>
> >>> My short-term roadmap is (those are the show stoppers) :
> >>>
> >>> - Get lttng ascii dump to work.
> >>> - create periodical buffer flush per-cpu timer for data streaming
> >>> - Modify LTTng/lttd/lttv to support variable-sized buffers, so we
> >>> don't have to copy the padding. This implies creating an index in
> >>> lttv when opening the trace to know where the subbuffer start is.
> >>> - Waiting for Lai's channel and event ID management modifications
> >>> following my comments. This is needed so the IDs stays valid for the
> >>> binary->ascii in-kernel converter.
> >>> - Support dynamic frequency scaling on x86.
> >>>
> >>> Other nice-to-have, but not a priority :
> >>>
> >>> - Add support for Performance Monitoring Counters (PMC) so they can be
> >>> dumped in the traces.
> >>> - Put back support for kernel and userspace stack dump so it can be
> >>> connected to any given tracepoint.
> >>> - Linux ABI for fast userspace tracing.
> >>> - Then, add NPTL instrumentation (mutexes, phtreads).
> >>> - Integrate LTTng with LKCD, test with kernel crash extraction,
> >>> create tools to simplify extraction of traces from crashed kernel,
> >>> integrate those tools to ltt-control.
> >> We will implement it.
> >>
> >
> > Great :) Note that there has already been some work done on this. This
> > in available as an addition to the crosscrash tool :
> >
> > http://sourceforge.net/projects/crosscrash/
> >
> > There is a cross-crash-ltt.patch file available on the project website,
> > but I think it has not been updated since 2007. Some integration work
> > will have to be done.
> >
> > Also outputting the traces in the video card's memory would be a
> > nice-to-have, because this memory often survives hot reboots.
>
> I will use kdump. kdump is in the mainline, I think kdump is better
> than crosscrash & LKCD.
Yup, LKCD was perfectly gone.
please ignore LKCD.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-01-20 1:55 ` Mathieu Desnoyers
2009-01-20 6:58 ` Lai Jiangshan
@ 2009-01-23 3:21 ` KOSAKI Motohiro
2009-01-23 4:49 ` Mathieu Desnoyers
1 sibling, 1 reply; 24+ messages in thread
From: KOSAKI Motohiro @ 2009-01-23 3:21 UTC (permalink / raw)
> > > - Integrate LTTng with LKCD, test with kernel crash extraction,
> > > create tools to simplify extraction of traces from crashed kernel,
> > > integrate those tools to ltt-control.
> >
> > We will implement it.
> >
>
> Great :) Note that there has already been some work done on this. This
> in available as an addition to the crosscrash tool :
>
> http://sourceforge.net/projects/crosscrash/
>
> There is a cross-crash-ltt.patch file available on the project website,
> but I think it has not been updated since 2007. Some integration work
> will have to be done.
>
> Also outputting the traces in the video card's memory would be a
> nice-to-have, because this memory often survives hot reboots.
Just dumb question.
Do you receive this feature requesting from anybody?
Currently, expected flight-recored mode user is enterprise guys.
They usually use crash dumps. then, kernel panic cause tons i/o anyway
although tracing infomation don't.
Then, this feature doesn't reduce rebooting times, I think.
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-01-23 3:21 ` KOSAKI Motohiro
@ 2009-01-23 4:49 ` Mathieu Desnoyers
0 siblings, 0 replies; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-01-23 4:49 UTC (permalink / raw)
* KOSAKI Motohiro (kosaki.motohiro at jp.fujitsu.com) wrote:
> > > > - Integrate LTTng with LKCD, test with kernel crash extraction,
> > > > create tools to simplify extraction of traces from crashed kernel,
> > > > integrate those tools to ltt-control.
> > >
> > > We will implement it.
> > >
> >
> > Great :) Note that there has already been some work done on this. This
> > in available as an addition to the crosscrash tool :
> >
> > http://sourceforge.net/projects/crosscrash/
> >
> > There is a cross-crash-ltt.patch file available on the project website,
> > but I think it has not been updated since 2007. Some integration work
> > will have to be done.
> >
> > Also outputting the traces in the video card's memory would be a
> > nice-to-have, because this memory often survives hot reboots.
>
> Just dumb question.
>
> Do you receive this feature requesting from anybody?
>
> Currently, expected flight-recored mode user is enterprise guys.
> They usually use crash dumps. then, kernel panic cause tons i/o anyway
> although tracing infomation don't.
>
> Then, this feature doesn't reduce rebooting times, I think.
>
Yes, people in enterprise need to be able to diagnose _why_ the system
failed. Failing once is acceptable from a customer perspective, but
failing again isn't. In this case, being able to extract the last events
before the crash can be very valuable and helps solving the problems
before they happen again.
I agree with you that short reboot times are also very important in
production, and we need to find a solution which ideally could provide
both :
- short reboot time
- availability of tracing data across reboot
I think that using memory such as what is available in video cards
(which survives hot reboots) could be a solution to this : the system
could reboot as soon as it crashes, and the trace would be available to
be extracted from the machine once the system is back online (given that
we put the next trace buffers at different memory addresses of the video
card).
Long reboot times seems to be a core problem with the crash dump
approach. Do you think the video card memory scheme could become
attractive enough that people would need it ?
Mathieu
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-01-07 17:05 ` [ltt-dev] lttng development plan Mathieu Desnoyers
2009-01-19 3:43 ` Lai Jiangshan
@ 2009-02-02 4:59 ` Gui Jianfeng
2009-02-03 5:11 ` Mathieu Desnoyers
1 sibling, 1 reply; 24+ messages in thread
From: Gui Jianfeng @ 2009-02-02 4:59 UTC (permalink / raw)
Mathieu Desnoyers wrote:
> Hi Gui,
...
>
> Other nice-to-have, but not a priority :
>
> - Put back support for kernel and userspace stack dump so it can be
> connected to any given tracepoint.
> - Linux ABI for fast userspace tracing.
> - Then, add NPTL instrumentation (mutexes, phtreads).
> - Create an in-kernel event filtering module which connects on
> LTTng.
Hi Mathieu,
Could you elaborate these three todos a bit?
>
> Please ask if you need more information on specific items.
>
> Best regards, and many thanks to Fujitsu for the good work,
>
> Mathieu
>
>
> * Gui Jianfeng (guijianfeng at cn.fujitsu.com) wrote:
>> Hi Mathieu,
>>
>> I'd like to know whether you have a plan or roadmap
>> for lttng's further developping.
>> If you have one, would you share it?
>>
>> --
>> Regards
>> Gui Jianfeng
>>
>
--
Regards
Gui Jianfeng
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-02-02 4:59 ` Gui Jianfeng
@ 2009-02-03 5:11 ` Mathieu Desnoyers
2009-02-11 6:33 ` Zhaolei
0 siblings, 1 reply; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-02-03 5:11 UTC (permalink / raw)
* Gui Jianfeng (guijianfeng at cn.fujitsu.com) wrote:
> Mathieu Desnoyers wrote:
> > Hi Gui,
> ...
> >
> > Other nice-to-have, but not a priority :
> >
> > - Put back support for kernel and userspace stack dump so it can be
> > connected to any given tracepoint.
> > - Linux ABI for fast userspace tracing.
> > - Then, add NPTL instrumentation (mutexes, phtreads).
> > - Create an in-kernel event filtering module which connects on
> > LTTng.
>
> Hi Mathieu,
>
> Could you elaborate these three todos a bit?
>
Hi Gui,
Yes, I'll be happy to.
- Put back support for kernel and userspace stack dump so it can be
connected to any given tracepoint.
Kernel stack dump :
It was available with older LTTng versions for a few architectures. It's
basically to have the ability to walk the stack like
arch/x86/kernel/stacktrace.c does, but to output the information as
trace events rather than to send it through printk.
A good starting point is the current arch/x86/kernel/stacktrace.c
implementation (and each architecture's implementation). We should
probably hook into this code which is now much more generic than it
previously was. We should probably add the "sequence" type back into
LTTng to support events with the following field layout :
{
u32 size;
data_type data[size];
}
Basically, it's a variable-length array which starts by indicating its
size. We would have to reserve a format-string identifier which could
look like #a#4u#p%d#p (#a would stand for variable-length sequence). This
example would expect an integer (the number of elements) and a pointer
from C, and write :
{
u32 size; (expressed by #4u)
void *data[size]; (expressed by #p)
}
in the trace.
The parameters would mean :
#a : sequence
#4u : field expressing the number of elements is a u32
#p : elements are of type void *
%d : number of elements
%p : pointer to an array containing the data to record
Note that this basic type would not completely fulfill what we need,
because it would imply copying all the input data (the pointers on the
stack) to an intermediate buffer before the tracer code is called. This
kind of supplementary copy is just unacceptable for performance.
Given we don't know how many pointers we will have to save we have to
calculate the length we need with ltt_get_data_size() and then we copy
the data in the ltt_write_event_data call. Note that we have to make
this solid and deal with concurrent source data modification by padding
missing data with zeroes and by making sure we never go over the
allocated event space.
The way I figure to use the "sequence" field without requiring
intermediate copy of the data is to create a variation of the sequence
which we could call "callback sequence". Basically, in the trace data,
the layout is exactly the same as the sequence. However, the C
parameters are not the same. It would receive a function pointer and a
pointer to pass as parameter to this callback. Those would be called
from both ltt_get_data_size() (to get the sequence size) and from
ltt_write_event_data() to copy the data to the trace buffer. This could
be represented as #A#4u#p%p%p for instance. Those would respectively
mean :
#A : callback sequence
#4u : field expressing the number of elements is a u32
#p : elements are of type void *
%p : callback function pointer
%p : data passed as parameter to the function pointer
This could all be added to ltt-serialize.c. (note that my large comment
before parse_trace_type() should be updated according to what I just
wrote here)
Userspace stack dump :
It was also available with older LTTng versions. Also
architecture-specific. It provides basic ability to save as trace events
what looks like instruction pointers on the userspace stack. It's very
useful when done at system call entry to find out what is the call stack
doing such syscall. It used the "text addresses" of the process to guess
what would be a function pointer or not. It did not support listing
instruction pointers within libraries neither. Multithreading had to be
taken care of appropriately, since thread stacks are close one to
another (and we don't know when we skip from one stack to the next). I
had a couple of heuristics for this. Supporting stacks both with and
without frame pointers is also good.
We will also need the sequence type to record this type of event.
- Linux ABI for fast userspace tracing.
- Then, add NPTL instrumentation (mutexes, phtreads).
We are currently proposing a design document for this. Pierre-Marc
Fournier will likely start working on this this winter. Comments,
feedback and help is welcome. Please see :
http://www.lttng.org/svn/trunk/lttv/doc/developer/ust.html
- Create an in-kernel event filtering module which connects on
LTTng.
Well, I think this last item would be a generalization of the filtering
module I created for ext4 and jbd2 recently. The one I did provided
basic filtering for inode and device number.
The idea would be to extend this and to connect it as a filter callback
with
ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
THIS_MODULE);
This module would be called by ltt_vtrace and _ltt_specialized_trace
with this test :
if (unlikely(!ltt_run_filter(trace, eID)))
continue;
for each active trace.
This filter would receive the trace information and the event ID.
We may have to add or change some parameters to this to support
filtering by fields. For the filter called from ltt_vtrace, passing :
const struct marker *mdata
and
const char *fmt, va_list *args
Should be more than enough to filter generically by
- channel name
- event name
- field name -> typed field data.
Filtering should be pre-computed as much as possible and be O(1) when
executed. Creating callbacks for each expected data type to filter will
probably be requried. A technique similar to what we have done in lttv
filter.c should be considered.
For specialized probes, it might be more difficult to do this
generically, because _ltt_specialized_trace has no knowledge of the
event fields. I guess we would have to create "specialized" filtering
callbacks for those custom trace points. It's their nature anyway.
Please ask if anything is unclear. Comments/ideas are welcome.
Mathieu
> >
> > Please ask if you need more information on specific items.
> >
> > Best regards, and many thanks to Fujitsu for the good work,
> >
> > Mathieu
> >
> >
> > * Gui Jianfeng (guijianfeng at cn.fujitsu.com) wrote:
> >> Hi Mathieu,
> >>
> >> I'd like to know whether you have a plan or roadmap
> >> for lttng's further developping.
> >> If you have one, would you share it?
> >>
> >> --
> >> Regards
> >> Gui Jianfeng
> >>
> >
>
> --
> Regards
> Gui Jianfeng
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-02-03 5:11 ` Mathieu Desnoyers
@ 2009-02-11 6:33 ` Zhaolei
2009-02-11 6:50 ` Mathieu Desnoyers
0 siblings, 1 reply; 24+ messages in thread
From: Zhaolei @ 2009-02-11 6:33 UTC (permalink / raw)
* Mathieu Desnoyers Wrote:
>
> - Create an in-kernel event filtering module which connects on
> LTTng.
>
> Well, I think this last item would be a generalization of the filtering
> module I created for ext4 and jbd2 recently. The one I did provided
> basic filtering for inode and device number.
Hello, Matuieu
I readed filter code of ext4 and jbd2 module,
and I have some questions of generalization filter implementation.
>
> The idea would be to extend this and to connect it as a filter callback
> with
> ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
> THIS_MODULE);
Now ltt_module_register can only register one filter_callback.
But I think when we make one filter for all types of tracepoint, it is hard to
maintenance.
Maybe it is better to make each filter for each type of event, for ex:
ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
We can select to add filter code into ltt/probes/XXX_trace.c, and call
ltt_module_register on modile_init, or write filter code in a module alone.
Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
more readable.
Another thing needs consider is call which filter on a event.
1: Call every filter for one event.
If one filter say "no", this event is ignore.
It is simple to write, and each filter can do every thing(flexible).
But this way is inefficient because it needs more cpu cycles.
2: Call only filter for current event type.
Filter register a event type on ltt_module_register, and only used to
process that type.
I think 2 is better because it uses less CPU.
>
> This module would be called by ltt_vtrace and _ltt_specialized_trace
> with this test :
>
> if (unlikely(!ltt_run_filter(trace, eID)))
> continue;
>
> for each active trace.
I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
more efficient than this way.
Consider that a event which is filtered(don't send to relay), more
unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
So, in generalization filter:
1: call filter in ltt_vtrace(_ltt_specialized_trace)
2: call filter in probe_XXX_checkpoint()
I think 2 is better.
Which is your opinion about this?
B.R.
Zhaolei
>
> This filter would receive the trace information and the event ID.
> We may have to add or change some parameters to this to support
> filtering by fields. For the filter called from ltt_vtrace, passing :
>
> const struct marker *mdata
> and
> const char *fmt, va_list *args
>
> Should be more than enough to filter generically by
> - channel name
> - event name
> - field name -> typed field data.
>
> Filtering should be pre-computed as much as possible and be O(1) when
> executed. Creating callbacks for each expected data type to filter will
> probably be requried. A technique similar to what we have done in lttv
> filter.c should be considered.
>
> For specialized probes, it might be more difficult to do this
> generically, because _ltt_specialized_trace has no knowledge of the
> event fields. I guess we would have to create "specialized" filtering
> callbacks for those custom trace points. It's their nature anyway.
>
> Please ask if anything is unclear. Comments/ideas are welcome.
>
> Mathieu
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-02-11 6:33 ` Zhaolei
@ 2009-02-11 6:50 ` Mathieu Desnoyers
2009-02-11 7:24 ` Zhaolei
0 siblings, 1 reply; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-02-11 6:50 UTC (permalink / raw)
* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> * Mathieu Desnoyers Wrote:
> >
> > - Create an in-kernel event filtering module which connects on
> > LTTng.
> >
> > Well, I think this last item would be a generalization of the filtering
> > module I created for ext4 and jbd2 recently. The one I did provided
> > basic filtering for inode and device number.
> Hello, Matuieu
>
> I readed filter code of ext4 and jbd2 module,
> and I have some questions of generalization filter implementation.
>
> >
> > The idea would be to extend this and to connect it as a filter callback
> > with
> > ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
> > THIS_MODULE);
> Now ltt_module_register can only register one filter_callback.
> But I think when we make one filter for all types of tracepoint, it is hard to
> maintenance.
>
Not exactly. That would be one filter for all types of markers. And it
would iterate on the marker format string to filter by field name. So I
don't see where the maintenant burden is ?
> Maybe it is better to make each filter for each type of event, for ex:
> ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
>
> We can select to add filter code into ltt/probes/XXX_trace.c, and call
> ltt_module_register on modile_init, or write filter code in a module alone.
> Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
> more readable.
>
> Another thing needs consider is call which filter on a event.
> 1: Call every filter for one event.
> If one filter say "no", this event is ignore.
> It is simple to write, and each filter can do every thing(flexible).
> But this way is inefficient because it needs more cpu cycles.
> 2: Call only filter for current event type.
> Filter register a event type on ltt_module_register, and only used to
> process that type.
> I think 2 is better because it uses less CPU.
>
Yes, filters definitely have to be called only for their own event ID
for performance reasons.
> >
> > This module would be called by ltt_vtrace and _ltt_specialized_trace
> > with this test :
> >
> > if (unlikely(!ltt_run_filter(trace, eID)))
> > continue;
> >
> > for each active trace.
> I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
> more efficient than this way.
> Consider that a event which is filtered(don't send to relay), more
> unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
>
> So, in generalization filter:
> 1: call filter in ltt_vtrace(_ltt_specialized_trace)
> 2: call filter in probe_XXX_checkpoint()
> I think 2 is better.
>
> Which is your opinion about this?
>
There is a feature of LTTng which would benefit of 1 : lttng can have
multiple trace sessions active at once. Therefore, if one trace session
needs a subset of events and another trace session need a different set,
then they could each have their own filter structure associated with
them. I think having this level of flexibility is more important than
the few cycles we could save by doing (2). Also, we already have the
tracepoints and markers to deactivate the event source at the kernel
level when we need to have nearly-zero overhead.
Mathieu
> B.R.
> Zhaolei
>
> >
> > This filter would receive the trace information and the event ID.
> > We may have to add or change some parameters to this to support
> > filtering by fields. For the filter called from ltt_vtrace, passing :
> >
> > const struct marker *mdata
> > and
> > const char *fmt, va_list *args
> >
> > Should be more than enough to filter generically by
> > - channel name
> > - event name
> > - field name -> typed field data.
> >
> > Filtering should be pre-computed as much as possible and be O(1) when
> > executed. Creating callbacks for each expected data type to filter will
> > probably be requried. A technique similar to what we have done in lttv
> > filter.c should be considered.
> >
> > For specialized probes, it might be more difficult to do this
> > generically, because _ltt_specialized_trace has no knowledge of the
> > event fields. I guess we would have to create "specialized" filtering
> > callbacks for those custom trace points. It's their nature anyway.
> >
> > Please ask if anything is unclear. Comments/ideas are welcome.
> >
> > Mathieu
> >
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-02-11 6:50 ` Mathieu Desnoyers
@ 2009-02-11 7:24 ` Zhaolei
2009-02-11 7:37 ` Mathieu Desnoyers
0 siblings, 1 reply; 24+ messages in thread
From: Zhaolei @ 2009-02-11 7:24 UTC (permalink / raw)
* Mathieu Desnoyers Wrote:
> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>> * Mathieu Desnoyers Wrote:
>>> - Create an in-kernel event filtering module which connects on
>>> LTTng.
>>>
>>> Well, I think this last item would be a generalization of the filtering
>>> module I created for ext4 and jbd2 recently. The one I did provided
>>> basic filtering for inode and device number.
>> Hello, Matuieu
>>
>> I readed filter code of ext4 and jbd2 module,
>> and I have some questions of generalization filter implementation.
>>
>>> The idea would be to extend this and to connect it as a filter callback
>>> with
>>> ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
>>> THIS_MODULE);
>> Now ltt_module_register can only register one filter_callback.
>> But I think when we make one filter for all types of tracepoint, it is hard to
>> maintenance.
>>
>
> Not exactly. That would be one filter for all types of markers. And it
> would iterate on the marker format string to filter by field name. So I
> don't see where the maintenant burden is ?
>
Hello, Mathieu
Thanks for your response.
Do you means we only need one filter callback function in all lttng source,
and this callback is used for every event in one trace?
And programmer of ltt/probes/* don't need to consider about filter function
as ext4 and jbd2?
B.R.
Zhaolei
>> Maybe it is better to make each filter for each type of event, for ex:
>> ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
>>
>> We can select to add filter code into ltt/probes/XXX_trace.c, and call
>> ltt_module_register on modile_init, or write filter code in a module alone.
>> Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
>> more readable.
>>
>> Another thing needs consider is call which filter on a event.
>> 1: Call every filter for one event.
>> If one filter say "no", this event is ignore.
>> It is simple to write, and each filter can do every thing(flexible).
>> But this way is inefficient because it needs more cpu cycles.
>> 2: Call only filter for current event type.
>> Filter register a event type on ltt_module_register, and only used to
>> process that type.
>> I think 2 is better because it uses less CPU.
>>
>
> Yes, filters definitely have to be called only for their own event ID
> for performance reasons.
>
>>> This module would be called by ltt_vtrace and _ltt_specialized_trace
>>> with this test :
>>>
>>> if (unlikely(!ltt_run_filter(trace, eID)))
>>> continue;
>>>
>>> for each active trace.
>> I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
>> more efficient than this way.
>> Consider that a event which is filtered(don't send to relay), more
>> unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
>>
>> So, in generalization filter:
>> 1: call filter in ltt_vtrace(_ltt_specialized_trace)
>> 2: call filter in probe_XXX_checkpoint()
>> I think 2 is better.
>>
>> Which is your opinion about this?
>>
>
> There is a feature of LTTng which would benefit of 1 : lttng can have
> multiple trace sessions active at once. Therefore, if one trace session
> needs a subset of events and another trace session need a different set,
> then they could each have their own filter structure associated with
> them. I think having this level of flexibility is more important than
> the few cycles we could save by doing (2). Also, we already have the
> tracepoints and markers to deactivate the event source at the kernel
> level when we need to have nearly-zero overhead.
>
> Mathieu
>
>> B.R.
>> Zhaolei
>>
>>> This filter would receive the trace information and the event ID.
>>> We may have to add or change some parameters to this to support
>>> filtering by fields. For the filter called from ltt_vtrace, passing :
>>>
>>> const struct marker *mdata
>>> and
>>> const char *fmt, va_list *args
>>>
>>> Should be more than enough to filter generically by
>>> - channel name
>>> - event name
>>> - field name -> typed field data.
>>>
>>> Filtering should be pre-computed as much as possible and be O(1) when
>>> executed. Creating callbacks for each expected data type to filter will
>>> probably be requried. A technique similar to what we have done in lttv
>>> filter.c should be considered.
>>>
>>> For specialized probes, it might be more difficult to do this
>>> generically, because _ltt_specialized_trace has no knowledge of the
>>> event fields. I guess we would have to create "specialized" filtering
>>> callbacks for those custom trace points. It's their nature anyway.
>>>
>>> Please ask if anything is unclear. Comments/ideas are welcome.
>>>
>>> Mathieu
>>>
>>
>> _______________________________________________
>> ltt-dev mailing list
>> ltt-dev at lists.casi.polymtl.ca
>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-02-11 7:24 ` Zhaolei
@ 2009-02-11 7:37 ` Mathieu Desnoyers
2009-02-19 8:17 ` Zhaolei
0 siblings, 1 reply; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-02-11 7:37 UTC (permalink / raw)
* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> * Mathieu Desnoyers Wrote:
> > * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> >> * Mathieu Desnoyers Wrote:
> >>> - Create an in-kernel event filtering module which connects on
> >>> LTTng.
> >>>
> >>> Well, I think this last item would be a generalization of the filtering
> >>> module I created for ext4 and jbd2 recently. The one I did provided
> >>> basic filtering for inode and device number.
> >> Hello, Matuieu
> >>
> >> I readed filter code of ext4 and jbd2 module,
> >> and I have some questions of generalization filter implementation.
> >>
> >>> The idea would be to extend this and to connect it as a filter callback
> >>> with
> >>> ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
> >>> THIS_MODULE);
> >> Now ltt_module_register can only register one filter_callback.
> >> But I think when we make one filter for all types of tracepoint, it is hard to
> >> maintenance.
> >>
> >
> > Not exactly. That would be one filter for all types of markers. And it
> > would iterate on the marker format string to filter by field name. So I
> > don't see where the maintenant burden is ?
> >
> Hello, Mathieu
>
> Thanks for your response.
>
> Do you means we only need one filter callback function in all lttng source,
> and this callback is used for every event in one trace?
>
> And programmer of ltt/probes/* don't need to consider about filter function
> as ext4 and jbd2?
>
Exactly.
Mathieu
> B.R.
> Zhaolei
>
> >> Maybe it is better to make each filter for each type of event, for ex:
> >> ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
> >>
> >> We can select to add filter code into ltt/probes/XXX_trace.c, and call
> >> ltt_module_register on modile_init, or write filter code in a module alone.
> >> Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
> >> more readable.
> >>
> >> Another thing needs consider is call which filter on a event.
> >> 1: Call every filter for one event.
> >> If one filter say "no", this event is ignore.
> >> It is simple to write, and each filter can do every thing(flexible).
> >> But this way is inefficient because it needs more cpu cycles.
> >> 2: Call only filter for current event type.
> >> Filter register a event type on ltt_module_register, and only used to
> >> process that type.
> >> I think 2 is better because it uses less CPU.
> >>
> >
> > Yes, filters definitely have to be called only for their own event ID
> > for performance reasons.
> >
> >>> This module would be called by ltt_vtrace and _ltt_specialized_trace
> >>> with this test :
> >>>
> >>> if (unlikely(!ltt_run_filter(trace, eID)))
> >>> continue;
> >>>
> >>> for each active trace.
> >> I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
> >> more efficient than this way.
> >> Consider that a event which is filtered(don't send to relay), more
> >> unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
> >>
> >> So, in generalization filter:
> >> 1: call filter in ltt_vtrace(_ltt_specialized_trace)
> >> 2: call filter in probe_XXX_checkpoint()
> >> I think 2 is better.
> >>
> >> Which is your opinion about this?
> >>
> >
> > There is a feature of LTTng which would benefit of 1 : lttng can have
> > multiple trace sessions active at once. Therefore, if one trace session
> > needs a subset of events and another trace session need a different set,
> > then they could each have their own filter structure associated with
> > them. I think having this level of flexibility is more important than
> > the few cycles we could save by doing (2). Also, we already have the
> > tracepoints and markers to deactivate the event source at the kernel
> > level when we need to have nearly-zero overhead.
> >
> > Mathieu
> >
> >> B.R.
> >> Zhaolei
> >>
> >>> This filter would receive the trace information and the event ID.
> >>> We may have to add or change some parameters to this to support
> >>> filtering by fields. For the filter called from ltt_vtrace, passing :
> >>>
> >>> const struct marker *mdata
> >>> and
> >>> const char *fmt, va_list *args
> >>>
> >>> Should be more than enough to filter generically by
> >>> - channel name
> >>> - event name
> >>> - field name -> typed field data.
> >>>
> >>> Filtering should be pre-computed as much as possible and be O(1) when
> >>> executed. Creating callbacks for each expected data type to filter will
> >>> probably be requried. A technique similar to what we have done in lttv
> >>> filter.c should be considered.
> >>>
> >>> For specialized probes, it might be more difficult to do this
> >>> generically, because _ltt_specialized_trace has no knowledge of the
> >>> event fields. I guess we would have to create "specialized" filtering
> >>> callbacks for those custom trace points. It's their nature anyway.
> >>>
> >>> Please ask if anything is unclear. Comments/ideas are welcome.
> >>>
> >>> Mathieu
> >>>
> >>
> >> _______________________________________________
> >> ltt-dev mailing list
> >> ltt-dev at lists.casi.polymtl.ca
> >> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
> >>
> >
>
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-02-11 7:37 ` Mathieu Desnoyers
@ 2009-02-19 8:17 ` Zhaolei
2009-03-02 7:01 ` Zhaolei
0 siblings, 1 reply; 24+ messages in thread
From: Zhaolei @ 2009-02-19 8:17 UTC (permalink / raw)
* Mathieu Desnoyers wrote:
> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>> * Mathieu Desnoyers Wrote:
>>> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>>>> * Mathieu Desnoyers Wrote:
>>>>> - Create an in-kernel event filtering module which connects on
>>>>> LTTng.
>>>>>
>>>>> Well, I think this last item would be a generalization of the filtering
>>>>> module I created for ext4 and jbd2 recently. The one I did provided
>>>>> basic filtering for inode and device number.
>>>> Hello, Matuieu
>>>>
>>>> I readed filter code of ext4 and jbd2 module,
>>>> and I have some questions of generalization filter implementation.
>>>>
>>>>> The idea would be to extend this and to connect it as a filter callback
>>>>> with
>>>>> ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
>>>>> THIS_MODULE);
>>>> Now ltt_module_register can only register one filter_callback.
>>>> But I think when we make one filter for all types of tracepoint, it is hard to
>>>> maintenance.
>>>>
>>> Not exactly. That would be one filter for all types of markers. And it
>>> would iterate on the marker format string to filter by field name. So I
>>> don't see where the maintenant burden is ?
>>>
>> Hello, Mathieu
>>
>> Thanks for your response.
>>
>> Do you means we only need one filter callback function in all lttng source,
>> and this callback is used for every event in one trace?
>>
>> And programmer of ltt/probes/* don't need to consider about filter function
>> as ext4 and jbd2?
>>
>
> Exactly.
>
> Mathieu
>
Hello, Mathieu
I'm planing to make a in-kernel-filter for lttng.
This is my image of function and schedule:
Function:
Build config dir-struct in debugfs
Dir can dynamic updated when add/remove a module with matkers.
Data's name is a symlink to __argX, because some data is noname, or some
data's name is not filename, or some data have samename, for ex:
Normal: "name1 %d name2 %d" -> __arg1(name1) __arg2(name2)
No name: "%d %d" -> __arg1() __arg2()
Not filenane: "* %d" -> __arg1()
Same name: "name %d name %d" -> __arg1(name) __arg2()
__arg0 is format string, to make user easy to read:
# echo [debugfs]/ltt/filter/allocate_blocks/__arg0:
dev %s block %llu flags %u len %u ino %lu logical %llu goal %llu lleft %llu lright %llu pleft %llu pright %llu
Dir struct is like this:
[debugfs]/ltt/filter
|-- allocate_blocks
| |-- __arg0
| |-- __arg1
| |-- __arg2
| |-- __arg3
| |-- __arg4
| |-- __arg5
| |-- __arg6
| |-- __arg7
| |-- __arg8
| |-- __arg9
| |-- __arg10
| |-- __arg11
| |-- dev -> __arg1
| |-- block -> __arg2
| |-- flags -> __arg3
| |-- len -> __arg4
| |-- ino -> __arg5
| |-- logical -> __arg6
| |-- goal -> __arg7
| |-- lleft -> __arg8
| |-- lright -> __arg9
| |-- pleft -> __arg10
| `-- pright -> __arg11
|-- allocate_inode
| |-- __arg0
| |-- __arg1
| |-- __arg2
| |-- __arg3
| |-- __arg4
| |-- dev -> __arg1
| |-- dir -> __arg3
| |-- ino -> __arg2
| `-- mode -> __arg4
|-- bio_backmerge
| `-- ...
|-- bio_bounce
| `-- ...
|-- bio_complete
| `-- ...
|-- bio_frontmerge
| `-- ...
`-- ...
User can config filter by write something to following debugfs files:
Ex:
echo "*" > [debugfs]/ltt/filter/allocate_blocks/len # enable all len
echo ">100" > [debugfs]/ltt/filter/allocate_blocks/len # enable len>100
Schedule:
1: Make basic function work
2: Make support for per-trace-filter
Make dir struct from:
[debugfs]/ltt/filter/[marker_name]/[arg_name]
to
[debugfs]/ltt/filter/[trace]/[marker_name]/[arg_name]
Make and use callback when add/delete trace.
Update filter control dir when add/delete trace.
3: Otimize for speed(use hlist, rcu, ...)
4: (Maybe) Support echo "(>0 && <10) || (>50 && <100)" > len
and echo "eth*" > name
Do you have some suggestion?
B.R.
Zhaolei
>> B.R.
>> Zhaolei
>>
>>>> Maybe it is better to make each filter for each type of event, for ex:
>>>> ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
>>>>
>>>> We can select to add filter code into ltt/probes/XXX_trace.c, and call
>>>> ltt_module_register on modile_init, or write filter code in a module alone.
>>>> Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
>>>> more readable.
>>>>
>>>> Another thing needs consider is call which filter on a event.
>>>> 1: Call every filter for one event.
>>>> If one filter say "no", this event is ignore.
>>>> It is simple to write, and each filter can do every thing(flexible).
>>>> But this way is inefficient because it needs more cpu cycles.
>>>> 2: Call only filter for current event type.
>>>> Filter register a event type on ltt_module_register, and only used to
>>>> process that type.
>>>> I think 2 is better because it uses less CPU.
>>>>
>>> Yes, filters definitely have to be called only for their own event ID
>>> for performance reasons.
>>>
>>>>> This module would be called by ltt_vtrace and _ltt_specialized_trace
>>>>> with this test :
>>>>>
>>>>> if (unlikely(!ltt_run_filter(trace, eID)))
>>>>> continue;
>>>>>
>>>>> for each active trace.
>>>> I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
>>>> more efficient than this way.
>>>> Consider that a event which is filtered(don't send to relay), more
>>>> unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
>>>>
>>>> So, in generalization filter:
>>>> 1: call filter in ltt_vtrace(_ltt_specialized_trace)
>>>> 2: call filter in probe_XXX_checkpoint()
>>>> I think 2 is better.
>>>>
>>>> Which is your opinion about this?
>>>>
>>> There is a feature of LTTng which would benefit of 1 : lttng can have
>>> multiple trace sessions active at once. Therefore, if one trace session
>>> needs a subset of events and another trace session need a different set,
>>> then they could each have their own filter structure associated with
>>> them. I think having this level of flexibility is more important than
>>> the few cycles we could save by doing (2). Also, we already have the
>>> tracepoints and markers to deactivate the event source at the kernel
>>> level when we need to have nearly-zero overhead.
>>>
>>> Mathieu
>>>
>>>> B.R.
>>>> Zhaolei
>>>>
>>>>> This filter would receive the trace information and the event ID.
>>>>> We may have to add or change some parameters to this to support
>>>>> filtering by fields. For the filter called from ltt_vtrace, passing :
>>>>>
>>>>> const struct marker *mdata
>>>>> and
>>>>> const char *fmt, va_list *args
>>>>>
>>>>> Should be more than enough to filter generically by
>>>>> - channel name
>>>>> - event name
>>>>> - field name -> typed field data.
>>>>>
>>>>> Filtering should be pre-computed as much as possible and be O(1) when
>>>>> executed. Creating callbacks for each expected data type to filter will
>>>>> probably be requried. A technique similar to what we have done in lttv
>>>>> filter.c should be considered.
>>>>>
>>>>> For specialized probes, it might be more difficult to do this
>>>>> generically, because _ltt_specialized_trace has no knowledge of the
>>>>> event fields. I guess we would have to create "specialized" filtering
>>>>> callbacks for those custom trace points. It's their nature anyway.
>>>>>
>>>>> Please ask if anything is unclear. Comments/ideas are welcome.
>>>>>
>>>>> Mathieu
>>>>>
>>>> _______________________________________________
>>>> ltt-dev mailing list
>>>> ltt-dev at lists.casi.polymtl.ca
>>>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>>>
>>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-02-19 8:17 ` Zhaolei
@ 2009-03-02 7:01 ` Zhaolei
2009-03-03 19:25 ` Mathieu Desnoyers
0 siblings, 1 reply; 24+ messages in thread
From: Zhaolei @ 2009-03-02 7:01 UTC (permalink / raw)
Hello, Mathieu
Sorry fot trouble you, how about my design of ltt-filter?
It no objection, I'd like to starting it.
B.R.
Zhaolei
* Zhaolei Wrote:
> * Mathieu Desnoyers wrote:
>> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>>> * Mathieu Desnoyers Wrote:
>>>> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>>>>> * Mathieu Desnoyers Wrote:
>>>>>> - Create an in-kernel event filtering module which connects on
>>>>>> LTTng.
>>>>>>
>>>>>> Well, I think this last item would be a generalization of the filtering
>>>>>> module I created for ext4 and jbd2 recently. The one I did provided
>>>>>> basic filtering for inode and device number.
>>>>> Hello, Matuieu
>>>>>
>>>>> I readed filter code of ext4 and jbd2 module,
>>>>> and I have some questions of generalization filter implementation.
>>>>>
>>>>>> The idea would be to extend this and to connect it as a filter callback
>>>>>> with
>>>>>> ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
>>>>>> THIS_MODULE);
>>>>> Now ltt_module_register can only register one filter_callback.
>>>>> But I think when we make one filter for all types of tracepoint, it is hard to
>>>>> maintenance.
>>>>>
>>>> Not exactly. That would be one filter for all types of markers. And it
>>>> would iterate on the marker format string to filter by field name. So I
>>>> don't see where the maintenant burden is ?
>>>>
>>> Hello, Mathieu
>>>
>>> Thanks for your response.
>>>
>>> Do you means we only need one filter callback function in all lttng source,
>>> and this callback is used for every event in one trace?
>>>
>>> And programmer of ltt/probes/* don't need to consider about filter function
>>> as ext4 and jbd2?
>>>
>> Exactly.
>>
>> Mathieu
>>
> Hello, Mathieu
>
> I'm planing to make a in-kernel-filter for lttng.
> This is my image of function and schedule:
>
> Function:
> Build config dir-struct in debugfs
>
> Dir can dynamic updated when add/remove a module with matkers.
>
> Data's name is a symlink to __argX, because some data is noname, or some
> data's name is not filename, or some data have samename, for ex:
> Normal: "name1 %d name2 %d" -> __arg1(name1) __arg2(name2)
> No name: "%d %d" -> __arg1() __arg2()
> Not filenane: "* %d" -> __arg1()
> Same name: "name %d name %d" -> __arg1(name) __arg2()
>
> __arg0 is format string, to make user easy to read:
> # echo [debugfs]/ltt/filter/allocate_blocks/__arg0:
> dev %s block %llu flags %u len %u ino %lu logical %llu goal %llu lleft %llu lright %llu pleft %llu pright %llu
>
> Dir struct is like this:
> [debugfs]/ltt/filter
> |-- allocate_blocks
> | |-- __arg0
> | |-- __arg1
> | |-- __arg2
> | |-- __arg3
> | |-- __arg4
> | |-- __arg5
> | |-- __arg6
> | |-- __arg7
> | |-- __arg8
> | |-- __arg9
> | |-- __arg10
> | |-- __arg11
> | |-- dev -> __arg1
> | |-- block -> __arg2
> | |-- flags -> __arg3
> | |-- len -> __arg4
> | |-- ino -> __arg5
> | |-- logical -> __arg6
> | |-- goal -> __arg7
> | |-- lleft -> __arg8
> | |-- lright -> __arg9
> | |-- pleft -> __arg10
> | `-- pright -> __arg11
> |-- allocate_inode
> | |-- __arg0
> | |-- __arg1
> | |-- __arg2
> | |-- __arg3
> | |-- __arg4
> | |-- dev -> __arg1
> | |-- dir -> __arg3
> | |-- ino -> __arg2
> | `-- mode -> __arg4
> |-- bio_backmerge
> | `-- ...
> |-- bio_bounce
> | `-- ...
> |-- bio_complete
> | `-- ...
> |-- bio_frontmerge
> | `-- ...
> `-- ...
>
> User can config filter by write something to following debugfs files:
> Ex:
> echo "*" > [debugfs]/ltt/filter/allocate_blocks/len # enable all len
> echo ">100" > [debugfs]/ltt/filter/allocate_blocks/len # enable len>100
>
> Schedule:
> 1: Make basic function work
> 2: Make support for per-trace-filter
> Make dir struct from:
> [debugfs]/ltt/filter/[marker_name]/[arg_name]
> to
> [debugfs]/ltt/filter/[trace]/[marker_name]/[arg_name]
> Make and use callback when add/delete trace.
> Update filter control dir when add/delete trace.
> 3: Otimize for speed(use hlist, rcu, ...)
> 4: (Maybe) Support echo "(>0 && <10) || (>50 && <100)" > len
> and echo "eth*" > name
>
> Do you have some suggestion?
>
> B.R.
> Zhaolei
>
>>> B.R.
>>> Zhaolei
>>>
>>>>> Maybe it is better to make each filter for each type of event, for ex:
>>>>> ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
>>>>>
>>>>> We can select to add filter code into ltt/probes/XXX_trace.c, and call
>>>>> ltt_module_register on modile_init, or write filter code in a module alone.
>>>>> Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
>>>>> more readable.
>>>>>
>>>>> Another thing needs consider is call which filter on a event.
>>>>> 1: Call every filter for one event.
>>>>> If one filter say "no", this event is ignore.
>>>>> It is simple to write, and each filter can do every thing(flexible).
>>>>> But this way is inefficient because it needs more cpu cycles.
>>>>> 2: Call only filter for current event type.
>>>>> Filter register a event type on ltt_module_register, and only used to
>>>>> process that type.
>>>>> I think 2 is better because it uses less CPU.
>>>>>
>>>> Yes, filters definitely have to be called only for their own event ID
>>>> for performance reasons.
>>>>
>>>>>> This module would be called by ltt_vtrace and _ltt_specialized_trace
>>>>>> with this test :
>>>>>>
>>>>>> if (unlikely(!ltt_run_filter(trace, eID)))
>>>>>> continue;
>>>>>>
>>>>>> for each active trace.
>>>>> I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
>>>>> more efficient than this way.
>>>>> Consider that a event which is filtered(don't send to relay), more
>>>>> unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
>>>>>
>>>>> So, in generalization filter:
>>>>> 1: call filter in ltt_vtrace(_ltt_specialized_trace)
>>>>> 2: call filter in probe_XXX_checkpoint()
>>>>> I think 2 is better.
>>>>>
>>>>> Which is your opinion about this?
>>>>>
>>>> There is a feature of LTTng which would benefit of 1 : lttng can have
>>>> multiple trace sessions active at once. Therefore, if one trace session
>>>> needs a subset of events and another trace session need a different set,
>>>> then they could each have their own filter structure associated with
>>>> them. I think having this level of flexibility is more important than
>>>> the few cycles we could save by doing (2). Also, we already have the
>>>> tracepoints and markers to deactivate the event source at the kernel
>>>> level when we need to have nearly-zero overhead.
>>>>
>>>> Mathieu
>>>>
>>>>> B.R.
>>>>> Zhaolei
>>>>>
>>>>>> This filter would receive the trace information and the event ID.
>>>>>> We may have to add or change some parameters to this to support
>>>>>> filtering by fields. For the filter called from ltt_vtrace, passing :
>>>>>>
>>>>>> const struct marker *mdata
>>>>>> and
>>>>>> const char *fmt, va_list *args
>>>>>>
>>>>>> Should be more than enough to filter generically by
>>>>>> - channel name
>>>>>> - event name
>>>>>> - field name -> typed field data.
>>>>>>
>>>>>> Filtering should be pre-computed as much as possible and be O(1) when
>>>>>> executed. Creating callbacks for each expected data type to filter will
>>>>>> probably be requried. A technique similar to what we have done in lttv
>>>>>> filter.c should be considered.
>>>>>>
>>>>>> For specialized probes, it might be more difficult to do this
>>>>>> generically, because _ltt_specialized_trace has no knowledge of the
>>>>>> event fields. I guess we would have to create "specialized" filtering
>>>>>> callbacks for those custom trace points. It's their nature anyway.
>>>>>>
>>>>>> Please ask if anything is unclear. Comments/ideas are welcome.
>>>>>>
>>>>>> Mathieu
>>>>>>
>>>>> _______________________________________________
>>>>> ltt-dev mailing list
>>>>> ltt-dev at lists.casi.polymtl.ca
>>>>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>>>>
>
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] lttng development plan
2009-03-02 7:01 ` Zhaolei
@ 2009-03-03 19:25 ` Mathieu Desnoyers
2009-03-29 8:41 ` [ltt-dev] [PATCH 0/3] In-kernel filter module Zhaolei
0 siblings, 1 reply; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-03-03 19:25 UTC (permalink / raw)
* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> Hello, Mathieu
>
> Sorry fot trouble you, how about my design of ltt-filter?
> It no objection, I'd like to starting it.
>
> B.R.
> Zhaolei
>
> * Zhaolei Wrote:
> > * Mathieu Desnoyers wrote:
> >> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> >>> * Mathieu Desnoyers Wrote:
> >>>> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> >>>>> * Mathieu Desnoyers Wrote:
> >>>>>> - Create an in-kernel event filtering module which connects on
> >>>>>> LTTng.
> >>>>>>
> >>>>>> Well, I think this last item would be a generalization of the filtering
> >>>>>> module I created for ext4 and jbd2 recently. The one I did provided
> >>>>>> basic filtering for inode and device number.
> >>>>> Hello, Matuieu
> >>>>>
> >>>>> I readed filter code of ext4 and jbd2 module,
> >>>>> and I have some questions of generalization filter implementation.
> >>>>>
> >>>>>> The idea would be to extend this and to connect it as a filter callback
> >>>>>> with
> >>>>>> ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
> >>>>>> THIS_MODULE);
> >>>>> Now ltt_module_register can only register one filter_callback.
> >>>>> But I think when we make one filter for all types of tracepoint, it is hard to
> >>>>> maintenance.
> >>>>>
> >>>> Not exactly. That would be one filter for all types of markers. And it
> >>>> would iterate on the marker format string to filter by field name. So I
> >>>> don't see where the maintenant burden is ?
> >>>>
> >>> Hello, Mathieu
> >>>
> >>> Thanks for your response.
> >>>
> >>> Do you means we only need one filter callback function in all lttng source,
> >>> and this callback is used for every event in one trace?
> >>>
> >>> And programmer of ltt/probes/* don't need to consider about filter function
> >>> as ext4 and jbd2?
> >>>
> >> Exactly.
> >>
> >> Mathieu
> >>
> > Hello, Mathieu
> >
> > I'm planing to make a in-kernel-filter for lttng.
> > This is my image of function and schedule:
> >
> > Function:
> > Build config dir-struct in debugfs
> >
> > Dir can dynamic updated when add/remove a module with matkers.
> >
> > Data's name is a symlink to __argX, because some data is noname, or some
> > data's name is not filename, or some data have samename, for ex:
> > Normal: "name1 %d name2 %d" -> __arg1(name1) __arg2(name2)
> > No name: "%d %d" -> __arg1() __arg2()
> > Not filenane: "* %d" -> __arg1()
> > Same name: "name %d name %d" -> __arg1(name) __arg2()
> >
> > __arg0 is format string, to make user easy to read:
> > # echo [debugfs]/ltt/filter/allocate_blocks/__arg0:
> > dev %s block %llu flags %u len %u ino %lu logical %llu goal %llu lleft %llu lright %llu pleft %llu pright %llu
> >
> > Dir struct is like this:
> > [debugfs]/ltt/filter
> > |-- allocate_blocks
> > | |-- __arg0
> > | |-- __arg1
> > | |-- __arg2
> > | |-- __arg3
> > | |-- __arg4
> > | |-- __arg5
> > | |-- __arg6
> > | |-- __arg7
> > | |-- __arg8
> > | |-- __arg9
> > | |-- __arg10
> > | |-- __arg11
> > | |-- dev -> __arg1
> > | |-- block -> __arg2
> > | |-- flags -> __arg3
> > | |-- len -> __arg4
> > | |-- ino -> __arg5
> > | |-- logical -> __arg6
> > | |-- goal -> __arg7
> > | |-- lleft -> __arg8
> > | |-- lright -> __arg9
> > | |-- pleft -> __arg10
> > | `-- pright -> __arg11
> > |-- allocate_inode
> > | |-- __arg0
> > | |-- __arg1
> > | |-- __arg2
> > | |-- __arg3
> > | |-- __arg4
> > | |-- dev -> __arg1
> > | |-- dir -> __arg3
> > | |-- ino -> __arg2
> > | `-- mode -> __arg4
> > |-- bio_backmerge
> > | `-- ...
> > |-- bio_bounce
> > | `-- ...
> > |-- bio_complete
> > | `-- ...
> > |-- bio_frontmerge
> > | `-- ...
> > `-- ...
> >
> > User can config filter by write something to following debugfs files:
> > Ex:
> > echo "*" > [debugfs]/ltt/filter/allocate_blocks/len # enable all len
> > echo ">100" > [debugfs]/ltt/filter/allocate_blocks/len # enable len>100
> >
> > Schedule:
> > 1: Make basic function work
> > 2: Make support for per-trace-filter
> > Make dir struct from:
> > [debugfs]/ltt/filter/[marker_name]/[arg_name]
> > to
> > [debugfs]/ltt/filter/[trace]/[marker_name]/[arg_name]
> > Make and use callback when add/delete trace.
> > Update filter control dir when add/delete trace.
> > 3: Otimize for speed(use hlist, rcu, ...)
> > 4: (Maybe) Support echo "(>0 && <10) || (>50 && <100)" > len
> > and echo "eth*" > name
> >
> > Do you have some suggestion?
> >
This proposal sounds great. I look forward to see the implementation. :)
The patches from Lai I am currently merging (making marker id permanent
for the whole trace session) will be useful to you when you try to
express your filter data structures.
Mathieu
> > B.R.
> > Zhaolei
> >
> >>> B.R.
> >>> Zhaolei
> >>>
> >>>>> Maybe it is better to make each filter for each type of event, for ex:
> >>>>> ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
> >>>>>
> >>>>> We can select to add filter code into ltt/probes/XXX_trace.c, and call
> >>>>> ltt_module_register on modile_init, or write filter code in a module alone.
> >>>>> Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
> >>>>> more readable.
> >>>>>
> >>>>> Another thing needs consider is call which filter on a event.
> >>>>> 1: Call every filter for one event.
> >>>>> If one filter say "no", this event is ignore.
> >>>>> It is simple to write, and each filter can do every thing(flexible).
> >>>>> But this way is inefficient because it needs more cpu cycles.
> >>>>> 2: Call only filter for current event type.
> >>>>> Filter register a event type on ltt_module_register, and only used to
> >>>>> process that type.
> >>>>> I think 2 is better because it uses less CPU.
> >>>>>
> >>>> Yes, filters definitely have to be called only for their own event ID
> >>>> for performance reasons.
> >>>>
> >>>>>> This module would be called by ltt_vtrace and _ltt_specialized_trace
> >>>>>> with this test :
> >>>>>>
> >>>>>> if (unlikely(!ltt_run_filter(trace, eID)))
> >>>>>> continue;
> >>>>>>
> >>>>>> for each active trace.
> >>>>> I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
> >>>>> more efficient than this way.
> >>>>> Consider that a event which is filtered(don't send to relay), more
> >>>>> unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
> >>>>>
> >>>>> So, in generalization filter:
> >>>>> 1: call filter in ltt_vtrace(_ltt_specialized_trace)
> >>>>> 2: call filter in probe_XXX_checkpoint()
> >>>>> I think 2 is better.
> >>>>>
> >>>>> Which is your opinion about this?
> >>>>>
> >>>> There is a feature of LTTng which would benefit of 1 : lttng can have
> >>>> multiple trace sessions active at once. Therefore, if one trace session
> >>>> needs a subset of events and another trace session need a different set,
> >>>> then they could each have their own filter structure associated with
> >>>> them. I think having this level of flexibility is more important than
> >>>> the few cycles we could save by doing (2). Also, we already have the
> >>>> tracepoints and markers to deactivate the event source at the kernel
> >>>> level when we need to have nearly-zero overhead.
> >>>>
> >>>> Mathieu
> >>>>
> >>>>> B.R.
> >>>>> Zhaolei
> >>>>>
> >>>>>> This filter would receive the trace information and the event ID.
> >>>>>> We may have to add or change some parameters to this to support
> >>>>>> filtering by fields. For the filter called from ltt_vtrace, passing :
> >>>>>>
> >>>>>> const struct marker *mdata
> >>>>>> and
> >>>>>> const char *fmt, va_list *args
> >>>>>>
> >>>>>> Should be more than enough to filter generically by
> >>>>>> - channel name
> >>>>>> - event name
> >>>>>> - field name -> typed field data.
> >>>>>>
> >>>>>> Filtering should be pre-computed as much as possible and be O(1) when
> >>>>>> executed. Creating callbacks for each expected data type to filter will
> >>>>>> probably be requried. A technique similar to what we have done in lttv
> >>>>>> filter.c should be considered.
> >>>>>>
> >>>>>> For specialized probes, it might be more difficult to do this
> >>>>>> generically, because _ltt_specialized_trace has no knowledge of the
> >>>>>> event fields. I guess we would have to create "specialized" filtering
> >>>>>> callbacks for those custom trace points. It's their nature anyway.
> >>>>>>
> >>>>>> Please ask if anything is unclear. Comments/ideas are welcome.
> >>>>>>
> >>>>>> Mathieu
> >>>>>>
> >>>>> _______________________________________________
> >>>>> ltt-dev mailing list
> >>>>> ltt-dev at lists.casi.polymtl.ca
> >>>>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
> >>>>>
> >
> >
> >
> > _______________________________________________
> > ltt-dev mailing list
> > ltt-dev at lists.casi.polymtl.ca
> > http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
> >
>
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 0/3] In-kernel filter module
2009-03-03 19:25 ` Mathieu Desnoyers
@ 2009-03-29 8:41 ` Zhaolei
2009-03-29 8:42 ` [ltt-dev] [PATCH 1/3] Remove old per-type filter Zhaolei
` (2 more replies)
0 siblings, 3 replies; 24+ messages in thread
From: Zhaolei @ 2009-03-29 8:41 UTC (permalink / raw)
Mathieu Desnoyers Wrote:
> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>> Hello, Mathieu
>>
>> Sorry fot trouble you, how about my design of ltt-filter?
>> It no objection, I'd like to starting it.
>>
>> B.R.
>> Zhaolei
>>
>> * Zhaolei Wrote:
>>> * Mathieu Desnoyers wrote:
>>>> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>>>>> * Mathieu Desnoyers Wrote:
>>>>>> * Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>>>>>>> * Mathieu Desnoyers Wrote:
>>>>>>>> - Create an in-kernel event filtering module which connects on
>>>>>>>> LTTng.
>>>>>>>>
>>>>>>>> Well, I think this last item would be a generalization of the filtering
>>>>>>>> module I created for ext4 and jbd2 recently. The one I did provided
>>>>>>>> basic filtering for inode and device number.
>>>>>>> Hello, Matuieu
>>>>>>>
>>>>>>> I readed filter code of ext4 and jbd2 module,
>>>>>>> and I have some questions of generalization filter implementation.
>>>>>>>
>>>>>>>> The idea would be to extend this and to connect it as a filter callback
>>>>>>>> with
>>>>>>>> ltt_module_register(LTT_FUNCTION_RUN_FILTER, filter_callback,
>>>>>>>> THIS_MODULE);
>>>>>>> Now ltt_module_register can only register one filter_callback.
>>>>>>> But I think when we make one filter for all types of tracepoint, it is hard to
>>>>>>> maintenance.
>>>>>>>
>>>>>> Not exactly. That would be one filter for all types of markers. And it
>>>>>> would iterate on the marker format string to filter by field name. So I
>>>>>> don't see where the maintenant burden is ?
>>>>>>
>>>>> Hello, Mathieu
>>>>>
>>>>> Thanks for your response.
>>>>>
>>>>> Do you means we only need one filter callback function in all lttng source,
>>>>> and this callback is used for every event in one trace?
>>>>>
>>>>> And programmer of ltt/probes/* don't need to consider about filter function
>>>>> as ext4 and jbd2?
>>>>>
>>>> Exactly.
>>>>
>>>> Mathieu
>>>>
>>> Hello, Mathieu
>>>
>>> I'm planing to make a in-kernel-filter for lttng.
>>> This is my image of function and schedule:
>>>
>>> Function:
>>> Build config dir-struct in debugfs
>>>
>>> Dir can dynamic updated when add/remove a module with matkers.
>>>
>>> Data's name is a symlink to __argX, because some data is noname, or some
>>> data's name is not filename, or some data have samename, for ex:
>>> Normal: "name1 %d name2 %d" -> __arg1(name1) __arg2(name2)
>>> No name: "%d %d" -> __arg1() __arg2()
>>> Not filenane: "* %d" -> __arg1()
>>> Same name: "name %d name %d" -> __arg1(name) __arg2()
>>>
>>> __arg0 is format string, to make user easy to read:
>>> # echo [debugfs]/ltt/filter/allocate_blocks/__arg0:
>>> dev %s block %llu flags %u len %u ino %lu logical %llu goal %llu lleft %llu lright %llu pleft %llu pright %llu
>>>
>>> Dir struct is like this:
>>> [debugfs]/ltt/filter
>>> |-- allocate_blocks
>>> | |-- __arg0
>>> | |-- __arg1
>>> | |-- __arg2
>>> | |-- __arg3
>>> | |-- __arg4
>>> | |-- __arg5
>>> | |-- __arg6
>>> | |-- __arg7
>>> | |-- __arg8
>>> | |-- __arg9
>>> | |-- __arg10
>>> | |-- __arg11
>>> | |-- dev -> __arg1
>>> | |-- block -> __arg2
>>> | |-- flags -> __arg3
>>> | |-- len -> __arg4
>>> | |-- ino -> __arg5
>>> | |-- logical -> __arg6
>>> | |-- goal -> __arg7
>>> | |-- lleft -> __arg8
>>> | |-- lright -> __arg9
>>> | |-- pleft -> __arg10
>>> | `-- pright -> __arg11
>>> |-- allocate_inode
>>> | |-- __arg0
>>> | |-- __arg1
>>> | |-- __arg2
>>> | |-- __arg3
>>> | |-- __arg4
>>> | |-- dev -> __arg1
>>> | |-- dir -> __arg3
>>> | |-- ino -> __arg2
>>> | `-- mode -> __arg4
>>> |-- bio_backmerge
>>> | `-- ...
>>> |-- bio_bounce
>>> | `-- ...
>>> |-- bio_complete
>>> | `-- ...
>>> |-- bio_frontmerge
>>> | `-- ...
>>> `-- ...
>>>
>>> User can config filter by write something to following debugfs files:
>>> Ex:
>>> echo "*" > [debugfs]/ltt/filter/allocate_blocks/len # enable all len
>>> echo ">100" > [debugfs]/ltt/filter/allocate_blocks/len # enable len>100
>>>
>>> Schedule:
>>> 1: Make basic function work
>>> 2: Make support for per-trace-filter
>>> Make dir struct from:
>>> [debugfs]/ltt/filter/[marker_name]/[arg_name]
>>> to
>>> [debugfs]/ltt/filter/[trace]/[marker_name]/[arg_name]
>>> Make and use callback when add/delete trace.
>>> Update filter control dir when add/delete trace.
>>> 3: Otimize for speed(use hlist, rcu, ...)
>>> 4: (Maybe) Support echo "(>0 && <10) || (>50 && <100)" > len
>>> and echo "eth*" > name
>>>
>>> Do you have some suggestion?
>>>
>
> This proposal sounds great. I look forward to see the implementation. :)
> The patches from Lai I am currently merging (making marker id permanent
> for the whole trace session) will be useful to you when you try to
> express your filter data structures.
>
> Mathieu
>
Hello, Mathieu
I finished a in-kernel filter for lttng.
Its function and usage is same as description in this mail before.
Maybe it is useful for user to filter events to trace.
B.R.
Zhaolei
>>> B.R.
>>> Zhaolei
>>>
>>>>> B.R.
>>>>> Zhaolei
>>>>>
>>>>>>> Maybe it is better to make each filter for each type of event, for ex:
>>>>>>> ltt-ext4-filter for ext4, ltt-jbd2-filter for jbd2, ...
>>>>>>>
>>>>>>> We can select to add filter code into ltt/probes/XXX_trace.c, and call
>>>>>>> ltt_module_register on modile_init, or write filter code in a module alone.
>>>>>>> Each way is ok, but integrate filter code into ltt/probes/XXX_trace.c seems
>>>>>>> more readable.
>>>>>>>
>>>>>>> Another thing needs consider is call which filter on a event.
>>>>>>> 1: Call every filter for one event.
>>>>>>> If one filter say "no", this event is ignore.
>>>>>>> It is simple to write, and each filter can do every thing(flexible).
>>>>>>> But this way is inefficient because it needs more cpu cycles.
>>>>>>> 2: Call only filter for current event type.
>>>>>>> Filter register a event type on ltt_module_register, and only used to
>>>>>>> process that type.
>>>>>>> I think 2 is better because it uses less CPU.
>>>>>>>
>>>>>> Yes, filters definitely have to be called only for their own event ID
>>>>>> for performance reasons.
>>>>>>
>>>>>>>> This module would be called by ltt_vtrace and _ltt_specialized_trace
>>>>>>>> with this test :
>>>>>>>>
>>>>>>>> if (unlikely(!ltt_run_filter(trace, eID)))
>>>>>>>> continue;
>>>>>>>>
>>>>>>>> for each active trace.
>>>>>>> I think call filter in tracepoint's callback as probe_jbd2_checkpoint is
>>>>>>> more efficient than this way.
>>>>>>> Consider that a event which is filtered(don't send to relay), more
>>>>>>> unnecessary process is done before ltt_vtrace if we call filter in ltt_vtrace.
>>>>>>>
>>>>>>> So, in generalization filter:
>>>>>>> 1: call filter in ltt_vtrace(_ltt_specialized_trace)
>>>>>>> 2: call filter in probe_XXX_checkpoint()
>>>>>>> I think 2 is better.
>>>>>>>
>>>>>>> Which is your opinion about this?
>>>>>>>
>>>>>> There is a feature of LTTng which would benefit of 1 : lttng can have
>>>>>> multiple trace sessions active at once. Therefore, if one trace session
>>>>>> needs a subset of events and another trace session need a different set,
>>>>>> then they could each have their own filter structure associated with
>>>>>> them. I think having this level of flexibility is more important than
>>>>>> the few cycles we could save by doing (2). Also, we already have the
>>>>>> tracepoints and markers to deactivate the event source at the kernel
>>>>>> level when we need to have nearly-zero overhead.
>>>>>>
>>>>>> Mathieu
>>>>>>
>>>>>>> B.R.
>>>>>>> Zhaolei
>>>>>>>
>>>>>>>> This filter would receive the trace information and the event ID.
>>>>>>>> We may have to add or change some parameters to this to support
>>>>>>>> filtering by fields. For the filter called from ltt_vtrace, passing :
>>>>>>>>
>>>>>>>> const struct marker *mdata
>>>>>>>> and
>>>>>>>> const char *fmt, va_list *args
>>>>>>>>
>>>>>>>> Should be more than enough to filter generically by
>>>>>>>> - channel name
>>>>>>>> - event name
>>>>>>>> - field name -> typed field data.
>>>>>>>>
>>>>>>>> Filtering should be pre-computed as much as possible and be O(1) when
>>>>>>>> executed. Creating callbacks for each expected data type to filter will
>>>>>>>> probably be requried. A technique similar to what we have done in lttv
>>>>>>>> filter.c should be considered.
>>>>>>>>
>>>>>>>> For specialized probes, it might be more difficult to do this
>>>>>>>> generically, because _ltt_specialized_trace has no knowledge of the
>>>>>>>> event fields. I guess we would have to create "specialized" filtering
>>>>>>>> callbacks for those custom trace points. It's their nature anyway.
>>>>>>>>
>>>>>>>> Please ask if anything is unclear. Comments/ideas are welcome.
>>>>>>>>
>>>>>>>> Mathieu
>>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> ltt-dev mailing list
>>>>>>> ltt-dev at lists.casi.polymtl.ca
>>>>>>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>>>>>>
>>>
>>>
>>> _______________________________________________
>>> ltt-dev mailing list
>>> ltt-dev at lists.casi.polymtl.ca
>>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>>
>>
>>
>> _______________________________________________
>> ltt-dev mailing list
>> ltt-dev at lists.casi.polymtl.ca
>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 1/3] Remove old per-type filter
2009-03-29 8:41 ` [ltt-dev] [PATCH 0/3] In-kernel filter module Zhaolei
@ 2009-03-29 8:42 ` Zhaolei
2009-03-29 8:43 ` [ltt-dev] [PATCH 2/3] Change ltt_run_filter_functor arguments to support in-kernel filter Zhaolei
2009-03-29 8:44 ` [ltt-dev] [PATCH 3/3] In-kernel filter module Zhaolei
2 siblings, 0 replies; 24+ messages in thread
From: Zhaolei @ 2009-03-29 8:42 UTC (permalink / raw)
We remove old filter because we'll use new in-kernel filter instead.
Signed-off-by: Zhao Lei <zhaolei at cn.fujitsu.com>
---
include/linux/ltt-tracer.h | 2 -
ltt/Kconfig | 4 -
ltt/Makefile | 1 -
ltt/ltt-filter.c | 66 ---------
ltt/probes/ext4-trace.c | 339 --------------------------------------------
ltt/probes/jbd2-trace.c | 168 ----------------------
6 files changed, 0 insertions(+), 580 deletions(-)
delete mode 100644 ltt/ltt-filter.c
diff --git a/include/linux/ltt-tracer.h b/include/linux/ltt-tracer.h
index 29ae8cf..f3e91f5 100644
--- a/include/linux/ltt-tracer.h
+++ b/include/linux/ltt-tracer.h
@@ -632,8 +632,6 @@ enum ltt_filter_control_msg {
extern int ltt_filter_control(enum ltt_filter_control_msg msg,
const char *trace_name);
-extern struct dentry *get_filter_root(void);
-
extern void ltt_buffer_destroy(struct ltt_channel_struct *ltt_chan);
void ltt_core_register(int (*function)(u8, void *));
diff --git a/ltt/Kconfig b/ltt/Kconfig
index 7e59a87..3c520f5 100644
--- a/ltt/Kconfig
+++ b/ltt/Kconfig
@@ -28,9 +28,6 @@ menuconfig LTT
if LTT
-config LTT_FILTER
- tristate
-
config HAVE_LTT_DUMP_TABLES
def_bool n
@@ -97,7 +94,6 @@ config LTT_TRACEPROBES
depends on LTT_FAST_SERIALIZE
depends on LTT_SERIALIZE
default m
- select LTT_FILTER
help
Compile lttng tracing probes, which connect to the tracepoints when
loaded and format the information collected by the tracepoints with
diff --git a/ltt/Makefile b/ltt/Makefile
index 3f59d70..5ea5cca 100644
--- a/ltt/Makefile
+++ b/ltt/Makefile
@@ -15,7 +15,6 @@ obj-$(CONFIG_LTT_STATEDUMP) += ltt-statedump.o
obj-$(CONFIG_LTT_FAST_SERIALIZE) += ltt-type-serializer.o
obj-$(CONFIG_LTT_TRACE_CONTROL) += ltt-trace-control.o
obj-$(CONFIG_LTT_USERSPACE_EVENT) += ltt-userspace-event.o
-obj-$(CONFIG_LTT_FILTER) += ltt-filter.o
obj-$(CONFIG_LTT_KPROBES) += ltt-kprobes.o
obj-$(CONFIG_LTT_TRACEPROBES) += probes/
obj-$(CONFIG_LTT_FTRACE) += ltt-ftrace.o
diff --git a/ltt/ltt-filter.c b/ltt/ltt-filter.c
deleted file mode 100644
index 92b062e..0000000
--- a/ltt/ltt-filter.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2008 Mathieu Desnoyers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/fs.h>
-#include <linux/ltt-tracer.h>
-#include <linux/mutex.h>
-
-#define LTT_FILTER_DIR "filter"
-
-/*
- * Protects the ltt_filter_dir allocation.
- */
-static DEFINE_MUTEX(ltt_filter_mutex);
-
-static struct dentry *ltt_filter_dir;
-
-struct dentry *get_filter_root(void)
-{
- struct dentry *ltt_root_dentry;
-
- mutex_lock(<t_filter_mutex);
- if (!ltt_filter_dir) {
- ltt_root_dentry = get_ltt_root();
- if (!ltt_root_dentry)
- goto err_no_root;
-
- ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR,
- ltt_root_dentry);
- if (!ltt_filter_dir)
- printk(KERN_ERR
- "ltt_filter_init: failed to create dir %s\n",
- LTT_FILTER_DIR);
- }
-err_no_root:
- mutex_unlock(<t_filter_mutex);
- return ltt_filter_dir;
-}
-EXPORT_SYMBOL_GPL(get_filter_root);
-
-static void __exit ltt_filter_exit(void)
-{
- debugfs_remove(ltt_filter_dir);
-}
-
-module_exit(ltt_filter_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers at polymtl.ca>");
-MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
diff --git a/ltt/probes/ext4-trace.c b/ltt/probes/ext4-trace.c
index 671067e..545fdc2 100644
--- a/ltt/probes/ext4-trace.c
+++ b/ltt/probes/ext4-trace.c
@@ -6,68 +6,12 @@
#include <linux/module.h>
#include <linux/writeback.h>
-#include <linux/ltt-tracer.h>
-#include <linux/debugfs.h>
-#include <linux/mutex.h>
-#include <linux/rcupdate.h>
#include <trace/ext4.h>
#include "../../fs/ext4/mballoc.h"
-static struct dentry *ext4_filter_dentry, *ext4_filter_dev_dentry,
- *ext4_filter_inode_dentry;
-static DEFINE_MUTEX(ext4_filter_mutex);
-/* Make sure we don't race between module exit and file write */
-static int module_exits;
-
-struct rcu_dev_filter {
- struct rcu_head rcu;
- char devname[NAME_MAX];
-};
-
-static struct rcu_dev_filter *dev_filter;
-/* ~0UL inode_filter enables all inodes */
-static unsigned long inode_filter = ~0UL;
-
-/*
- * Probes are executed in rcu_sched read-side critical section.
- */
-
-static int do_dev_filter(const char *dev)
-{
- struct rcu_dev_filter *ldev_filter = rcu_dereference(dev_filter);
-
- if (unlikely(ldev_filter))
- if (unlikely(strcmp(ldev_filter->devname, dev)))
- return 0;
- return 1;
-}
-
-static int do_inode_filter(unsigned long ino)
-{
- if (unlikely(inode_filter != ~0UL))
- if (unlikely(inode_filter != ino))
- return 0;
- return 1;
-}
-
-/*
- * Logical AND between dev and inode filter.
- */
-static int do_filter(const char *dev, unsigned long ino)
-{
- if (unlikely(!do_dev_filter(dev)))
- return 0;
- if (unlikely(!do_inode_filter(ino)))
- return 0;
- return 1;
-}
-
-
void probe_ext4_free_inode(struct inode *inode)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, free_inode, ext4_free_inode,
probe_ext4_free_inode,
"dev %s ino %lu mode %d uid %lu gid %lu blocks %llu",
@@ -78,8 +22,6 @@ void probe_ext4_free_inode(struct inode *inode)
void probe_ext4_request_inode(struct inode *dir, int mode)
{
- if (unlikely(!do_filter(dir->i_sb->s_id, dir->i_ino)))
- return;
trace_mark_tp(ext4, request_inode, ext4_request_inode,
probe_ext4_request_inode,
"dev %s dir %lu mode %d",
@@ -88,9 +30,6 @@ void probe_ext4_request_inode(struct inode *dir, int mode)
void probe_ext4_allocate_inode(struct inode *inode, struct inode *dir, int mode)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)
- && !do_filter(dir->i_sb->s_id, dir->i_ino)))
- return;
trace_mark_tp(ext4, allocate_inode, ext4_allocate_inode,
probe_ext4_allocate_inode,
"dev %s ino %lu dir %lu mode %d",
@@ -100,8 +39,6 @@ void probe_ext4_allocate_inode(struct inode *inode, struct inode *dir, int mode)
void probe_ext4_write_begin(struct inode *inode, loff_t pos, unsigned int len,
unsigned int flags)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, write_begin, ext4_write_begin,
probe_ext4_write_begin,
"dev %s ino %lu pos %llu len %u flags %u",
@@ -112,8 +49,6 @@ void probe_ext4_write_begin(struct inode *inode, loff_t pos, unsigned int len,
void probe_ext4_ordered_write_end(struct inode *inode, loff_t pos,
unsigned int len, unsigned int copied)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, ordered_write_end, ext4_ordered_write_end,
probe_ext4_ordered_write_end,
"dev %s ino %lu pos %llu len %u copied %u",
@@ -124,8 +59,6 @@ void probe_ext4_ordered_write_end(struct inode *inode, loff_t pos,
void probe_ext4_writeback_write_end(struct inode *inode, loff_t pos,
unsigned int len, unsigned int copied)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, writeback_write_end, ext4_writeback_write_end,
probe_ext4_writeback_write_end,
"dev %s ino %lu pos %llu len %u copied %u",
@@ -136,8 +69,6 @@ void probe_ext4_writeback_write_end(struct inode *inode, loff_t pos,
void probe_ext4_journalled_write_end(struct inode *inode, loff_t pos,
unsigned int len, unsigned int copied)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, journalled_write_end, ext4_journalled_write_end,
probe_ext4_journalled_write_end,
"dev %s ino %lu pos %llu len %u copied %u",
@@ -147,8 +78,6 @@ void probe_ext4_journalled_write_end(struct inode *inode, loff_t pos,
void probe_ext4_da_writepage(struct inode *inode, struct page *page)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, da_writepage, ext4_da_writepage,
probe_ext4_da_writepage,
"dev %s ino %lu page_index %lu",
@@ -162,8 +91,6 @@ void probe_ext4_da_writepage(struct inode *inode, struct page *page)
void probe_ext4_da_writepages(struct inode *inode,
struct writeback_control *wbc)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, da_writepages, ext4_da_writepages,
probe_ext4_da_writepages,
"dev %s ino %lu nr_to_write %ld "
@@ -189,8 +116,6 @@ void probe_ext4_da_writepages_result(struct inode *inode,
struct writeback_control *wbc,
int ret, int pages_written)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, da_writepages_result, ext4_da_writepages_result,
probe_ext4_da_writepages_result,
"dev %s ino %lu ret %d pages_written %d "
@@ -207,8 +132,6 @@ void probe_ext4_da_writepages_result(struct inode *inode,
void probe_ext4_da_write_begin(struct inode *inode, loff_t pos,
unsigned int len, unsigned int flags)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, da_write_begin, ext4_da_write_begin,
probe_ext4_da_write_begin,
"dev %s ino %lu pos %llu len %u flags %u",
@@ -219,8 +142,6 @@ void probe_ext4_da_write_begin(struct inode *inode, loff_t pos,
void probe_ext4_da_write_end(struct inode *inode, loff_t pos,
unsigned int len, unsigned int copied)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, da_write_end, ext4_da_write_end,
probe_ext4_da_write_end,
"dev %s ino %lu pos %llu len %u copied %u",
@@ -230,8 +151,6 @@ void probe_ext4_da_write_end(struct inode *inode, loff_t pos,
void probe_ext4_normal_writepage(struct inode *inode, struct page *page)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, normal_writepage, ext4_normal_writepage,
probe_ext4_normal_writepage,
"dev %s ino %lu page_index %lu",
@@ -240,8 +159,6 @@ void probe_ext4_normal_writepage(struct inode *inode, struct page *page)
void probe_ext4_journalled_writepage(struct inode *inode, struct page *page)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, journalled_writepage, ext4_journalled_writepage,
probe_ext4_journalled_writepage,
"dev %s ino %lu page_index %lu",
@@ -251,8 +168,6 @@ void probe_ext4_journalled_writepage(struct inode *inode, struct page *page)
void probe_ext4_discard_blocks(struct super_block *sb, unsigned long long blk,
struct ext4_free_data *entry)
{
- if (unlikely(!do_dev_filter(sb->s_id)))
- return;
trace_mark_tp(ext4, discard_blocks, ext4_discard_blocks,
probe_ext4_discard_blocks,
"dev %s blk %llu count %u",
@@ -262,8 +177,6 @@ void probe_ext4_discard_blocks(struct super_block *sb, unsigned long long blk,
void probe_ext4_mb_new_inode_pa(struct ext4_allocation_context *ac,
struct ext4_prealloc_space *pa)
{
- if (unlikely(!do_filter(ac->ac_sb->s_id, ac->ac_inode->i_ino)))
- return;
trace_mark_tp(ext4, mb_new_inode_pa, ext4_mb_new_inode_pa,
probe_ext4_mb_new_inode_pa,
"dev %s ino %lu pstart %llu len %u lstart %u",
@@ -274,8 +187,6 @@ void probe_ext4_mb_new_inode_pa(struct ext4_allocation_context *ac,
void probe_ext4_mb_new_group_pa(struct ext4_allocation_context *ac,
struct ext4_prealloc_space *pa)
{
- if (unlikely(!do_dev_filter(ac->ac_sb->s_id)))
- return;
trace_mark_tp(ext4, mb_new_group_pa, ext4_mb_new_group_pa,
probe_ext4_mb_new_group_pa,
"dev %s pstart %llu len %u lstart %u",
@@ -288,8 +199,6 @@ void probe_ext4_mb_release_inode_pa(struct ext4_allocation_context *ac,
unsigned long long block,
unsigned int count)
{
- if (unlikely(!do_filter(ac->ac_sb->s_id, ac->ac_inode->i_ino)))
- return;
trace_mark_tp(ext4, mb_release_inode_pa, ext4_mb_release_inode_pa,
probe_ext4_mb_release_inode_pa,
"dev %s ino %lu block %llu count %u",
@@ -299,8 +208,6 @@ void probe_ext4_mb_release_inode_pa(struct ext4_allocation_context *ac,
void probe_ext4_mb_release_group_pa(struct ext4_allocation_context *ac,
struct ext4_prealloc_space *pa)
{
- if (unlikely(!do_dev_filter(ac->ac_sb->s_id)))
- return;
trace_mark_tp(ext4, mb_release_group_pa, ext4_mb_release_group_pa,
probe_ext4_mb_release_group_pa,
"dev %s pstart %llu len %d",
@@ -309,8 +216,6 @@ void probe_ext4_mb_release_group_pa(struct ext4_allocation_context *ac,
void probe_ext4_discard_preallocations(struct inode *inode)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, discard_preallocations,
ext4_discard_preallocations,
probe_ext4_discard_preallocations,
@@ -320,8 +225,6 @@ void probe_ext4_discard_preallocations(struct inode *inode)
void probe_ext4_mb_discard_preallocations(struct super_block *sb, int needed)
{
- if (unlikely(!do_dev_filter(sb->s_id)))
- return;
trace_mark_tp(ext4, mb_discard_preallocations,
ext4_mb_discard_preallocations,
probe_ext4_mb_discard_preallocations,
@@ -331,14 +234,6 @@ void probe_ext4_mb_discard_preallocations(struct super_block *sb, int needed)
void probe_ext4_request_blocks(struct ext4_allocation_request *ar)
{
- if (ar->inode) {
- if (unlikely(!do_filter(ar->inode->i_sb->s_id,
- ar->inode->i_ino)))
- return;
- } else {
- if (unlikely(!do_dev_filter(ar->inode->i_sb->s_id)))
- return;
- }
trace_mark_tp(ext4, request_blocks, ext4_request_blocks,
probe_ext4_request_blocks,
"dev %s flags %u len %u ino %lu "
@@ -357,14 +252,6 @@ void probe_ext4_request_blocks(struct ext4_allocation_request *ar)
void probe_ext4_allocate_blocks(struct ext4_allocation_request *ar,
unsigned long long block)
{
- if (ar->inode) {
- if (unlikely(!do_filter(ar->inode->i_sb->s_id,
- ar->inode->i_ino)))
- return;
- } else {
- if (unlikely(!do_dev_filter(ar->inode->i_sb->s_id)))
- return;
- }
trace_mark_tp(ext4, allocate_blocks, ext4_allocate_blocks,
probe_ext4_allocate_blocks,
"dev %s block %llu flags %u len %u ino %lu "
@@ -383,8 +270,6 @@ void probe_ext4_allocate_blocks(struct ext4_allocation_request *ar,
void probe_ext4_free_blocks(struct inode *inode, unsigned long block,
unsigned long count, int metadata)
{
- if (unlikely(!do_filter(inode->i_sb->s_id, inode->i_ino)))
- return;
trace_mark_tp(ext4, free_blocks, ext4_free_blocks,
probe_ext4_free_blocks,
"dev %s block %lu count %lu metadata %d ino %lu",
@@ -394,11 +279,6 @@ void probe_ext4_free_blocks(struct inode *inode, unsigned long block,
void probe_ext4_sync_file(struct file *file, struct dentry *dentry,
int datasync)
{
- if (unlikely(!do_dev_filter(dentry->d_inode->i_sb->s_id)))
- return;
- if (unlikely(!do_inode_filter(dentry->d_inode->i_ino)
- && !do_inode_filter(dentry->d_parent->d_inode->i_ino)))
- return;
trace_mark_tp(ext4, sync_file, ext4_sync_file,
probe_ext4_sync_file,
"dev %s datasync %d ino %ld parent %ld",
@@ -408,231 +288,12 @@ void probe_ext4_sync_file(struct file *file, struct dentry *dentry,
void probe_ext4_sync_fs(struct super_block *sb, int wait)
{
- if (unlikely(!do_dev_filter(sb->s_id)))
- return;
trace_mark_tp(ext4, sync_fs, ext4_sync_fs,
probe_ext4_sync_fs,
"dev %s wait %d",
sb->s_id, wait);
}
-static void free_dev_filter(struct rcu_head *head)
-{
- kfree(container_of(head, struct rcu_dev_filter, rcu));
-}
-
-static ssize_t dev_filter_op_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- int err = 0;
- char buf[NAME_MAX];
- int buf_size;
- char name[NAME_MAX];
- struct rcu_dev_filter *new, *old;
-
- mutex_lock(&ext4_filter_mutex);
- if (module_exits) {
- err = -EPERM;
- goto error;
- }
- buf_size = min(count, sizeof(buf) - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto error;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", name) != 1) {
- err = -EPERM;
- goto error;
- }
-
- old = dev_filter;
-
- /* Empty string or * means all active */
- if (name[0] == '\0' || (name[0] == '*' && name[1] == '\0')) {
- new = NULL;
- } else {
- new = kmalloc(sizeof(*new), GFP_KERNEL);
- strcpy(new->devname, name);
- }
-
- rcu_assign_pointer(dev_filter, new);
- if (old)
- call_rcu_sched(&old->rcu, free_dev_filter);
-
- mutex_unlock(&ext4_filter_mutex);
- return count;
-
-error:
- mutex_unlock(&ext4_filter_mutex);
- return err;
-}
-
-static ssize_t dev_filter_op_read(struct file *filp, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- ssize_t bcount;
- const char *devname;
-
- mutex_lock(&ext4_filter_mutex);
- if (!dev_filter)
- devname = "*";
- else
- devname = dev_filter->devname;
- bcount = simple_read_from_buffer(buffer, count, ppos,
- devname, strlen(devname));
- mutex_unlock(&ext4_filter_mutex);
- return bcount;
-}
-
-static struct file_operations ext4_dev_file_operations = {
- .write = dev_filter_op_write,
- .read = dev_filter_op_read,
-};
-
-static ssize_t inode_filter_op_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- int err = 0;
- char buf[NAME_MAX];
- int buf_size;
- char name[NAME_MAX];
- unsigned long inode_num;
-
- mutex_lock(&ext4_filter_mutex);
- if (module_exits) {
- err = -EPERM;
- goto error;
- }
- buf_size = min(count, sizeof(buf) - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto error;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", name) != 1) {
- err = -EPERM;
- goto error;
- }
-
- /* Empty string or * means all active */
- if (name[0] == '\0' || (name[0] == '*' && name[1] == '\0')) {
- inode_filter = ~0UL;
- } else {
- if (sscanf(buf, "%lu", &inode_num) != 1) {
- err = -EPERM;
- goto error;
- }
- inode_filter = inode_num;
- }
-
- mutex_unlock(&ext4_filter_mutex);
- return count;
-
-error:
- mutex_unlock(&ext4_filter_mutex);
- return err;
-}
-
-static ssize_t inode_filter_op_read(struct file *filp, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- ssize_t bcount;
- char inode_str[NAME_MAX];
-
- mutex_lock(&ext4_filter_mutex);
- if (inode_filter == ~0UL)
- strcpy(inode_str, "*");
- else {
- bcount = snprintf(inode_str, sizeof(inode_str), "%lu",
- inode_filter);
- if (bcount == sizeof(inode_str))
- bcount = -ENOSPC;
- if (bcount < 0)
- goto end;
- }
- bcount = simple_read_from_buffer(buffer, count, ppos,
- inode_str, strlen(inode_str));
-end:
- mutex_unlock(&ext4_filter_mutex);
- return bcount;
-}
-
-static struct file_operations ext4_inode_file_operations = {
- .write = inode_filter_op_write,
- .read = inode_filter_op_read,
-};
-
-static void release_filter_dev(void)
-{
- struct rcu_dev_filter *old;
-
- mutex_lock(&ext4_filter_mutex);
- module_exits = 1;
- old = dev_filter;
- rcu_assign_pointer(dev_filter, NULL);
- if (old)
- call_rcu_sched(&old->rcu, free_dev_filter);
- mutex_unlock(&ext4_filter_mutex);
-}
-
-static int __init filter_init(void)
-{
- struct dentry *filter_root_dentry;
- int err = 0;
-
- filter_root_dentry = get_filter_root();
- if (!filter_root_dentry) {
- err = -ENOENT;
- goto end;
- }
-
- ext4_filter_dentry = debugfs_create_dir("ext4", filter_root_dentry);
-
- if (IS_ERR(ext4_filter_dentry) || !ext4_filter_dentry) {
- printk(KERN_ERR "Failed to create ext4 filter file\n");
- err = -ENOMEM;
- goto end;
- }
-
- ext4_filter_dev_dentry = debugfs_create_file("dev", S_IWUSR,
- ext4_filter_dentry, NULL, &ext4_dev_file_operations);
- if (IS_ERR(ext4_filter_dev_dentry) || !ext4_filter_dev_dentry) {
- printk(KERN_ERR "Failed to create ext4 dev filter file\n");
- err = -ENOMEM;
- goto release_filter_dentry;
- }
-
- ext4_filter_inode_dentry = debugfs_create_file("inode", S_IWUSR,
- ext4_filter_dentry, NULL, &ext4_inode_file_operations);
- if (IS_ERR(ext4_filter_inode_dentry) || !ext4_filter_inode_dentry) {
- printk(KERN_ERR "Failed to create ext4 inode filter file\n");
- err = -ENOMEM;
- goto release_filter_dev_dentry;
- }
-
- goto end;
-
-release_filter_dev_dentry:
- debugfs_remove(ext4_filter_dev_dentry);
-release_filter_dentry:
- debugfs_remove(ext4_filter_dentry);
- release_filter_dev();
-end:
- return err;
-}
-
-static void __exit filter_exit(void)
-{
- debugfs_remove(ext4_filter_dev_dentry);
- debugfs_remove(ext4_filter_inode_dentry);
- debugfs_remove(ext4_filter_dentry);
- release_filter_dev();
-}
-
-module_init(filter_init);
-module_exit(filter_exit);
-
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mathieu Desnoyers");
MODULE_DESCRIPTION("ext4 Tracepoint Probes");
diff --git a/ltt/probes/jbd2-trace.c b/ltt/probes/jbd2-trace.c
index 22b851e..b5ce5de 100644
--- a/ltt/probes/jbd2-trace.c
+++ b/ltt/probes/jbd2-trace.c
@@ -5,41 +5,10 @@
*/
#include <linux/module.h>
-#include <linux/ltt-tracer.h>
-#include <linux/debugfs.h>
-#include <linux/mutex.h>
-#include <linux/rcupdate.h>
#include <trace/jbd2.h>
-static struct dentry *jbd2_filter_dentry, *jbd2_filter_dev_dentry;
-static DEFINE_MUTEX(jbd2_filter_mutex);
-/* Make sure we don't race between module exit and file write */
-static int module_exits;
-
-struct rcu_dev_filter {
- struct rcu_head rcu;
- char devname[NAME_MAX];
-};
-
-static struct rcu_dev_filter *dev_filter;
-
-/*
- * Probes are executed in rcu_sched read-side critical section.
- */
-static int do_filter(const char *dev)
-{
- struct rcu_dev_filter *ldev_filter = rcu_dereference(dev_filter);
-
- if (unlikely(ldev_filter))
- if (unlikely(strcmp(ldev_filter->devname, dev)))
- return 0;
- return 1;
-}
-
void probe_jbd2_checkpoint(journal_t *journal, int result)
{
- if (unlikely(!do_filter(journal->j_devname)))
- return;
trace_mark_tp(jbd2, checkpoint, jbd2_checkpoint,
probe_jbd2_checkpoint, "dev %s need_checkpoint %d",
journal->j_devname, result);
@@ -48,8 +17,6 @@ void probe_jbd2_checkpoint(journal_t *journal, int result)
void probe_jbd2_start_commit(journal_t *journal,
transaction_t *commit_transaction)
{
- if (unlikely(!do_filter(journal->j_devname)))
- return;
trace_mark_tp(jbd2, start_commit, jbd2_start_commit,
probe_jbd2_start_commit, "dev %s transaction %d",
journal->j_devname, commit_transaction->t_tid);
@@ -58,147 +25,12 @@ void probe_jbd2_start_commit(journal_t *journal,
void probe_jbd2_end_commit(journal_t *journal,
transaction_t *commit_transaction)
{
- if (unlikely(!do_filter(journal->j_devname)))
- return;
trace_mark_tp(jbd2, end_commit, jbd2_end_commit,
probe_jbd2_end_commit, "dev %s transaction %d head %d",
journal->j_devname, commit_transaction->t_tid,
journal->j_tail_sequence);
}
-static void free_dev_filter(struct rcu_head *head)
-{
- kfree(container_of(head, struct rcu_dev_filter, rcu));
-}
-
-static ssize_t filter_op_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- int err = 0;
- char buf[NAME_MAX];
- int buf_size;
- char name[NAME_MAX];
- struct rcu_dev_filter *new, *old;
-
- mutex_lock(&jbd2_filter_mutex);
- if (module_exits) {
- err = -EPERM;
- goto error;
- }
- buf_size = min(count, sizeof(buf) - 1);
- err = copy_from_user(buf, user_buf, buf_size);
- if (err)
- goto error;
- buf[buf_size] = 0;
-
- if (sscanf(buf, "%s", name) != 1) {
- err = -EPERM;
- goto error;
- }
-
- old = dev_filter;
-
- /* Empty string or * means all active */
- if (name[0] == '\0' || (name[0] == '*' && name[1] == '\0')) {
- new = NULL;
- } else {
- new = kmalloc(sizeof(*new), GFP_KERNEL);
- strcpy(new->devname, name);
- }
-
- rcu_assign_pointer(dev_filter, new);
- if (old)
- call_rcu_sched(&old->rcu, free_dev_filter);
-
- mutex_unlock(&jbd2_filter_mutex);
- return count;
-
-error:
- mutex_unlock(&jbd2_filter_mutex);
- return err;
-}
-
-static ssize_t filter_op_read(struct file *filp, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- ssize_t bcount;
- const char *devname;
-
- mutex_lock(&jbd2_filter_mutex);
- if (!dev_filter)
- devname = "*";
- else
- devname = dev_filter->devname;
- bcount = simple_read_from_buffer(buffer, count, ppos,
- devname, strlen(devname));
- mutex_unlock(&jbd2_filter_mutex);
- return bcount;
-}
-
-static struct file_operations jbd2_file_operations = {
- .write = filter_op_write,
- .read = filter_op_read,
-};
-
-static void release_filter_dev(void)
-{
- struct rcu_dev_filter *old;
-
- mutex_lock(&jbd2_filter_mutex);
- module_exits = 1;
- old = dev_filter;
- rcu_assign_pointer(dev_filter, NULL);
- if (old)
- call_rcu_sched(&old->rcu, free_dev_filter);
- mutex_unlock(&jbd2_filter_mutex);
-}
-
-static int __init filter_init(void)
-{
- struct dentry *filter_root_dentry;
- int err = 0;
-
- filter_root_dentry = get_filter_root();
- if (!filter_root_dentry) {
- err = -ENOENT;
- goto end;
- }
-
- jbd2_filter_dentry = debugfs_create_dir("jbd2", filter_root_dentry);
-
- if (IS_ERR(jbd2_filter_dentry) || !jbd2_filter_dentry) {
- printk(KERN_ERR "Failed to create jbd2 filter file\n");
- err = -ENOMEM;
- goto end;
- }
-
- jbd2_filter_dev_dentry = debugfs_create_file("dev", S_IWUSR,
- jbd2_filter_dentry, NULL, &jbd2_file_operations);
- if (IS_ERR(jbd2_filter_dentry) || !jbd2_filter_dentry) {
- printk(KERN_ERR "Failed to create jbd2 filter file\n");
- err = -ENOMEM;
- goto release_filter_dentry;
- }
-
- goto end;
-
-release_filter_dentry:
- debugfs_remove(jbd2_filter_dentry);
- release_filter_dev();
-end:
- return err;
-}
-
-static void __exit filter_exit(void)
-{
- debugfs_remove(jbd2_filter_dev_dentry);
- debugfs_remove(jbd2_filter_dentry);
- release_filter_dev();
-}
-
-module_init(filter_init);
-module_exit(filter_exit);
-
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mathieu Desnoyers");
MODULE_DESCRIPTION("JBD2 Tracepoint Probes");
--
1.5.5.3
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 2/3] Change ltt_run_filter_functor arguments to support in-kernel filter
2009-03-29 8:41 ` [ltt-dev] [PATCH 0/3] In-kernel filter module Zhaolei
2009-03-29 8:42 ` [ltt-dev] [PATCH 1/3] Remove old per-type filter Zhaolei
@ 2009-03-29 8:43 ` Zhaolei
2009-03-29 8:44 ` [ltt-dev] [PATCH 3/3] In-kernel filter module Zhaolei
2 siblings, 0 replies; 24+ messages in thread
From: Zhaolei @ 2009-03-29 8:43 UTC (permalink / raw)
In-kernel filter need trace, marker and arguments information to
determine if to allow event output.
So we add these information to ltt_run_filter_functor's arguments.
Signed-off-by: Zhao Lei <zhaolei at cn.fujitsu.com>
---
include/linux/ltt-core.h | 5 ++++-
ltt/ltt-core.c | 3 ++-
ltt/ltt-serialize.c | 2 +-
ltt/ltt-type-serializer.c | 10 ++++++++--
4 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/include/linux/ltt-core.h b/include/linux/ltt-core.h
index b666ad2..a59fad7 100644
--- a/include/linux/ltt-core.h
+++ b/include/linux/ltt-core.h
@@ -37,7 +37,10 @@ void put_ltt_root(void);
/* Keep track of trap nesting inside LTT */
DECLARE_PER_CPU(unsigned int, ltt_nesting);
-typedef int (*ltt_run_filter_functor)(void *trace, uint16_t eID);
+struct marker;
+struct ltt_trace_struct;
+typedef int (*ltt_run_filter_functor)(struct ltt_trace_struct *trace,
+ const struct marker *mdata, va_list *args);
extern ltt_run_filter_functor ltt_run_filter;
diff --git a/ltt/ltt-core.c b/ltt/ltt-core.c
index 3556738..90e50ae 100644
--- a/ltt/ltt-core.c
+++ b/ltt/ltt-core.c
@@ -79,7 +79,8 @@ EXPORT_SYMBOL_GPL(ltt_unlock_traces);
DEFINE_PER_CPU(unsigned int, ltt_nesting);
EXPORT_PER_CPU_SYMBOL(ltt_nesting);
-int ltt_run_filter_default(void *trace, uint16_t eID)
+int ltt_run_filter_default(struct ltt_trace_struct *trace,
+ const struct marker *mdata, va_list *args)
{
return 1;
}
diff --git a/ltt/ltt-serialize.c b/ltt/ltt-serialize.c
index 77f6317..f706664 100644
--- a/ltt/ltt-serialize.c
+++ b/ltt/ltt-serialize.c
@@ -849,7 +849,7 @@ notrace void ltt_vtrace(const struct marker *mdata, void *probe_data,
continue;
if (unlikely(!trace->active))
continue;
- if (unlikely(!ltt_run_filter(trace, eID)))
+ if (unlikely(!ltt_run_filter(trace, mdata, args)))
continue;
#ifdef CONFIG_LTT_DEBUG_EVENT_SIZE
rflags = LTT_RFLAG_ID_SIZE;
diff --git a/ltt/ltt-type-serializer.c b/ltt/ltt-type-serializer.c
index ebf9cb6..38c9130 100644
--- a/ltt/ltt-type-serializer.c
+++ b/ltt/ltt-type-serializer.c
@@ -48,8 +48,14 @@ notrace void _ltt_specialized_trace(const struct marker *mdata,
__list_for_each_entry_rcu(trace, <t_traces.head, list) {
if (unlikely(!trace->active))
continue;
- if (unlikely(!ltt_run_filter(trace, eID)))
- continue;
+ /*
+ * specialized trace filter is temporarily disabled
+ * because we are implement a generic filter which can't
+ * support specialized trace now.
+ *
+ * if (unlikely(!ltt_run_filter(trace, eID)))
+ * continue;
+ */
#ifdef CONFIG_LTT_DEBUG_EVENT_SIZE
rflags = LTT_RFLAG_ID_SIZE;
#else
--
1.5.5.3
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 3/3] In-kernel filter module
2009-03-29 8:41 ` [ltt-dev] [PATCH 0/3] In-kernel filter module Zhaolei
2009-03-29 8:42 ` [ltt-dev] [PATCH 1/3] Remove old per-type filter Zhaolei
2009-03-29 8:43 ` [ltt-dev] [PATCH 2/3] Change ltt_run_filter_functor arguments to support in-kernel filter Zhaolei
@ 2009-03-29 8:44 ` Zhaolei
2009-04-06 17:52 ` Mathieu Desnoyers
2 siblings, 1 reply; 24+ messages in thread
From: Zhaolei @ 2009-03-29 8:44 UTC (permalink / raw)
LTTng's in-kernel filter is used to filter event by its arguments.
User can config every event's filter option by echo to filts in
debugfs/ltt/filter
Signed-off-by: Zhao Lei <zhaolei at cn.fujitsu.com>
---
ltt/Kconfig | 9 +
ltt/Makefile | 1 +
ltt/ltt-filter.c | 1831 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1841 insertions(+), 0 deletions(-)
create mode 100644 ltt/ltt-filter.c
diff --git a/ltt/Kconfig b/ltt/Kconfig
index 3c520f5..2cfb479 100644
--- a/ltt/Kconfig
+++ b/ltt/Kconfig
@@ -214,4 +214,13 @@ config LTT_ASCII
Output trace data in a text-formatted ascii file, presented in
/mnt/debugfs/ltt/ascii/<trace name>.
+config LTT_FILTER
+ tristate "Linux Trace Toolkit Filter"
+ depends on LTT_TRACER
+ depends on LTT_SERIALIZE
+ default n
+ help
+ Filter for LTTng to control which type of event to be traced.
+ Config filter rule by debugfs/ltt/filter
+
endif # LTT
diff --git a/ltt/Makefile b/ltt/Makefile
index 5ea5cca..29ab6e4 100644
--- a/ltt/Makefile
+++ b/ltt/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_LTT_KPROBES) += ltt-kprobes.o
obj-$(CONFIG_LTT_TRACEPROBES) += probes/
obj-$(CONFIG_LTT_FTRACE) += ltt-ftrace.o
obj-$(CONFIG_LTT_ASCII) += ltt-ascii.o
+obj-$(CONFIG_LTT_FILTER) += ltt-filter.o
diff --git a/ltt/ltt-filter.c b/ltt/ltt-filter.c
new file mode 100644
index 0000000..bf2760a
--- /dev/null
+++ b/ltt/ltt-filter.c
@@ -0,0 +1,1831 @@
+/*
+ * LTT filter module.
+ *
+ * Copyright 2009 -
+ * Zhaolei <zhaolei at cn.fujitsu.com>
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/ltt-core.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/marker.h>
+#include <linux/ltt-tracer.h>
+#include <asm/atomic.h>
+
+#define LTT_FILTER_DIR "filter"
+
+struct dentry *ltt_filter_dir;
+
+/*
+ * Marker format operations
+ *
+ * Maintenance a marker_format list sync with module add and release
+ * Each data-type in format are paresed and stored in marker_format struct
+ * Impl a notifier for respond add/remove marker format event
+ */
+
+enum format_item_type {
+ SIGNED,
+ UNSIGNED,
+ STRING,
+ CHAR,
+};
+const char *format_item_type_string[] = {
+ "signed int",
+ "unsigned int",
+ "string",
+ "char",
+};
+
+struct format_item {
+ struct list_head list;
+ char name[PATH_MAX];
+ enum format_item_type type;
+ size_t size;
+};
+
+struct marker_format {
+ atomic_t refcnt;
+ struct list_head list;
+ const char *name;
+ const char *format;
+ struct format_item **items;
+ size_t format_item_cnt;
+};
+
+static LIST_HEAD(marker_format_head);
+static DEFINE_MUTEX(format_lock);
+
+BLOCKING_NOTIFIER_HEAD(format_notifier_list);
+enum FORMAT_EVENT {
+ ADD_FORMAT,
+ DEL_FORMAT,
+};
+
+static struct format_item *new_format_item(const char *name_head,
+ const char *name_tail, int type_type, int type_size)
+{
+ struct format_item *format_item;
+ const char *p;
+ int i, name_len;
+
+ format_item = kmalloc(sizeof(struct format_item), GFP_KERNEL);
+ if (!format_item) {
+ printk(KERN_ERR "%s: alloc format_item failed\n", __func__);
+ goto err_alloc_format_item;
+ }
+
+ INIT_LIST_HEAD(&format_item->list);
+
+ /* trim itemname */
+ while (name_head < name_tail
+ && *name_head == ' ')
+ name_head++;
+ while (name_tail > name_head
+ && *(name_tail - 1) == ' ')
+ name_tail--;
+
+ name_len = name_tail - name_head;
+ if (name_len) {
+ for (i = 0, p = name_head; i < PATH_MAX - 1 && p < name_tail;
+ i++, p++) {
+ if ((*p == '%' && p + 1 < name_tail && *(p + 1) == '%')
+ || (*p == '#' &&
+ p + 1 < name_tail && *(p + 1) == '#')) {
+ p++;
+ }
+ format_item->name[i] = isalnum(*p) ? *p : '_';
+ }
+ format_item->name[i] = 0;
+ } else {
+ format_item->name[0] = 0;
+ }
+
+ format_item->type = type_type;
+ format_item->size = type_size;
+
+ return format_item;
+
+err_alloc_format_item:
+ return NULL;
+}
+
+static size_t parse_format(const char *format, struct format_item ***items)
+{
+ int ret = 0; /* number of types in format string, or -ERRNO on fail */
+ int state; /* 0: in name, 1: in type, 2: in # */
+ const char *p; /* format string's iter */
+ /* name for current type, init with 0 to avoid compile warning */
+ const char *name_head, *name_tail = 0;
+ enum format_item_type type_type;
+ /*
+ * size(in bytes) of current type
+ * init with 0 to avoid compile warning
+ */
+ int type_size = 0;
+
+ LIST_HEAD(format_item_list);
+ struct format_item *format_item, *format_item_next;
+
+ for (state = 0, name_head = p = format; *p; p++) {
+ switch (state) {
+ case 0:
+ switch (*p) {
+ case '%':
+ if (*(p+1) != '%') {
+ name_tail = p;
+ type_size = sizeof(int);
+ state = 1;
+ } else {
+ p++;
+ }
+ break;
+ case '#':
+ if (*(p+1) != '#') {
+ name_tail = p;
+ state = 2;
+ } else {
+ p++;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 1:
+ switch (*p) {
+ case 'l':
+ if (*(p+1) == 'l') {
+ type_size = sizeof(long long);
+ p++;
+ } else {
+ type_size = sizeof(long);
+ }
+ break;
+ case 'L':
+ type_size = sizeof(long long);
+ break;
+ case 'z':
+ case 'Z':
+ type_size = sizeof(size_t);
+ break;
+ case 't':
+ type_size = sizeof(ptrdiff_t);
+ break;
+ case 'h':
+ type_size = sizeof(short);
+ break;
+ case 'd':
+ case 'i':
+ type_type = SIGNED;
+ goto type_finish;
+ break;
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ type_type = UNSIGNED;
+ goto type_finish;
+ break;
+ case 'p':
+ type_type = UNSIGNED;
+ type_size = sizeof(void *);
+ goto type_finish;
+ break;
+ case 's':
+ type_type = STRING;
+ type_size = sizeof(void *);
+ goto type_finish;
+ break;
+ case 'c':
+ type_type = CHAR;
+ type_size = sizeof(char);
+ goto type_finish;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ default:
+ printk(KERN_ERR
+ "%s: unknown char after %%: %c\n",
+ __func__, *p);
+ break;
+ }
+ break;
+ case 2:
+ switch (*p) {
+ case 'l':
+ case 'L':
+ case 'Z':
+ case 'z':
+ case 't':
+ case 'h':
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'p':
+ case 's':
+ case 'c':
+ case '1':
+ case '2':
+ case '4':
+ case '8':
+ break;
+ case '%':
+ type_size = sizeof(int);
+ state = 1;
+ break;
+ default:
+ printk(KERN_ERR "%s: unknown char after #: %c\n",
+ __func__, *p);
+ break;
+ }
+ break;
+ default:
+ /* should not run to here */
+ ret = -EINVAL;
+ goto err_unknown_state;
+ }
+ continue;
+type_finish:
+ format_item = new_format_item(name_head, name_tail, type_type,
+ type_size);
+ if (!format_item) {
+ printk(KERN_ERR "%s: add_format_item failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_new_format_item;
+ }
+
+ list_add_tail(&format_item->list, &format_item_list);
+ ret++;
+ name_head = p + 1;
+ state = 0;
+ continue;
+ }
+
+ /*
+ * We have parsed types in format, and build linked_list to save
+ * there information.
+ * Now we will build a point array to save each format_item for
+ * easy access.
+ */
+
+ *items = kmalloc(ret * sizeof(struct format_item *), GFP_KERNEL);
+ if (!*items) {
+ printk(KERN_ERR "%s: alloc format_item index failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_alloc_format_item;
+ }
+
+ /* we reuse state as a iter for (*items)[] */
+ state = 0;
+ list_for_each_entry(format_item, &format_item_list, list)
+ (*items)[state++] = format_item;
+
+ return ret;
+
+err_alloc_format_item:
+err_unknown_state:
+err_new_format_item:
+ list_for_each_entry_safe(format_item, format_item_next,
+ &format_item_list, list)
+ kfree(format_item);
+ return ret;
+}
+
+/* need have format_lock */
+static struct marker_format *_find_marker_format(const char *name)
+{
+ struct marker_format *format;
+ list_for_each_entry(format, &marker_format_head, list) {
+ if (!strcmp(name, format->name))
+ return format;
+ }
+ return NULL;
+}
+
+/*
+ * For add_marker_format internal use
+ * need have format_lock
+ */
+static struct marker_format *_new_marker_format(struct marker *marker)
+{
+ struct marker_format *format;
+ char *tmp;
+
+ format = kmalloc(sizeof(struct marker_format), GFP_KERNEL);
+ if (!format)
+ goto err_alloc_format;
+
+ atomic_set(&format->refcnt, 1);
+
+ INIT_LIST_HEAD(&format->list);
+
+ tmp = kmalloc(strlen(marker->name) + 1, GFP_KERNEL);
+ if (!tmp)
+ goto err_alloc_name;
+ strcpy(tmp, marker->name);
+ format->name = tmp;
+
+ tmp = kmalloc(strlen(marker->format) + 1, GFP_KERNEL);
+ if (!tmp)
+ goto err_alloc_formatstr;
+ strcpy(tmp, marker->format);
+ format->format = tmp;
+
+ format->format_item_cnt = parse_format(marker->format,
+ &format->items);
+ if (IS_ERR_VALUE(format->format_item_cnt)) {
+ printk(KERN_ERR "%s: parse format failed: %s\n", __func__,
+ marker->format);
+ goto err_parse_file;
+ }
+
+ list_add(&format->list, &marker_format_head);
+
+ blocking_notifier_call_chain(&format_notifier_list, ADD_FORMAT, format);
+
+ return format;
+
+err_parse_file:
+ kfree(format->format);
+err_alloc_formatstr:
+ kfree(format->name);
+err_alloc_name:
+ kfree(format);
+err_alloc_format:
+ return NULL;
+}
+
+/* need have format_lock */
+static void _destroy_marker_format(struct marker_format *format)
+{
+ int i;
+
+ blocking_notifier_call_chain(&format_notifier_list, DEL_FORMAT, format);
+
+ list_del(&format->list);
+
+ for (i = 0; i < format->format_item_cnt; i++)
+ kfree(format->items[i]);
+ kfree(format->items);
+ kfree(format->format);
+ kfree(format->name);
+ kfree(format);
+
+ return;
+}
+
+static struct marker_format *add_marker_format(struct marker *marker)
+{
+ struct marker_format *format;
+
+ mutex_lock(&format_lock);
+
+ format = _find_marker_format(marker->name);
+ if (!format) {
+ format = _new_marker_format(marker);
+ if (!format) {
+ printk(KERN_ERR
+ "%s: new format struct for %s failed\n",
+ __func__, marker->name);
+ goto err_alloc_format;
+ }
+ } else {
+ atomic_inc(&format->refcnt);
+ }
+
+ mutex_unlock(&format_lock);
+ return format;
+
+err_alloc_format:
+ mutex_unlock(&format_lock);
+ return NULL;
+}
+
+static int put_marker_format(const char *name)
+{
+ struct marker_format *format;
+ int err = 0;
+
+ mutex_lock(&format_lock);
+
+ format = _find_marker_format(name);
+ if (!format) {
+ printk(KERN_ERR "%s: can't find format item for %s\n",
+ __func__, name);
+ err = -ENOENT;
+ goto err_find_marker_format;
+ }
+
+ if (atomic_dec_and_test(&format->refcnt))
+ _destroy_marker_format(format);
+
+ mutex_unlock(&format_lock);
+ return 0;
+
+err_find_marker_format:
+ mutex_unlock(&format_lock);
+ return err;
+}
+
+static void destroy_all_format(void)
+{
+ struct marker_format *format, *next;
+
+ mutex_lock(&format_lock);
+
+ list_for_each_entry_safe(format, next, &marker_format_head, list) {
+ _destroy_marker_format(format);
+ }
+
+ mutex_unlock(&format_lock);
+
+ return;
+}
+
+static int bulid_exist_format(void)
+{
+ struct marker_iter iter;
+ struct marker_format *format;
+ int err = 0;
+
+ if (!list_empty(&marker_format_head)) {
+ err = -EEXIST;
+ goto err_not_empty;
+ }
+
+ marker_iter_reset(&iter);
+ marker_iter_start(&iter);
+ for (; iter.marker != NULL; marker_iter_next(&iter)) {
+ format = add_marker_format(iter.marker);
+ if (!format) {
+ err = -EIO;
+ goto err_build_fail;
+ }
+ }
+ marker_iter_stop(&iter);
+ return 0;
+
+err_build_fail:
+ destroy_all_format();
+err_not_empty:
+ return err;
+}
+
+static int module_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct module *mod = data;
+ struct marker *iter;
+
+ switch (val) {
+ case MODULE_STATE_COMING:
+ for (iter = mod->markers;
+ iter < mod->markers + mod->num_markers; iter++)
+ add_marker_format(iter);
+ break;
+ case MODULE_STATE_GOING:
+ for (iter = mod->markers;
+ iter < mod->markers + mod->num_markers; iter++)
+ put_marker_format(iter->name);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block module_nb = {
+ .notifier_call = module_notify,
+};
+
+static int marker_format_init(void)
+{
+ int err = 0;
+
+ err = bulid_exist_format();
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "%s: bulid_exist_format failed\n", __func__);
+ goto err_add_exist_filter;
+ }
+
+ /* Todo:
+ * If a module is insert/delete here(between
+ * bulid_exist_format() and register_module_notifier()),
+ * marker_format struct is not uptodate.
+ */
+
+ err = register_module_notifier(&module_nb);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "%s register module notifier failed\n",
+ __func__);
+ goto err_register_module_notifier;
+ }
+
+ return 0;
+
+err_register_module_notifier:
+ destroy_all_format();
+err_add_exist_filter:
+ return err;
+}
+
+static void marker_format_destroy(void)
+{
+ unregister_module_notifier(&module_nb);
+ destroy_all_format();
+}
+
+static int register_format_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&format_notifier_list, nb);
+}
+
+static int unregister_format_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&format_notifier_list, nb);
+}
+
+
+/*
+ * Control operations
+ *
+ * Maintenance a dir struct and datastruct for control filter
+ * dir struct will dynamical updated when marker_format got changed
+ *
+ * Todo:
+ * Control per-trace filter
+ *
+ */
+
+enum compare_op {
+ gt,
+ ge,
+ eq,
+ le,
+ lt,
+};
+const char *compare_op_string[] = {
+ ">",
+ ">=",
+ "=",
+ "<=",
+ "<",
+};
+
+struct marker_filter;
+struct filter_item {
+ int enable;
+ union {
+ struct {
+ enum compare_op op;
+ long long value;
+ } signed_type;
+ struct {
+ enum compare_op op;
+ unsigned long long value;
+ } unsigned_type;
+ struct {
+ enum compare_op op;
+ char value[PATH_MAX]; /* Todo: Make it dynamic */
+ } string_type;
+ struct {
+ enum compare_op op;
+ unsigned char value;
+ } char_type;
+ } setting;
+ struct marker_filter *parent;
+ struct dentry *den[2]; /* [0]: __argX, [1]: symbol link to __argX */
+};
+
+struct marker_filter {
+ struct list_head list;
+ struct marker_format *format;
+ struct filter_item *items;
+ struct dentry *rootden;
+ struct dentry *formatden;
+ /*
+ * is any of items[] are enabled
+ * for bypass marker which haven't any filter fast
+ */
+ int enable;
+};
+static inline unsigned int filter_item_no(struct filter_item *filter_item)
+{
+ return filter_item - filter_item->parent->items;
+}
+static inline struct format_item *filter_item_format(
+ struct filter_item *filter_item)
+{
+ return filter_item->parent->format->items[filter_item_no(filter_item)];
+}
+
+static LIST_HEAD(marker_filter_head);
+
+/*
+ * Use spinlock to lock filter struct because we can't use mutex in filter
+ * callback
+ */
+static DEFINE_SPINLOCK(filter_lock);
+
+/*
+ * We need call some kernel function in ltt-filter function, but many kernel
+ * function have tp/markers which cause cpu jump to ltt, then jump to
+ * ltt-filter from ltt code.
+ * Even if we don't call any kernel function in filter code, interrupt will
+ * do similar thing.
+ * To make it easy, we avoid this type of nesting by lock/unlock_filter.
+ */
+DEFINE_PER_CPU(atomic_t, filter_nesting) = ATOMIC_INIT(0);
+static inline int lock_filter(void)
+{
+ preempt_disable();
+ if (atomic_xchg(&__get_cpu_var(filter_nesting), 1) > 0) {
+ /* Already in lock */
+ preempt_enable();
+ return 1;
+ }
+ return 0;
+}
+static inline void unlock_filter(void)
+{
+ atomic_set(&__get_cpu_var(filter_nesting), 0);
+ preempt_enable();
+}
+
+struct marker_filter *_find_marker_filter(const char *name)
+{
+ struct marker_filter *filter;
+ list_for_each_entry(filter, &marker_filter_head, list) {
+ if (!strcmp(name, filter->format->name))
+ return filter;
+ }
+ return NULL;
+}
+
+static ssize_t format_read(struct file *filp, char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ size_t ret;
+ struct marker_filter *filter;
+
+ lock_filter();
+ spin_lock(&filter_lock);
+ filter = filp->f_dentry->d_inode->i_private;
+ if (filter) {
+ ret = simple_read_from_buffer(ubuf, cnt, ppos,
+ filter->format->format, strlen(filter->format->format));
+ } else {
+ ret = -EIO;
+ }
+ spin_unlock(&filter_lock);
+ unlock_filter();
+
+ return ret;
+}
+
+static const struct file_operations format_fops = {
+ .read = format_read,
+};
+
+static int signed_type_read(char *buf, size_t bufsize,
+ struct filter_item *filter_item)
+{
+ int ret;
+ int pos = 0;
+
+ ret = scnprintf(buf + pos, bufsize - pos, "%s",
+ compare_op_string[filter_item->setting.signed_type.op]);
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ ret = scnprintf(buf + pos, bufsize - pos, " %lld",
+ filter_item->setting.signed_type.value);
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ return pos;
+}
+
+static int unsigned_type_read(char *buf, size_t bufsize,
+ struct filter_item *filter_item)
+{
+ int ret;
+ int pos = 0;
+
+ ret = scnprintf(buf + pos, bufsize - pos, "%s",
+ compare_op_string[filter_item->setting.unsigned_type.op]);
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ ret = scnprintf(buf + pos, bufsize - pos, " %llu",
+ filter_item->setting.unsigned_type.value);
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ return pos;
+}
+
+static int string_type_read(char *buf, size_t bufsize,
+ struct filter_item *filter_item)
+{
+ int ret;
+ int pos = 0;
+
+ ret = scnprintf(buf + pos, bufsize - pos, "%s",
+ compare_op_string[filter_item->setting.string_type.op]);
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ ret = scnprintf(buf + pos, bufsize - pos, " %s",
+ filter_item->setting.string_type.value);
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ return pos;
+}
+
+static int char_type_read(char *buf, size_t bufsize,
+ struct filter_item *filter_item)
+{
+ int ret;
+ int pos = 0;
+
+ ret = scnprintf(buf + pos, bufsize - pos, "%s",
+ compare_op_string[filter_item->setting.char_type.op]);
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ switch (filter_item->setting.char_type.value) {
+ case '\t':
+ ret = scnprintf(buf + pos, bufsize - pos, " \\t");
+ break;
+ case ' ':
+ ret = scnprintf(buf + pos, bufsize - pos, " blank");
+ break;
+ case '\n':
+ ret = scnprintf(buf + pos, bufsize - pos, " \\n");
+ break;
+ default:
+ if (isgraph(filter_item->setting.char_type.value)) {
+ ret = scnprintf(buf + pos, bufsize - pos, " %c",
+ filter_item->setting.char_type.value);
+ } else {
+ ret = scnprintf(buf + pos, bufsize - pos,
+ " \\%u", (unsigned int)
+ filter_item->setting.char_type.value);
+ }
+ break;
+ }
+ if (!ret)
+ return 0;
+ pos += ret;
+
+ return pos;
+}
+
+static ssize_t type_read(struct file *filp, char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ size_t ret;
+ struct filter_item *filter_item;
+
+ lock_filter();
+ spin_lock(&filter_lock);
+ filter_item = filp->f_dentry->d_inode->i_private;
+ if (filter_item) {
+ struct format_item *format_item;
+ char buf[128];
+ int pos = 0;
+
+ format_item = filter_item_format(filter_item);
+
+ ret = scnprintf(buf + pos, sizeof(buf) - pos,
+ "type: %u byte(s) %s\n",
+ format_item->size,
+ format_item_type_string[format_item->type]);
+ if (!ret) {
+ ret = -EINVAL;
+ goto err_format_string;
+ }
+ pos += ret;
+
+ ret = scnprintf(buf + pos, sizeof(buf) - pos, "setting: ");
+ if (!ret) {
+ ret = -EINVAL;
+ goto err_format_string;
+ }
+ pos += ret;
+
+ if (!filter_item->enable) {
+ ret = scnprintf(buf + pos, sizeof(buf) - pos, "*");
+ if (!ret) {
+ ret = -EINVAL;
+ goto err_format_string;
+ }
+ pos += ret;
+ goto format_finish;
+ }
+
+ switch (format_item->type) {
+ case SIGNED:
+ ret = signed_type_read(buf + pos, sizeof(buf) - pos,
+ filter_item);
+ break;
+ case UNSIGNED:
+ ret = unsigned_type_read(buf + pos, sizeof(buf) - pos,
+ filter_item);
+ break;
+ case STRING:
+ ret = string_type_read(buf + pos, sizeof(buf) - pos,
+ filter_item);
+ break;
+ case CHAR:
+ ret = char_type_read(buf + pos, sizeof(buf) - pos,
+ filter_item);
+ break;
+ default:
+ /* should not run to here */
+ printk(KERN_ERR "%s: unknown type %d\n",
+ __func__, format_item->type);
+ ret = -EIO;
+ goto err_unknown_type;
+ }
+ if (!ret) {
+ ret = -EINVAL;
+ goto err_format_string;
+ }
+ pos += ret;
+
+format_finish:
+ ret = scnprintf(buf + pos, sizeof(buf) - pos, "\n");
+ if (!ret) {
+ ret = -EINVAL;
+ goto err_format_string;
+ }
+ pos += ret;
+
+ ret = simple_read_from_buffer(ubuf, cnt, ppos, buf,
+ strlen(buf));
+ } else {
+ ret = -EIO;
+ }
+
+err_format_string:
+err_unknown_type:
+ spin_unlock(&filter_lock);
+ unlock_filter();
+
+ return ret;
+}
+
+/*
+ * Check op info in a input string
+ * return length of op part on success, *op is set.
+ * return 0 on fail
+ */
+static size_t scan_op(const char *str, enum compare_op *op)
+{
+ /* Must reverse sort with strlen */
+ static struct {
+ const char *str;
+ enum compare_op op;
+ } str_op[] = {
+ {"gt", gt},
+ {"ge", ge},
+ {"eq", eq},
+ {"le", le},
+ {"lt", lt},
+ {">=", ge},
+ {"<=", le},
+ {">", gt},
+ {"=", eq},
+ {"<", lt},
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(str_op); i++) {
+ if (!strncmp(str, str_op[i].str, strlen(str_op[i].str))) {
+ *op = str_op[i].op;
+ return strlen(str_op[i].str);
+ }
+ }
+
+ return 0;
+}
+
+static int signed_type_write(const char *buf, struct format_item *format_item,
+ struct filter_item *filter_item)
+{
+ int oplen;
+ enum compare_op op;
+ long long value = 0;
+
+ oplen = scan_op(buf, &op);
+ if (!oplen)
+ return -EINVAL;
+
+ switch (format_item->size) {
+ case 1:
+ {
+ char tmp;
+ if (sscanf(buf + oplen, "%hhd", &tmp) != 1)
+ return -EINVAL;
+ value = tmp;
+ }
+ break;
+ case 2:
+ {
+ short int tmp;
+ if (sscanf(buf + oplen, "%hd", &tmp) != 1)
+ return -EINVAL;
+ value = tmp;
+ }
+ break;
+ case 4:
+ {
+ int tmp;
+ if (sscanf(buf + oplen, "%d", &tmp) != 1)
+ return -EINVAL;
+ value = tmp;
+ }
+ break;
+ case 8:
+ if (sscanf(buf + oplen, "%lld", &value) != 1)
+ return -EINVAL;
+ break;
+ default:
+ /* should not run to here */
+ printk(KERN_ERR "%s: unknown size %zu\n", __func__,
+ format_item->size);
+ return -EIO;
+ }
+
+ filter_item->setting.signed_type.op = op;
+ filter_item->setting.signed_type.value = value;
+
+ return 0;
+}
+
+static int unsigned_type_write(const char *buf, struct format_item *format_item,
+ struct filter_item *filter_item)
+{
+ int oplen;
+ enum compare_op op;
+ long long value = 0;
+
+ oplen = scan_op(buf, &op);
+ if (!oplen)
+ return -EINVAL;
+
+ switch (format_item->size) {
+ case 1:
+ {
+ unsigned char tmp;
+ if (sscanf(buf + oplen, "%hhu", &tmp) != 1)
+ return -EINVAL;
+ value = tmp;
+ }
+ break;
+ case 2:
+ {
+ unsigned short int tmp;
+ if (sscanf(buf + oplen, "%hu", &tmp) != 1)
+ return -EINVAL;
+ value = tmp;
+ }
+ break;
+ case 4:
+ {
+ unsigned int tmp;
+ if (sscanf(buf + oplen, "%u", &tmp) != 1)
+ return -EINVAL;
+ value = tmp;
+ }
+ break;
+ case 8:
+ if (sscanf(buf + oplen, "%llu", &value) != 1)
+ return -EINVAL;
+ break;
+ default:
+ /* should not run to here */
+ printk(KERN_ERR "%s: unknown size %zu\n", __func__,
+ format_item->size);
+ return -EIO;
+ }
+
+ filter_item->setting.unsigned_type.op = op;
+ filter_item->setting.unsigned_type.value = value;
+
+ return 0;
+}
+
+static int string_type_write(const char *buf, struct format_item *format_item,
+ struct filter_item *filter_item)
+{
+ int oplen;
+ enum compare_op op;
+ int val_len;
+
+ oplen = scan_op(buf, &op);
+ if (!oplen)
+ return -EINVAL;
+
+ /* End string on \n */
+ for (val_len = 0; *(buf + oplen + val_len); val_len++) {
+ if (*(buf + oplen + val_len) == '\n')
+ break;
+ }
+
+ if (val_len > ARRAY_SIZE(filter_item->setting.string_type.value) - 1)
+ return -EINVAL;
+
+ filter_item->setting.string_type.op = op;
+ memcpy(filter_item->setting.string_type.value, buf + oplen, val_len);
+ filter_item->setting.string_type.value[val_len] = 0;
+
+ return 0;
+}
+
+static int char_type_write(const char *buf, struct format_item *format_item,
+ struct filter_item *filter_item)
+{
+ int oplen;
+ enum compare_op op;
+ unsigned char value;
+ int val_start;
+
+ oplen = scan_op(buf, &op);
+ if (!oplen)
+ return -EINVAL;
+
+ /* bypass blanks */
+ for (val_start = 0; *(buf + oplen + val_start) == ' ' ||
+ *(buf + oplen + val_start) == '\t'; val_start++)
+ ;
+
+ /*
+ * parse escape string, following escape string are supported:
+ * "\n"
+ * "\[num]"
+ * "\[char expect \n and \[num]]", as "\\", "\ ", ...
+ */
+ switch (*(buf + oplen + val_start)) {
+ case '\0':
+ case '\n':
+ return -EINVAL;
+ case '\\':
+ switch (*(buf + oplen + val_start + 1)) {
+ case '\0':
+ case '\n':
+ return -EINVAL;
+ case 'n':
+ value = '\n';
+ break;
+ default:
+ if (isdigit(*(buf + oplen + val_start + 1))) {
+ if (sscanf(buf + oplen + val_start + 1, "%hhu",
+ &value) != 1)
+ return -EINVAL;
+ } else {
+ value =
+ (*(buf + oplen + val_start + 1));
+ }
+ break;
+ }
+ break;
+ default:
+ value = *(buf + oplen + val_start);
+ break;
+ }
+
+ filter_item->setting.char_type.op = op;
+ filter_item->setting.char_type.value = value;
+
+ return 0;
+}
+
+static ssize_t type_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ size_t ret;
+ struct filter_item *filter_item;
+
+ lock_filter();
+ spin_lock(&filter_lock);
+ filter_item = filp->f_dentry->d_inode->i_private;
+ if (filter_item) {
+ struct format_item *format_item;
+ char buf[NAME_MAX];
+ size_t buf_size;
+ int val_start;
+
+ format_item = filter_item_format(filter_item);
+
+ buf_size = min(cnt, sizeof(buf) - 1);
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto err_copy_from_user;
+ }
+ buf[buf_size] = 0;
+
+ /* Check is to disable filter */
+ for (val_start = 0; *(buf + val_start) == ' ' ||
+ *(buf + val_start) == '\t'; val_start++)
+ ;
+ if (!strncmp(buf + val_start, "*", strlen("*"))
+ || !strncmp(buf + val_start, "off", strlen("off"))) {
+ struct marker_filter *filter;
+ int i;
+
+ filter_item->enable = 0;
+
+ /*
+ * rescan all items in this marker to
+ * update marker_filter->enable
+ */
+ filter = filter_item->parent;
+ filter->enable = 0;
+ for (i = 0; i < filter->format->format_item_cnt; i++) {
+ if (filter->items[i].enable) {
+ filter->enable = 1;
+ break;
+ }
+ }
+
+ goto scan_finish;
+ }
+
+ switch (format_item->type) {
+ case SIGNED:
+ ret = signed_type_write(buf, format_item, filter_item);
+ break;
+ case UNSIGNED:
+ ret = unsigned_type_write(buf, format_item,
+ filter_item);
+ break;
+ case STRING:
+ ret = string_type_write(buf, format_item, filter_item);
+ break;
+ case CHAR:
+ ret = char_type_write(buf, format_item, filter_item);
+ break;
+ default:
+ /* should not run to here */
+ printk(KERN_ERR "%s: unknown type %d\n",
+ __func__, format_item->type);
+ ret = -EIO;
+ goto err_unknown_type;
+ }
+ if (IS_ERR_VALUE(ret))
+ goto err_scan_string;
+
+ filter_item->enable = 1;
+ filter_item->parent->enable = 1;
+
+scan_finish:
+ ret = cnt;
+ } else {
+ ret = -EIO;
+ }
+
+err_scan_string:
+err_unknown_type:
+err_copy_from_user:
+ spin_unlock(&filter_lock);
+ unlock_filter();
+
+ return ret;
+}
+
+static const struct file_operations type_fops = {
+ .read = type_read,
+ .write = type_write,
+};
+
+static int init_filter_items(struct marker_filter *filter,
+ struct marker_format *format)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < format->format_item_cnt; i++) {
+ char fname[16];
+
+ filter->items[i].parent = filter;
+ filter->items[i].enable = 0;
+
+ /* Create type file as __arg1, __arg2 */
+ sprintf(fname, "__arg%d", i + 1);
+ filter->items[i].den[0] = debugfs_create_file(fname, S_IRUGO,
+ filter->rootden, &filter->items[i], &type_fops);
+ if (IS_ERR(filter->items[i].den[0])
+ || !filter->items[i].den[0]) {
+ printk(KERN_ERR
+ "%s: create type file of %s for %s failed\n",
+ __func__, fname, filter->format->name);
+ err = -EIO;
+ goto err_build_typefile;
+ }
+
+ /* Create symbol link to type file */
+ if (filter->format->items[i]->name[0]) {
+
+ filter->items[i].den[1] = debugfs_create_symlink(
+ filter->format->items[i]->name,
+ filter->rootden, fname);
+ if (IS_ERR(filter->items[i].den[1])
+ || !filter->items[i].den[1]) {
+ /*
+ * same-name type is allowed, but it will cause
+ * create symlink fail.
+ * we ignore this type of error.
+ */
+ filter->items[i].den[1] = NULL;
+ }
+ }
+ }
+
+ return 0;
+
+err_build_typefile:
+ for (i--; i >= 0; i--) {
+ /* see comments in _destroy_marker_filter() for why set NULL */
+ filter->items[i].den[0]->d_inode->i_private = NULL;
+ debugfs_remove(filter->items[i].den[0]);
+ debugfs_remove(filter->items[i].den[1]);
+ }
+ return err;
+}
+
+static int add_marker_filter(struct marker_format *format)
+{
+ int err = 0;
+ struct marker_filter *filter;
+
+ lock_filter();
+ spin_lock(&filter_lock);
+
+#ifdef _DEBUG
+ if (_find_marker_filter(format->name)) {
+ printk(KERN_ERR "%s: filter %s already exist\n", __func__,
+ format->name);
+ err = -EIO;
+ goto err_already_exist;
+ }
+#endif /* _DEBUG */
+
+ filter = kmalloc(sizeof(struct marker_filter), GFP_KERNEL);
+ if (!filter) {
+ printk(KERN_ERR "%s: alloc struct marker_filter failed\n",
+ __func__);
+ err = -ENOMEM;
+ goto err_alloc_marker_filter;
+ }
+
+ INIT_LIST_HEAD(&filter->list);
+
+ filter->format = format;
+
+ filter->rootden = debugfs_create_dir(format->name, ltt_filter_dir);
+ if (IS_ERR(filter->rootden) || !filter->rootden) {
+ printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
+ format->name);
+ err = -EIO;
+ goto err_create_dir;
+ }
+
+ filter->formatden = debugfs_create_file("__arg0", S_IRUGO,
+ filter->rootden, filter, &format_fops);
+ if (IS_ERR(filter->formatden) || !filter->formatden) {
+ printk(KERN_ERR
+ "%s: create typefile failed\n", __func__);
+ err = -EIO;
+ goto err_build_typefile;
+ }
+
+ if (format->format_item_cnt) {
+ filter->items = kmalloc(sizeof(struct filter_item)
+ * format->format_item_cnt, GFP_KERNEL);
+ if (!filter->items) {
+ printk(KERN_ERR
+ "%s: alloc filter's items failed\n", __func__);
+ err = -ENOMEM;
+ goto err_alloc_types;
+ }
+ err = init_filter_items(filter, format);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "%s: init filter's items failed\n", __func__);
+ goto err_init_types;
+ }
+ } else {
+ filter->items = NULL;
+ }
+
+ filter->enable = 0;
+
+ list_add(&filter->list, &marker_filter_head);
+
+ spin_unlock(&filter_lock);
+ unlock_filter();
+
+ return 0;
+
+err_init_types:
+ kfree(filter->items);
+err_alloc_types:
+ /* see comments in _destroy_marker_filter() for why set NULL */
+ filter->formatden->d_inode->i_private = NULL;
+ debugfs_remove(filter->formatden);
+err_build_typefile:
+ debugfs_remove(filter->rootden);
+err_create_dir:
+ kfree(filter);
+err_alloc_marker_filter:
+#ifdef _DEBUG
+err_already_exist:
+#endif /* _DEBUG */
+ spin_unlock(&filter_lock);
+ unlock_filter();
+ return err;
+}
+
+static void _destroy_marker_filter(struct marker_filter *filter)
+{
+ int i;
+
+ list_del(&filter->list);
+
+ for (i = 0; i < filter->format->format_item_cnt; i++) {
+ filter->items[i].den[0]->d_inode->i_private = NULL;
+ debugfs_remove(filter->items[i].den[0]);
+ debugfs_remove(filter->items[i].den[1]);
+ }
+ kfree(filter->items);
+
+ /*
+ * Set i_private to NULL, so that if some fileop in process,
+ * it can avoid illegal access using i_private in fileop.
+ * (we must get filter_lock in fileop to ensure no other change
+ * in filter struct)
+ */
+ filter->formatden->d_inode->i_private = NULL;
+ debugfs_remove(filter->formatden);
+
+ debugfs_remove(filter->rootden);
+
+ kfree(filter);
+
+ return;
+}
+
+static void destroy_all_filter(void)
+{
+ struct marker_filter *filter, *next;
+
+ lock_filter();
+ spin_lock(&filter_lock);
+ list_for_each_entry_safe(filter, next, &marker_filter_head, list) {
+ _destroy_marker_filter(filter);
+ }
+ spin_unlock(&filter_lock);
+ unlock_filter();
+}
+
+static int bulid_exist_filter(void)
+{
+ int err;
+ struct marker_format *format;
+
+ if (!list_empty(&marker_filter_head)) {
+ err = -EEXIST;
+ goto err_not_empty;
+ }
+
+ mutex_lock(&format_lock);
+ list_for_each_entry(format, &marker_format_head, list) {
+ err = add_marker_filter(format);
+ if (IS_ERR_VALUE(err)) {
+ printk("%s: add_marker_filter failed\n", __func__);
+ goto err_add_filter;
+ }
+ }
+ mutex_unlock(&format_lock);
+
+ return 0;
+
+err_add_filter:
+ destroy_all_filter();
+ mutex_unlock(&format_lock);
+err_not_empty:
+ return err;
+}
+
+static int format_notify(struct notifier_block *self, unsigned long val,
+ void *data)
+{
+ struct marker_format *format = data;
+ struct marker_filter *filter, *next;
+
+ switch (val) {
+ case ADD_FORMAT:
+ add_marker_filter(format);
+ break;
+ case DEL_FORMAT:
+ lock_filter();
+ spin_lock(&filter_lock);
+ list_for_each_entry_safe(filter, next, &marker_filter_head,
+ list) {
+ if (filter->format == format)
+ _destroy_marker_filter(filter);
+ }
+ spin_unlock(&filter_lock);
+ unlock_filter();
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block format_nb = {
+ .notifier_call = format_notify,
+};
+
+static int marker_filter_init(void)
+{
+ int err = 0;
+
+ err = bulid_exist_filter();
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "%s: bulid_exist_filter failed\n", __func__);
+ goto err_add_exist_filter;
+ }
+
+ err = register_format_notifier(&format_nb);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "%s register format notifier failed\n",
+ __func__);
+ goto err_register_format_notifier;
+ }
+
+ return 0;
+
+err_register_format_notifier:
+ destroy_all_filter();
+err_add_exist_filter:
+ return err;
+}
+
+static void marker_filter_destroy(void)
+{
+ unregister_format_notifier(&format_nb);
+ destroy_all_filter();
+}
+
+
+/*
+ * Filter operations
+ *
+ * define filter callback for lttng to determine if to output a event
+ * by user's setting.
+ *
+ */
+
+/*
+ * Ret:
+ * 0: filted
+ * 1: passed
+ * <0: err
+ */
+static int signed_filter(struct format_item *format_item,
+ struct filter_item *filter_item, va_list *args)
+{
+ long long int val;
+
+ switch (format_item->size) {
+ case 1:
+ val = (char)va_arg(*args, int);
+ break;
+ case 2:
+ val = (short int)va_arg(*args, int);
+ break;
+ case 4:
+ val = va_arg(*args, int);
+ break;
+ case 8:
+ val = va_arg(*args, long long int);
+ break;
+ default:
+ /* should not run to here */
+ printk(KERN_ERR "%s: unknown size %d\n", __func__,
+ format_item->size);
+ return -1;
+ }
+
+ /*
+ * We judge filter_item->enable here, because even if filter is not
+ * enabled, we still need to bypass this data in va_list
+ */
+ if (!filter_item->enable)
+ return 1;
+
+ switch (filter_item->setting.signed_type.op) {
+ case gt:
+ return val > filter_item->setting.signed_type.value;
+ case ge:
+ return val >= filter_item->setting.signed_type.value;
+ case eq:
+ return val == filter_item->setting.signed_type.value;
+ case le:
+ return val <= filter_item->setting.signed_type.value;
+ case lt:
+ return val < filter_item->setting.signed_type.value;
+ }
+
+ /* should not run to here */
+ return -1;
+}
+
+static int unsigned_filter(struct format_item *format_item,
+ struct filter_item *filter_item, va_list *args)
+{
+ unsigned long long int val;
+
+ switch (format_item->size) {
+ case 1:
+ val = (unsigned char)va_arg(*args, unsigned int);
+ break;
+ case 2:
+ val = (short int)va_arg(*args, unsigned int);
+ break;
+ case 4:
+ val = va_arg(*args, unsigned int);
+ break;
+ case 8:
+ val = va_arg(*args, unsigned long long int);
+ break;
+ default:
+ /* should not run to here */
+ printk(KERN_ERR "%s: unknown size %d\n", __func__,
+ format_item->size);
+ return -1;
+ }
+
+ /*
+ * We judge filter_item->enable here, because even if filter is not
+ * enabled, we still need to bypass this data in va_list
+ */
+ if (!filter_item->enable)
+ return 1;
+
+ switch (filter_item->setting.unsigned_type.op) {
+ case gt:
+ return val > filter_item->setting.unsigned_type.value;
+ case ge:
+ return val >= filter_item->setting.unsigned_type.value;
+ case eq:
+ return val == filter_item->setting.unsigned_type.value;
+ case le:
+ return val <= filter_item->setting.unsigned_type.value;
+ case lt:
+ return val < filter_item->setting.unsigned_type.value;
+ }
+
+ /* should not run to here */
+ return -1;
+}
+
+static int string_filter(struct format_item *format_item,
+ struct filter_item *filter_item, va_list *args)
+{
+ const char *val;
+
+ val = va_arg(*args, const char *);
+
+ /*
+ * We judge filter_item->enable here, because even if filter is not
+ * enabled, we still need to bypass this data in va_list
+ */
+ if (!filter_item->enable)
+ return 1;
+
+ switch (filter_item->setting.string_type.op) {
+ case gt:
+ return strcmp(val, filter_item->setting.string_type.value) > 0;
+ case ge:
+ return strcmp(val, filter_item->setting.string_type.value) >= 0;
+ case eq:
+ return strcmp(val, filter_item->setting.string_type.value) == 0;
+ case le:
+ return strcmp(val, filter_item->setting.string_type.value) <= 0;
+ case lt:
+ return strcmp(val, filter_item->setting.string_type.value) < 0;
+ }
+
+ /* should not run to here */
+ return -1;
+}
+
+static int char_filter(struct format_item *format_item,
+ struct filter_item *filter_item, va_list *args)
+{
+ unsigned char val;
+
+ val = (unsigned char)va_arg(*args, unsigned int);
+
+ /*
+ * We judge filter_item->enable here, because even if filter is not
+ * enabled, we still need to bypass this data in va_list
+ */
+ if (!filter_item->enable)
+ return 1;
+
+ switch (filter_item->setting.char_type.op) {
+ case gt:
+ return val > filter_item->setting.char_type.value;
+ case ge:
+ return val >= filter_item->setting.char_type.value;
+ case eq:
+ return val == filter_item->setting.char_type.value;
+ case le:
+ return val <= filter_item->setting.char_type.value;
+ case lt:
+ return val < filter_item->setting.char_type.value;
+ }
+
+ /* should not run to here */
+ return -1;
+}
+
+static int ltt_filter(struct ltt_trace_struct *trace,
+ const struct marker *mdata, va_list *args)
+{
+ int ret;
+ struct marker_filter *filter;
+ struct marker_format *format;
+ int i;
+
+ if (lock_filter()) {
+ /* Don't output events caused by ltt-filter */
+ return 0;
+ }
+
+ spin_lock(&filter_lock);
+
+ filter = _find_marker_filter(mdata->name);
+ if (!filter) {
+ printk(KERN_ERR "%s: unknown marker %s\n", __func__,
+ mdata->name);
+ ret = 1;
+ goto done;
+ }
+
+ /* Bypass if no filter setting on this marker */
+ if (!filter->enable) {
+ ret = 1;
+ goto done;
+ }
+
+ format = filter->format;
+
+ for (i = 0; i < format->format_item_cnt; i++) {
+ int result;
+
+ switch (format->items[i]->type) {
+ case SIGNED:
+ result = signed_filter(format->items[i],
+ filter->items + i, args);
+ break;
+ case UNSIGNED:
+ result = unsigned_filter(format->items[i],
+ filter->items + i, args);
+ break;
+ case STRING:
+ result = string_filter(format->items[i],
+ filter->items + i, args);
+ break;
+ case CHAR:
+ result = char_filter(format->items[i],
+ filter->items + i, args);
+ break;
+ default:
+ /* should not run to here */
+ printk(KERN_ERR "%s: unknown type %d\n", __func__,
+ format->items[i]->type);
+ ret = 1;
+ goto done;
+ }
+ if (result < 0) {
+ ret = 1;
+ goto done;
+ }
+ if (!result) {
+ ret = 0;
+ goto done;
+ }
+ }
+
+ ret = 1;
+
+done:
+ spin_unlock(&filter_lock);
+ unlock_filter();
+ return ret;
+}
+
+
+/*
+ * Init/exit option of module
+ *
+ * Init marker_format and marker_filter operations
+ * register filter callback in ltt
+ *
+ */
+
+static int __init ltt_filter_init(void)
+{
+ int err = 0;
+ struct dentry *ltt_root_dentry;
+
+ ltt_root_dentry = get_ltt_root();
+ if (!ltt_root_dentry) {
+ err = -ENOENT;
+ goto err_no_root;
+ }
+
+ ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR, ltt_root_dentry);
+ if (IS_ERR(ltt_filter_dir) || !ltt_filter_dir) {
+ printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
+ LTT_FILTER_DIR);
+ err = -ENOMEM;
+ goto err_create_filter_dir;
+ }
+
+ err = marker_format_init();
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "%s: init marker format failed\n", __func__);
+ goto err_marker_format_init;
+ }
+
+ err = marker_filter_init();
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "%s: init marker filter failed\n", __func__);
+ goto err_marker_filter_init;
+ }
+
+ err = ltt_module_register(LTT_FUNCTION_RUN_FILTER, ltt_filter,
+ THIS_MODULE);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "%s: register ltt filter failed\n", __func__);
+ goto err_filter_register;
+ }
+
+ return 0;
+
+err_filter_register:
+ marker_filter_destroy();
+err_marker_filter_init:
+ marker_format_destroy();
+err_marker_format_init:
+ debugfs_remove(ltt_filter_dir);
+err_create_filter_dir:
+err_no_root:
+ return err;
+}
+
+static void __exit ltt_filter_exit(void)
+{
+ ltt_module_unregister(LTT_FUNCTION_RUN_FILTER);
+ marker_filter_destroy();
+ marker_format_destroy();
+
+ debugfs_remove(ltt_filter_dir);
+}
+
+module_init(ltt_filter_init);
+module_exit(ltt_filter_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhao Lei <zhaolei at cn.fujitsu.com>");
+MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
--
1.5.5.3
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 3/3] In-kernel filter module
2009-03-29 8:44 ` [ltt-dev] [PATCH 3/3] In-kernel filter module Zhaolei
@ 2009-04-06 17:52 ` Mathieu Desnoyers
2009-04-07 5:45 ` Zhaolei
0 siblings, 1 reply; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-04-06 17:52 UTC (permalink / raw)
* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> LTTng's in-kernel filter is used to filter event by its arguments.
> User can config every event's filter option by echo to filts in
> debugfs/ltt/filter
>
Hi Zhaolei,
That's a good start, but locking will have to be rethought. Please see
comments below,
> Signed-off-by: Zhao Lei <zhaolei at cn.fujitsu.com>
> ---
> ltt/Kconfig | 9 +
> ltt/Makefile | 1 +
> ltt/ltt-filter.c | 1831 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1841 insertions(+), 0 deletions(-)
> create mode 100644 ltt/ltt-filter.c
>
> diff --git a/ltt/Kconfig b/ltt/Kconfig
> index 3c520f5..2cfb479 100644
> --- a/ltt/Kconfig
> +++ b/ltt/Kconfig
> @@ -214,4 +214,13 @@ config LTT_ASCII
> Output trace data in a text-formatted ascii file, presented in
> /mnt/debugfs/ltt/ascii/<trace name>.
>
> +config LTT_FILTER
> + tristate "Linux Trace Toolkit Filter"
> + depends on LTT_TRACER
> + depends on LTT_SERIALIZE
> + default n
> + help
> + Filter for LTTng to control which type of event to be traced.
> + Config filter rule by debugfs/ltt/filter
> +
> endif # LTT
> diff --git a/ltt/Makefile b/ltt/Makefile
> index 5ea5cca..29ab6e4 100644
> --- a/ltt/Makefile
> +++ b/ltt/Makefile
> @@ -19,3 +19,4 @@ obj-$(CONFIG_LTT_KPROBES) += ltt-kprobes.o
> obj-$(CONFIG_LTT_TRACEPROBES) += probes/
> obj-$(CONFIG_LTT_FTRACE) += ltt-ftrace.o
> obj-$(CONFIG_LTT_ASCII) += ltt-ascii.o
> +obj-$(CONFIG_LTT_FILTER) += ltt-filter.o
> diff --git a/ltt/ltt-filter.c b/ltt/ltt-filter.c
> new file mode 100644
> index 0000000..bf2760a
> --- /dev/null
> +++ b/ltt/ltt-filter.c
> @@ -0,0 +1,1831 @@
> +/*
> + * LTT filter module.
> + *
> + * Copyright 2009 -
> + * Zhaolei <zhaolei at cn.fujitsu.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/debugfs.h>
> +#include <linux/ltt-core.h>
> +#include <linux/ctype.h>
> +#include <linux/uaccess.h>
> +#include <linux/marker.h>
> +#include <linux/ltt-tracer.h>
> +#include <asm/atomic.h>
> +
> +#define LTT_FILTER_DIR "filter"
> +
> +struct dentry *ltt_filter_dir;
> +
> +/*
> + * Marker format operations
> + *
> + * Maintenance a marker_format list sync with module add and release
> + * Each data-type in format are paresed and stored in marker_format struct
paresed -> parsed
> + * Impl a notifier for respond add/remove marker format event
Notifier which responds to add/remove marker format event
> + */
> +
> +enum format_item_type {
> + SIGNED,
> + UNSIGNED,
> + STRING,
> + CHAR,
> +};
> +const char *format_item_type_string[] = {
> + "signed int",
> + "unsigned int",
> + "string",
> + "char",
> +};
> +
> +struct format_item {
> + struct list_head list;
> + char name[PATH_MAX];
this rather large "name" field should go at the end of the struct.
> + enum format_item_type type;
> + size_t size;
> +};
> +
> +struct marker_format {
> + atomic_t refcnt;
> + struct list_head list;
> + const char *name;
> + const char *format;
> + struct format_item **items;
> + size_t format_item_cnt;
> +};
> +
> +static LIST_HEAD(marker_format_head);
> +static DEFINE_MUTEX(format_lock);
> +
> +BLOCKING_NOTIFIER_HEAD(format_notifier_list);
> +enum FORMAT_EVENT {
> + ADD_FORMAT,
> + DEL_FORMAT,
> +};
> +
> +static struct format_item *new_format_item(const char *name_head,
> + const char *name_tail, int type_type, int type_size)
> +{
> + struct format_item *format_item;
> + const char *p;
> + int i, name_len;
> +
> + format_item = kmalloc(sizeof(struct format_item), GFP_KERNEL);
> + if (!format_item) {
> + printk(KERN_ERR "%s: alloc format_item failed\n", __func__);
> + goto err_alloc_format_item;
> + }
> +
> + INIT_LIST_HEAD(&format_item->list);
> +
> + /* trim itemname */
> + while (name_head < name_tail
> + && *name_head == ' ')
> + name_head++;
> + while (name_tail > name_head
> + && *(name_tail - 1) == ' ')
> + name_tail--;
> +
> + name_len = name_tail - name_head;
> + if (name_len) {
> + for (i = 0, p = name_head; i < PATH_MAX - 1 && p < name_tail;
> + i++, p++) {
> + if ((*p == '%' && p + 1 < name_tail && *(p + 1) == '%')
> + || (*p == '#' &&
> + p + 1 < name_tail && *(p + 1) == '#')) {
> + p++;
> + }
> + format_item->name[i] = isalnum(*p) ? *p : '_';
> + }
> + format_item->name[i] = 0;
> + } else {
> + format_item->name[0] = 0;
> + }
> +
> + format_item->type = type_type;
> + format_item->size = type_size;
> +
> + return format_item;
> +
> +err_alloc_format_item:
> + return NULL;
> +}
> +
> +static size_t parse_format(const char *format, struct format_item ***items)
> +{
> + int ret = 0; /* number of types in format string, or -ERRNO on fail */
> + int state; /* 0: in name, 1: in type, 2: in # */
> + const char *p; /* format string's iter */
> + /* name for current type, init with 0 to avoid compile warning */
> + const char *name_head, *name_tail = 0;
> + enum format_item_type type_type;
> + /*
> + * size(in bytes) of current type
> + * init with 0 to avoid compile warning
> + */
> + int type_size = 0;
> +
> + LIST_HEAD(format_item_list);
> + struct format_item *format_item, *format_item_next;
> +
> + for (state = 0, name_head = p = format; *p; p++) {
> + switch (state) {
> + case 0:
> + switch (*p) {
> + case '%':
> + if (*(p+1) != '%') {
> + name_tail = p;
> + type_size = sizeof(int);
> + state = 1;
> + } else {
> + p++;
> + }
> + break;
> + case '#':
> + if (*(p+1) != '#') {
> + name_tail = p;
> + state = 2;
> + } else {
> + p++;
> + }
> + break;
> + default:
> + break;
> + }
> + break;
> + case 1:
> + switch (*p) {
> + case 'l':
> + if (*(p+1) == 'l') {
> + type_size = sizeof(long long);
> + p++;
> + } else {
> + type_size = sizeof(long);
> + }
> + break;
> + case 'L':
> + type_size = sizeof(long long);
> + break;
> + case 'z':
> + case 'Z':
> + type_size = sizeof(size_t);
> + break;
> + case 't':
> + type_size = sizeof(ptrdiff_t);
> + break;
> + case 'h':
> + type_size = sizeof(short);
> + break;
> + case 'd':
> + case 'i':
> + type_type = SIGNED;
> + goto type_finish;
> + break;
> + case 'u':
> + case 'o':
> + case 'x':
> + case 'X':
> + type_type = UNSIGNED;
> + goto type_finish;
> + break;
> + case 'p':
> + type_type = UNSIGNED;
> + type_size = sizeof(void *);
> + goto type_finish;
> + break;
> + case 's':
> + type_type = STRING;
> + type_size = sizeof(void *);
> + goto type_finish;
> + break;
> + case 'c':
> + type_type = CHAR;
> + type_size = sizeof(char);
> + goto type_finish;
> + break;
> + case '0':
> + case '1':
> + case '2':
> + case '3':
> + case '4':
> + case '5':
> + case '6':
> + case '7':
> + case '8':
> + case '9':
> + break;
> + default:
> + printk(KERN_ERR
> + "%s: unknown char after %%: %c\n",
> + __func__, *p);
> + break;
> + }
> + break;
> + case 2:
> + switch (*p) {
> + case 'l':
> + case 'L':
> + case 'Z':
> + case 'z':
> + case 't':
> + case 'h':
> + case 'd':
> + case 'i':
> + case 'u':
> + case 'o':
> + case 'x':
> + case 'X':
> + case 'p':
> + case 's':
> + case 'c':
> + case '1':
> + case '2':
> + case '4':
> + case '8':
> + break;
> + case '%':
> + type_size = sizeof(int);
> + state = 1;
> + break;
> + default:
> + printk(KERN_ERR "%s: unknown char after #: %c\n",
> + __func__, *p);
> + break;
> + }
> + break;
> + default:
> + /* should not run to here */
> + ret = -EINVAL;
> + goto err_unknown_state;
> + }
> + continue;
> +type_finish:
> + format_item = new_format_item(name_head, name_tail, type_type,
> + type_size);
> + if (!format_item) {
> + printk(KERN_ERR "%s: add_format_item failed\n",
> + __func__);
> + ret = -ENOMEM;
> + goto err_new_format_item;
> + }
> +
> + list_add_tail(&format_item->list, &format_item_list);
> + ret++;
> + name_head = p + 1;
> + state = 0;
> + continue;
> + }
> +
> + /*
> + * We have parsed types in format, and build linked_list to save
> + * there information.
> + * Now we will build a point array to save each format_item for
> + * easy access.
> + */
> +
> + *items = kmalloc(ret * sizeof(struct format_item *), GFP_KERNEL);
> + if (!*items) {
> + printk(KERN_ERR "%s: alloc format_item index failed\n",
> + __func__);
> + ret = -ENOMEM;
> + goto err_alloc_format_item;
> + }
> +
> + /* we reuse state as a iter for (*items)[] */
> + state = 0;
> + list_for_each_entry(format_item, &format_item_list, list)
> + (*items)[state++] = format_item;
> +
> + return ret;
> +
> +err_alloc_format_item:
> +err_unknown_state:
> +err_new_format_item:
> + list_for_each_entry_safe(format_item, format_item_next,
> + &format_item_list, list)
> + kfree(format_item);
> + return ret;
> +}
> +
> +/* need have format_lock */
> +static struct marker_format *_find_marker_format(const char *name)
> +{
> + struct marker_format *format;
> + list_for_each_entry(format, &marker_format_head, list) {
> + if (!strcmp(name, format->name))
> + return format;
> + }
> + return NULL;
> +}
> +
> +/*
> + * For add_marker_format internal use
> + * need have format_lock
> + */
> +static struct marker_format *_new_marker_format(struct marker *marker)
> +{
> + struct marker_format *format;
> + char *tmp;
> +
> + format = kmalloc(sizeof(struct marker_format), GFP_KERNEL);
> + if (!format)
> + goto err_alloc_format;
> +
> + atomic_set(&format->refcnt, 1);
> +
> + INIT_LIST_HEAD(&format->list);
> +
> + tmp = kmalloc(strlen(marker->name) + 1, GFP_KERNEL);
> + if (!tmp)
> + goto err_alloc_name;
> + strcpy(tmp, marker->name);
> + format->name = tmp;
> +
> + tmp = kmalloc(strlen(marker->format) + 1, GFP_KERNEL);
> + if (!tmp)
> + goto err_alloc_formatstr;
> + strcpy(tmp, marker->format);
> + format->format = tmp;
> +
> + format->format_item_cnt = parse_format(marker->format,
> + &format->items);
> + if (IS_ERR_VALUE(format->format_item_cnt)) {
> + printk(KERN_ERR "%s: parse format failed: %s\n", __func__,
> + marker->format);
> + goto err_parse_file;
> + }
> +
> + list_add(&format->list, &marker_format_head);
> +
> + blocking_notifier_call_chain(&format_notifier_list, ADD_FORMAT, format);
> +
> + return format;
> +
> +err_parse_file:
> + kfree(format->format);
> +err_alloc_formatstr:
> + kfree(format->name);
> +err_alloc_name:
> + kfree(format);
> +err_alloc_format:
> + return NULL;
> +}
> +
> +/* need have format_lock */
> +static void _destroy_marker_format(struct marker_format *format)
> +{
> + int i;
> +
> + blocking_notifier_call_chain(&format_notifier_list, DEL_FORMAT, format);
> +
> + list_del(&format->list);
> +
> + for (i = 0; i < format->format_item_cnt; i++)
> + kfree(format->items[i]);
> + kfree(format->items);
> + kfree(format->format);
> + kfree(format->name);
> + kfree(format);
> +
> + return;
> +}
> +
> +static struct marker_format *add_marker_format(struct marker *marker)
> +{
> + struct marker_format *format;
> +
> + mutex_lock(&format_lock);
> +
> + format = _find_marker_format(marker->name);
> + if (!format) {
> + format = _new_marker_format(marker);
> + if (!format) {
> + printk(KERN_ERR
> + "%s: new format struct for %s failed\n",
> + __func__, marker->name);
> + goto err_alloc_format;
> + }
> + } else {
> + atomic_inc(&format->refcnt);
> + }
> +
> + mutex_unlock(&format_lock);
> + return format;
> +
> +err_alloc_format:
> + mutex_unlock(&format_lock);
> + return NULL;
> +}
> +
> +static int put_marker_format(const char *name)
> +{
> + struct marker_format *format;
> + int err = 0;
> +
> + mutex_lock(&format_lock);
> +
> + format = _find_marker_format(name);
> + if (!format) {
> + printk(KERN_ERR "%s: can't find format item for %s\n",
> + __func__, name);
> + err = -ENOENT;
> + goto err_find_marker_format;
> + }
> +
> + if (atomic_dec_and_test(&format->refcnt))
> + _destroy_marker_format(format);
> +
> + mutex_unlock(&format_lock);
> + return 0;
> +
> +err_find_marker_format:
> + mutex_unlock(&format_lock);
> + return err;
> +}
> +
> +static void destroy_all_format(void)
> +{
> + struct marker_format *format, *next;
> +
> + mutex_lock(&format_lock);
> +
> + list_for_each_entry_safe(format, next, &marker_format_head, list) {
> + _destroy_marker_format(format);
> + }
> +
> + mutex_unlock(&format_lock);
> +
> + return;
> +}
> +
> +static int bulid_exist_format(void)
> +{
> + struct marker_iter iter;
> + struct marker_format *format;
> + int err = 0;
> +
> + if (!list_empty(&marker_format_head)) {
> + err = -EEXIST;
> + goto err_not_empty;
> + }
> +
> + marker_iter_reset(&iter);
> + marker_iter_start(&iter);
> + for (; iter.marker != NULL; marker_iter_next(&iter)) {
> + format = add_marker_format(iter.marker);
> + if (!format) {
> + err = -EIO;
> + goto err_build_fail;
> + }
> + }
> + marker_iter_stop(&iter);
> + return 0;
> +
> +err_build_fail:
> + destroy_all_format();
> +err_not_empty:
> + return err;
> +}
> +
> +static int module_notify(struct notifier_block *self,
> + unsigned long val, void *data)
> +{
> + struct module *mod = data;
> + struct marker *iter;
> +
> + switch (val) {
> + case MODULE_STATE_COMING:
> + for (iter = mod->markers;
> + iter < mod->markers + mod->num_markers; iter++)
> + add_marker_format(iter);
> + break;
> + case MODULE_STATE_GOING:
> + for (iter = mod->markers;
> + iter < mod->markers + mod->num_markers; iter++)
> + put_marker_format(iter->name);
> + break;
> + }
> + return NOTIFY_OK;
> +}
> +
> +static struct notifier_block module_nb = {
> + .notifier_call = module_notify,
> +};
> +
> +static int marker_format_init(void)
> +{
> + int err = 0;
> +
> + err = bulid_exist_format();
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR "%s: bulid_exist_format failed\n", __func__);
> + goto err_add_exist_filter;
> + }
> +
> + /* Todo:
> + * If a module is insert/delete here(between
> + * bulid_exist_format() and register_module_notifier()),
> + * marker_format struct is not uptodate.
> + */
> +
> + err = register_module_notifier(&module_nb);
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR "%s register module notifier failed\n",
> + __func__);
> + goto err_register_module_notifier;
> + }
> +
> + return 0;
> +
> +err_register_module_notifier:
> + destroy_all_format();
> +err_add_exist_filter:
> + return err;
> +}
> +
> +static void marker_format_destroy(void)
> +{
> + unregister_module_notifier(&module_nb);
> + destroy_all_format();
> +}
> +
> +static int register_format_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_register(&format_notifier_list, nb);
> +}
> +
> +static int unregister_format_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_unregister(&format_notifier_list, nb);
> +}
> +
> +
> +/*
> + * Control operations
> + *
> + * Maintenance a dir struct and datastruct for control filter
> + * dir struct will dynamical updated when marker_format got changed
> + *
> + * Todo:
> + * Control per-trace filter
> + *
> + */
> +
> +enum compare_op {
> + gt,
> + ge,
> + eq,
> + le,
> + lt,
> +};
> +const char *compare_op_string[] = {
> + ">",
> + ">=",
> + "=",
> + "<=",
> + "<",
> +};
> +
> +struct marker_filter;
> +struct filter_item {
> + int enable;
> + union {
> + struct {
> + enum compare_op op;
> + long long value;
> + } signed_type;
> + struct {
> + enum compare_op op;
> + unsigned long long value;
> + } unsigned_type;
> + struct {
> + enum compare_op op;
> + char value[PATH_MAX]; /* Todo: Make it dynamic */
> + } string_type;
> + struct {
> + enum compare_op op;
> + unsigned char value;
> + } char_type;
> + } setting;
> + struct marker_filter *parent;
> + struct dentry *den[2]; /* [0]: __argX, [1]: symbol link to __argX */
> +};
> +
> +struct marker_filter {
> + struct list_head list;
> + struct marker_format *format;
> + struct filter_item *items;
> + struct dentry *rootden;
> + struct dentry *formatden;
> + /*
> + * is any of items[] are enabled
> + * for bypass marker which haven't any filter fast
> + */
> + int enable;
> +};
> +static inline unsigned int filter_item_no(struct filter_item *filter_item)
> +{
> + return filter_item - filter_item->parent->items;
> +}
> +static inline struct format_item *filter_item_format(
> + struct filter_item *filter_item)
> +{
> + return filter_item->parent->format->items[filter_item_no(filter_item)];
> +}
> +
> +static LIST_HEAD(marker_filter_head);
> +
> +/*
> + * Use spinlock to lock filter struct because we can't use mutex in filter
> + * callback
> + */
> +static DEFINE_SPINLOCK(filter_lock);
> +
> +/*
> + * We need call some kernel function in ltt-filter function, but many kernel
> + * function have tp/markers which cause cpu jump to ltt, then jump to
> + * ltt-filter from ltt code.
> + * Even if we don't call any kernel function in filter code, interrupt will
> + * do similar thing.
> + * To make it easy, we avoid this type of nesting by lock/unlock_filter.
> + */
> +DEFINE_PER_CPU(atomic_t, filter_nesting) = ATOMIC_INIT(0);
> +static inline int lock_filter(void)
> +{
> + preempt_disable();
> + if (atomic_xchg(&__get_cpu_var(filter_nesting), 1) > 0) {
> + /* Already in lock */
> + preempt_enable();
> + return 1;
> + }
> + return 0;
> +}
> +static inline void unlock_filter(void)
> +{
> + atomic_set(&__get_cpu_var(filter_nesting), 0);
> + preempt_enable();
> +}
Hrm, this lock_filter seems broken. If you have an interrupt nesting
over lock_filter, and takes lock_filter again, the interrupt will set
the filter_nesting to 0, which creates a race.
Basically, what you want is to disallow filter modification used by an
active tracing session _or_, if you want to allow filter modification
while a tracing session is active. But to do it properly, you want to
use RCU data structures synchronized with call_rcu_sched and
synchronize_sched and use the RCU read lock (which is _already_ taken by
LTTng around probe execution anyway) to synchronize.
The filter_lock spinlock should probably become a simple mutex too.
Mathieu
> +
> +struct marker_filter *_find_marker_filter(const char *name)
> +{
> + struct marker_filter *filter;
> + list_for_each_entry(filter, &marker_filter_head, list) {
> + if (!strcmp(name, filter->format->name))
> + return filter;
> + }
> + return NULL;
> +}
> +
> +static ssize_t format_read(struct file *filp, char __user *ubuf, size_t cnt,
> + loff_t *ppos)
> +{
> + size_t ret;
> + struct marker_filter *filter;
> +
> + lock_filter();
> + spin_lock(&filter_lock);
> + filter = filp->f_dentry->d_inode->i_private;
> + if (filter) {
> + ret = simple_read_from_buffer(ubuf, cnt, ppos,
> + filter->format->format, strlen(filter->format->format));
> + } else {
> + ret = -EIO;
> + }
> + spin_unlock(&filter_lock);
> + unlock_filter();
> +
> + return ret;
> +}
> +
> +static const struct file_operations format_fops = {
> + .read = format_read,
> +};
> +
> +static int signed_type_read(char *buf, size_t bufsize,
> + struct filter_item *filter_item)
> +{
> + int ret;
> + int pos = 0;
> +
> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> + compare_op_string[filter_item->setting.signed_type.op]);
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + ret = scnprintf(buf + pos, bufsize - pos, " %lld",
> + filter_item->setting.signed_type.value);
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + return pos;
> +}
> +
> +static int unsigned_type_read(char *buf, size_t bufsize,
> + struct filter_item *filter_item)
> +{
> + int ret;
> + int pos = 0;
> +
> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> + compare_op_string[filter_item->setting.unsigned_type.op]);
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + ret = scnprintf(buf + pos, bufsize - pos, " %llu",
> + filter_item->setting.unsigned_type.value);
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + return pos;
> +}
> +
> +static int string_type_read(char *buf, size_t bufsize,
> + struct filter_item *filter_item)
> +{
> + int ret;
> + int pos = 0;
> +
> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> + compare_op_string[filter_item->setting.string_type.op]);
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + ret = scnprintf(buf + pos, bufsize - pos, " %s",
> + filter_item->setting.string_type.value);
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + return pos;
> +}
> +
> +static int char_type_read(char *buf, size_t bufsize,
> + struct filter_item *filter_item)
> +{
> + int ret;
> + int pos = 0;
> +
> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> + compare_op_string[filter_item->setting.char_type.op]);
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + switch (filter_item->setting.char_type.value) {
> + case '\t':
> + ret = scnprintf(buf + pos, bufsize - pos, " \\t");
> + break;
> + case ' ':
> + ret = scnprintf(buf + pos, bufsize - pos, " blank");
> + break;
> + case '\n':
> + ret = scnprintf(buf + pos, bufsize - pos, " \\n");
> + break;
> + default:
> + if (isgraph(filter_item->setting.char_type.value)) {
> + ret = scnprintf(buf + pos, bufsize - pos, " %c",
> + filter_item->setting.char_type.value);
> + } else {
> + ret = scnprintf(buf + pos, bufsize - pos,
> + " \\%u", (unsigned int)
> + filter_item->setting.char_type.value);
> + }
> + break;
> + }
> + if (!ret)
> + return 0;
> + pos += ret;
> +
> + return pos;
> +}
> +
> +static ssize_t type_read(struct file *filp, char __user *ubuf, size_t cnt,
> + loff_t *ppos)
> +{
> + size_t ret;
> + struct filter_item *filter_item;
> +
> + lock_filter();
> + spin_lock(&filter_lock);
> + filter_item = filp->f_dentry->d_inode->i_private;
> + if (filter_item) {
> + struct format_item *format_item;
> + char buf[128];
> + int pos = 0;
> +
> + format_item = filter_item_format(filter_item);
> +
> + ret = scnprintf(buf + pos, sizeof(buf) - pos,
> + "type: %u byte(s) %s\n",
> + format_item->size,
> + format_item_type_string[format_item->type]);
> + if (!ret) {
> + ret = -EINVAL;
> + goto err_format_string;
> + }
> + pos += ret;
> +
> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "setting: ");
> + if (!ret) {
> + ret = -EINVAL;
> + goto err_format_string;
> + }
> + pos += ret;
> +
> + if (!filter_item->enable) {
> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "*");
> + if (!ret) {
> + ret = -EINVAL;
> + goto err_format_string;
> + }
> + pos += ret;
> + goto format_finish;
> + }
> +
> + switch (format_item->type) {
> + case SIGNED:
> + ret = signed_type_read(buf + pos, sizeof(buf) - pos,
> + filter_item);
> + break;
> + case UNSIGNED:
> + ret = unsigned_type_read(buf + pos, sizeof(buf) - pos,
> + filter_item);
> + break;
> + case STRING:
> + ret = string_type_read(buf + pos, sizeof(buf) - pos,
> + filter_item);
> + break;
> + case CHAR:
> + ret = char_type_read(buf + pos, sizeof(buf) - pos,
> + filter_item);
> + break;
> + default:
> + /* should not run to here */
> + printk(KERN_ERR "%s: unknown type %d\n",
> + __func__, format_item->type);
> + ret = -EIO;
> + goto err_unknown_type;
> + }
> + if (!ret) {
> + ret = -EINVAL;
> + goto err_format_string;
> + }
> + pos += ret;
> +
> +format_finish:
> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "\n");
> + if (!ret) {
> + ret = -EINVAL;
> + goto err_format_string;
> + }
> + pos += ret;
> +
> + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf,
> + strlen(buf));
> + } else {
> + ret = -EIO;
> + }
> +
> +err_format_string:
> +err_unknown_type:
> + spin_unlock(&filter_lock);
> + unlock_filter();
> +
> + return ret;
> +}
> +
> +/*
> + * Check op info in a input string
> + * return length of op part on success, *op is set.
> + * return 0 on fail
> + */
> +static size_t scan_op(const char *str, enum compare_op *op)
> +{
> + /* Must reverse sort with strlen */
> + static struct {
> + const char *str;
> + enum compare_op op;
> + } str_op[] = {
> + {"gt", gt},
> + {"ge", ge},
> + {"eq", eq},
> + {"le", le},
> + {"lt", lt},
> + {">=", ge},
> + {"<=", le},
> + {">", gt},
> + {"=", eq},
> + {"<", lt},
> + };
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(str_op); i++) {
> + if (!strncmp(str, str_op[i].str, strlen(str_op[i].str))) {
> + *op = str_op[i].op;
> + return strlen(str_op[i].str);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int signed_type_write(const char *buf, struct format_item *format_item,
> + struct filter_item *filter_item)
> +{
> + int oplen;
> + enum compare_op op;
> + long long value = 0;
> +
> + oplen = scan_op(buf, &op);
> + if (!oplen)
> + return -EINVAL;
> +
> + switch (format_item->size) {
> + case 1:
> + {
> + char tmp;
> + if (sscanf(buf + oplen, "%hhd", &tmp) != 1)
> + return -EINVAL;
> + value = tmp;
> + }
> + break;
> + case 2:
> + {
> + short int tmp;
> + if (sscanf(buf + oplen, "%hd", &tmp) != 1)
> + return -EINVAL;
> + value = tmp;
> + }
> + break;
> + case 4:
> + {
> + int tmp;
> + if (sscanf(buf + oplen, "%d", &tmp) != 1)
> + return -EINVAL;
> + value = tmp;
> + }
> + break;
> + case 8:
> + if (sscanf(buf + oplen, "%lld", &value) != 1)
> + return -EINVAL;
> + break;
> + default:
> + /* should not run to here */
> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
> + format_item->size);
> + return -EIO;
> + }
> +
> + filter_item->setting.signed_type.op = op;
> + filter_item->setting.signed_type.value = value;
> +
> + return 0;
> +}
> +
> +static int unsigned_type_write(const char *buf, struct format_item *format_item,
> + struct filter_item *filter_item)
> +{
> + int oplen;
> + enum compare_op op;
> + long long value = 0;
> +
> + oplen = scan_op(buf, &op);
> + if (!oplen)
> + return -EINVAL;
> +
> + switch (format_item->size) {
> + case 1:
> + {
> + unsigned char tmp;
> + if (sscanf(buf + oplen, "%hhu", &tmp) != 1)
> + return -EINVAL;
> + value = tmp;
> + }
> + break;
> + case 2:
> + {
> + unsigned short int tmp;
> + if (sscanf(buf + oplen, "%hu", &tmp) != 1)
> + return -EINVAL;
> + value = tmp;
> + }
> + break;
> + case 4:
> + {
> + unsigned int tmp;
> + if (sscanf(buf + oplen, "%u", &tmp) != 1)
> + return -EINVAL;
> + value = tmp;
> + }
> + break;
> + case 8:
> + if (sscanf(buf + oplen, "%llu", &value) != 1)
> + return -EINVAL;
> + break;
> + default:
> + /* should not run to here */
> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
> + format_item->size);
> + return -EIO;
> + }
> +
> + filter_item->setting.unsigned_type.op = op;
> + filter_item->setting.unsigned_type.value = value;
> +
> + return 0;
> +}
> +
> +static int string_type_write(const char *buf, struct format_item *format_item,
> + struct filter_item *filter_item)
> +{
> + int oplen;
> + enum compare_op op;
> + int val_len;
> +
> + oplen = scan_op(buf, &op);
> + if (!oplen)
> + return -EINVAL;
> +
> + /* End string on \n */
> + for (val_len = 0; *(buf + oplen + val_len); val_len++) {
> + if (*(buf + oplen + val_len) == '\n')
> + break;
> + }
> +
> + if (val_len > ARRAY_SIZE(filter_item->setting.string_type.value) - 1)
> + return -EINVAL;
> +
> + filter_item->setting.string_type.op = op;
> + memcpy(filter_item->setting.string_type.value, buf + oplen, val_len);
> + filter_item->setting.string_type.value[val_len] = 0;
> +
> + return 0;
> +}
> +
> +static int char_type_write(const char *buf, struct format_item *format_item,
> + struct filter_item *filter_item)
> +{
> + int oplen;
> + enum compare_op op;
> + unsigned char value;
> + int val_start;
> +
> + oplen = scan_op(buf, &op);
> + if (!oplen)
> + return -EINVAL;
> +
> + /* bypass blanks */
> + for (val_start = 0; *(buf + oplen + val_start) == ' ' ||
> + *(buf + oplen + val_start) == '\t'; val_start++)
> + ;
> +
> + /*
> + * parse escape string, following escape string are supported:
> + * "\n"
> + * "\[num]"
> + * "\[char expect \n and \[num]]", as "\\", "\ ", ...
> + */
> + switch (*(buf + oplen + val_start)) {
> + case '\0':
> + case '\n':
> + return -EINVAL;
> + case '\\':
> + switch (*(buf + oplen + val_start + 1)) {
> + case '\0':
> + case '\n':
> + return -EINVAL;
> + case 'n':
> + value = '\n';
> + break;
> + default:
> + if (isdigit(*(buf + oplen + val_start + 1))) {
> + if (sscanf(buf + oplen + val_start + 1, "%hhu",
> + &value) != 1)
> + return -EINVAL;
> + } else {
> + value =
> + (*(buf + oplen + val_start + 1));
> + }
> + break;
> + }
> + break;
> + default:
> + value = *(buf + oplen + val_start);
> + break;
> + }
> +
> + filter_item->setting.char_type.op = op;
> + filter_item->setting.char_type.value = value;
> +
> + return 0;
> +}
> +
> +static ssize_t type_write(struct file *filp, const char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + size_t ret;
> + struct filter_item *filter_item;
> +
> + lock_filter();
> + spin_lock(&filter_lock);
> + filter_item = filp->f_dentry->d_inode->i_private;
> + if (filter_item) {
> + struct format_item *format_item;
> + char buf[NAME_MAX];
> + size_t buf_size;
> + int val_start;
> +
> + format_item = filter_item_format(filter_item);
> +
> + buf_size = min(cnt, sizeof(buf) - 1);
> + if (copy_from_user(buf, ubuf, buf_size)) {
> + ret = -EFAULT;
> + goto err_copy_from_user;
> + }
> + buf[buf_size] = 0;
> +
> + /* Check is to disable filter */
> + for (val_start = 0; *(buf + val_start) == ' ' ||
> + *(buf + val_start) == '\t'; val_start++)
> + ;
> + if (!strncmp(buf + val_start, "*", strlen("*"))
> + || !strncmp(buf + val_start, "off", strlen("off"))) {
> + struct marker_filter *filter;
> + int i;
> +
> + filter_item->enable = 0;
> +
> + /*
> + * rescan all items in this marker to
> + * update marker_filter->enable
> + */
> + filter = filter_item->parent;
> + filter->enable = 0;
> + for (i = 0; i < filter->format->format_item_cnt; i++) {
> + if (filter->items[i].enable) {
> + filter->enable = 1;
> + break;
> + }
> + }
> +
> + goto scan_finish;
> + }
> +
> + switch (format_item->type) {
> + case SIGNED:
> + ret = signed_type_write(buf, format_item, filter_item);
> + break;
> + case UNSIGNED:
> + ret = unsigned_type_write(buf, format_item,
> + filter_item);
> + break;
> + case STRING:
> + ret = string_type_write(buf, format_item, filter_item);
> + break;
> + case CHAR:
> + ret = char_type_write(buf, format_item, filter_item);
> + break;
> + default:
> + /* should not run to here */
> + printk(KERN_ERR "%s: unknown type %d\n",
> + __func__, format_item->type);
> + ret = -EIO;
> + goto err_unknown_type;
> + }
> + if (IS_ERR_VALUE(ret))
> + goto err_scan_string;
> +
> + filter_item->enable = 1;
> + filter_item->parent->enable = 1;
> +
> +scan_finish:
> + ret = cnt;
> + } else {
> + ret = -EIO;
> + }
> +
> +err_scan_string:
> +err_unknown_type:
> +err_copy_from_user:
> + spin_unlock(&filter_lock);
> + unlock_filter();
> +
> + return ret;
> +}
> +
> +static const struct file_operations type_fops = {
> + .read = type_read,
> + .write = type_write,
> +};
> +
> +static int init_filter_items(struct marker_filter *filter,
> + struct marker_format *format)
> +{
> + int err = 0;
> + int i;
> +
> + for (i = 0; i < format->format_item_cnt; i++) {
> + char fname[16];
> +
> + filter->items[i].parent = filter;
> + filter->items[i].enable = 0;
> +
> + /* Create type file as __arg1, __arg2 */
> + sprintf(fname, "__arg%d", i + 1);
> + filter->items[i].den[0] = debugfs_create_file(fname, S_IRUGO,
> + filter->rootden, &filter->items[i], &type_fops);
> + if (IS_ERR(filter->items[i].den[0])
> + || !filter->items[i].den[0]) {
> + printk(KERN_ERR
> + "%s: create type file of %s for %s failed\n",
> + __func__, fname, filter->format->name);
> + err = -EIO;
> + goto err_build_typefile;
> + }
> +
> + /* Create symbol link to type file */
> + if (filter->format->items[i]->name[0]) {
> +
> + filter->items[i].den[1] = debugfs_create_symlink(
> + filter->format->items[i]->name,
> + filter->rootden, fname);
> + if (IS_ERR(filter->items[i].den[1])
> + || !filter->items[i].den[1]) {
> + /*
> + * same-name type is allowed, but it will cause
> + * create symlink fail.
> + * we ignore this type of error.
> + */
> + filter->items[i].den[1] = NULL;
> + }
> + }
> + }
> +
> + return 0;
> +
> +err_build_typefile:
> + for (i--; i >= 0; i--) {
> + /* see comments in _destroy_marker_filter() for why set NULL */
> + filter->items[i].den[0]->d_inode->i_private = NULL;
> + debugfs_remove(filter->items[i].den[0]);
> + debugfs_remove(filter->items[i].den[1]);
> + }
> + return err;
> +}
> +
> +static int add_marker_filter(struct marker_format *format)
> +{
> + int err = 0;
> + struct marker_filter *filter;
> +
> + lock_filter();
> + spin_lock(&filter_lock);
> +
> +#ifdef _DEBUG
> + if (_find_marker_filter(format->name)) {
> + printk(KERN_ERR "%s: filter %s already exist\n", __func__,
> + format->name);
> + err = -EIO;
> + goto err_already_exist;
> + }
> +#endif /* _DEBUG */
> +
> + filter = kmalloc(sizeof(struct marker_filter), GFP_KERNEL);
> + if (!filter) {
> + printk(KERN_ERR "%s: alloc struct marker_filter failed\n",
> + __func__);
> + err = -ENOMEM;
> + goto err_alloc_marker_filter;
> + }
> +
> + INIT_LIST_HEAD(&filter->list);
> +
> + filter->format = format;
> +
> + filter->rootden = debugfs_create_dir(format->name, ltt_filter_dir);
> + if (IS_ERR(filter->rootden) || !filter->rootden) {
> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
> + format->name);
> + err = -EIO;
> + goto err_create_dir;
> + }
> +
> + filter->formatden = debugfs_create_file("__arg0", S_IRUGO,
> + filter->rootden, filter, &format_fops);
> + if (IS_ERR(filter->formatden) || !filter->formatden) {
> + printk(KERN_ERR
> + "%s: create typefile failed\n", __func__);
> + err = -EIO;
> + goto err_build_typefile;
> + }
> +
> + if (format->format_item_cnt) {
> + filter->items = kmalloc(sizeof(struct filter_item)
> + * format->format_item_cnt, GFP_KERNEL);
> + if (!filter->items) {
> + printk(KERN_ERR
> + "%s: alloc filter's items failed\n", __func__);
> + err = -ENOMEM;
> + goto err_alloc_types;
> + }
> + err = init_filter_items(filter, format);
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR
> + "%s: init filter's items failed\n", __func__);
> + goto err_init_types;
> + }
> + } else {
> + filter->items = NULL;
> + }
> +
> + filter->enable = 0;
> +
> + list_add(&filter->list, &marker_filter_head);
> +
> + spin_unlock(&filter_lock);
> + unlock_filter();
> +
> + return 0;
> +
> +err_init_types:
> + kfree(filter->items);
> +err_alloc_types:
> + /* see comments in _destroy_marker_filter() for why set NULL */
> + filter->formatden->d_inode->i_private = NULL;
> + debugfs_remove(filter->formatden);
> +err_build_typefile:
> + debugfs_remove(filter->rootden);
> +err_create_dir:
> + kfree(filter);
> +err_alloc_marker_filter:
> +#ifdef _DEBUG
> +err_already_exist:
> +#endif /* _DEBUG */
> + spin_unlock(&filter_lock);
> + unlock_filter();
> + return err;
> +}
> +
> +static void _destroy_marker_filter(struct marker_filter *filter)
> +{
> + int i;
> +
> + list_del(&filter->list);
> +
> + for (i = 0; i < filter->format->format_item_cnt; i++) {
> + filter->items[i].den[0]->d_inode->i_private = NULL;
> + debugfs_remove(filter->items[i].den[0]);
> + debugfs_remove(filter->items[i].den[1]);
> + }
> + kfree(filter->items);
> +
> + /*
> + * Set i_private to NULL, so that if some fileop in process,
> + * it can avoid illegal access using i_private in fileop.
> + * (we must get filter_lock in fileop to ensure no other change
> + * in filter struct)
> + */
> + filter->formatden->d_inode->i_private = NULL;
> + debugfs_remove(filter->formatden);
> +
> + debugfs_remove(filter->rootden);
> +
> + kfree(filter);
> +
> + return;
> +}
> +
> +static void destroy_all_filter(void)
> +{
> + struct marker_filter *filter, *next;
> +
> + lock_filter();
> + spin_lock(&filter_lock);
> + list_for_each_entry_safe(filter, next, &marker_filter_head, list) {
> + _destroy_marker_filter(filter);
> + }
> + spin_unlock(&filter_lock);
> + unlock_filter();
> +}
> +
> +static int bulid_exist_filter(void)
> +{
> + int err;
> + struct marker_format *format;
> +
> + if (!list_empty(&marker_filter_head)) {
> + err = -EEXIST;
> + goto err_not_empty;
> + }
> +
> + mutex_lock(&format_lock);
> + list_for_each_entry(format, &marker_format_head, list) {
> + err = add_marker_filter(format);
> + if (IS_ERR_VALUE(err)) {
> + printk("%s: add_marker_filter failed\n", __func__);
> + goto err_add_filter;
> + }
> + }
> + mutex_unlock(&format_lock);
> +
> + return 0;
> +
> +err_add_filter:
> + destroy_all_filter();
> + mutex_unlock(&format_lock);
> +err_not_empty:
> + return err;
> +}
> +
> +static int format_notify(struct notifier_block *self, unsigned long val,
> + void *data)
> +{
> + struct marker_format *format = data;
> + struct marker_filter *filter, *next;
> +
> + switch (val) {
> + case ADD_FORMAT:
> + add_marker_filter(format);
> + break;
> + case DEL_FORMAT:
> + lock_filter();
> + spin_lock(&filter_lock);
> + list_for_each_entry_safe(filter, next, &marker_filter_head,
> + list) {
> + if (filter->format == format)
> + _destroy_marker_filter(filter);
> + }
> + spin_unlock(&filter_lock);
> + unlock_filter();
> + break;
> + }
> + return NOTIFY_OK;
> +}
> +
> +static struct notifier_block format_nb = {
> + .notifier_call = format_notify,
> +};
> +
> +static int marker_filter_init(void)
> +{
> + int err = 0;
> +
> + err = bulid_exist_filter();
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR "%s: bulid_exist_filter failed\n", __func__);
> + goto err_add_exist_filter;
> + }
> +
> + err = register_format_notifier(&format_nb);
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR "%s register format notifier failed\n",
> + __func__);
> + goto err_register_format_notifier;
> + }
> +
> + return 0;
> +
> +err_register_format_notifier:
> + destroy_all_filter();
> +err_add_exist_filter:
> + return err;
> +}
> +
> +static void marker_filter_destroy(void)
> +{
> + unregister_format_notifier(&format_nb);
> + destroy_all_filter();
> +}
> +
> +
> +/*
> + * Filter operations
> + *
> + * define filter callback for lttng to determine if to output a event
> + * by user's setting.
> + *
> + */
> +
> +/*
> + * Ret:
> + * 0: filted
> + * 1: passed
> + * <0: err
> + */
> +static int signed_filter(struct format_item *format_item,
> + struct filter_item *filter_item, va_list *args)
> +{
> + long long int val;
> +
> + switch (format_item->size) {
> + case 1:
> + val = (char)va_arg(*args, int);
> + break;
> + case 2:
> + val = (short int)va_arg(*args, int);
> + break;
> + case 4:
> + val = va_arg(*args, int);
> + break;
> + case 8:
> + val = va_arg(*args, long long int);
> + break;
> + default:
> + /* should not run to here */
> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
> + format_item->size);
> + return -1;
> + }
> +
> + /*
> + * We judge filter_item->enable here, because even if filter is not
> + * enabled, we still need to bypass this data in va_list
> + */
> + if (!filter_item->enable)
> + return 1;
> +
> + switch (filter_item->setting.signed_type.op) {
> + case gt:
> + return val > filter_item->setting.signed_type.value;
> + case ge:
> + return val >= filter_item->setting.signed_type.value;
> + case eq:
> + return val == filter_item->setting.signed_type.value;
> + case le:
> + return val <= filter_item->setting.signed_type.value;
> + case lt:
> + return val < filter_item->setting.signed_type.value;
> + }
> +
> + /* should not run to here */
> + return -1;
> +}
> +
> +static int unsigned_filter(struct format_item *format_item,
> + struct filter_item *filter_item, va_list *args)
> +{
> + unsigned long long int val;
> +
> + switch (format_item->size) {
> + case 1:
> + val = (unsigned char)va_arg(*args, unsigned int);
> + break;
> + case 2:
> + val = (short int)va_arg(*args, unsigned int);
> + break;
> + case 4:
> + val = va_arg(*args, unsigned int);
> + break;
> + case 8:
> + val = va_arg(*args, unsigned long long int);
> + break;
> + default:
> + /* should not run to here */
> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
> + format_item->size);
> + return -1;
> + }
> +
> + /*
> + * We judge filter_item->enable here, because even if filter is not
> + * enabled, we still need to bypass this data in va_list
> + */
> + if (!filter_item->enable)
> + return 1;
> +
> + switch (filter_item->setting.unsigned_type.op) {
> + case gt:
> + return val > filter_item->setting.unsigned_type.value;
> + case ge:
> + return val >= filter_item->setting.unsigned_type.value;
> + case eq:
> + return val == filter_item->setting.unsigned_type.value;
> + case le:
> + return val <= filter_item->setting.unsigned_type.value;
> + case lt:
> + return val < filter_item->setting.unsigned_type.value;
> + }
> +
> + /* should not run to here */
> + return -1;
> +}
> +
> +static int string_filter(struct format_item *format_item,
> + struct filter_item *filter_item, va_list *args)
> +{
> + const char *val;
> +
> + val = va_arg(*args, const char *);
> +
> + /*
> + * We judge filter_item->enable here, because even if filter is not
> + * enabled, we still need to bypass this data in va_list
> + */
> + if (!filter_item->enable)
> + return 1;
> +
> + switch (filter_item->setting.string_type.op) {
> + case gt:
> + return strcmp(val, filter_item->setting.string_type.value) > 0;
> + case ge:
> + return strcmp(val, filter_item->setting.string_type.value) >= 0;
> + case eq:
> + return strcmp(val, filter_item->setting.string_type.value) == 0;
> + case le:
> + return strcmp(val, filter_item->setting.string_type.value) <= 0;
> + case lt:
> + return strcmp(val, filter_item->setting.string_type.value) < 0;
> + }
> +
> + /* should not run to here */
> + return -1;
> +}
> +
> +static int char_filter(struct format_item *format_item,
> + struct filter_item *filter_item, va_list *args)
> +{
> + unsigned char val;
> +
> + val = (unsigned char)va_arg(*args, unsigned int);
> +
> + /*
> + * We judge filter_item->enable here, because even if filter is not
> + * enabled, we still need to bypass this data in va_list
> + */
> + if (!filter_item->enable)
> + return 1;
> +
> + switch (filter_item->setting.char_type.op) {
> + case gt:
> + return val > filter_item->setting.char_type.value;
> + case ge:
> + return val >= filter_item->setting.char_type.value;
> + case eq:
> + return val == filter_item->setting.char_type.value;
> + case le:
> + return val <= filter_item->setting.char_type.value;
> + case lt:
> + return val < filter_item->setting.char_type.value;
> + }
> +
> + /* should not run to here */
> + return -1;
> +}
> +
> +static int ltt_filter(struct ltt_trace_struct *trace,
> + const struct marker *mdata, va_list *args)
> +{
> + int ret;
> + struct marker_filter *filter;
> + struct marker_format *format;
> + int i;
> +
> + if (lock_filter()) {
Hrm do you allow session filter modification when tracing is active for
this session ?
I think the lock_filter approach you are taking here is wrong.
> + /* Don't output events caused by ltt-filter */
> + return 0;
> + }
> +
> + spin_lock(&filter_lock);
> +
> + filter = _find_marker_filter(mdata->name);
> + if (!filter) {
> + printk(KERN_ERR "%s: unknown marker %s\n", __func__,
> + mdata->name);
> + ret = 1;
> + goto done;
> + }
> +
> + /* Bypass if no filter setting on this marker */
> + if (!filter->enable) {
> + ret = 1;
> + goto done;
> + }
> +
> + format = filter->format;
> +
> + for (i = 0; i < format->format_item_cnt; i++) {
> + int result;
> +
> + switch (format->items[i]->type) {
> + case SIGNED:
> + result = signed_filter(format->items[i],
> + filter->items + i, args);
> + break;
> + case UNSIGNED:
> + result = unsigned_filter(format->items[i],
> + filter->items + i, args);
> + break;
> + case STRING:
> + result = string_filter(format->items[i],
> + filter->items + i, args);
> + break;
> + case CHAR:
> + result = char_filter(format->items[i],
> + filter->items + i, args);
> + break;
> + default:
> + /* should not run to here */
> + printk(KERN_ERR "%s: unknown type %d\n", __func__,
> + format->items[i]->type);
> + ret = 1;
> + goto done;
> + }
> + if (result < 0) {
> + ret = 1;
> + goto done;
> + }
> + if (!result) {
> + ret = 0;
> + goto done;
> + }
> + }
> +
> + ret = 1;
> +
> +done:
> + spin_unlock(&filter_lock);
> + unlock_filter();
> + return ret;
> +}
> +
> +
> +/*
> + * Init/exit option of module
> + *
> + * Init marker_format and marker_filter operations
> + * register filter callback in ltt
> + *
> + */
> +
> +static int __init ltt_filter_init(void)
> +{
> + int err = 0;
> + struct dentry *ltt_root_dentry;
> +
> + ltt_root_dentry = get_ltt_root();
> + if (!ltt_root_dentry) {
> + err = -ENOENT;
> + goto err_no_root;
> + }
> +
> + ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR, ltt_root_dentry);
> + if (IS_ERR(ltt_filter_dir) || !ltt_filter_dir) {
> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
> + LTT_FILTER_DIR);
> + err = -ENOMEM;
> + goto err_create_filter_dir;
> + }
> +
> + err = marker_format_init();
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR "%s: init marker format failed\n", __func__);
> + goto err_marker_format_init;
> + }
> +
> + err = marker_filter_init();
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR "%s: init marker filter failed\n", __func__);
> + goto err_marker_filter_init;
> + }
> +
> + err = ltt_module_register(LTT_FUNCTION_RUN_FILTER, ltt_filter,
> + THIS_MODULE);
> + if (IS_ERR_VALUE(err)) {
> + printk(KERN_ERR "%s: register ltt filter failed\n", __func__);
> + goto err_filter_register;
> + }
> +
> + return 0;
> +
> +err_filter_register:
> + marker_filter_destroy();
> +err_marker_filter_init:
> + marker_format_destroy();
> +err_marker_format_init:
> + debugfs_remove(ltt_filter_dir);
> +err_create_filter_dir:
> +err_no_root:
> + return err;
> +}
> +
> +static void __exit ltt_filter_exit(void)
> +{
> + ltt_module_unregister(LTT_FUNCTION_RUN_FILTER);
> + marker_filter_destroy();
> + marker_format_destroy();
> +
> + debugfs_remove(ltt_filter_dir);
> +}
> +
> +module_init(ltt_filter_init);
> +module_exit(ltt_filter_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Zhao Lei <zhaolei at cn.fujitsu.com>");
> +MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
> --
> 1.5.5.3
>
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 3/3] In-kernel filter module
2009-04-06 17:52 ` Mathieu Desnoyers
@ 2009-04-07 5:45 ` Zhaolei
2009-04-07 16:48 ` Mathieu Desnoyers
0 siblings, 1 reply; 24+ messages in thread
From: Zhaolei @ 2009-04-07 5:45 UTC (permalink / raw)
* From: "Mathieu Desnoyers" <compudj@krystal.dyndns.org>
Hello, Mathieu,
Thanks for your review of this patch.
I see your comments, and this is my thinking:
>* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>> LTTng's in-kernel filter is used to filter event by its arguments.
>> User can config every event's filter option by echo to filts in
>> debugfs/ltt/filter
>>
>
> Hi Zhaolei,
>
> That's a good start, but locking will have to be rethought. Please see
> comments below,
>
>> Signed-off-by: Zhao Lei <zhaolei at cn.fujitsu.com>
>> ---
>> ltt/Kconfig | 9 +
>> ltt/Makefile | 1 +
>> ltt/ltt-filter.c | 1831 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 1841 insertions(+), 0 deletions(-)
>> create mode 100644 ltt/ltt-filter.c
>>
>> diff --git a/ltt/Kconfig b/ltt/Kconfig
>> index 3c520f5..2cfb479 100644
>> --- a/ltt/Kconfig
>> +++ b/ltt/Kconfig
>> @@ -214,4 +214,13 @@ config LTT_ASCII
>> Output trace data in a text-formatted ascii file, presented in
>> /mnt/debugfs/ltt/ascii/<trace name>.
>>
>> +config LTT_FILTER
>> + tristate "Linux Trace Toolkit Filter"
>> + depends on LTT_TRACER
>> + depends on LTT_SERIALIZE
>> + default n
>> + help
>> + Filter for LTTng to control which type of event to be traced.
>> + Config filter rule by debugfs/ltt/filter
>> +
>> endif # LTT
>> diff --git a/ltt/Makefile b/ltt/Makefile
>> index 5ea5cca..29ab6e4 100644
>> --- a/ltt/Makefile
>> +++ b/ltt/Makefile
>> @@ -19,3 +19,4 @@ obj-$(CONFIG_LTT_KPROBES) += ltt-kprobes.o
>> obj-$(CONFIG_LTT_TRACEPROBES) += probes/
>> obj-$(CONFIG_LTT_FTRACE) += ltt-ftrace.o
>> obj-$(CONFIG_LTT_ASCII) += ltt-ascii.o
>> +obj-$(CONFIG_LTT_FILTER) += ltt-filter.o
>> diff --git a/ltt/ltt-filter.c b/ltt/ltt-filter.c
>> new file mode 100644
>> index 0000000..bf2760a
>> --- /dev/null
>> +++ b/ltt/ltt-filter.c
>> @@ -0,0 +1,1831 @@
>> +/*
>> + * LTT filter module.
>> + *
>> + * Copyright 2009 -
>> + * Zhaolei <zhaolei at cn.fujitsu.com>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/ltt-core.h>
>> +#include <linux/ctype.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/marker.h>
>> +#include <linux/ltt-tracer.h>
>> +#include <asm/atomic.h>
>> +
>> +#define LTT_FILTER_DIR "filter"
>> +
>> +struct dentry *ltt_filter_dir;
>> +
>> +/*
>> + * Marker format operations
>> + *
>> + * Maintenance a marker_format list sync with module add and release
>> + * Each data-type in format are paresed and stored in marker_format struct
>
> paresed -> parsed
I agree, thanks!
>
>> + * Impl a notifier for respond add/remove marker format event
>
> Notifier which responds to add/remove marker format event
I agree, thanks!
>
>> + */
>> +
>> +enum format_item_type {
>> + SIGNED,
>> + UNSIGNED,
>> + STRING,
>> + CHAR,
>> +};
>> +const char *format_item_type_string[] = {
>> + "signed int",
>> + "unsigned int",
>> + "string",
>> + "char",
>> +};
>> +
>> +struct format_item {
>> + struct list_head list;
>> + char name[PATH_MAX];
>
> this rather large "name" field should go at the end of the struct.
I agree, thanks!
>
>> + enum format_item_type type;
>> + size_t size;
>> +};
>> +
>> +struct marker_format {
>> + atomic_t refcnt;
>> + struct list_head list;
>> + const char *name;
>> + const char *format;
>> + struct format_item **items;
>> + size_t format_item_cnt;
>> +};
>> +
>> +static LIST_HEAD(marker_format_head);
>> +static DEFINE_MUTEX(format_lock);
>> +
>> +BLOCKING_NOTIFIER_HEAD(format_notifier_list);
>> +enum FORMAT_EVENT {
>> + ADD_FORMAT,
>> + DEL_FORMAT,
>> +};
>> +
>> +static struct format_item *new_format_item(const char *name_head,
>> + const char *name_tail, int type_type, int type_size)
>> +{
>> + struct format_item *format_item;
>> + const char *p;
>> + int i, name_len;
>> +
>> + format_item = kmalloc(sizeof(struct format_item), GFP_KERNEL);
>> + if (!format_item) {
>> + printk(KERN_ERR "%s: alloc format_item failed\n", __func__);
>> + goto err_alloc_format_item;
>> + }
>> +
>> + INIT_LIST_HEAD(&format_item->list);
>> +
>> + /* trim itemname */
>> + while (name_head < name_tail
>> + && *name_head == ' ')
>> + name_head++;
>> + while (name_tail > name_head
>> + && *(name_tail - 1) == ' ')
>> + name_tail--;
>> +
>> + name_len = name_tail - name_head;
>> + if (name_len) {
>> + for (i = 0, p = name_head; i < PATH_MAX - 1 && p < name_tail;
>> + i++, p++) {
>> + if ((*p == '%' && p + 1 < name_tail && *(p + 1) == '%')
>> + || (*p == '#' &&
>> + p + 1 < name_tail && *(p + 1) == '#')) {
>> + p++;
>> + }
>> + format_item->name[i] = isalnum(*p) ? *p : '_';
>> + }
>> + format_item->name[i] = 0;
>> + } else {
>> + format_item->name[0] = 0;
>> + }
>> +
>> + format_item->type = type_type;
>> + format_item->size = type_size;
>> +
>> + return format_item;
>> +
>> +err_alloc_format_item:
>> + return NULL;
>> +}
>> +
>> +static size_t parse_format(const char *format, struct format_item ***items)
>> +{
>> + int ret = 0; /* number of types in format string, or -ERRNO on fail */
>> + int state; /* 0: in name, 1: in type, 2: in # */
>> + const char *p; /* format string's iter */
>> + /* name for current type, init with 0 to avoid compile warning */
>> + const char *name_head, *name_tail = 0;
>> + enum format_item_type type_type;
>> + /*
>> + * size(in bytes) of current type
>> + * init with 0 to avoid compile warning
>> + */
>> + int type_size = 0;
>> +
>> + LIST_HEAD(format_item_list);
>> + struct format_item *format_item, *format_item_next;
>> +
>> + for (state = 0, name_head = p = format; *p; p++) {
>> + switch (state) {
>> + case 0:
>> + switch (*p) {
>> + case '%':
>> + if (*(p+1) != '%') {
>> + name_tail = p;
>> + type_size = sizeof(int);
>> + state = 1;
>> + } else {
>> + p++;
>> + }
>> + break;
>> + case '#':
>> + if (*(p+1) != '#') {
>> + name_tail = p;
>> + state = 2;
>> + } else {
>> + p++;
>> + }
>> + break;
>> + default:
>> + break;
>> + }
>> + break;
>> + case 1:
>> + switch (*p) {
>> + case 'l':
>> + if (*(p+1) == 'l') {
>> + type_size = sizeof(long long);
>> + p++;
>> + } else {
>> + type_size = sizeof(long);
>> + }
>> + break;
>> + case 'L':
>> + type_size = sizeof(long long);
>> + break;
>> + case 'z':
>> + case 'Z':
>> + type_size = sizeof(size_t);
>> + break;
>> + case 't':
>> + type_size = sizeof(ptrdiff_t);
>> + break;
>> + case 'h':
>> + type_size = sizeof(short);
>> + break;
>> + case 'd':
>> + case 'i':
>> + type_type = SIGNED;
>> + goto type_finish;
>> + break;
>> + case 'u':
>> + case 'o':
>> + case 'x':
>> + case 'X':
>> + type_type = UNSIGNED;
>> + goto type_finish;
>> + break;
>> + case 'p':
>> + type_type = UNSIGNED;
>> + type_size = sizeof(void *);
>> + goto type_finish;
>> + break;
>> + case 's':
>> + type_type = STRING;
>> + type_size = sizeof(void *);
>> + goto type_finish;
>> + break;
>> + case 'c':
>> + type_type = CHAR;
>> + type_size = sizeof(char);
>> + goto type_finish;
>> + break;
>> + case '0':
>> + case '1':
>> + case '2':
>> + case '3':
>> + case '4':
>> + case '5':
>> + case '6':
>> + case '7':
>> + case '8':
>> + case '9':
>> + break;
>> + default:
>> + printk(KERN_ERR
>> + "%s: unknown char after %%: %c\n",
>> + __func__, *p);
>> + break;
>> + }
>> + break;
>> + case 2:
>> + switch (*p) {
>> + case 'l':
>> + case 'L':
>> + case 'Z':
>> + case 'z':
>> + case 't':
>> + case 'h':
>> + case 'd':
>> + case 'i':
>> + case 'u':
>> + case 'o':
>> + case 'x':
>> + case 'X':
>> + case 'p':
>> + case 's':
>> + case 'c':
>> + case '1':
>> + case '2':
>> + case '4':
>> + case '8':
>> + break;
>> + case '%':
>> + type_size = sizeof(int);
>> + state = 1;
>> + break;
>> + default:
>> + printk(KERN_ERR "%s: unknown char after #: %c\n",
>> + __func__, *p);
>> + break;
>> + }
>> + break;
>> + default:
>> + /* should not run to here */
>> + ret = -EINVAL;
>> + goto err_unknown_state;
>> + }
>> + continue;
>> +type_finish:
>> + format_item = new_format_item(name_head, name_tail, type_type,
>> + type_size);
>> + if (!format_item) {
>> + printk(KERN_ERR "%s: add_format_item failed\n",
>> + __func__);
>> + ret = -ENOMEM;
>> + goto err_new_format_item;
>> + }
>> +
>> + list_add_tail(&format_item->list, &format_item_list);
>> + ret++;
>> + name_head = p + 1;
>> + state = 0;
>> + continue;
>> + }
>> +
>> + /*
>> + * We have parsed types in format, and build linked_list to save
>> + * there information.
>> + * Now we will build a point array to save each format_item for
>> + * easy access.
>> + */
>> +
>> + *items = kmalloc(ret * sizeof(struct format_item *), GFP_KERNEL);
>> + if (!*items) {
>> + printk(KERN_ERR "%s: alloc format_item index failed\n",
>> + __func__);
>> + ret = -ENOMEM;
>> + goto err_alloc_format_item;
>> + }
>> +
>> + /* we reuse state as a iter for (*items)[] */
>> + state = 0;
>> + list_for_each_entry(format_item, &format_item_list, list)
>> + (*items)[state++] = format_item;
>> +
>> + return ret;
>> +
>> +err_alloc_format_item:
>> +err_unknown_state:
>> +err_new_format_item:
>> + list_for_each_entry_safe(format_item, format_item_next,
>> + &format_item_list, list)
>> + kfree(format_item);
>> + return ret;
>> +}
>> +
>> +/* need have format_lock */
>> +static struct marker_format *_find_marker_format(const char *name)
>> +{
>> + struct marker_format *format;
>> + list_for_each_entry(format, &marker_format_head, list) {
>> + if (!strcmp(name, format->name))
>> + return format;
>> + }
>> + return NULL;
>> +}
>> +
>> +/*
>> + * For add_marker_format internal use
>> + * need have format_lock
>> + */
>> +static struct marker_format *_new_marker_format(struct marker *marker)
>> +{
>> + struct marker_format *format;
>> + char *tmp;
>> +
>> + format = kmalloc(sizeof(struct marker_format), GFP_KERNEL);
>> + if (!format)
>> + goto err_alloc_format;
>> +
>> + atomic_set(&format->refcnt, 1);
>> +
>> + INIT_LIST_HEAD(&format->list);
>> +
>> + tmp = kmalloc(strlen(marker->name) + 1, GFP_KERNEL);
>> + if (!tmp)
>> + goto err_alloc_name;
>> + strcpy(tmp, marker->name);
>> + format->name = tmp;
>> +
>> + tmp = kmalloc(strlen(marker->format) + 1, GFP_KERNEL);
>> + if (!tmp)
>> + goto err_alloc_formatstr;
>> + strcpy(tmp, marker->format);
>> + format->format = tmp;
>> +
>> + format->format_item_cnt = parse_format(marker->format,
>> + &format->items);
>> + if (IS_ERR_VALUE(format->format_item_cnt)) {
>> + printk(KERN_ERR "%s: parse format failed: %s\n", __func__,
>> + marker->format);
>> + goto err_parse_file;
>> + }
>> +
>> + list_add(&format->list, &marker_format_head);
>> +
>> + blocking_notifier_call_chain(&format_notifier_list, ADD_FORMAT, format);
>> +
>> + return format;
>> +
>> +err_parse_file:
>> + kfree(format->format);
>> +err_alloc_formatstr:
>> + kfree(format->name);
>> +err_alloc_name:
>> + kfree(format);
>> +err_alloc_format:
>> + return NULL;
>> +}
>> +
>> +/* need have format_lock */
>> +static void _destroy_marker_format(struct marker_format *format)
>> +{
>> + int i;
>> +
>> + blocking_notifier_call_chain(&format_notifier_list, DEL_FORMAT, format);
>> +
>> + list_del(&format->list);
>> +
>> + for (i = 0; i < format->format_item_cnt; i++)
>> + kfree(format->items[i]);
>> + kfree(format->items);
>> + kfree(format->format);
>> + kfree(format->name);
>> + kfree(format);
>> +
>> + return;
>> +}
>> +
>> +static struct marker_format *add_marker_format(struct marker *marker)
>> +{
>> + struct marker_format *format;
>> +
>> + mutex_lock(&format_lock);
>> +
>> + format = _find_marker_format(marker->name);
>> + if (!format) {
>> + format = _new_marker_format(marker);
>> + if (!format) {
>> + printk(KERN_ERR
>> + "%s: new format struct for %s failed\n",
>> + __func__, marker->name);
>> + goto err_alloc_format;
>> + }
>> + } else {
>> + atomic_inc(&format->refcnt);
>> + }
>> +
>> + mutex_unlock(&format_lock);
>> + return format;
>> +
>> +err_alloc_format:
>> + mutex_unlock(&format_lock);
>> + return NULL;
>> +}
>> +
>> +static int put_marker_format(const char *name)
>> +{
>> + struct marker_format *format;
>> + int err = 0;
>> +
>> + mutex_lock(&format_lock);
>> +
>> + format = _find_marker_format(name);
>> + if (!format) {
>> + printk(KERN_ERR "%s: can't find format item for %s\n",
>> + __func__, name);
>> + err = -ENOENT;
>> + goto err_find_marker_format;
>> + }
>> +
>> + if (atomic_dec_and_test(&format->refcnt))
>> + _destroy_marker_format(format);
>> +
>> + mutex_unlock(&format_lock);
>> + return 0;
>> +
>> +err_find_marker_format:
>> + mutex_unlock(&format_lock);
>> + return err;
>> +}
>> +
>> +static void destroy_all_format(void)
>> +{
>> + struct marker_format *format, *next;
>> +
>> + mutex_lock(&format_lock);
>> +
>> + list_for_each_entry_safe(format, next, &marker_format_head, list) {
>> + _destroy_marker_format(format);
>> + }
>> +
>> + mutex_unlock(&format_lock);
>> +
>> + return;
>> +}
>> +
>> +static int bulid_exist_format(void)
>> +{
>> + struct marker_iter iter;
>> + struct marker_format *format;
>> + int err = 0;
>> +
>> + if (!list_empty(&marker_format_head)) {
>> + err = -EEXIST;
>> + goto err_not_empty;
>> + }
>> +
>> + marker_iter_reset(&iter);
>> + marker_iter_start(&iter);
>> + for (; iter.marker != NULL; marker_iter_next(&iter)) {
>> + format = add_marker_format(iter.marker);
>> + if (!format) {
>> + err = -EIO;
>> + goto err_build_fail;
>> + }
>> + }
>> + marker_iter_stop(&iter);
>> + return 0;
>> +
>> +err_build_fail:
>> + destroy_all_format();
>> +err_not_empty:
>> + return err;
>> +}
>> +
>> +static int module_notify(struct notifier_block *self,
>> + unsigned long val, void *data)
>> +{
>> + struct module *mod = data;
>> + struct marker *iter;
>> +
>> + switch (val) {
>> + case MODULE_STATE_COMING:
>> + for (iter = mod->markers;
>> + iter < mod->markers + mod->num_markers; iter++)
>> + add_marker_format(iter);
>> + break;
>> + case MODULE_STATE_GOING:
>> + for (iter = mod->markers;
>> + iter < mod->markers + mod->num_markers; iter++)
>> + put_marker_format(iter->name);
>> + break;
>> + }
>> + return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block module_nb = {
>> + .notifier_call = module_notify,
>> +};
>> +
>> +static int marker_format_init(void)
>> +{
>> + int err = 0;
>> +
>> + err = bulid_exist_format();
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR "%s: bulid_exist_format failed\n", __func__);
>> + goto err_add_exist_filter;
>> + }
>> +
>> + /* Todo:
>> + * If a module is insert/delete here(between
>> + * bulid_exist_format() and register_module_notifier()),
>> + * marker_format struct is not uptodate.
>> + */
>> +
>> + err = register_module_notifier(&module_nb);
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR "%s register module notifier failed\n",
>> + __func__);
>> + goto err_register_module_notifier;
>> + }
>> +
>> + return 0;
>> +
>> +err_register_module_notifier:
>> + destroy_all_format();
>> +err_add_exist_filter:
>> + return err;
>> +}
>> +
>> +static void marker_format_destroy(void)
>> +{
>> + unregister_module_notifier(&module_nb);
>> + destroy_all_format();
>> +}
>> +
>> +static int register_format_notifier(struct notifier_block *nb)
>> +{
>> + return blocking_notifier_chain_register(&format_notifier_list, nb);
>> +}
>> +
>> +static int unregister_format_notifier(struct notifier_block *nb)
>> +{
>> + return blocking_notifier_chain_unregister(&format_notifier_list, nb);
>> +}
>> +
>> +
>> +/*
>> + * Control operations
>> + *
>> + * Maintenance a dir struct and datastruct for control filter
>> + * dir struct will dynamical updated when marker_format got changed
>> + *
>> + * Todo:
>> + * Control per-trace filter
>> + *
>> + */
>> +
>> +enum compare_op {
>> + gt,
>> + ge,
>> + eq,
>> + le,
>> + lt,
>> +};
>> +const char *compare_op_string[] = {
>> + ">",
>> + ">=",
>> + "=",
>> + "<=",
>> + "<",
>> +};
>> +
>> +struct marker_filter;
>> +struct filter_item {
>> + int enable;
>> + union {
>> + struct {
>> + enum compare_op op;
>> + long long value;
>> + } signed_type;
>> + struct {
>> + enum compare_op op;
>> + unsigned long long value;
>> + } unsigned_type;
>> + struct {
>> + enum compare_op op;
>> + char value[PATH_MAX]; /* Todo: Make it dynamic */
>> + } string_type;
>> + struct {
>> + enum compare_op op;
>> + unsigned char value;
>> + } char_type;
>> + } setting;
>> + struct marker_filter *parent;
>> + struct dentry *den[2]; /* [0]: __argX, [1]: symbol link to __argX */
>> +};
>> +
>> +struct marker_filter {
>> + struct list_head list;
>> + struct marker_format *format;
>> + struct filter_item *items;
>> + struct dentry *rootden;
>> + struct dentry *formatden;
>> + /*
>> + * is any of items[] are enabled
>> + * for bypass marker which haven't any filter fast
>> + */
>> + int enable;
>> +};
>> +static inline unsigned int filter_item_no(struct filter_item *filter_item)
>> +{
>> + return filter_item - filter_item->parent->items;
>> +}
>> +static inline struct format_item *filter_item_format(
>> + struct filter_item *filter_item)
>> +{
>> + return filter_item->parent->format->items[filter_item_no(filter_item)];
>> +}
>> +
>> +static LIST_HEAD(marker_filter_head);
>> +
>> +/*
>> + * Use spinlock to lock filter struct because we can't use mutex in filter
>> + * callback
>> + */
>> +static DEFINE_SPINLOCK(filter_lock);
>> +
>> +/*
>> + * We need call some kernel function in ltt-filter function, but many kernel
>> + * function have tp/markers which cause cpu jump to ltt, then jump to
>> + * ltt-filter from ltt code.
>> + * Even if we don't call any kernel function in filter code, interrupt will
>> + * do similar thing.
>> + * To make it easy, we avoid this type of nesting by lock/unlock_filter.
>> + */
>> +DEFINE_PER_CPU(atomic_t, filter_nesting) = ATOMIC_INIT(0);
>> +static inline int lock_filter(void)
>> +{
>> + preempt_disable();
>> + if (atomic_xchg(&__get_cpu_var(filter_nesting), 1) > 0) {
>> + /* Already in lock */
>> + preempt_enable();
>> + return 1;
>> + }
>> + return 0;
>> +}
>> +static inline void unlock_filter(void)
>> +{
>> + atomic_set(&__get_cpu_var(filter_nesting), 0);
>> + preempt_enable();
>> +}
>
> Hrm, this lock_filter seems broken. If you have an interrupt nesting
> over lock_filter, and takes lock_filter again, the interrupt will set
> the filter_nesting to 0, which creates a race.
If an interrupt nesting over lock_filter, and takes lock_filter again,
filter_nesting is still 1, because we use atomic_xchg(filter_nesting, 1).
>
> Basically, what you want is to disallow filter modification used by an
> active tracing session _or_, if you want to allow filter modification
> while a tracing session is active. But to do it properly, you want to
> use RCU data structures synchronized with call_rcu_sched and
> synchronize_sched and use the RCU read lock (which is _already_ taken by
> LTTng around probe execution anyway) to synchronize.
I understand your opinion.
I agree your opinion that RCU can allow filter modification while a tracing
session is active.
But if we call a kernel function in filter function, and this kernel function
run into filter again, we can't prevent this kind of nesting by rcu.
Ltt have a ltt_nesting to prevent this kind of nesting, but it can't help
filter function.
Maybe we can call no kernel function in filter() to prevent this kind of
nesting, and use rcu to allow filter modification when filter() function is
running, and I aggre to using rcu, but sorry that I am some busy these days,
it is not easy to get sometime to finish this change.
Current code is ok to work, and it passed my test with a my testset.
(I can send it to you if you need)
>
> The filter_lock spinlock should probably become a simple mutex too.
I attempted to use mutex for filter_lock, but always cause panic in my testset.
Thanks!
Zhaolei
>
> Mathieu
>
>> +
>> +struct marker_filter *_find_marker_filter(const char *name)
>> +{
>> + struct marker_filter *filter;
>> + list_for_each_entry(filter, &marker_filter_head, list) {
>> + if (!strcmp(name, filter->format->name))
>> + return filter;
>> + }
>> + return NULL;
>> +}
>> +
>> +static ssize_t format_read(struct file *filp, char __user *ubuf, size_t cnt,
>> + loff_t *ppos)
>> +{
>> + size_t ret;
>> + struct marker_filter *filter;
>> +
>> + lock_filter();
>> + spin_lock(&filter_lock);
>> + filter = filp->f_dentry->d_inode->i_private;
>> + if (filter) {
>> + ret = simple_read_from_buffer(ubuf, cnt, ppos,
>> + filter->format->format, strlen(filter->format->format));
>> + } else {
>> + ret = -EIO;
>> + }
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> +
>> + return ret;
>> +}
>> +
>> +static const struct file_operations format_fops = {
>> + .read = format_read,
>> +};
>> +
>> +static int signed_type_read(char *buf, size_t bufsize,
>> + struct filter_item *filter_item)
>> +{
>> + int ret;
>> + int pos = 0;
>> +
>> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> + compare_op_string[filter_item->setting.signed_type.op]);
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + ret = scnprintf(buf + pos, bufsize - pos, " %lld",
>> + filter_item->setting.signed_type.value);
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + return pos;
>> +}
>> +
>> +static int unsigned_type_read(char *buf, size_t bufsize,
>> + struct filter_item *filter_item)
>> +{
>> + int ret;
>> + int pos = 0;
>> +
>> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> + compare_op_string[filter_item->setting.unsigned_type.op]);
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + ret = scnprintf(buf + pos, bufsize - pos, " %llu",
>> + filter_item->setting.unsigned_type.value);
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + return pos;
>> +}
>> +
>> +static int string_type_read(char *buf, size_t bufsize,
>> + struct filter_item *filter_item)
>> +{
>> + int ret;
>> + int pos = 0;
>> +
>> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> + compare_op_string[filter_item->setting.string_type.op]);
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + ret = scnprintf(buf + pos, bufsize - pos, " %s",
>> + filter_item->setting.string_type.value);
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + return pos;
>> +}
>> +
>> +static int char_type_read(char *buf, size_t bufsize,
>> + struct filter_item *filter_item)
>> +{
>> + int ret;
>> + int pos = 0;
>> +
>> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> + compare_op_string[filter_item->setting.char_type.op]);
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + switch (filter_item->setting.char_type.value) {
>> + case '\t':
>> + ret = scnprintf(buf + pos, bufsize - pos, " \\t");
>> + break;
>> + case ' ':
>> + ret = scnprintf(buf + pos, bufsize - pos, " blank");
>> + break;
>> + case '\n':
>> + ret = scnprintf(buf + pos, bufsize - pos, " \\n");
>> + break;
>> + default:
>> + if (isgraph(filter_item->setting.char_type.value)) {
>> + ret = scnprintf(buf + pos, bufsize - pos, " %c",
>> + filter_item->setting.char_type.value);
>> + } else {
>> + ret = scnprintf(buf + pos, bufsize - pos,
>> + " \\%u", (unsigned int)
>> + filter_item->setting.char_type.value);
>> + }
>> + break;
>> + }
>> + if (!ret)
>> + return 0;
>> + pos += ret;
>> +
>> + return pos;
>> +}
>> +
>> +static ssize_t type_read(struct file *filp, char __user *ubuf, size_t cnt,
>> + loff_t *ppos)
>> +{
>> + size_t ret;
>> + struct filter_item *filter_item;
>> +
>> + lock_filter();
>> + spin_lock(&filter_lock);
>> + filter_item = filp->f_dentry->d_inode->i_private;
>> + if (filter_item) {
>> + struct format_item *format_item;
>> + char buf[128];
>> + int pos = 0;
>> +
>> + format_item = filter_item_format(filter_item);
>> +
>> + ret = scnprintf(buf + pos, sizeof(buf) - pos,
>> + "type: %u byte(s) %s\n",
>> + format_item->size,
>> + format_item_type_string[format_item->type]);
>> + if (!ret) {
>> + ret = -EINVAL;
>> + goto err_format_string;
>> + }
>> + pos += ret;
>> +
>> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "setting: ");
>> + if (!ret) {
>> + ret = -EINVAL;
>> + goto err_format_string;
>> + }
>> + pos += ret;
>> +
>> + if (!filter_item->enable) {
>> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "*");
>> + if (!ret) {
>> + ret = -EINVAL;
>> + goto err_format_string;
>> + }
>> + pos += ret;
>> + goto format_finish;
>> + }
>> +
>> + switch (format_item->type) {
>> + case SIGNED:
>> + ret = signed_type_read(buf + pos, sizeof(buf) - pos,
>> + filter_item);
>> + break;
>> + case UNSIGNED:
>> + ret = unsigned_type_read(buf + pos, sizeof(buf) - pos,
>> + filter_item);
>> + break;
>> + case STRING:
>> + ret = string_type_read(buf + pos, sizeof(buf) - pos,
>> + filter_item);
>> + break;
>> + case CHAR:
>> + ret = char_type_read(buf + pos, sizeof(buf) - pos,
>> + filter_item);
>> + break;
>> + default:
>> + /* should not run to here */
>> + printk(KERN_ERR "%s: unknown type %d\n",
>> + __func__, format_item->type);
>> + ret = -EIO;
>> + goto err_unknown_type;
>> + }
>> + if (!ret) {
>> + ret = -EINVAL;
>> + goto err_format_string;
>> + }
>> + pos += ret;
>> +
>> +format_finish:
>> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "\n");
>> + if (!ret) {
>> + ret = -EINVAL;
>> + goto err_format_string;
>> + }
>> + pos += ret;
>> +
>> + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf,
>> + strlen(buf));
>> + } else {
>> + ret = -EIO;
>> + }
>> +
>> +err_format_string:
>> +err_unknown_type:
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> +
>> + return ret;
>> +}
>> +
>> +/*
>> + * Check op info in a input string
>> + * return length of op part on success, *op is set.
>> + * return 0 on fail
>> + */
>> +static size_t scan_op(const char *str, enum compare_op *op)
>> +{
>> + /* Must reverse sort with strlen */
>> + static struct {
>> + const char *str;
>> + enum compare_op op;
>> + } str_op[] = {
>> + {"gt", gt},
>> + {"ge", ge},
>> + {"eq", eq},
>> + {"le", le},
>> + {"lt", lt},
>> + {">=", ge},
>> + {"<=", le},
>> + {">", gt},
>> + {"=", eq},
>> + {"<", lt},
>> + };
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(str_op); i++) {
>> + if (!strncmp(str, str_op[i].str, strlen(str_op[i].str))) {
>> + *op = str_op[i].op;
>> + return strlen(str_op[i].str);
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int signed_type_write(const char *buf, struct format_item *format_item,
>> + struct filter_item *filter_item)
>> +{
>> + int oplen;
>> + enum compare_op op;
>> + long long value = 0;
>> +
>> + oplen = scan_op(buf, &op);
>> + if (!oplen)
>> + return -EINVAL;
>> +
>> + switch (format_item->size) {
>> + case 1:
>> + {
>> + char tmp;
>> + if (sscanf(buf + oplen, "%hhd", &tmp) != 1)
>> + return -EINVAL;
>> + value = tmp;
>> + }
>> + break;
>> + case 2:
>> + {
>> + short int tmp;
>> + if (sscanf(buf + oplen, "%hd", &tmp) != 1)
>> + return -EINVAL;
>> + value = tmp;
>> + }
>> + break;
>> + case 4:
>> + {
>> + int tmp;
>> + if (sscanf(buf + oplen, "%d", &tmp) != 1)
>> + return -EINVAL;
>> + value = tmp;
>> + }
>> + break;
>> + case 8:
>> + if (sscanf(buf + oplen, "%lld", &value) != 1)
>> + return -EINVAL;
>> + break;
>> + default:
>> + /* should not run to here */
>> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
>> + format_item->size);
>> + return -EIO;
>> + }
>> +
>> + filter_item->setting.signed_type.op = op;
>> + filter_item->setting.signed_type.value = value;
>> +
>> + return 0;
>> +}
>> +
>> +static int unsigned_type_write(const char *buf, struct format_item *format_item,
>> + struct filter_item *filter_item)
>> +{
>> + int oplen;
>> + enum compare_op op;
>> + long long value = 0;
>> +
>> + oplen = scan_op(buf, &op);
>> + if (!oplen)
>> + return -EINVAL;
>> +
>> + switch (format_item->size) {
>> + case 1:
>> + {
>> + unsigned char tmp;
>> + if (sscanf(buf + oplen, "%hhu", &tmp) != 1)
>> + return -EINVAL;
>> + value = tmp;
>> + }
>> + break;
>> + case 2:
>> + {
>> + unsigned short int tmp;
>> + if (sscanf(buf + oplen, "%hu", &tmp) != 1)
>> + return -EINVAL;
>> + value = tmp;
>> + }
>> + break;
>> + case 4:
>> + {
>> + unsigned int tmp;
>> + if (sscanf(buf + oplen, "%u", &tmp) != 1)
>> + return -EINVAL;
>> + value = tmp;
>> + }
>> + break;
>> + case 8:
>> + if (sscanf(buf + oplen, "%llu", &value) != 1)
>> + return -EINVAL;
>> + break;
>> + default:
>> + /* should not run to here */
>> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
>> + format_item->size);
>> + return -EIO;
>> + }
>> +
>> + filter_item->setting.unsigned_type.op = op;
>> + filter_item->setting.unsigned_type.value = value;
>> +
>> + return 0;
>> +}
>> +
>> +static int string_type_write(const char *buf, struct format_item *format_item,
>> + struct filter_item *filter_item)
>> +{
>> + int oplen;
>> + enum compare_op op;
>> + int val_len;
>> +
>> + oplen = scan_op(buf, &op);
>> + if (!oplen)
>> + return -EINVAL;
>> +
>> + /* End string on \n */
>> + for (val_len = 0; *(buf + oplen + val_len); val_len++) {
>> + if (*(buf + oplen + val_len) == '\n')
>> + break;
>> + }
>> +
>> + if (val_len > ARRAY_SIZE(filter_item->setting.string_type.value) - 1)
>> + return -EINVAL;
>> +
>> + filter_item->setting.string_type.op = op;
>> + memcpy(filter_item->setting.string_type.value, buf + oplen, val_len);
>> + filter_item->setting.string_type.value[val_len] = 0;
>> +
>> + return 0;
>> +}
>> +
>> +static int char_type_write(const char *buf, struct format_item *format_item,
>> + struct filter_item *filter_item)
>> +{
>> + int oplen;
>> + enum compare_op op;
>> + unsigned char value;
>> + int val_start;
>> +
>> + oplen = scan_op(buf, &op);
>> + if (!oplen)
>> + return -EINVAL;
>> +
>> + /* bypass blanks */
>> + for (val_start = 0; *(buf + oplen + val_start) == ' ' ||
>> + *(buf + oplen + val_start) == '\t'; val_start++)
>> + ;
>> +
>> + /*
>> + * parse escape string, following escape string are supported:
>> + * "\n"
>> + * "\[num]"
>> + * "\[char expect \n and \[num]]", as "\\", "\ ", ...
>> + */
>> + switch (*(buf + oplen + val_start)) {
>> + case '\0':
>> + case '\n':
>> + return -EINVAL;
>> + case '\\':
>> + switch (*(buf + oplen + val_start + 1)) {
>> + case '\0':
>> + case '\n':
>> + return -EINVAL;
>> + case 'n':
>> + value = '\n';
>> + break;
>> + default:
>> + if (isdigit(*(buf + oplen + val_start + 1))) {
>> + if (sscanf(buf + oplen + val_start + 1, "%hhu",
>> + &value) != 1)
>> + return -EINVAL;
>> + } else {
>> + value =
>> + (*(buf + oplen + val_start + 1));
>> + }
>> + break;
>> + }
>> + break;
>> + default:
>> + value = *(buf + oplen + val_start);
>> + break;
>> + }
>> +
>> + filter_item->setting.char_type.op = op;
>> + filter_item->setting.char_type.value = value;
>> +
>> + return 0;
>> +}
>> +
>> +static ssize_t type_write(struct file *filp, const char __user *ubuf,
>> + size_t cnt, loff_t *ppos)
>> +{
>> + size_t ret;
>> + struct filter_item *filter_item;
>> +
>> + lock_filter();
>> + spin_lock(&filter_lock);
>> + filter_item = filp->f_dentry->d_inode->i_private;
>> + if (filter_item) {
>> + struct format_item *format_item;
>> + char buf[NAME_MAX];
>> + size_t buf_size;
>> + int val_start;
>> +
>> + format_item = filter_item_format(filter_item);
>> +
>> + buf_size = min(cnt, sizeof(buf) - 1);
>> + if (copy_from_user(buf, ubuf, buf_size)) {
>> + ret = -EFAULT;
>> + goto err_copy_from_user;
>> + }
>> + buf[buf_size] = 0;
>> +
>> + /* Check is to disable filter */
>> + for (val_start = 0; *(buf + val_start) == ' ' ||
>> + *(buf + val_start) == '\t'; val_start++)
>> + ;
>> + if (!strncmp(buf + val_start, "*", strlen("*"))
>> + || !strncmp(buf + val_start, "off", strlen("off"))) {
>> + struct marker_filter *filter;
>> + int i;
>> +
>> + filter_item->enable = 0;
>> +
>> + /*
>> + * rescan all items in this marker to
>> + * update marker_filter->enable
>> + */
>> + filter = filter_item->parent;
>> + filter->enable = 0;
>> + for (i = 0; i < filter->format->format_item_cnt; i++) {
>> + if (filter->items[i].enable) {
>> + filter->enable = 1;
>> + break;
>> + }
>> + }
>> +
>> + goto scan_finish;
>> + }
>> +
>> + switch (format_item->type) {
>> + case SIGNED:
>> + ret = signed_type_write(buf, format_item, filter_item);
>> + break;
>> + case UNSIGNED:
>> + ret = unsigned_type_write(buf, format_item,
>> + filter_item);
>> + break;
>> + case STRING:
>> + ret = string_type_write(buf, format_item, filter_item);
>> + break;
>> + case CHAR:
>> + ret = char_type_write(buf, format_item, filter_item);
>> + break;
>> + default:
>> + /* should not run to here */
>> + printk(KERN_ERR "%s: unknown type %d\n",
>> + __func__, format_item->type);
>> + ret = -EIO;
>> + goto err_unknown_type;
>> + }
>> + if (IS_ERR_VALUE(ret))
>> + goto err_scan_string;
>> +
>> + filter_item->enable = 1;
>> + filter_item->parent->enable = 1;
>> +
>> +scan_finish:
>> + ret = cnt;
>> + } else {
>> + ret = -EIO;
>> + }
>> +
>> +err_scan_string:
>> +err_unknown_type:
>> +err_copy_from_user:
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> +
>> + return ret;
>> +}
>> +
>> +static const struct file_operations type_fops = {
>> + .read = type_read,
>> + .write = type_write,
>> +};
>> +
>> +static int init_filter_items(struct marker_filter *filter,
>> + struct marker_format *format)
>> +{
>> + int err = 0;
>> + int i;
>> +
>> + for (i = 0; i < format->format_item_cnt; i++) {
>> + char fname[16];
>> +
>> + filter->items[i].parent = filter;
>> + filter->items[i].enable = 0;
>> +
>> + /* Create type file as __arg1, __arg2 */
>> + sprintf(fname, "__arg%d", i + 1);
>> + filter->items[i].den[0] = debugfs_create_file(fname, S_IRUGO,
>> + filter->rootden, &filter->items[i], &type_fops);
>> + if (IS_ERR(filter->items[i].den[0])
>> + || !filter->items[i].den[0]) {
>> + printk(KERN_ERR
>> + "%s: create type file of %s for %s failed\n",
>> + __func__, fname, filter->format->name);
>> + err = -EIO;
>> + goto err_build_typefile;
>> + }
>> +
>> + /* Create symbol link to type file */
>> + if (filter->format->items[i]->name[0]) {
>> +
>> + filter->items[i].den[1] = debugfs_create_symlink(
>> + filter->format->items[i]->name,
>> + filter->rootden, fname);
>> + if (IS_ERR(filter->items[i].den[1])
>> + || !filter->items[i].den[1]) {
>> + /*
>> + * same-name type is allowed, but it will cause
>> + * create symlink fail.
>> + * we ignore this type of error.
>> + */
>> + filter->items[i].den[1] = NULL;
>> + }
>> + }
>> + }
>> +
>> + return 0;
>> +
>> +err_build_typefile:
>> + for (i--; i >= 0; i--) {
>> + /* see comments in _destroy_marker_filter() for why set NULL */
>> + filter->items[i].den[0]->d_inode->i_private = NULL;
>> + debugfs_remove(filter->items[i].den[0]);
>> + debugfs_remove(filter->items[i].den[1]);
>> + }
>> + return err;
>> +}
>> +
>> +static int add_marker_filter(struct marker_format *format)
>> +{
>> + int err = 0;
>> + struct marker_filter *filter;
>> +
>> + lock_filter();
>> + spin_lock(&filter_lock);
>> +
>> +#ifdef _DEBUG
>> + if (_find_marker_filter(format->name)) {
>> + printk(KERN_ERR "%s: filter %s already exist\n", __func__,
>> + format->name);
>> + err = -EIO;
>> + goto err_already_exist;
>> + }
>> +#endif /* _DEBUG */
>> +
>> + filter = kmalloc(sizeof(struct marker_filter), GFP_KERNEL);
>> + if (!filter) {
>> + printk(KERN_ERR "%s: alloc struct marker_filter failed\n",
>> + __func__);
>> + err = -ENOMEM;
>> + goto err_alloc_marker_filter;
>> + }
>> +
>> + INIT_LIST_HEAD(&filter->list);
>> +
>> + filter->format = format;
>> +
>> + filter->rootden = debugfs_create_dir(format->name, ltt_filter_dir);
>> + if (IS_ERR(filter->rootden) || !filter->rootden) {
>> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
>> + format->name);
>> + err = -EIO;
>> + goto err_create_dir;
>> + }
>> +
>> + filter->formatden = debugfs_create_file("__arg0", S_IRUGO,
>> + filter->rootden, filter, &format_fops);
>> + if (IS_ERR(filter->formatden) || !filter->formatden) {
>> + printk(KERN_ERR
>> + "%s: create typefile failed\n", __func__);
>> + err = -EIO;
>> + goto err_build_typefile;
>> + }
>> +
>> + if (format->format_item_cnt) {
>> + filter->items = kmalloc(sizeof(struct filter_item)
>> + * format->format_item_cnt, GFP_KERNEL);
>> + if (!filter->items) {
>> + printk(KERN_ERR
>> + "%s: alloc filter's items failed\n", __func__);
>> + err = -ENOMEM;
>> + goto err_alloc_types;
>> + }
>> + err = init_filter_items(filter, format);
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR
>> + "%s: init filter's items failed\n", __func__);
>> + goto err_init_types;
>> + }
>> + } else {
>> + filter->items = NULL;
>> + }
>> +
>> + filter->enable = 0;
>> +
>> + list_add(&filter->list, &marker_filter_head);
>> +
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> +
>> + return 0;
>> +
>> +err_init_types:
>> + kfree(filter->items);
>> +err_alloc_types:
>> + /* see comments in _destroy_marker_filter() for why set NULL */
>> + filter->formatden->d_inode->i_private = NULL;
>> + debugfs_remove(filter->formatden);
>> +err_build_typefile:
>> + debugfs_remove(filter->rootden);
>> +err_create_dir:
>> + kfree(filter);
>> +err_alloc_marker_filter:
>> +#ifdef _DEBUG
>> +err_already_exist:
>> +#endif /* _DEBUG */
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> + return err;
>> +}
>> +
>> +static void _destroy_marker_filter(struct marker_filter *filter)
>> +{
>> + int i;
>> +
>> + list_del(&filter->list);
>> +
>> + for (i = 0; i < filter->format->format_item_cnt; i++) {
>> + filter->items[i].den[0]->d_inode->i_private = NULL;
>> + debugfs_remove(filter->items[i].den[0]);
>> + debugfs_remove(filter->items[i].den[1]);
>> + }
>> + kfree(filter->items);
>> +
>> + /*
>> + * Set i_private to NULL, so that if some fileop in process,
>> + * it can avoid illegal access using i_private in fileop.
>> + * (we must get filter_lock in fileop to ensure no other change
>> + * in filter struct)
>> + */
>> + filter->formatden->d_inode->i_private = NULL;
>> + debugfs_remove(filter->formatden);
>> +
>> + debugfs_remove(filter->rootden);
>> +
>> + kfree(filter);
>> +
>> + return;
>> +}
>> +
>> +static void destroy_all_filter(void)
>> +{
>> + struct marker_filter *filter, *next;
>> +
>> + lock_filter();
>> + spin_lock(&filter_lock);
>> + list_for_each_entry_safe(filter, next, &marker_filter_head, list) {
>> + _destroy_marker_filter(filter);
>> + }
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> +}
>> +
>> +static int bulid_exist_filter(void)
>> +{
>> + int err;
>> + struct marker_format *format;
>> +
>> + if (!list_empty(&marker_filter_head)) {
>> + err = -EEXIST;
>> + goto err_not_empty;
>> + }
>> +
>> + mutex_lock(&format_lock);
>> + list_for_each_entry(format, &marker_format_head, list) {
>> + err = add_marker_filter(format);
>> + if (IS_ERR_VALUE(err)) {
>> + printk("%s: add_marker_filter failed\n", __func__);
>> + goto err_add_filter;
>> + }
>> + }
>> + mutex_unlock(&format_lock);
>> +
>> + return 0;
>> +
>> +err_add_filter:
>> + destroy_all_filter();
>> + mutex_unlock(&format_lock);
>> +err_not_empty:
>> + return err;
>> +}
>> +
>> +static int format_notify(struct notifier_block *self, unsigned long val,
>> + void *data)
>> +{
>> + struct marker_format *format = data;
>> + struct marker_filter *filter, *next;
>> +
>> + switch (val) {
>> + case ADD_FORMAT:
>> + add_marker_filter(format);
>> + break;
>> + case DEL_FORMAT:
>> + lock_filter();
>> + spin_lock(&filter_lock);
>> + list_for_each_entry_safe(filter, next, &marker_filter_head,
>> + list) {
>> + if (filter->format == format)
>> + _destroy_marker_filter(filter);
>> + }
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> + break;
>> + }
>> + return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block format_nb = {
>> + .notifier_call = format_notify,
>> +};
>> +
>> +static int marker_filter_init(void)
>> +{
>> + int err = 0;
>> +
>> + err = bulid_exist_filter();
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR "%s: bulid_exist_filter failed\n", __func__);
>> + goto err_add_exist_filter;
>> + }
>> +
>> + err = register_format_notifier(&format_nb);
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR "%s register format notifier failed\n",
>> + __func__);
>> + goto err_register_format_notifier;
>> + }
>> +
>> + return 0;
>> +
>> +err_register_format_notifier:
>> + destroy_all_filter();
>> +err_add_exist_filter:
>> + return err;
>> +}
>> +
>> +static void marker_filter_destroy(void)
>> +{
>> + unregister_format_notifier(&format_nb);
>> + destroy_all_filter();
>> +}
>> +
>> +
>> +/*
>> + * Filter operations
>> + *
>> + * define filter callback for lttng to determine if to output a event
>> + * by user's setting.
>> + *
>> + */
>> +
>> +/*
>> + * Ret:
>> + * 0: filted
>> + * 1: passed
>> + * <0: err
>> + */
>> +static int signed_filter(struct format_item *format_item,
>> + struct filter_item *filter_item, va_list *args)
>> +{
>> + long long int val;
>> +
>> + switch (format_item->size) {
>> + case 1:
>> + val = (char)va_arg(*args, int);
>> + break;
>> + case 2:
>> + val = (short int)va_arg(*args, int);
>> + break;
>> + case 4:
>> + val = va_arg(*args, int);
>> + break;
>> + case 8:
>> + val = va_arg(*args, long long int);
>> + break;
>> + default:
>> + /* should not run to here */
>> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
>> + format_item->size);
>> + return -1;
>> + }
>> +
>> + /*
>> + * We judge filter_item->enable here, because even if filter is not
>> + * enabled, we still need to bypass this data in va_list
>> + */
>> + if (!filter_item->enable)
>> + return 1;
>> +
>> + switch (filter_item->setting.signed_type.op) {
>> + case gt:
>> + return val > filter_item->setting.signed_type.value;
>> + case ge:
>> + return val >= filter_item->setting.signed_type.value;
>> + case eq:
>> + return val == filter_item->setting.signed_type.value;
>> + case le:
>> + return val <= filter_item->setting.signed_type.value;
>> + case lt:
>> + return val < filter_item->setting.signed_type.value;
>> + }
>> +
>> + /* should not run to here */
>> + return -1;
>> +}
>> +
>> +static int unsigned_filter(struct format_item *format_item,
>> + struct filter_item *filter_item, va_list *args)
>> +{
>> + unsigned long long int val;
>> +
>> + switch (format_item->size) {
>> + case 1:
>> + val = (unsigned char)va_arg(*args, unsigned int);
>> + break;
>> + case 2:
>> + val = (short int)va_arg(*args, unsigned int);
>> + break;
>> + case 4:
>> + val = va_arg(*args, unsigned int);
>> + break;
>> + case 8:
>> + val = va_arg(*args, unsigned long long int);
>> + break;
>> + default:
>> + /* should not run to here */
>> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
>> + format_item->size);
>> + return -1;
>> + }
>> +
>> + /*
>> + * We judge filter_item->enable here, because even if filter is not
>> + * enabled, we still need to bypass this data in va_list
>> + */
>> + if (!filter_item->enable)
>> + return 1;
>> +
>> + switch (filter_item->setting.unsigned_type.op) {
>> + case gt:
>> + return val > filter_item->setting.unsigned_type.value;
>> + case ge:
>> + return val >= filter_item->setting.unsigned_type.value;
>> + case eq:
>> + return val == filter_item->setting.unsigned_type.value;
>> + case le:
>> + return val <= filter_item->setting.unsigned_type.value;
>> + case lt:
>> + return val < filter_item->setting.unsigned_type.value;
>> + }
>> +
>> + /* should not run to here */
>> + return -1;
>> +}
>> +
>> +static int string_filter(struct format_item *format_item,
>> + struct filter_item *filter_item, va_list *args)
>> +{
>> + const char *val;
>> +
>> + val = va_arg(*args, const char *);
>> +
>> + /*
>> + * We judge filter_item->enable here, because even if filter is not
>> + * enabled, we still need to bypass this data in va_list
>> + */
>> + if (!filter_item->enable)
>> + return 1;
>> +
>> + switch (filter_item->setting.string_type.op) {
>> + case gt:
>> + return strcmp(val, filter_item->setting.string_type.value) > 0;
>> + case ge:
>> + return strcmp(val, filter_item->setting.string_type.value) >= 0;
>> + case eq:
>> + return strcmp(val, filter_item->setting.string_type.value) == 0;
>> + case le:
>> + return strcmp(val, filter_item->setting.string_type.value) <= 0;
>> + case lt:
>> + return strcmp(val, filter_item->setting.string_type.value) < 0;
>> + }
>> +
>> + /* should not run to here */
>> + return -1;
>> +}
>> +
>> +static int char_filter(struct format_item *format_item,
>> + struct filter_item *filter_item, va_list *args)
>> +{
>> + unsigned char val;
>> +
>> + val = (unsigned char)va_arg(*args, unsigned int);
>> +
>> + /*
>> + * We judge filter_item->enable here, because even if filter is not
>> + * enabled, we still need to bypass this data in va_list
>> + */
>> + if (!filter_item->enable)
>> + return 1;
>> +
>> + switch (filter_item->setting.char_type.op) {
>> + case gt:
>> + return val > filter_item->setting.char_type.value;
>> + case ge:
>> + return val >= filter_item->setting.char_type.value;
>> + case eq:
>> + return val == filter_item->setting.char_type.value;
>> + case le:
>> + return val <= filter_item->setting.char_type.value;
>> + case lt:
>> + return val < filter_item->setting.char_type.value;
>> + }
>> +
>> + /* should not run to here */
>> + return -1;
>> +}
>> +
>> +static int ltt_filter(struct ltt_trace_struct *trace,
>> + const struct marker *mdata, va_list *args)
>> +{
>> + int ret;
>> + struct marker_filter *filter;
>> + struct marker_format *format;
>> + int i;
>> +
>> + if (lock_filter()) {
>
> Hrm do you allow session filter modification when tracing is active for
> this session ?
>
> I think the lock_filter approach you are taking here is wrong.
>
>
>> + /* Don't output events caused by ltt-filter */
>> + return 0;
>> + }
>> +
>> + spin_lock(&filter_lock);
>> +
>> + filter = _find_marker_filter(mdata->name);
>> + if (!filter) {
>> + printk(KERN_ERR "%s: unknown marker %s\n", __func__,
>> + mdata->name);
>> + ret = 1;
>> + goto done;
>> + }
>> +
>> + /* Bypass if no filter setting on this marker */
>> + if (!filter->enable) {
>> + ret = 1;
>> + goto done;
>> + }
>> +
>> + format = filter->format;
>> +
>> + for (i = 0; i < format->format_item_cnt; i++) {
>> + int result;
>> +
>> + switch (format->items[i]->type) {
>> + case SIGNED:
>> + result = signed_filter(format->items[i],
>> + filter->items + i, args);
>> + break;
>> + case UNSIGNED:
>> + result = unsigned_filter(format->items[i],
>> + filter->items + i, args);
>> + break;
>> + case STRING:
>> + result = string_filter(format->items[i],
>> + filter->items + i, args);
>> + break;
>> + case CHAR:
>> + result = char_filter(format->items[i],
>> + filter->items + i, args);
>> + break;
>> + default:
>> + /* should not run to here */
>> + printk(KERN_ERR "%s: unknown type %d\n", __func__,
>> + format->items[i]->type);
>> + ret = 1;
>> + goto done;
>> + }
>> + if (result < 0) {
>> + ret = 1;
>> + goto done;
>> + }
>> + if (!result) {
>> + ret = 0;
>> + goto done;
>> + }
>> + }
>> +
>> + ret = 1;
>> +
>> +done:
>> + spin_unlock(&filter_lock);
>> + unlock_filter();
>> + return ret;
>> +}
>> +
>> +
>> +/*
>> + * Init/exit option of module
>> + *
>> + * Init marker_format and marker_filter operations
>> + * register filter callback in ltt
>> + *
>> + */
>> +
>> +static int __init ltt_filter_init(void)
>> +{
>> + int err = 0;
>> + struct dentry *ltt_root_dentry;
>> +
>> + ltt_root_dentry = get_ltt_root();
>> + if (!ltt_root_dentry) {
>> + err = -ENOENT;
>> + goto err_no_root;
>> + }
>> +
>> + ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR, ltt_root_dentry);
>> + if (IS_ERR(ltt_filter_dir) || !ltt_filter_dir) {
>> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
>> + LTT_FILTER_DIR);
>> + err = -ENOMEM;
>> + goto err_create_filter_dir;
>> + }
>> +
>> + err = marker_format_init();
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR "%s: init marker format failed\n", __func__);
>> + goto err_marker_format_init;
>> + }
>> +
>> + err = marker_filter_init();
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR "%s: init marker filter failed\n", __func__);
>> + goto err_marker_filter_init;
>> + }
>> +
>> + err = ltt_module_register(LTT_FUNCTION_RUN_FILTER, ltt_filter,
>> + THIS_MODULE);
>> + if (IS_ERR_VALUE(err)) {
>> + printk(KERN_ERR "%s: register ltt filter failed\n", __func__);
>> + goto err_filter_register;
>> + }
>> +
>> + return 0;
>> +
>> +err_filter_register:
>> + marker_filter_destroy();
>> +err_marker_filter_init:
>> + marker_format_destroy();
>> +err_marker_format_init:
>> + debugfs_remove(ltt_filter_dir);
>> +err_create_filter_dir:
>> +err_no_root:
>> + return err;
>> +}
>> +
>> +static void __exit ltt_filter_exit(void)
>> +{
>> + ltt_module_unregister(LTT_FUNCTION_RUN_FILTER);
>> + marker_filter_destroy();
>> + marker_format_destroy();
>> +
>> + debugfs_remove(ltt_filter_dir);
>> +}
>> +
>> +module_init(ltt_filter_init);
>> +module_exit(ltt_filter_exit);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Zhao Lei <zhaolei at cn.fujitsu.com>");
>> +MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
>> --
>> 1.5.5.3
>>
>>
>>
>> _______________________________________________
>> ltt-dev mailing list
>> ltt-dev at lists.casi.polymtl.ca
>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>
>
> --
> Mathieu Desnoyers
> OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 3/3] In-kernel filter module
2009-04-07 5:45 ` Zhaolei
@ 2009-04-07 16:48 ` Mathieu Desnoyers
2009-04-08 3:19 ` Zhaolei
0 siblings, 1 reply; 24+ messages in thread
From: Mathieu Desnoyers @ 2009-04-07 16:48 UTC (permalink / raw)
* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
> * From: "Mathieu Desnoyers" <compudj@krystal.dyndns.org>
[...]
> >> +static LIST_HEAD(marker_filter_head);
> >> +
> >> +/*
> >> + * Use spinlock to lock filter struct because we can't use mutex in filter
> >> + * callback
> >> + */
> >> +static DEFINE_SPINLOCK(filter_lock);
> >> +
> >> +/*
> >> + * We need call some kernel function in ltt-filter function, but many kernel
> >> + * function have tp/markers which cause cpu jump to ltt, then jump to
> >> + * ltt-filter from ltt code.
> >> + * Even if we don't call any kernel function in filter code, interrupt will
> >> + * do similar thing.
> >> + * To make it easy, we avoid this type of nesting by lock/unlock_filter.
> >> + */
> >> +DEFINE_PER_CPU(atomic_t, filter_nesting) = ATOMIC_INIT(0);
> >> +static inline int lock_filter(void)
> >> +{
> >> + preempt_disable();
> >> + if (atomic_xchg(&__get_cpu_var(filter_nesting), 1) > 0) {
> >> + /* Already in lock */
> >> + preempt_enable();
> >> + return 1;
> >> + }
> >> + return 0;
> >> +}
> >> +static inline void unlock_filter(void)
> >> +{
> >> + atomic_set(&__get_cpu_var(filter_nesting), 0);
> >> + preempt_enable();
> >> +}
> >
> > Hrm, this lock_filter seems broken. If you have an interrupt nesting
> > over lock_filter, and takes lock_filter again, the interrupt will set
> > the filter_nesting to 0, which creates a race.
> If an interrupt nesting over lock_filter, and takes lock_filter again,
> filter_nesting is still 1, because we use atomic_xchg(filter_nesting, 1).
>
The problem is not when the interrupt handler takes the lock, but when
it releases it. It will set if back to 0, but the interrupted thread
still holds this lock, which creates a race window. Basically your
primitive is lacking a sane nesting counter.
> >
> > Basically, what you want is to disallow filter modification used by an
> > active tracing session _or_, if you want to allow filter modification
> > while a tracing session is active. But to do it properly, you want to
> > use RCU data structures synchronized with call_rcu_sched and
> > synchronize_sched and use the RCU read lock (which is _already_ taken by
> > LTTng around probe execution anyway) to synchronize.
> I understand your opinion.
> I agree your opinion that RCU can allow filter modification while a tracing
> session is active.
> But if we call a kernel function in filter function, and this kernel function
> run into filter again, we can't prevent this kind of nesting by rcu.
> Ltt have a ltt_nesting to prevent this kind of nesting, but it can't help
> filter function.
You are right in that the filter function is called before reserving
space, and therefore the ltt_nesting value is not tested. So you should
simply test the already existing ltt_nesting value at the beginning of
the filter function, you don't need another nesting variable. You can
look at the ltt_reserve_slot function to see how to test this.
>
> Maybe we can call no kernel function in filter() to prevent this kind of
> nesting, and use rcu to allow filter modification when filter() function is
> running, and I aggre to using rcu, but sorry that I am some busy these days,
> it is not easy to get sometime to finish this change.
I doubt typical use-cases would include filter modification while the
trace session is running. I would aim for the simpler goal : filter
modifications should only be allowed when a filter is not attached to an
active trace session. This way we don't have to use more complex
RCU-aware data structures, which could be a real pain for filters.
>
> Current code is ok to work, and it passed my test with a my testset.
> (I can send it to you if you need)
I would really prefer to have a solution that does not add supplementary
custom locking into the tracer. Supporting filter modification when
sessions are inactive seems like a perfect middle-ground between
implementation complexity (it's simple) and not modifying the tracer
locking semantics.
>
> >
> > The filter_lock spinlock should probably become a simple mutex too.
> I attempted to use mutex for filter_lock, but always cause panic in my testset.
If I may suggest :
Let's remove the custom locking, use the trace_lock mutex to protect
filter modifications, disallow filter modification if it belongs to an
active trace, and also wait for a quiescent state with
synchronize_sched() after we see that a filter belongs to an inactive
trace (while we hold the trace_lock), so that we can make sure that no
trace probes are seeing this filter while we modify it.
Thanks,
Mathieu
>
> Thanks!
> Zhaolei
> >
> > Mathieu
> >
> >> +
> >> +struct marker_filter *_find_marker_filter(const char *name)
> >> +{
> >> + struct marker_filter *filter;
> >> + list_for_each_entry(filter, &marker_filter_head, list) {
> >> + if (!strcmp(name, filter->format->name))
> >> + return filter;
> >> + }
> >> + return NULL;
> >> +}
> >> +
> >> +static ssize_t format_read(struct file *filp, char __user *ubuf, size_t cnt,
> >> + loff_t *ppos)
> >> +{
> >> + size_t ret;
> >> + struct marker_filter *filter;
> >> +
> >> + lock_filter();
> >> + spin_lock(&filter_lock);
> >> + filter = filp->f_dentry->d_inode->i_private;
> >> + if (filter) {
> >> + ret = simple_read_from_buffer(ubuf, cnt, ppos,
> >> + filter->format->format, strlen(filter->format->format));
> >> + } else {
> >> + ret = -EIO;
> >> + }
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +static const struct file_operations format_fops = {
> >> + .read = format_read,
> >> +};
> >> +
> >> +static int signed_type_read(char *buf, size_t bufsize,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int ret;
> >> + int pos = 0;
> >> +
> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> >> + compare_op_string[filter_item->setting.signed_type.op]);
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + ret = scnprintf(buf + pos, bufsize - pos, " %lld",
> >> + filter_item->setting.signed_type.value);
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + return pos;
> >> +}
> >> +
> >> +static int unsigned_type_read(char *buf, size_t bufsize,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int ret;
> >> + int pos = 0;
> >> +
> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> >> + compare_op_string[filter_item->setting.unsigned_type.op]);
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + ret = scnprintf(buf + pos, bufsize - pos, " %llu",
> >> + filter_item->setting.unsigned_type.value);
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + return pos;
> >> +}
> >> +
> >> +static int string_type_read(char *buf, size_t bufsize,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int ret;
> >> + int pos = 0;
> >> +
> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> >> + compare_op_string[filter_item->setting.string_type.op]);
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + ret = scnprintf(buf + pos, bufsize - pos, " %s",
> >> + filter_item->setting.string_type.value);
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + return pos;
> >> +}
> >> +
> >> +static int char_type_read(char *buf, size_t bufsize,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int ret;
> >> + int pos = 0;
> >> +
> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
> >> + compare_op_string[filter_item->setting.char_type.op]);
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + switch (filter_item->setting.char_type.value) {
> >> + case '\t':
> >> + ret = scnprintf(buf + pos, bufsize - pos, " \\t");
> >> + break;
> >> + case ' ':
> >> + ret = scnprintf(buf + pos, bufsize - pos, " blank");
> >> + break;
> >> + case '\n':
> >> + ret = scnprintf(buf + pos, bufsize - pos, " \\n");
> >> + break;
> >> + default:
> >> + if (isgraph(filter_item->setting.char_type.value)) {
> >> + ret = scnprintf(buf + pos, bufsize - pos, " %c",
> >> + filter_item->setting.char_type.value);
> >> + } else {
> >> + ret = scnprintf(buf + pos, bufsize - pos,
> >> + " \\%u", (unsigned int)
> >> + filter_item->setting.char_type.value);
> >> + }
> >> + break;
> >> + }
> >> + if (!ret)
> >> + return 0;
> >> + pos += ret;
> >> +
> >> + return pos;
> >> +}
> >> +
> >> +static ssize_t type_read(struct file *filp, char __user *ubuf, size_t cnt,
> >> + loff_t *ppos)
> >> +{
> >> + size_t ret;
> >> + struct filter_item *filter_item;
> >> +
> >> + lock_filter();
> >> + spin_lock(&filter_lock);
> >> + filter_item = filp->f_dentry->d_inode->i_private;
> >> + if (filter_item) {
> >> + struct format_item *format_item;
> >> + char buf[128];
> >> + int pos = 0;
> >> +
> >> + format_item = filter_item_format(filter_item);
> >> +
> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos,
> >> + "type: %u byte(s) %s\n",
> >> + format_item->size,
> >> + format_item_type_string[format_item->type]);
> >> + if (!ret) {
> >> + ret = -EINVAL;
> >> + goto err_format_string;
> >> + }
> >> + pos += ret;
> >> +
> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "setting: ");
> >> + if (!ret) {
> >> + ret = -EINVAL;
> >> + goto err_format_string;
> >> + }
> >> + pos += ret;
> >> +
> >> + if (!filter_item->enable) {
> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "*");
> >> + if (!ret) {
> >> + ret = -EINVAL;
> >> + goto err_format_string;
> >> + }
> >> + pos += ret;
> >> + goto format_finish;
> >> + }
> >> +
> >> + switch (format_item->type) {
> >> + case SIGNED:
> >> + ret = signed_type_read(buf + pos, sizeof(buf) - pos,
> >> + filter_item);
> >> + break;
> >> + case UNSIGNED:
> >> + ret = unsigned_type_read(buf + pos, sizeof(buf) - pos,
> >> + filter_item);
> >> + break;
> >> + case STRING:
> >> + ret = string_type_read(buf + pos, sizeof(buf) - pos,
> >> + filter_item);
> >> + break;
> >> + case CHAR:
> >> + ret = char_type_read(buf + pos, sizeof(buf) - pos,
> >> + filter_item);
> >> + break;
> >> + default:
> >> + /* should not run to here */
> >> + printk(KERN_ERR "%s: unknown type %d\n",
> >> + __func__, format_item->type);
> >> + ret = -EIO;
> >> + goto err_unknown_type;
> >> + }
> >> + if (!ret) {
> >> + ret = -EINVAL;
> >> + goto err_format_string;
> >> + }
> >> + pos += ret;
> >> +
> >> +format_finish:
> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "\n");
> >> + if (!ret) {
> >> + ret = -EINVAL;
> >> + goto err_format_string;
> >> + }
> >> + pos += ret;
> >> +
> >> + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf,
> >> + strlen(buf));
> >> + } else {
> >> + ret = -EIO;
> >> + }
> >> +
> >> +err_format_string:
> >> +err_unknown_type:
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +/*
> >> + * Check op info in a input string
> >> + * return length of op part on success, *op is set.
> >> + * return 0 on fail
> >> + */
> >> +static size_t scan_op(const char *str, enum compare_op *op)
> >> +{
> >> + /* Must reverse sort with strlen */
> >> + static struct {
> >> + const char *str;
> >> + enum compare_op op;
> >> + } str_op[] = {
> >> + {"gt", gt},
> >> + {"ge", ge},
> >> + {"eq", eq},
> >> + {"le", le},
> >> + {"lt", lt},
> >> + {">=", ge},
> >> + {"<=", le},
> >> + {">", gt},
> >> + {"=", eq},
> >> + {"<", lt},
> >> + };
> >> + int i;
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(str_op); i++) {
> >> + if (!strncmp(str, str_op[i].str, strlen(str_op[i].str))) {
> >> + *op = str_op[i].op;
> >> + return strlen(str_op[i].str);
> >> + }
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int signed_type_write(const char *buf, struct format_item *format_item,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int oplen;
> >> + enum compare_op op;
> >> + long long value = 0;
> >> +
> >> + oplen = scan_op(buf, &op);
> >> + if (!oplen)
> >> + return -EINVAL;
> >> +
> >> + switch (format_item->size) {
> >> + case 1:
> >> + {
> >> + char tmp;
> >> + if (sscanf(buf + oplen, "%hhd", &tmp) != 1)
> >> + return -EINVAL;
> >> + value = tmp;
> >> + }
> >> + break;
> >> + case 2:
> >> + {
> >> + short int tmp;
> >> + if (sscanf(buf + oplen, "%hd", &tmp) != 1)
> >> + return -EINVAL;
> >> + value = tmp;
> >> + }
> >> + break;
> >> + case 4:
> >> + {
> >> + int tmp;
> >> + if (sscanf(buf + oplen, "%d", &tmp) != 1)
> >> + return -EINVAL;
> >> + value = tmp;
> >> + }
> >> + break;
> >> + case 8:
> >> + if (sscanf(buf + oplen, "%lld", &value) != 1)
> >> + return -EINVAL;
> >> + break;
> >> + default:
> >> + /* should not run to here */
> >> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
> >> + format_item->size);
> >> + return -EIO;
> >> + }
> >> +
> >> + filter_item->setting.signed_type.op = op;
> >> + filter_item->setting.signed_type.value = value;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int unsigned_type_write(const char *buf, struct format_item *format_item,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int oplen;
> >> + enum compare_op op;
> >> + long long value = 0;
> >> +
> >> + oplen = scan_op(buf, &op);
> >> + if (!oplen)
> >> + return -EINVAL;
> >> +
> >> + switch (format_item->size) {
> >> + case 1:
> >> + {
> >> + unsigned char tmp;
> >> + if (sscanf(buf + oplen, "%hhu", &tmp) != 1)
> >> + return -EINVAL;
> >> + value = tmp;
> >> + }
> >> + break;
> >> + case 2:
> >> + {
> >> + unsigned short int tmp;
> >> + if (sscanf(buf + oplen, "%hu", &tmp) != 1)
> >> + return -EINVAL;
> >> + value = tmp;
> >> + }
> >> + break;
> >> + case 4:
> >> + {
> >> + unsigned int tmp;
> >> + if (sscanf(buf + oplen, "%u", &tmp) != 1)
> >> + return -EINVAL;
> >> + value = tmp;
> >> + }
> >> + break;
> >> + case 8:
> >> + if (sscanf(buf + oplen, "%llu", &value) != 1)
> >> + return -EINVAL;
> >> + break;
> >> + default:
> >> + /* should not run to here */
> >> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
> >> + format_item->size);
> >> + return -EIO;
> >> + }
> >> +
> >> + filter_item->setting.unsigned_type.op = op;
> >> + filter_item->setting.unsigned_type.value = value;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int string_type_write(const char *buf, struct format_item *format_item,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int oplen;
> >> + enum compare_op op;
> >> + int val_len;
> >> +
> >> + oplen = scan_op(buf, &op);
> >> + if (!oplen)
> >> + return -EINVAL;
> >> +
> >> + /* End string on \n */
> >> + for (val_len = 0; *(buf + oplen + val_len); val_len++) {
> >> + if (*(buf + oplen + val_len) == '\n')
> >> + break;
> >> + }
> >> +
> >> + if (val_len > ARRAY_SIZE(filter_item->setting.string_type.value) - 1)
> >> + return -EINVAL;
> >> +
> >> + filter_item->setting.string_type.op = op;
> >> + memcpy(filter_item->setting.string_type.value, buf + oplen, val_len);
> >> + filter_item->setting.string_type.value[val_len] = 0;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int char_type_write(const char *buf, struct format_item *format_item,
> >> + struct filter_item *filter_item)
> >> +{
> >> + int oplen;
> >> + enum compare_op op;
> >> + unsigned char value;
> >> + int val_start;
> >> +
> >> + oplen = scan_op(buf, &op);
> >> + if (!oplen)
> >> + return -EINVAL;
> >> +
> >> + /* bypass blanks */
> >> + for (val_start = 0; *(buf + oplen + val_start) == ' ' ||
> >> + *(buf + oplen + val_start) == '\t'; val_start++)
> >> + ;
> >> +
> >> + /*
> >> + * parse escape string, following escape string are supported:
> >> + * "\n"
> >> + * "\[num]"
> >> + * "\[char expect \n and \[num]]", as "\\", "\ ", ...
> >> + */
> >> + switch (*(buf + oplen + val_start)) {
> >> + case '\0':
> >> + case '\n':
> >> + return -EINVAL;
> >> + case '\\':
> >> + switch (*(buf + oplen + val_start + 1)) {
> >> + case '\0':
> >> + case '\n':
> >> + return -EINVAL;
> >> + case 'n':
> >> + value = '\n';
> >> + break;
> >> + default:
> >> + if (isdigit(*(buf + oplen + val_start + 1))) {
> >> + if (sscanf(buf + oplen + val_start + 1, "%hhu",
> >> + &value) != 1)
> >> + return -EINVAL;
> >> + } else {
> >> + value =
> >> + (*(buf + oplen + val_start + 1));
> >> + }
> >> + break;
> >> + }
> >> + break;
> >> + default:
> >> + value = *(buf + oplen + val_start);
> >> + break;
> >> + }
> >> +
> >> + filter_item->setting.char_type.op = op;
> >> + filter_item->setting.char_type.value = value;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static ssize_t type_write(struct file *filp, const char __user *ubuf,
> >> + size_t cnt, loff_t *ppos)
> >> +{
> >> + size_t ret;
> >> + struct filter_item *filter_item;
> >> +
> >> + lock_filter();
> >> + spin_lock(&filter_lock);
> >> + filter_item = filp->f_dentry->d_inode->i_private;
> >> + if (filter_item) {
> >> + struct format_item *format_item;
> >> + char buf[NAME_MAX];
> >> + size_t buf_size;
> >> + int val_start;
> >> +
> >> + format_item = filter_item_format(filter_item);
> >> +
> >> + buf_size = min(cnt, sizeof(buf) - 1);
> >> + if (copy_from_user(buf, ubuf, buf_size)) {
> >> + ret = -EFAULT;
> >> + goto err_copy_from_user;
> >> + }
> >> + buf[buf_size] = 0;
> >> +
> >> + /* Check is to disable filter */
> >> + for (val_start = 0; *(buf + val_start) == ' ' ||
> >> + *(buf + val_start) == '\t'; val_start++)
> >> + ;
> >> + if (!strncmp(buf + val_start, "*", strlen("*"))
> >> + || !strncmp(buf + val_start, "off", strlen("off"))) {
> >> + struct marker_filter *filter;
> >> + int i;
> >> +
> >> + filter_item->enable = 0;
> >> +
> >> + /*
> >> + * rescan all items in this marker to
> >> + * update marker_filter->enable
> >> + */
> >> + filter = filter_item->parent;
> >> + filter->enable = 0;
> >> + for (i = 0; i < filter->format->format_item_cnt; i++) {
> >> + if (filter->items[i].enable) {
> >> + filter->enable = 1;
> >> + break;
> >> + }
> >> + }
> >> +
> >> + goto scan_finish;
> >> + }
> >> +
> >> + switch (format_item->type) {
> >> + case SIGNED:
> >> + ret = signed_type_write(buf, format_item, filter_item);
> >> + break;
> >> + case UNSIGNED:
> >> + ret = unsigned_type_write(buf, format_item,
> >> + filter_item);
> >> + break;
> >> + case STRING:
> >> + ret = string_type_write(buf, format_item, filter_item);
> >> + break;
> >> + case CHAR:
> >> + ret = char_type_write(buf, format_item, filter_item);
> >> + break;
> >> + default:
> >> + /* should not run to here */
> >> + printk(KERN_ERR "%s: unknown type %d\n",
> >> + __func__, format_item->type);
> >> + ret = -EIO;
> >> + goto err_unknown_type;
> >> + }
> >> + if (IS_ERR_VALUE(ret))
> >> + goto err_scan_string;
> >> +
> >> + filter_item->enable = 1;
> >> + filter_item->parent->enable = 1;
> >> +
> >> +scan_finish:
> >> + ret = cnt;
> >> + } else {
> >> + ret = -EIO;
> >> + }
> >> +
> >> +err_scan_string:
> >> +err_unknown_type:
> >> +err_copy_from_user:
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +static const struct file_operations type_fops = {
> >> + .read = type_read,
> >> + .write = type_write,
> >> +};
> >> +
> >> +static int init_filter_items(struct marker_filter *filter,
> >> + struct marker_format *format)
> >> +{
> >> + int err = 0;
> >> + int i;
> >> +
> >> + for (i = 0; i < format->format_item_cnt; i++) {
> >> + char fname[16];
> >> +
> >> + filter->items[i].parent = filter;
> >> + filter->items[i].enable = 0;
> >> +
> >> + /* Create type file as __arg1, __arg2 */
> >> + sprintf(fname, "__arg%d", i + 1);
> >> + filter->items[i].den[0] = debugfs_create_file(fname, S_IRUGO,
> >> + filter->rootden, &filter->items[i], &type_fops);
> >> + if (IS_ERR(filter->items[i].den[0])
> >> + || !filter->items[i].den[0]) {
> >> + printk(KERN_ERR
> >> + "%s: create type file of %s for %s failed\n",
> >> + __func__, fname, filter->format->name);
> >> + err = -EIO;
> >> + goto err_build_typefile;
> >> + }
> >> +
> >> + /* Create symbol link to type file */
> >> + if (filter->format->items[i]->name[0]) {
> >> +
> >> + filter->items[i].den[1] = debugfs_create_symlink(
> >> + filter->format->items[i]->name,
> >> + filter->rootden, fname);
> >> + if (IS_ERR(filter->items[i].den[1])
> >> + || !filter->items[i].den[1]) {
> >> + /*
> >> + * same-name type is allowed, but it will cause
> >> + * create symlink fail.
> >> + * we ignore this type of error.
> >> + */
> >> + filter->items[i].den[1] = NULL;
> >> + }
> >> + }
> >> + }
> >> +
> >> + return 0;
> >> +
> >> +err_build_typefile:
> >> + for (i--; i >= 0; i--) {
> >> + /* see comments in _destroy_marker_filter() for why set NULL */
> >> + filter->items[i].den[0]->d_inode->i_private = NULL;
> >> + debugfs_remove(filter->items[i].den[0]);
> >> + debugfs_remove(filter->items[i].den[1]);
> >> + }
> >> + return err;
> >> +}
> >> +
> >> +static int add_marker_filter(struct marker_format *format)
> >> +{
> >> + int err = 0;
> >> + struct marker_filter *filter;
> >> +
> >> + lock_filter();
> >> + spin_lock(&filter_lock);
> >> +
> >> +#ifdef _DEBUG
> >> + if (_find_marker_filter(format->name)) {
> >> + printk(KERN_ERR "%s: filter %s already exist\n", __func__,
> >> + format->name);
> >> + err = -EIO;
> >> + goto err_already_exist;
> >> + }
> >> +#endif /* _DEBUG */
> >> +
> >> + filter = kmalloc(sizeof(struct marker_filter), GFP_KERNEL);
> >> + if (!filter) {
> >> + printk(KERN_ERR "%s: alloc struct marker_filter failed\n",
> >> + __func__);
> >> + err = -ENOMEM;
> >> + goto err_alloc_marker_filter;
> >> + }
> >> +
> >> + INIT_LIST_HEAD(&filter->list);
> >> +
> >> + filter->format = format;
> >> +
> >> + filter->rootden = debugfs_create_dir(format->name, ltt_filter_dir);
> >> + if (IS_ERR(filter->rootden) || !filter->rootden) {
> >> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
> >> + format->name);
> >> + err = -EIO;
> >> + goto err_create_dir;
> >> + }
> >> +
> >> + filter->formatden = debugfs_create_file("__arg0", S_IRUGO,
> >> + filter->rootden, filter, &format_fops);
> >> + if (IS_ERR(filter->formatden) || !filter->formatden) {
> >> + printk(KERN_ERR
> >> + "%s: create typefile failed\n", __func__);
> >> + err = -EIO;
> >> + goto err_build_typefile;
> >> + }
> >> +
> >> + if (format->format_item_cnt) {
> >> + filter->items = kmalloc(sizeof(struct filter_item)
> >> + * format->format_item_cnt, GFP_KERNEL);
> >> + if (!filter->items) {
> >> + printk(KERN_ERR
> >> + "%s: alloc filter's items failed\n", __func__);
> >> + err = -ENOMEM;
> >> + goto err_alloc_types;
> >> + }
> >> + err = init_filter_items(filter, format);
> >> + if (IS_ERR_VALUE(err)) {
> >> + printk(KERN_ERR
> >> + "%s: init filter's items failed\n", __func__);
> >> + goto err_init_types;
> >> + }
> >> + } else {
> >> + filter->items = NULL;
> >> + }
> >> +
> >> + filter->enable = 0;
> >> +
> >> + list_add(&filter->list, &marker_filter_head);
> >> +
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> +
> >> + return 0;
> >> +
> >> +err_init_types:
> >> + kfree(filter->items);
> >> +err_alloc_types:
> >> + /* see comments in _destroy_marker_filter() for why set NULL */
> >> + filter->formatden->d_inode->i_private = NULL;
> >> + debugfs_remove(filter->formatden);
> >> +err_build_typefile:
> >> + debugfs_remove(filter->rootden);
> >> +err_create_dir:
> >> + kfree(filter);
> >> +err_alloc_marker_filter:
> >> +#ifdef _DEBUG
> >> +err_already_exist:
> >> +#endif /* _DEBUG */
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> + return err;
> >> +}
> >> +
> >> +static void _destroy_marker_filter(struct marker_filter *filter)
> >> +{
> >> + int i;
> >> +
> >> + list_del(&filter->list);
> >> +
> >> + for (i = 0; i < filter->format->format_item_cnt; i++) {
> >> + filter->items[i].den[0]->d_inode->i_private = NULL;
> >> + debugfs_remove(filter->items[i].den[0]);
> >> + debugfs_remove(filter->items[i].den[1]);
> >> + }
> >> + kfree(filter->items);
> >> +
> >> + /*
> >> + * Set i_private to NULL, so that if some fileop in process,
> >> + * it can avoid illegal access using i_private in fileop.
> >> + * (we must get filter_lock in fileop to ensure no other change
> >> + * in filter struct)
> >> + */
> >> + filter->formatden->d_inode->i_private = NULL;
> >> + debugfs_remove(filter->formatden);
> >> +
> >> + debugfs_remove(filter->rootden);
> >> +
> >> + kfree(filter);
> >> +
> >> + return;
> >> +}
> >> +
> >> +static void destroy_all_filter(void)
> >> +{
> >> + struct marker_filter *filter, *next;
> >> +
> >> + lock_filter();
> >> + spin_lock(&filter_lock);
> >> + list_for_each_entry_safe(filter, next, &marker_filter_head, list) {
> >> + _destroy_marker_filter(filter);
> >> + }
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> +}
> >> +
> >> +static int bulid_exist_filter(void)
> >> +{
> >> + int err;
> >> + struct marker_format *format;
> >> +
> >> + if (!list_empty(&marker_filter_head)) {
> >> + err = -EEXIST;
> >> + goto err_not_empty;
> >> + }
> >> +
> >> + mutex_lock(&format_lock);
> >> + list_for_each_entry(format, &marker_format_head, list) {
> >> + err = add_marker_filter(format);
> >> + if (IS_ERR_VALUE(err)) {
> >> + printk("%s: add_marker_filter failed\n", __func__);
> >> + goto err_add_filter;
> >> + }
> >> + }
> >> + mutex_unlock(&format_lock);
> >> +
> >> + return 0;
> >> +
> >> +err_add_filter:
> >> + destroy_all_filter();
> >> + mutex_unlock(&format_lock);
> >> +err_not_empty:
> >> + return err;
> >> +}
> >> +
> >> +static int format_notify(struct notifier_block *self, unsigned long val,
> >> + void *data)
> >> +{
> >> + struct marker_format *format = data;
> >> + struct marker_filter *filter, *next;
> >> +
> >> + switch (val) {
> >> + case ADD_FORMAT:
> >> + add_marker_filter(format);
> >> + break;
> >> + case DEL_FORMAT:
> >> + lock_filter();
> >> + spin_lock(&filter_lock);
> >> + list_for_each_entry_safe(filter, next, &marker_filter_head,
> >> + list) {
> >> + if (filter->format == format)
> >> + _destroy_marker_filter(filter);
> >> + }
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> + break;
> >> + }
> >> + return NOTIFY_OK;
> >> +}
> >> +
> >> +static struct notifier_block format_nb = {
> >> + .notifier_call = format_notify,
> >> +};
> >> +
> >> +static int marker_filter_init(void)
> >> +{
> >> + int err = 0;
> >> +
> >> + err = bulid_exist_filter();
> >> + if (IS_ERR_VALUE(err)) {
> >> + printk(KERN_ERR "%s: bulid_exist_filter failed\n", __func__);
> >> + goto err_add_exist_filter;
> >> + }
> >> +
> >> + err = register_format_notifier(&format_nb);
> >> + if (IS_ERR_VALUE(err)) {
> >> + printk(KERN_ERR "%s register format notifier failed\n",
> >> + __func__);
> >> + goto err_register_format_notifier;
> >> + }
> >> +
> >> + return 0;
> >> +
> >> +err_register_format_notifier:
> >> + destroy_all_filter();
> >> +err_add_exist_filter:
> >> + return err;
> >> +}
> >> +
> >> +static void marker_filter_destroy(void)
> >> +{
> >> + unregister_format_notifier(&format_nb);
> >> + destroy_all_filter();
> >> +}
> >> +
> >> +
> >> +/*
> >> + * Filter operations
> >> + *
> >> + * define filter callback for lttng to determine if to output a event
> >> + * by user's setting.
> >> + *
> >> + */
> >> +
> >> +/*
> >> + * Ret:
> >> + * 0: filted
> >> + * 1: passed
> >> + * <0: err
> >> + */
> >> +static int signed_filter(struct format_item *format_item,
> >> + struct filter_item *filter_item, va_list *args)
> >> +{
> >> + long long int val;
> >> +
> >> + switch (format_item->size) {
> >> + case 1:
> >> + val = (char)va_arg(*args, int);
> >> + break;
> >> + case 2:
> >> + val = (short int)va_arg(*args, int);
> >> + break;
> >> + case 4:
> >> + val = va_arg(*args, int);
> >> + break;
> >> + case 8:
> >> + val = va_arg(*args, long long int);
> >> + break;
> >> + default:
> >> + /* should not run to here */
> >> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
> >> + format_item->size);
> >> + return -1;
> >> + }
> >> +
> >> + /*
> >> + * We judge filter_item->enable here, because even if filter is not
> >> + * enabled, we still need to bypass this data in va_list
> >> + */
> >> + if (!filter_item->enable)
> >> + return 1;
> >> +
> >> + switch (filter_item->setting.signed_type.op) {
> >> + case gt:
> >> + return val > filter_item->setting.signed_type.value;
> >> + case ge:
> >> + return val >= filter_item->setting.signed_type.value;
> >> + case eq:
> >> + return val == filter_item->setting.signed_type.value;
> >> + case le:
> >> + return val <= filter_item->setting.signed_type.value;
> >> + case lt:
> >> + return val < filter_item->setting.signed_type.value;
> >> + }
> >> +
> >> + /* should not run to here */
> >> + return -1;
> >> +}
> >> +
> >> +static int unsigned_filter(struct format_item *format_item,
> >> + struct filter_item *filter_item, va_list *args)
> >> +{
> >> + unsigned long long int val;
> >> +
> >> + switch (format_item->size) {
> >> + case 1:
> >> + val = (unsigned char)va_arg(*args, unsigned int);
> >> + break;
> >> + case 2:
> >> + val = (short int)va_arg(*args, unsigned int);
> >> + break;
> >> + case 4:
> >> + val = va_arg(*args, unsigned int);
> >> + break;
> >> + case 8:
> >> + val = va_arg(*args, unsigned long long int);
> >> + break;
> >> + default:
> >> + /* should not run to here */
> >> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
> >> + format_item->size);
> >> + return -1;
> >> + }
> >> +
> >> + /*
> >> + * We judge filter_item->enable here, because even if filter is not
> >> + * enabled, we still need to bypass this data in va_list
> >> + */
> >> + if (!filter_item->enable)
> >> + return 1;
> >> +
> >> + switch (filter_item->setting.unsigned_type.op) {
> >> + case gt:
> >> + return val > filter_item->setting.unsigned_type.value;
> >> + case ge:
> >> + return val >= filter_item->setting.unsigned_type.value;
> >> + case eq:
> >> + return val == filter_item->setting.unsigned_type.value;
> >> + case le:
> >> + return val <= filter_item->setting.unsigned_type.value;
> >> + case lt:
> >> + return val < filter_item->setting.unsigned_type.value;
> >> + }
> >> +
> >> + /* should not run to here */
> >> + return -1;
> >> +}
> >> +
> >> +static int string_filter(struct format_item *format_item,
> >> + struct filter_item *filter_item, va_list *args)
> >> +{
> >> + const char *val;
> >> +
> >> + val = va_arg(*args, const char *);
> >> +
> >> + /*
> >> + * We judge filter_item->enable here, because even if filter is not
> >> + * enabled, we still need to bypass this data in va_list
> >> + */
> >> + if (!filter_item->enable)
> >> + return 1;
> >> +
> >> + switch (filter_item->setting.string_type.op) {
> >> + case gt:
> >> + return strcmp(val, filter_item->setting.string_type.value) > 0;
> >> + case ge:
> >> + return strcmp(val, filter_item->setting.string_type.value) >= 0;
> >> + case eq:
> >> + return strcmp(val, filter_item->setting.string_type.value) == 0;
> >> + case le:
> >> + return strcmp(val, filter_item->setting.string_type.value) <= 0;
> >> + case lt:
> >> + return strcmp(val, filter_item->setting.string_type.value) < 0;
> >> + }
> >> +
> >> + /* should not run to here */
> >> + return -1;
> >> +}
> >> +
> >> +static int char_filter(struct format_item *format_item,
> >> + struct filter_item *filter_item, va_list *args)
> >> +{
> >> + unsigned char val;
> >> +
> >> + val = (unsigned char)va_arg(*args, unsigned int);
> >> +
> >> + /*
> >> + * We judge filter_item->enable here, because even if filter is not
> >> + * enabled, we still need to bypass this data in va_list
> >> + */
> >> + if (!filter_item->enable)
> >> + return 1;
> >> +
> >> + switch (filter_item->setting.char_type.op) {
> >> + case gt:
> >> + return val > filter_item->setting.char_type.value;
> >> + case ge:
> >> + return val >= filter_item->setting.char_type.value;
> >> + case eq:
> >> + return val == filter_item->setting.char_type.value;
> >> + case le:
> >> + return val <= filter_item->setting.char_type.value;
> >> + case lt:
> >> + return val < filter_item->setting.char_type.value;
> >> + }
> >> +
> >> + /* should not run to here */
> >> + return -1;
> >> +}
> >> +
> >> +static int ltt_filter(struct ltt_trace_struct *trace,
> >> + const struct marker *mdata, va_list *args)
> >> +{
> >> + int ret;
> >> + struct marker_filter *filter;
> >> + struct marker_format *format;
> >> + int i;
> >> +
> >> + if (lock_filter()) {
> >
> > Hrm do you allow session filter modification when tracing is active for
> > this session ?
> >
> > I think the lock_filter approach you are taking here is wrong.
> >
> >
> >> + /* Don't output events caused by ltt-filter */
> >> + return 0;
> >> + }
> >> +
> >> + spin_lock(&filter_lock);
> >> +
> >> + filter = _find_marker_filter(mdata->name);
> >> + if (!filter) {
> >> + printk(KERN_ERR "%s: unknown marker %s\n", __func__,
> >> + mdata->name);
> >> + ret = 1;
> >> + goto done;
> >> + }
> >> +
> >> + /* Bypass if no filter setting on this marker */
> >> + if (!filter->enable) {
> >> + ret = 1;
> >> + goto done;
> >> + }
> >> +
> >> + format = filter->format;
> >> +
> >> + for (i = 0; i < format->format_item_cnt; i++) {
> >> + int result;
> >> +
> >> + switch (format->items[i]->type) {
> >> + case SIGNED:
> >> + result = signed_filter(format->items[i],
> >> + filter->items + i, args);
> >> + break;
> >> + case UNSIGNED:
> >> + result = unsigned_filter(format->items[i],
> >> + filter->items + i, args);
> >> + break;
> >> + case STRING:
> >> + result = string_filter(format->items[i],
> >> + filter->items + i, args);
> >> + break;
> >> + case CHAR:
> >> + result = char_filter(format->items[i],
> >> + filter->items + i, args);
> >> + break;
> >> + default:
> >> + /* should not run to here */
> >> + printk(KERN_ERR "%s: unknown type %d\n", __func__,
> >> + format->items[i]->type);
> >> + ret = 1;
> >> + goto done;
> >> + }
> >> + if (result < 0) {
> >> + ret = 1;
> >> + goto done;
> >> + }
> >> + if (!result) {
> >> + ret = 0;
> >> + goto done;
> >> + }
> >> + }
> >> +
> >> + ret = 1;
> >> +
> >> +done:
> >> + spin_unlock(&filter_lock);
> >> + unlock_filter();
> >> + return ret;
> >> +}
> >> +
> >> +
> >> +/*
> >> + * Init/exit option of module
> >> + *
> >> + * Init marker_format and marker_filter operations
> >> + * register filter callback in ltt
> >> + *
> >> + */
> >> +
> >> +static int __init ltt_filter_init(void)
> >> +{
> >> + int err = 0;
> >> + struct dentry *ltt_root_dentry;
> >> +
> >> + ltt_root_dentry = get_ltt_root();
> >> + if (!ltt_root_dentry) {
> >> + err = -ENOENT;
> >> + goto err_no_root;
> >> + }
> >> +
> >> + ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR, ltt_root_dentry);
> >> + if (IS_ERR(ltt_filter_dir) || !ltt_filter_dir) {
> >> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
> >> + LTT_FILTER_DIR);
> >> + err = -ENOMEM;
> >> + goto err_create_filter_dir;
> >> + }
> >> +
> >> + err = marker_format_init();
> >> + if (IS_ERR_VALUE(err)) {
> >> + printk(KERN_ERR "%s: init marker format failed\n", __func__);
> >> + goto err_marker_format_init;
> >> + }
> >> +
> >> + err = marker_filter_init();
> >> + if (IS_ERR_VALUE(err)) {
> >> + printk(KERN_ERR "%s: init marker filter failed\n", __func__);
> >> + goto err_marker_filter_init;
> >> + }
> >> +
> >> + err = ltt_module_register(LTT_FUNCTION_RUN_FILTER, ltt_filter,
> >> + THIS_MODULE);
> >> + if (IS_ERR_VALUE(err)) {
> >> + printk(KERN_ERR "%s: register ltt filter failed\n", __func__);
> >> + goto err_filter_register;
> >> + }
> >> +
> >> + return 0;
> >> +
> >> +err_filter_register:
> >> + marker_filter_destroy();
> >> +err_marker_filter_init:
> >> + marker_format_destroy();
> >> +err_marker_format_init:
> >> + debugfs_remove(ltt_filter_dir);
> >> +err_create_filter_dir:
> >> +err_no_root:
> >> + return err;
> >> +}
> >> +
> >> +static void __exit ltt_filter_exit(void)
> >> +{
> >> + ltt_module_unregister(LTT_FUNCTION_RUN_FILTER);
> >> + marker_filter_destroy();
> >> + marker_format_destroy();
> >> +
> >> + debugfs_remove(ltt_filter_dir);
> >> +}
> >> +
> >> +module_init(ltt_filter_init);
> >> +module_exit(ltt_filter_exit);
> >> +
> >> +MODULE_LICENSE("GPL");
> >> +MODULE_AUTHOR("Zhao Lei <zhaolei at cn.fujitsu.com>");
> >> +MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
> >> --
> >> 1.5.5.3
> >>
> >>
> >>
> >> _______________________________________________
> >> ltt-dev mailing list
> >> ltt-dev at lists.casi.polymtl.ca
> >> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
> >>
> >
> > --
> > Mathieu Desnoyers
> > OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
> >
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
^ permalink raw reply [flat|nested] 24+ messages in thread
* [ltt-dev] [PATCH 3/3] In-kernel filter module
2009-04-07 16:48 ` Mathieu Desnoyers
@ 2009-04-08 3:19 ` Zhaolei
0 siblings, 0 replies; 24+ messages in thread
From: Zhaolei @ 2009-04-08 3:19 UTC (permalink / raw)
* From: "Mathieu Desnoyers" <compudj@krystal.dyndns.org>
Hello, Mathieu
Thanks for your answer.
>* Zhaolei (zhaolei at cn.fujitsu.com) wrote:
>> * From: "Mathieu Desnoyers" <compudj@krystal.dyndns.org>
>
> [...]
>
>> >> +static LIST_HEAD(marker_filter_head);
>> >> +
>> >> +/*
>> >> + * Use spinlock to lock filter struct because we can't use mutex in filter
>> >> + * callback
>> >> + */
>> >> +static DEFINE_SPINLOCK(filter_lock);
>> >> +
>> >> +/*
>> >> + * We need call some kernel function in ltt-filter function, but many kernel
>> >> + * function have tp/markers which cause cpu jump to ltt, then jump to
>> >> + * ltt-filter from ltt code.
>> >> + * Even if we don't call any kernel function in filter code, interrupt will
>> >> + * do similar thing.
>> >> + * To make it easy, we avoid this type of nesting by lock/unlock_filter.
>> >> + */
>> >> +DEFINE_PER_CPU(atomic_t, filter_nesting) = ATOMIC_INIT(0);
>> >> +static inline int lock_filter(void)
>> >> +{
>> >> + preempt_disable();
>> >> + if (atomic_xchg(&__get_cpu_var(filter_nesting), 1) > 0) {
>> >> + /* Already in lock */
>> >> + preempt_enable();
>> >> + return 1;
>> >> + }
>> >> + return 0;
>> >> +}
>> >> +static inline void unlock_filter(void)
>> >> +{
>> >> + atomic_set(&__get_cpu_var(filter_nesting), 0);
>> >> + preempt_enable();
>> >> +}
>> >
>> > Hrm, this lock_filter seems broken. If you have an interrupt nesting
>> > over lock_filter, and takes lock_filter again, the interrupt will set
>> > the filter_nesting to 0, which creates a race.
>> If an interrupt nesting over lock_filter, and takes lock_filter again,
>> filter_nesting is still 1, because we use atomic_xchg(filter_nesting, 1).
>>
>
> The problem is not when the interrupt handler takes the lock, but when
> it releases it. It will set if back to 0, but the interrupted thread
> still holds this lock, which creates a race window. Basically your
> primitive is lacking a sane nesting counter.
Interrupt handler will not take and release this lock, it will only return
from filter when it found that already in lock, please see ltt_filter().
>
>> >
>> > Basically, what you want is to disallow filter modification used by an
>> > active tracing session _or_, if you want to allow filter modification
>> > while a tracing session is active. But to do it properly, you want to
>> > use RCU data structures synchronized with call_rcu_sched and
>> > synchronize_sched and use the RCU read lock (which is _already_ taken by
>> > LTTng around probe execution anyway) to synchronize.
>> I understand your opinion.
>> I agree your opinion that RCU can allow filter modification while a tracing
>> session is active.
>> But if we call a kernel function in filter function, and this kernel function
>> run into filter again, we can't prevent this kind of nesting by rcu.
>> Ltt have a ltt_nesting to prevent this kind of nesting, but it can't help
>> filter function.
>
> You are right in that the filter function is called before reserving
> space, and therefore the ltt_nesting value is not tested. So you should
> simply test the already existing ltt_nesting value at the beginning of
> the filter function, you don't need another nesting variable. You can
> look at the ltt_reserve_slot function to see how to test this.
Good idea, i think.
>
>>
>> Maybe we can call no kernel function in filter() to prevent this kind of
>> nesting, and use rcu to allow filter modification when filter() function is
>> running, and I aggre to using rcu, but sorry that I am some busy these days,
>> it is not easy to get sometime to finish this change.
>
> I doubt typical use-cases would include filter modification while the
> trace session is running. I would aim for the simpler goal : filter
> modifications should only be allowed when a filter is not attached to an
> active trace session. This way we don't have to use more complex
> RCU-aware data structures, which could be a real pain for filters.
Yes, simple method is always recommend.
>
>>
>> Current code is ok to work, and it passed my test with a my testset.
>> (I can send it to you if you need)
>
> I would really prefer to have a solution that does not add supplementary
> custom locking into the tracer. Supporting filter modification when
> sessions are inactive seems like a perfect middle-ground between
> implementation complexity (it's simple) and not modifying the tracer
> locking semantics.
>
>>
>> >
>> > The filter_lock spinlock should probably become a simple mutex too.
>> I attempted to use mutex for filter_lock, but always cause panic in my testset.
>
> If I may suggest :
>
> Let's remove the custom locking, use the trace_lock mutex to protect
> filter modifications, disallow filter modification if it belongs to an
> active trace, and also wait for a quiescent state with
> synchronize_sched() after we see that a filter belongs to an inactive
> trace (while we hold the trace_lock), so that we can make sure that no
> trace probes are seeing this filter while we modify it.
This is a good idea to avoid filter modifications when filter is in operation,
and it is more simple than i did.
But IMHO, it can't resolve problem that markers changed when filter is in
operation.
We must modufy data struct when add/remove marker, and we can't ignore this
operation as we do for fileop.
However, I still like your method, because it is simple and unify.
I think we can find a way to solve marker's problem based on your idea,
welcome to fix this patch to avoid custom locking and complex pre_cpu nesting lock.
Thanks
Zhaolei
>
> Thanks,
>
> Mathieu
>
>>
>> Thanks!
>> Zhaolei
>> >
>> > Mathieu
>> >
>> >> +
>> >> +struct marker_filter *_find_marker_filter(const char *name)
>> >> +{
>> >> + struct marker_filter *filter;
>> >> + list_for_each_entry(filter, &marker_filter_head, list) {
>> >> + if (!strcmp(name, filter->format->name))
>> >> + return filter;
>> >> + }
>> >> + return NULL;
>> >> +}
>> >> +
>> >> +static ssize_t format_read(struct file *filp, char __user *ubuf, size_t cnt,
>> >> + loff_t *ppos)
>> >> +{
>> >> + size_t ret;
>> >> + struct marker_filter *filter;
>> >> +
>> >> + lock_filter();
>> >> + spin_lock(&filter_lock);
>> >> + filter = filp->f_dentry->d_inode->i_private;
>> >> + if (filter) {
>> >> + ret = simple_read_from_buffer(ubuf, cnt, ppos,
>> >> + filter->format->format, strlen(filter->format->format));
>> >> + } else {
>> >> + ret = -EIO;
>> >> + }
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> +
>> >> + return ret;
>> >> +}
>> >> +
>> >> +static const struct file_operations format_fops = {
>> >> + .read = format_read,
>> >> +};
>> >> +
>> >> +static int signed_type_read(char *buf, size_t bufsize,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int ret;
>> >> + int pos = 0;
>> >> +
>> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> >> + compare_op_string[filter_item->setting.signed_type.op]);
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + ret = scnprintf(buf + pos, bufsize - pos, " %lld",
>> >> + filter_item->setting.signed_type.value);
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + return pos;
>> >> +}
>> >> +
>> >> +static int unsigned_type_read(char *buf, size_t bufsize,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int ret;
>> >> + int pos = 0;
>> >> +
>> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> >> + compare_op_string[filter_item->setting.unsigned_type.op]);
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + ret = scnprintf(buf + pos, bufsize - pos, " %llu",
>> >> + filter_item->setting.unsigned_type.value);
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + return pos;
>> >> +}
>> >> +
>> >> +static int string_type_read(char *buf, size_t bufsize,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int ret;
>> >> + int pos = 0;
>> >> +
>> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> >> + compare_op_string[filter_item->setting.string_type.op]);
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + ret = scnprintf(buf + pos, bufsize - pos, " %s",
>> >> + filter_item->setting.string_type.value);
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + return pos;
>> >> +}
>> >> +
>> >> +static int char_type_read(char *buf, size_t bufsize,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int ret;
>> >> + int pos = 0;
>> >> +
>> >> + ret = scnprintf(buf + pos, bufsize - pos, "%s",
>> >> + compare_op_string[filter_item->setting.char_type.op]);
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + switch (filter_item->setting.char_type.value) {
>> >> + case '\t':
>> >> + ret = scnprintf(buf + pos, bufsize - pos, " \\t");
>> >> + break;
>> >> + case ' ':
>> >> + ret = scnprintf(buf + pos, bufsize - pos, " blank");
>> >> + break;
>> >> + case '\n':
>> >> + ret = scnprintf(buf + pos, bufsize - pos, " \\n");
>> >> + break;
>> >> + default:
>> >> + if (isgraph(filter_item->setting.char_type.value)) {
>> >> + ret = scnprintf(buf + pos, bufsize - pos, " %c",
>> >> + filter_item->setting.char_type.value);
>> >> + } else {
>> >> + ret = scnprintf(buf + pos, bufsize - pos,
>> >> + " \\%u", (unsigned int)
>> >> + filter_item->setting.char_type.value);
>> >> + }
>> >> + break;
>> >> + }
>> >> + if (!ret)
>> >> + return 0;
>> >> + pos += ret;
>> >> +
>> >> + return pos;
>> >> +}
>> >> +
>> >> +static ssize_t type_read(struct file *filp, char __user *ubuf, size_t cnt,
>> >> + loff_t *ppos)
>> >> +{
>> >> + size_t ret;
>> >> + struct filter_item *filter_item;
>> >> +
>> >> + lock_filter();
>> >> + spin_lock(&filter_lock);
>> >> + filter_item = filp->f_dentry->d_inode->i_private;
>> >> + if (filter_item) {
>> >> + struct format_item *format_item;
>> >> + char buf[128];
>> >> + int pos = 0;
>> >> +
>> >> + format_item = filter_item_format(filter_item);
>> >> +
>> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos,
>> >> + "type: %u byte(s) %s\n",
>> >> + format_item->size,
>> >> + format_item_type_string[format_item->type]);
>> >> + if (!ret) {
>> >> + ret = -EINVAL;
>> >> + goto err_format_string;
>> >> + }
>> >> + pos += ret;
>> >> +
>> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "setting: ");
>> >> + if (!ret) {
>> >> + ret = -EINVAL;
>> >> + goto err_format_string;
>> >> + }
>> >> + pos += ret;
>> >> +
>> >> + if (!filter_item->enable) {
>> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "*");
>> >> + if (!ret) {
>> >> + ret = -EINVAL;
>> >> + goto err_format_string;
>> >> + }
>> >> + pos += ret;
>> >> + goto format_finish;
>> >> + }
>> >> +
>> >> + switch (format_item->type) {
>> >> + case SIGNED:
>> >> + ret = signed_type_read(buf + pos, sizeof(buf) - pos,
>> >> + filter_item);
>> >> + break;
>> >> + case UNSIGNED:
>> >> + ret = unsigned_type_read(buf + pos, sizeof(buf) - pos,
>> >> + filter_item);
>> >> + break;
>> >> + case STRING:
>> >> + ret = string_type_read(buf + pos, sizeof(buf) - pos,
>> >> + filter_item);
>> >> + break;
>> >> + case CHAR:
>> >> + ret = char_type_read(buf + pos, sizeof(buf) - pos,
>> >> + filter_item);
>> >> + break;
>> >> + default:
>> >> + /* should not run to here */
>> >> + printk(KERN_ERR "%s: unknown type %d\n",
>> >> + __func__, format_item->type);
>> >> + ret = -EIO;
>> >> + goto err_unknown_type;
>> >> + }
>> >> + if (!ret) {
>> >> + ret = -EINVAL;
>> >> + goto err_format_string;
>> >> + }
>> >> + pos += ret;
>> >> +
>> >> +format_finish:
>> >> + ret = scnprintf(buf + pos, sizeof(buf) - pos, "\n");
>> >> + if (!ret) {
>> >> + ret = -EINVAL;
>> >> + goto err_format_string;
>> >> + }
>> >> + pos += ret;
>> >> +
>> >> + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf,
>> >> + strlen(buf));
>> >> + } else {
>> >> + ret = -EIO;
>> >> + }
>> >> +
>> >> +err_format_string:
>> >> +err_unknown_type:
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> +
>> >> + return ret;
>> >> +}
>> >> +
>> >> +/*
>> >> + * Check op info in a input string
>> >> + * return length of op part on success, *op is set.
>> >> + * return 0 on fail
>> >> + */
>> >> +static size_t scan_op(const char *str, enum compare_op *op)
>> >> +{
>> >> + /* Must reverse sort with strlen */
>> >> + static struct {
>> >> + const char *str;
>> >> + enum compare_op op;
>> >> + } str_op[] = {
>> >> + {"gt", gt},
>> >> + {"ge", ge},
>> >> + {"eq", eq},
>> >> + {"le", le},
>> >> + {"lt", lt},
>> >> + {">=", ge},
>> >> + {"<=", le},
>> >> + {">", gt},
>> >> + {"=", eq},
>> >> + {"<", lt},
>> >> + };
>> >> + int i;
>> >> +
>> >> + for (i = 0; i < ARRAY_SIZE(str_op); i++) {
>> >> + if (!strncmp(str, str_op[i].str, strlen(str_op[i].str))) {
>> >> + *op = str_op[i].op;
>> >> + return strlen(str_op[i].str);
>> >> + }
>> >> + }
>> >> +
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static int signed_type_write(const char *buf, struct format_item *format_item,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int oplen;
>> >> + enum compare_op op;
>> >> + long long value = 0;
>> >> +
>> >> + oplen = scan_op(buf, &op);
>> >> + if (!oplen)
>> >> + return -EINVAL;
>> >> +
>> >> + switch (format_item->size) {
>> >> + case 1:
>> >> + {
>> >> + char tmp;
>> >> + if (sscanf(buf + oplen, "%hhd", &tmp) != 1)
>> >> + return -EINVAL;
>> >> + value = tmp;
>> >> + }
>> >> + break;
>> >> + case 2:
>> >> + {
>> >> + short int tmp;
>> >> + if (sscanf(buf + oplen, "%hd", &tmp) != 1)
>> >> + return -EINVAL;
>> >> + value = tmp;
>> >> + }
>> >> + break;
>> >> + case 4:
>> >> + {
>> >> + int tmp;
>> >> + if (sscanf(buf + oplen, "%d", &tmp) != 1)
>> >> + return -EINVAL;
>> >> + value = tmp;
>> >> + }
>> >> + break;
>> >> + case 8:
>> >> + if (sscanf(buf + oplen, "%lld", &value) != 1)
>> >> + return -EINVAL;
>> >> + break;
>> >> + default:
>> >> + /* should not run to here */
>> >> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
>> >> + format_item->size);
>> >> + return -EIO;
>> >> + }
>> >> +
>> >> + filter_item->setting.signed_type.op = op;
>> >> + filter_item->setting.signed_type.value = value;
>> >> +
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static int unsigned_type_write(const char *buf, struct format_item *format_item,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int oplen;
>> >> + enum compare_op op;
>> >> + long long value = 0;
>> >> +
>> >> + oplen = scan_op(buf, &op);
>> >> + if (!oplen)
>> >> + return -EINVAL;
>> >> +
>> >> + switch (format_item->size) {
>> >> + case 1:
>> >> + {
>> >> + unsigned char tmp;
>> >> + if (sscanf(buf + oplen, "%hhu", &tmp) != 1)
>> >> + return -EINVAL;
>> >> + value = tmp;
>> >> + }
>> >> + break;
>> >> + case 2:
>> >> + {
>> >> + unsigned short int tmp;
>> >> + if (sscanf(buf + oplen, "%hu", &tmp) != 1)
>> >> + return -EINVAL;
>> >> + value = tmp;
>> >> + }
>> >> + break;
>> >> + case 4:
>> >> + {
>> >> + unsigned int tmp;
>> >> + if (sscanf(buf + oplen, "%u", &tmp) != 1)
>> >> + return -EINVAL;
>> >> + value = tmp;
>> >> + }
>> >> + break;
>> >> + case 8:
>> >> + if (sscanf(buf + oplen, "%llu", &value) != 1)
>> >> + return -EINVAL;
>> >> + break;
>> >> + default:
>> >> + /* should not run to here */
>> >> + printk(KERN_ERR "%s: unknown size %zu\n", __func__,
>> >> + format_item->size);
>> >> + return -EIO;
>> >> + }
>> >> +
>> >> + filter_item->setting.unsigned_type.op = op;
>> >> + filter_item->setting.unsigned_type.value = value;
>> >> +
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static int string_type_write(const char *buf, struct format_item *format_item,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int oplen;
>> >> + enum compare_op op;
>> >> + int val_len;
>> >> +
>> >> + oplen = scan_op(buf, &op);
>> >> + if (!oplen)
>> >> + return -EINVAL;
>> >> +
>> >> + /* End string on \n */
>> >> + for (val_len = 0; *(buf + oplen + val_len); val_len++) {
>> >> + if (*(buf + oplen + val_len) == '\n')
>> >> + break;
>> >> + }
>> >> +
>> >> + if (val_len > ARRAY_SIZE(filter_item->setting.string_type.value) - 1)
>> >> + return -EINVAL;
>> >> +
>> >> + filter_item->setting.string_type.op = op;
>> >> + memcpy(filter_item->setting.string_type.value, buf + oplen, val_len);
>> >> + filter_item->setting.string_type.value[val_len] = 0;
>> >> +
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static int char_type_write(const char *buf, struct format_item *format_item,
>> >> + struct filter_item *filter_item)
>> >> +{
>> >> + int oplen;
>> >> + enum compare_op op;
>> >> + unsigned char value;
>> >> + int val_start;
>> >> +
>> >> + oplen = scan_op(buf, &op);
>> >> + if (!oplen)
>> >> + return -EINVAL;
>> >> +
>> >> + /* bypass blanks */
>> >> + for (val_start = 0; *(buf + oplen + val_start) == ' ' ||
>> >> + *(buf + oplen + val_start) == '\t'; val_start++)
>> >> + ;
>> >> +
>> >> + /*
>> >> + * parse escape string, following escape string are supported:
>> >> + * "\n"
>> >> + * "\[num]"
>> >> + * "\[char expect \n and \[num]]", as "\\", "\ ", ...
>> >> + */
>> >> + switch (*(buf + oplen + val_start)) {
>> >> + case '\0':
>> >> + case '\n':
>> >> + return -EINVAL;
>> >> + case '\\':
>> >> + switch (*(buf + oplen + val_start + 1)) {
>> >> + case '\0':
>> >> + case '\n':
>> >> + return -EINVAL;
>> >> + case 'n':
>> >> + value = '\n';
>> >> + break;
>> >> + default:
>> >> + if (isdigit(*(buf + oplen + val_start + 1))) {
>> >> + if (sscanf(buf + oplen + val_start + 1, "%hhu",
>> >> + &value) != 1)
>> >> + return -EINVAL;
>> >> + } else {
>> >> + value =
>> >> + (*(buf + oplen + val_start + 1));
>> >> + }
>> >> + break;
>> >> + }
>> >> + break;
>> >> + default:
>> >> + value = *(buf + oplen + val_start);
>> >> + break;
>> >> + }
>> >> +
>> >> + filter_item->setting.char_type.op = op;
>> >> + filter_item->setting.char_type.value = value;
>> >> +
>> >> + return 0;
>> >> +}
>> >> +
>> >> +static ssize_t type_write(struct file *filp, const char __user *ubuf,
>> >> + size_t cnt, loff_t *ppos)
>> >> +{
>> >> + size_t ret;
>> >> + struct filter_item *filter_item;
>> >> +
>> >> + lock_filter();
>> >> + spin_lock(&filter_lock);
>> >> + filter_item = filp->f_dentry->d_inode->i_private;
>> >> + if (filter_item) {
>> >> + struct format_item *format_item;
>> >> + char buf[NAME_MAX];
>> >> + size_t buf_size;
>> >> + int val_start;
>> >> +
>> >> + format_item = filter_item_format(filter_item);
>> >> +
>> >> + buf_size = min(cnt, sizeof(buf) - 1);
>> >> + if (copy_from_user(buf, ubuf, buf_size)) {
>> >> + ret = -EFAULT;
>> >> + goto err_copy_from_user;
>> >> + }
>> >> + buf[buf_size] = 0;
>> >> +
>> >> + /* Check is to disable filter */
>> >> + for (val_start = 0; *(buf + val_start) == ' ' ||
>> >> + *(buf + val_start) == '\t'; val_start++)
>> >> + ;
>> >> + if (!strncmp(buf + val_start, "*", strlen("*"))
>> >> + || !strncmp(buf + val_start, "off", strlen("off"))) {
>> >> + struct marker_filter *filter;
>> >> + int i;
>> >> +
>> >> + filter_item->enable = 0;
>> >> +
>> >> + /*
>> >> + * rescan all items in this marker to
>> >> + * update marker_filter->enable
>> >> + */
>> >> + filter = filter_item->parent;
>> >> + filter->enable = 0;
>> >> + for (i = 0; i < filter->format->format_item_cnt; i++) {
>> >> + if (filter->items[i].enable) {
>> >> + filter->enable = 1;
>> >> + break;
>> >> + }
>> >> + }
>> >> +
>> >> + goto scan_finish;
>> >> + }
>> >> +
>> >> + switch (format_item->type) {
>> >> + case SIGNED:
>> >> + ret = signed_type_write(buf, format_item, filter_item);
>> >> + break;
>> >> + case UNSIGNED:
>> >> + ret = unsigned_type_write(buf, format_item,
>> >> + filter_item);
>> >> + break;
>> >> + case STRING:
>> >> + ret = string_type_write(buf, format_item, filter_item);
>> >> + break;
>> >> + case CHAR:
>> >> + ret = char_type_write(buf, format_item, filter_item);
>> >> + break;
>> >> + default:
>> >> + /* should not run to here */
>> >> + printk(KERN_ERR "%s: unknown type %d\n",
>> >> + __func__, format_item->type);
>> >> + ret = -EIO;
>> >> + goto err_unknown_type;
>> >> + }
>> >> + if (IS_ERR_VALUE(ret))
>> >> + goto err_scan_string;
>> >> +
>> >> + filter_item->enable = 1;
>> >> + filter_item->parent->enable = 1;
>> >> +
>> >> +scan_finish:
>> >> + ret = cnt;
>> >> + } else {
>> >> + ret = -EIO;
>> >> + }
>> >> +
>> >> +err_scan_string:
>> >> +err_unknown_type:
>> >> +err_copy_from_user:
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> +
>> >> + return ret;
>> >> +}
>> >> +
>> >> +static const struct file_operations type_fops = {
>> >> + .read = type_read,
>> >> + .write = type_write,
>> >> +};
>> >> +
>> >> +static int init_filter_items(struct marker_filter *filter,
>> >> + struct marker_format *format)
>> >> +{
>> >> + int err = 0;
>> >> + int i;
>> >> +
>> >> + for (i = 0; i < format->format_item_cnt; i++) {
>> >> + char fname[16];
>> >> +
>> >> + filter->items[i].parent = filter;
>> >> + filter->items[i].enable = 0;
>> >> +
>> >> + /* Create type file as __arg1, __arg2 */
>> >> + sprintf(fname, "__arg%d", i + 1);
>> >> + filter->items[i].den[0] = debugfs_create_file(fname, S_IRUGO,
>> >> + filter->rootden, &filter->items[i], &type_fops);
>> >> + if (IS_ERR(filter->items[i].den[0])
>> >> + || !filter->items[i].den[0]) {
>> >> + printk(KERN_ERR
>> >> + "%s: create type file of %s for %s failed\n",
>> >> + __func__, fname, filter->format->name);
>> >> + err = -EIO;
>> >> + goto err_build_typefile;
>> >> + }
>> >> +
>> >> + /* Create symbol link to type file */
>> >> + if (filter->format->items[i]->name[0]) {
>> >> +
>> >> + filter->items[i].den[1] = debugfs_create_symlink(
>> >> + filter->format->items[i]->name,
>> >> + filter->rootden, fname);
>> >> + if (IS_ERR(filter->items[i].den[1])
>> >> + || !filter->items[i].den[1]) {
>> >> + /*
>> >> + * same-name type is allowed, but it will cause
>> >> + * create symlink fail.
>> >> + * we ignore this type of error.
>> >> + */
>> >> + filter->items[i].den[1] = NULL;
>> >> + }
>> >> + }
>> >> + }
>> >> +
>> >> + return 0;
>> >> +
>> >> +err_build_typefile:
>> >> + for (i--; i >= 0; i--) {
>> >> + /* see comments in _destroy_marker_filter() for why set NULL */
>> >> + filter->items[i].den[0]->d_inode->i_private = NULL;
>> >> + debugfs_remove(filter->items[i].den[0]);
>> >> + debugfs_remove(filter->items[i].den[1]);
>> >> + }
>> >> + return err;
>> >> +}
>> >> +
>> >> +static int add_marker_filter(struct marker_format *format)
>> >> +{
>> >> + int err = 0;
>> >> + struct marker_filter *filter;
>> >> +
>> >> + lock_filter();
>> >> + spin_lock(&filter_lock);
>> >> +
>> >> +#ifdef _DEBUG
>> >> + if (_find_marker_filter(format->name)) {
>> >> + printk(KERN_ERR "%s: filter %s already exist\n", __func__,
>> >> + format->name);
>> >> + err = -EIO;
>> >> + goto err_already_exist;
>> >> + }
>> >> +#endif /* _DEBUG */
>> >> +
>> >> + filter = kmalloc(sizeof(struct marker_filter), GFP_KERNEL);
>> >> + if (!filter) {
>> >> + printk(KERN_ERR "%s: alloc struct marker_filter failed\n",
>> >> + __func__);
>> >> + err = -ENOMEM;
>> >> + goto err_alloc_marker_filter;
>> >> + }
>> >> +
>> >> + INIT_LIST_HEAD(&filter->list);
>> >> +
>> >> + filter->format = format;
>> >> +
>> >> + filter->rootden = debugfs_create_dir(format->name, ltt_filter_dir);
>> >> + if (IS_ERR(filter->rootden) || !filter->rootden) {
>> >> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
>> >> + format->name);
>> >> + err = -EIO;
>> >> + goto err_create_dir;
>> >> + }
>> >> +
>> >> + filter->formatden = debugfs_create_file("__arg0", S_IRUGO,
>> >> + filter->rootden, filter, &format_fops);
>> >> + if (IS_ERR(filter->formatden) || !filter->formatden) {
>> >> + printk(KERN_ERR
>> >> + "%s: create typefile failed\n", __func__);
>> >> + err = -EIO;
>> >> + goto err_build_typefile;
>> >> + }
>> >> +
>> >> + if (format->format_item_cnt) {
>> >> + filter->items = kmalloc(sizeof(struct filter_item)
>> >> + * format->format_item_cnt, GFP_KERNEL);
>> >> + if (!filter->items) {
>> >> + printk(KERN_ERR
>> >> + "%s: alloc filter's items failed\n", __func__);
>> >> + err = -ENOMEM;
>> >> + goto err_alloc_types;
>> >> + }
>> >> + err = init_filter_items(filter, format);
>> >> + if (IS_ERR_VALUE(err)) {
>> >> + printk(KERN_ERR
>> >> + "%s: init filter's items failed\n", __func__);
>> >> + goto err_init_types;
>> >> + }
>> >> + } else {
>> >> + filter->items = NULL;
>> >> + }
>> >> +
>> >> + filter->enable = 0;
>> >> +
>> >> + list_add(&filter->list, &marker_filter_head);
>> >> +
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> +
>> >> + return 0;
>> >> +
>> >> +err_init_types:
>> >> + kfree(filter->items);
>> >> +err_alloc_types:
>> >> + /* see comments in _destroy_marker_filter() for why set NULL */
>> >> + filter->formatden->d_inode->i_private = NULL;
>> >> + debugfs_remove(filter->formatden);
>> >> +err_build_typefile:
>> >> + debugfs_remove(filter->rootden);
>> >> +err_create_dir:
>> >> + kfree(filter);
>> >> +err_alloc_marker_filter:
>> >> +#ifdef _DEBUG
>> >> +err_already_exist:
>> >> +#endif /* _DEBUG */
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> + return err;
>> >> +}
>> >> +
>> >> +static void _destroy_marker_filter(struct marker_filter *filter)
>> >> +{
>> >> + int i;
>> >> +
>> >> + list_del(&filter->list);
>> >> +
>> >> + for (i = 0; i < filter->format->format_item_cnt; i++) {
>> >> + filter->items[i].den[0]->d_inode->i_private = NULL;
>> >> + debugfs_remove(filter->items[i].den[0]);
>> >> + debugfs_remove(filter->items[i].den[1]);
>> >> + }
>> >> + kfree(filter->items);
>> >> +
>> >> + /*
>> >> + * Set i_private to NULL, so that if some fileop in process,
>> >> + * it can avoid illegal access using i_private in fileop.
>> >> + * (we must get filter_lock in fileop to ensure no other change
>> >> + * in filter struct)
>> >> + */
>> >> + filter->formatden->d_inode->i_private = NULL;
>> >> + debugfs_remove(filter->formatden);
>> >> +
>> >> + debugfs_remove(filter->rootden);
>> >> +
>> >> + kfree(filter);
>> >> +
>> >> + return;
>> >> +}
>> >> +
>> >> +static void destroy_all_filter(void)
>> >> +{
>> >> + struct marker_filter *filter, *next;
>> >> +
>> >> + lock_filter();
>> >> + spin_lock(&filter_lock);
>> >> + list_for_each_entry_safe(filter, next, &marker_filter_head, list) {
>> >> + _destroy_marker_filter(filter);
>> >> + }
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> +}
>> >> +
>> >> +static int bulid_exist_filter(void)
>> >> +{
>> >> + int err;
>> >> + struct marker_format *format;
>> >> +
>> >> + if (!list_empty(&marker_filter_head)) {
>> >> + err = -EEXIST;
>> >> + goto err_not_empty;
>> >> + }
>> >> +
>> >> + mutex_lock(&format_lock);
>> >> + list_for_each_entry(format, &marker_format_head, list) {
>> >> + err = add_marker_filter(format);
>> >> + if (IS_ERR_VALUE(err)) {
>> >> + printk("%s: add_marker_filter failed\n", __func__);
>> >> + goto err_add_filter;
>> >> + }
>> >> + }
>> >> + mutex_unlock(&format_lock);
>> >> +
>> >> + return 0;
>> >> +
>> >> +err_add_filter:
>> >> + destroy_all_filter();
>> >> + mutex_unlock(&format_lock);
>> >> +err_not_empty:
>> >> + return err;
>> >> +}
>> >> +
>> >> +static int format_notify(struct notifier_block *self, unsigned long val,
>> >> + void *data)
>> >> +{
>> >> + struct marker_format *format = data;
>> >> + struct marker_filter *filter, *next;
>> >> +
>> >> + switch (val) {
>> >> + case ADD_FORMAT:
>> >> + add_marker_filter(format);
>> >> + break;
>> >> + case DEL_FORMAT:
>> >> + lock_filter();
>> >> + spin_lock(&filter_lock);
>> >> + list_for_each_entry_safe(filter, next, &marker_filter_head,
>> >> + list) {
>> >> + if (filter->format == format)
>> >> + _destroy_marker_filter(filter);
>> >> + }
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> + break;
>> >> + }
>> >> + return NOTIFY_OK;
>> >> +}
>> >> +
>> >> +static struct notifier_block format_nb = {
>> >> + .notifier_call = format_notify,
>> >> +};
>> >> +
>> >> +static int marker_filter_init(void)
>> >> +{
>> >> + int err = 0;
>> >> +
>> >> + err = bulid_exist_filter();
>> >> + if (IS_ERR_VALUE(err)) {
>> >> + printk(KERN_ERR "%s: bulid_exist_filter failed\n", __func__);
>> >> + goto err_add_exist_filter;
>> >> + }
>> >> +
>> >> + err = register_format_notifier(&format_nb);
>> >> + if (IS_ERR_VALUE(err)) {
>> >> + printk(KERN_ERR "%s register format notifier failed\n",
>> >> + __func__);
>> >> + goto err_register_format_notifier;
>> >> + }
>> >> +
>> >> + return 0;
>> >> +
>> >> +err_register_format_notifier:
>> >> + destroy_all_filter();
>> >> +err_add_exist_filter:
>> >> + return err;
>> >> +}
>> >> +
>> >> +static void marker_filter_destroy(void)
>> >> +{
>> >> + unregister_format_notifier(&format_nb);
>> >> + destroy_all_filter();
>> >> +}
>> >> +
>> >> +
>> >> +/*
>> >> + * Filter operations
>> >> + *
>> >> + * define filter callback for lttng to determine if to output a event
>> >> + * by user's setting.
>> >> + *
>> >> + */
>> >> +
>> >> +/*
>> >> + * Ret:
>> >> + * 0: filted
>> >> + * 1: passed
>> >> + * <0: err
>> >> + */
>> >> +static int signed_filter(struct format_item *format_item,
>> >> + struct filter_item *filter_item, va_list *args)
>> >> +{
>> >> + long long int val;
>> >> +
>> >> + switch (format_item->size) {
>> >> + case 1:
>> >> + val = (char)va_arg(*args, int);
>> >> + break;
>> >> + case 2:
>> >> + val = (short int)va_arg(*args, int);
>> >> + break;
>> >> + case 4:
>> >> + val = va_arg(*args, int);
>> >> + break;
>> >> + case 8:
>> >> + val = va_arg(*args, long long int);
>> >> + break;
>> >> + default:
>> >> + /* should not run to here */
>> >> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
>> >> + format_item->size);
>> >> + return -1;
>> >> + }
>> >> +
>> >> + /*
>> >> + * We judge filter_item->enable here, because even if filter is not
>> >> + * enabled, we still need to bypass this data in va_list
>> >> + */
>> >> + if (!filter_item->enable)
>> >> + return 1;
>> >> +
>> >> + switch (filter_item->setting.signed_type.op) {
>> >> + case gt:
>> >> + return val > filter_item->setting.signed_type.value;
>> >> + case ge:
>> >> + return val >= filter_item->setting.signed_type.value;
>> >> + case eq:
>> >> + return val == filter_item->setting.signed_type.value;
>> >> + case le:
>> >> + return val <= filter_item->setting.signed_type.value;
>> >> + case lt:
>> >> + return val < filter_item->setting.signed_type.value;
>> >> + }
>> >> +
>> >> + /* should not run to here */
>> >> + return -1;
>> >> +}
>> >> +
>> >> +static int unsigned_filter(struct format_item *format_item,
>> >> + struct filter_item *filter_item, va_list *args)
>> >> +{
>> >> + unsigned long long int val;
>> >> +
>> >> + switch (format_item->size) {
>> >> + case 1:
>> >> + val = (unsigned char)va_arg(*args, unsigned int);
>> >> + break;
>> >> + case 2:
>> >> + val = (short int)va_arg(*args, unsigned int);
>> >> + break;
>> >> + case 4:
>> >> + val = va_arg(*args, unsigned int);
>> >> + break;
>> >> + case 8:
>> >> + val = va_arg(*args, unsigned long long int);
>> >> + break;
>> >> + default:
>> >> + /* should not run to here */
>> >> + printk(KERN_ERR "%s: unknown size %d\n", __func__,
>> >> + format_item->size);
>> >> + return -1;
>> >> + }
>> >> +
>> >> + /*
>> >> + * We judge filter_item->enable here, because even if filter is not
>> >> + * enabled, we still need to bypass this data in va_list
>> >> + */
>> >> + if (!filter_item->enable)
>> >> + return 1;
>> >> +
>> >> + switch (filter_item->setting.unsigned_type.op) {
>> >> + case gt:
>> >> + return val > filter_item->setting.unsigned_type.value;
>> >> + case ge:
>> >> + return val >= filter_item->setting.unsigned_type.value;
>> >> + case eq:
>> >> + return val == filter_item->setting.unsigned_type.value;
>> >> + case le:
>> >> + return val <= filter_item->setting.unsigned_type.value;
>> >> + case lt:
>> >> + return val < filter_item->setting.unsigned_type.value;
>> >> + }
>> >> +
>> >> + /* should not run to here */
>> >> + return -1;
>> >> +}
>> >> +
>> >> +static int string_filter(struct format_item *format_item,
>> >> + struct filter_item *filter_item, va_list *args)
>> >> +{
>> >> + const char *val;
>> >> +
>> >> + val = va_arg(*args, const char *);
>> >> +
>> >> + /*
>> >> + * We judge filter_item->enable here, because even if filter is not
>> >> + * enabled, we still need to bypass this data in va_list
>> >> + */
>> >> + if (!filter_item->enable)
>> >> + return 1;
>> >> +
>> >> + switch (filter_item->setting.string_type.op) {
>> >> + case gt:
>> >> + return strcmp(val, filter_item->setting.string_type.value) > 0;
>> >> + case ge:
>> >> + return strcmp(val, filter_item->setting.string_type.value) >= 0;
>> >> + case eq:
>> >> + return strcmp(val, filter_item->setting.string_type.value) == 0;
>> >> + case le:
>> >> + return strcmp(val, filter_item->setting.string_type.value) <= 0;
>> >> + case lt:
>> >> + return strcmp(val, filter_item->setting.string_type.value) < 0;
>> >> + }
>> >> +
>> >> + /* should not run to here */
>> >> + return -1;
>> >> +}
>> >> +
>> >> +static int char_filter(struct format_item *format_item,
>> >> + struct filter_item *filter_item, va_list *args)
>> >> +{
>> >> + unsigned char val;
>> >> +
>> >> + val = (unsigned char)va_arg(*args, unsigned int);
>> >> +
>> >> + /*
>> >> + * We judge filter_item->enable here, because even if filter is not
>> >> + * enabled, we still need to bypass this data in va_list
>> >> + */
>> >> + if (!filter_item->enable)
>> >> + return 1;
>> >> +
>> >> + switch (filter_item->setting.char_type.op) {
>> >> + case gt:
>> >> + return val > filter_item->setting.char_type.value;
>> >> + case ge:
>> >> + return val >= filter_item->setting.char_type.value;
>> >> + case eq:
>> >> + return val == filter_item->setting.char_type.value;
>> >> + case le:
>> >> + return val <= filter_item->setting.char_type.value;
>> >> + case lt:
>> >> + return val < filter_item->setting.char_type.value;
>> >> + }
>> >> +
>> >> + /* should not run to here */
>> >> + return -1;
>> >> +}
>> >> +
>> >> +static int ltt_filter(struct ltt_trace_struct *trace,
>> >> + const struct marker *mdata, va_list *args)
>> >> +{
>> >> + int ret;
>> >> + struct marker_filter *filter;
>> >> + struct marker_format *format;
>> >> + int i;
>> >> +
>> >> + if (lock_filter()) {
>> >
>> > Hrm do you allow session filter modification when tracing is active for
>> > this session ?
>> >
>> > I think the lock_filter approach you are taking here is wrong.
>> >
>> >
>> >> + /* Don't output events caused by ltt-filter */
>> >> + return 0;
>> >> + }
>> >> +
>> >> + spin_lock(&filter_lock);
>> >> +
>> >> + filter = _find_marker_filter(mdata->name);
>> >> + if (!filter) {
>> >> + printk(KERN_ERR "%s: unknown marker %s\n", __func__,
>> >> + mdata->name);
>> >> + ret = 1;
>> >> + goto done;
>> >> + }
>> >> +
>> >> + /* Bypass if no filter setting on this marker */
>> >> + if (!filter->enable) {
>> >> + ret = 1;
>> >> + goto done;
>> >> + }
>> >> +
>> >> + format = filter->format;
>> >> +
>> >> + for (i = 0; i < format->format_item_cnt; i++) {
>> >> + int result;
>> >> +
>> >> + switch (format->items[i]->type) {
>> >> + case SIGNED:
>> >> + result = signed_filter(format->items[i],
>> >> + filter->items + i, args);
>> >> + break;
>> >> + case UNSIGNED:
>> >> + result = unsigned_filter(format->items[i],
>> >> + filter->items + i, args);
>> >> + break;
>> >> + case STRING:
>> >> + result = string_filter(format->items[i],
>> >> + filter->items + i, args);
>> >> + break;
>> >> + case CHAR:
>> >> + result = char_filter(format->items[i],
>> >> + filter->items + i, args);
>> >> + break;
>> >> + default:
>> >> + /* should not run to here */
>> >> + printk(KERN_ERR "%s: unknown type %d\n", __func__,
>> >> + format->items[i]->type);
>> >> + ret = 1;
>> >> + goto done;
>> >> + }
>> >> + if (result < 0) {
>> >> + ret = 1;
>> >> + goto done;
>> >> + }
>> >> + if (!result) {
>> >> + ret = 0;
>> >> + goto done;
>> >> + }
>> >> + }
>> >> +
>> >> + ret = 1;
>> >> +
>> >> +done:
>> >> + spin_unlock(&filter_lock);
>> >> + unlock_filter();
>> >> + return ret;
>> >> +}
>> >> +
>> >> +
>> >> +/*
>> >> + * Init/exit option of module
>> >> + *
>> >> + * Init marker_format and marker_filter operations
>> >> + * register filter callback in ltt
>> >> + *
>> >> + */
>> >> +
>> >> +static int __init ltt_filter_init(void)
>> >> +{
>> >> + int err = 0;
>> >> + struct dentry *ltt_root_dentry;
>> >> +
>> >> + ltt_root_dentry = get_ltt_root();
>> >> + if (!ltt_root_dentry) {
>> >> + err = -ENOENT;
>> >> + goto err_no_root;
>> >> + }
>> >> +
>> >> + ltt_filter_dir = debugfs_create_dir(LTT_FILTER_DIR, ltt_root_dentry);
>> >> + if (IS_ERR(ltt_filter_dir) || !ltt_filter_dir) {
>> >> + printk(KERN_ERR "%s: create dir of %s failed\n", __func__,
>> >> + LTT_FILTER_DIR);
>> >> + err = -ENOMEM;
>> >> + goto err_create_filter_dir;
>> >> + }
>> >> +
>> >> + err = marker_format_init();
>> >> + if (IS_ERR_VALUE(err)) {
>> >> + printk(KERN_ERR "%s: init marker format failed\n", __func__);
>> >> + goto err_marker_format_init;
>> >> + }
>> >> +
>> >> + err = marker_filter_init();
>> >> + if (IS_ERR_VALUE(err)) {
>> >> + printk(KERN_ERR "%s: init marker filter failed\n", __func__);
>> >> + goto err_marker_filter_init;
>> >> + }
>> >> +
>> >> + err = ltt_module_register(LTT_FUNCTION_RUN_FILTER, ltt_filter,
>> >> + THIS_MODULE);
>> >> + if (IS_ERR_VALUE(err)) {
>> >> + printk(KERN_ERR "%s: register ltt filter failed\n", __func__);
>> >> + goto err_filter_register;
>> >> + }
>> >> +
>> >> + return 0;
>> >> +
>> >> +err_filter_register:
>> >> + marker_filter_destroy();
>> >> +err_marker_filter_init:
>> >> + marker_format_destroy();
>> >> +err_marker_format_init:
>> >> + debugfs_remove(ltt_filter_dir);
>> >> +err_create_filter_dir:
>> >> +err_no_root:
>> >> + return err;
>> >> +}
>> >> +
>> >> +static void __exit ltt_filter_exit(void)
>> >> +{
>> >> + ltt_module_unregister(LTT_FUNCTION_RUN_FILTER);
>> >> + marker_filter_destroy();
>> >> + marker_format_destroy();
>> >> +
>> >> + debugfs_remove(ltt_filter_dir);
>> >> +}
>> >> +
>> >> +module_init(ltt_filter_init);
>> >> +module_exit(ltt_filter_exit);
>> >> +
>> >> +MODULE_LICENSE("GPL");
>> >> +MODULE_AUTHOR("Zhao Lei <zhaolei at cn.fujitsu.com>");
>> >> +MODULE_DESCRIPTION("Linux Trace Toolkit Filter");
>> >> --
>> >> 1.5.5.3
>> >>
>> >>
>> >>
>> >> _______________________________________________
>> >> ltt-dev mailing list
>> >> ltt-dev at lists.casi.polymtl.ca
>> >> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>> >>
>> >
>> > --
>> > Mathieu Desnoyers
>> > OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
>> >
>> _______________________________________________
>> ltt-dev mailing list
>> ltt-dev at lists.casi.polymtl.ca
>> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>>
>
> --
> Mathieu Desnoyers
> OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2009-04-08 3:19 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <49646F42.5040003@cn.fujitsu.com>
2009-01-07 17:05 ` [ltt-dev] lttng development plan Mathieu Desnoyers
2009-01-19 3:43 ` Lai Jiangshan
2009-01-20 1:55 ` Mathieu Desnoyers
2009-01-20 6:58 ` Lai Jiangshan
2009-01-23 3:21 ` KOSAKI Motohiro
2009-01-23 3:21 ` KOSAKI Motohiro
2009-01-23 4:49 ` Mathieu Desnoyers
2009-02-02 4:59 ` Gui Jianfeng
2009-02-03 5:11 ` Mathieu Desnoyers
2009-02-11 6:33 ` Zhaolei
2009-02-11 6:50 ` Mathieu Desnoyers
2009-02-11 7:24 ` Zhaolei
2009-02-11 7:37 ` Mathieu Desnoyers
2009-02-19 8:17 ` Zhaolei
2009-03-02 7:01 ` Zhaolei
2009-03-03 19:25 ` Mathieu Desnoyers
2009-03-29 8:41 ` [ltt-dev] [PATCH 0/3] In-kernel filter module Zhaolei
2009-03-29 8:42 ` [ltt-dev] [PATCH 1/3] Remove old per-type filter Zhaolei
2009-03-29 8:43 ` [ltt-dev] [PATCH 2/3] Change ltt_run_filter_functor arguments to support in-kernel filter Zhaolei
2009-03-29 8:44 ` [ltt-dev] [PATCH 3/3] In-kernel filter module Zhaolei
2009-04-06 17:52 ` Mathieu Desnoyers
2009-04-07 5:45 ` Zhaolei
2009-04-07 16:48 ` Mathieu Desnoyers
2009-04-08 3:19 ` Zhaolei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox