This file is indexed.

/usr/share/pyshared/tracer.py is in python-tracer 0.2.3-1build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# -*- coding: utf-8 -*-
#   Copyright (C) 2008, 2009 Rocky Bernstein <rocky@gnu.org>
#
#   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 3 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, see <http://www.gnu.org/licenses/>.
"""Centralized Trace management around sys.settrace. We allow several
sets of trace events to get registered and unregistered. We allow
certain functions to be registered to be not traced. We allow tracing
to be turned on and off temporarily without losing the trace
functions.
"""

import operator, sys, inspect, threading, types

# Python Cookbook Recipe 6.7. In Python 2.6 use collections.namedtuple
def superTuple(typename, *attribute_names):
    " create and return a subclass of `tuple', with named attributes "
    # make the subclass with appropriate __new__ and __repr__ specials
    nargs = len(attribute_names)
    class supertup(tuple):
        __slots__ = ()   # save memory, we don't need a per-instance dict
        def __new__(cls, *args):
            if len(args) !=nargs:
                raise TypeError, '%s takes exactly %d arguments (%d given)' % (
                    typename, nargs, len(args))
            return tuple.__new__(cls, args)
        def __repr__(self):
            return '%s(%s)' % (typename, ', '.join(map(repr, self)))
    # add a few key touches to our subclass of `tuple'
    for index, attr_name in enumerate(attribute_names):
        setattr(supertup, attr_name, property(operator.itemgetter(index)))
    supertup.__name__ = typename
    return supertup

Trace_entry = superTuple('Trace_entry', 'trace_fn', 'event_set',
                         'ignore_frameid')

HOOKS         = []    # List of Bunch(trace_fn, event_set)
                      # We run trace_fn if the event is in event_set.
STARTED_STATE = False # True if we are tracing. 
                      # FIXME: in 2.6 we can use sys.gettrace

ALL_EVENT_NAMES   = ('c_call', 'c_exception', 'c_return', 'call', 
                     'exception', 'line', 'return',)

# If you want short strings for the above event names
EVENT2SHORT       = {'c_call'     : 'C>', 
                     'c_exception': 'C!', 
                     'c_return'   : 'C<', 
                     'call'       : '->', 
                     'exception'  : '!!', 
                     'line'       : '--', 
                     'return'     : '<-'}

ALL_EVENTS    = frozenset(ALL_EVENT_NAMES)

TRACE_SUSPEND = False
debug = False  # Setting true 

def null_trace_hook(frame, event, arg): 
    """ A trace hook that doesn't do anything. Can use this to "turn off"
    tracing by setting frame.f_trace. Setting sys.settrace(None) sometimes
    doesn't work...
    """
    pass

def check_event_set(event_set):
    " check `event_set' for validity. Raise TypeError if not valid "
    if event_set is not None and not event_set.issubset(ALL_EVENTS):
        raise TypeError, 'event set is neither None nor a subset of ALL_EVENTS'
    return 

def find_hook(trace_fn):
    """Find `trace_fn' in `hooks', and return the index of it.
    return None is not found."""
    try:
        i = [entry.trace_fn for entry in HOOKS].index(trace_fn)
    except ValueError:
        return None
    return i

def option_set(options, value, default_options):
    if not options:
        return default_options.get(value)
    elif value in options:
        return options[value]
    else:
        return default_options.get(value)
    # Not reached
    return None

def _tracer_func(frame, event, arg):
    """The internal function set by sys.settrace which runs
    all of the user-registered trace hook functions."""

    global TRACE_SUSPEND, HOOKS, debug
    if debug:
        print "%s -- %s:%d" % (event, frame.f_code.co_filename, 
                               frame.f_lineno)
    if TRACE_SUSPEND: return _tracer_func

    # Leave a breadcrumb for this routine so we can know by 
    # frame inspection where the debugger ends. "info threads" 
    # by default for example wants to also not show the trace_hook
    # call from pytracer.
    tracer_func_frame = inspect.currentframe()

    # Go over all registered hooks
    for i in range(len(HOOKS)):
        hook = HOOKS[i]
        if hook.ignore_frameid == id(frame): continue
        if hook.event_set is None or event in hook.event_set:
            if not hook.trace_fn(frame, event, arg):
                # sys.settrace's semantics provide that a if trace
                # hook returns None or False, it should turn off
                # tracing for that frame.
                HOOKS[i] = Trace_entry(hook.trace_fn, hook.event_set,
                                       id(frame))
            pass
        pass
    # print "event_seen %s, keep_trace %s" % (event_triggered, keep_trace,)
        
    # From sys.settrace info: The local trace function
    # should return a reference to itself (or to another function
    # for further tracing in that scope), or None to turn off
    # tracing in that scope. 
    return _tracer_func


DEFAULT_ADD_HOOK_OPTS = {
    'position': -1,  # Which really means "back of list"
    'start': False, 
    'event_set': ALL_EVENTS, 
    'backlevel': 0
    }

def add_hook(trace_fn, options=None):
    """Add `trace_fn' to the list of callback functions that get run
    when tracing is turned on. The number of hook functions
    registered is returned. 

    A check is made on `trace_fn' to make sure it is a function
    which takes 3 parameters: a frame, an event, and an arg or which
    sometimes arg is None.

    `options' is a hash having potential keys: 'position', 'start',
    'event_set', and 'backlevel'. 

    If the event_set option-key is included, it should be is an event
    set that trace_fn will get run on. Use set() or frozenset() to
    create this set. ALL_EVENT_NAMES is a tuple contain a list of
    the event names. ALL_EVENTS is a frozenset of these.

    'position' is the index of where the hook should be place in the
    list, so 0 is first and -1 *after* is last item; the default is
    the very back of the list (-1). -2 is *after* the next to last
    item.

    'start' is a boolean which indicates the hooks should be started
    if they aren't already. 

    'backlevel' an integer indicates that the calling should continue
    backwards in return call frames and is the number of levels to
    skip ignore. 0 means that the caller of add_hook() is traced as
    well as all new frames the caller subsequently calls. 1 means
    that all the caller of add_hook() is ignored but prior parent
    frames are traced, and None means that no previous parent frames
    should be traced.
    """

    # Parameter checking:
    if inspect.ismethod(trace_fn):
        argcount = 4
    elif inspect.isfunction(trace_fn):
        argcount = 3        
    else:
        raise TypeError, (
            "trace_fn should be something isfunction() or ismethod() blesses")
    try:
        if argcount != trace_fn.func_code.co_argcount: 
            raise TypeError, (
                'trace fn should take exactly %d arguments (takes %d)' % (
                        argcount, trace_fn.func_code.co_argcount,))
    except:
        raise TypeError

    get_option = lambda key: option_set(options, key, DEFAULT_ADD_HOOK_OPTS)
    event_set = get_option( 'event_set')
    check_event_set(event_set)

    # Setup so we don't trace into this routine. 
    ignore_frame = inspect.currentframe()

    # Should we trace frames below the one that we issued this 
    # call? 
    backlevel = get_option('backlevel')
    if backlevel is not None:
        if types.IntType != type(backlevel):
            raise TypeError, (
                'backlevel should be an integer type, is %s' % (
                    backlevel))
        frame = ignore_frame
        while frame and backlevel >= 0:
            backlevel -= 1
            frame = frame.f_back
            pass
        
        # Set to trace all frames below this
        while frame:
            frame.f_trace = _tracer_func
            frame = frame.f_back
            pass
        
        pass
    # If the global tracer hook has been registered, the below will
    # trigger the hook to get called after the assignment.
    # That's why we set the hook for this frame to ignore tracing.
    entry = Trace_entry(trace_fn, event_set, ignore_frame)

    # based on position, figure out where to put the hook.
    position = get_option('position')
    if position == -1: 
        HOOKS.append(entry)
    else:
        if position < -1:
            # Recall we need -1 for *after* the end so -2 is normally what is
            # called -1.
            position += 1
            pass
        HOOKS[position:position] = [entry]
        pass
    
    if get_option('start'): start()
    return len(HOOKS)
    
def clear_hooks():
    ' clear all trace hooks '
    global HOOKS
    HOOKS = []
    return

def clear_hooks_and_stop():
    ' clear all trace hooks and stop tracing '
    global STARTED_STATE
    if STARTED_STATE: stop()
    clear_hooks()
    return

def size():
    ' return the number of trace hooks installed, an integer. '
    global HOOKS
    return len(HOOKS)

def is_started():
    """Return true if tracing has been started. Until we assume Python 2.6
    or better, keeping track is done by internal tracking. Thus calls to 
    sys.settrace outside of Tracer won't be detected :-(
    """
    global STARTED_STATE
    return STARTED_STATE

def remove_hook(trace_fn, stop_if_empty=False):
    """Remove `trace_fn' from list of callback functions run when
    tracing is turned on. If `trace_fn' is not in the list of
    callback functions, None is ruturned. On successful
    removal, the number of callback functions remaining is
    returned."""
    global HOOKS
    i = find_hook(trace_fn)
    if i is not None:
        del HOOKS[i]
        if 0 == len(HOOKS) and stop_if_empty:
            stop()
            return 0
        return len(HOOKS)
    return None

DEFAULT_START_OPTS = {
    'trace_fn':  None, 
    'add_hook_opts': DEFAULT_ADD_HOOK_OPTS,
    'include_threads': False
    }
    
def start(options = None):
    """Start using all previously-registered trace hooks. If `trace_fn'
    is not None, we'll search for that and add it, if it's not already
    added."""
    get_option = lambda key: option_set(options, key, DEFAULT_START_OPTS)
    trace_fn = get_option('trace_fn')
    if trace_fn is not None: 
        add_hook(trace_fn, get_option('add_hook_opts'))
        pass

    if get_option('include_threads'):
        threading.settrace(_tracer_func)
        pass

    # FIXME: in 2.6, there is the possibility for chaining 
    # existing hooks by using sys.gettrace().

    if sys.settrace(_tracer_func) is None:
        global STARTED_STATE, HOOKS
        STARTED_STATE = True
        return len(HOOKS)
    if trace_fn is not None: remove_hook(trace_fn)
    raise NotImplementedError, "sys.settrace() doesn't seem to be implemented"

def stop():
    """Stop all trace hooks"""
    if sys.settrace(None) is None:
        global HOOKS, STARTED_STATE
        STARTED_STATE = False
        return len(HOOKS)
    raise NotImplementedError, "sys.settrace() doesn't seem to be implemented"

# Demo it
if __name__ == '__main__':

    t = EVENT2SHORT.keys(); t.sort()
    print "EVENT2SHORT.keys() == ALL_EVENT_NAMES: ", tuple(t) == ALL_EVENT_NAMES
    trace_count = 10

    import tracefilter
    ignore_filter = tracefilter.TraceFilter([find_hook, stop, remove_hook])
    def my_trace_dispatch(frame, event, arg):
        global trace_count, ignore_filter
        'A sample trace function'
        if ignore_filter.is_included(frame): return None
        lineno = frame.f_lineno
        filename = frame.f_code.co_filename
        print "%s - %s:%d" % (event, filename, lineno),
        if 'call' == event:
            print (', %s()' % frame.f_code.co_name),
        if arg: 
            print "arg", arg
        else:
            print
            pass

        # print "event: %s frame %s arg %s\n" % [event, frame, arg]
        if trace_count > 0:
            trace_count -= 1
            return my_trace_dispatch
        else:
            print "Max trace count reached - turning off tracing"
            return None
        pass

    def foo(): print "foo"

    print "** Tracing started before start(): %s" % is_started()

    start() # tracer.start() outside of this file

    print "** Tracing started after start(): %s" % is_started()
    add_hook(my_trace_dispatch) # tracer.add_hook(...) outside
    eval("1+2")
    stop()
    y = 5
    start()
    foo()
    z = 5
    for i in range(6):
        print i
    trace_count = 25
    remove_hook(my_trace_dispatch, stop_if_empty=True)
    print "** Tracing started: %s" % is_started()

    print "** Tracing only 'call' now..."
    add_hook(my_trace_dispatch, 
             {'start': True, 'event_set': frozenset(('call',))})
    foo()
    stop()
    exit(0)