Class: RuntimeInspection::RTIManager

Inherits:
Object
  • Object
show all
Defined in:
lib/rti/manager.rb

Overview

This will be the list of all public instance methods that start with ‘rti_’. These methods are callable from a client’s connection to invoke various helpers remotely (e.g. manipulating breakpoints as well as other helpful functions). The first argument to these will always be the state. Remaining arguments will be whatever strings were obtained from the client.

Defined Under Namespace

Classes: BreakPoint

Constant Summary collapse

CMD_MARKER =

If this character is used as the first non-whitespace value in a command, the remaining data will be used to invoke an RTIManager method directly (outside the eval scope).

?.
RTI_METHODS =

Track the list of available commands.

[]
BP_RE =

Expression for splitting apart a user-supplied BreakPoint in bp_add.

Regexp.new( /^((.+):(\d+)|((.+)\#)?(.*?))(>(.+))?$/ )

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(breakpoints, tracing_proc, state) ⇒ RTIManager

Get a new RTIManager



46
47
48
49
50
# File 'lib/rti/manager.rb', line 46

def initialize( breakpoints, tracing_proc, state )
    @breakpoints = breakpoints
    @tracing_proc = tracing_proc
    @state = state
end

Class Method Details

.method_added(id) ⇒ Object

Collect all methods that we are willing to expose to a caller.



40
41
42
# File 'lib/rti/manager.rb', line 40

def self.method_added( id )
    RTI_METHODS << id.id2name
end

Instance Method Details

#backtrace(calls = 5) ⇒ Object

Convenience method for what is essentially caller. The argument specifies the number of calls to report.



116
117
118
# File 'lib/rti/manager.rb', line 116

def backtrace( calls=5 )
    caller[1..calls]
end

#bp_add(key) ⇒ Object

Add a new BreakPoint. Execution will not stop at any BreakPoints until bp_start is called. The argument should use one of the following forms:

  • Class#method

  • Module#method

  • method

  • file:line

  • Append >thread to any of the above

In the first three forms, the BreakPoint will stop on the ‘call’ event into that method.

The last form enables a BreakPoint to be declared for a particular thread. The thread value may either be an object_id (see #threads) or it’s name as defined by the thread’s :thread_name key.

Once a BreakPoint is stopped in a thread, bp_finish, bp_next, and bp_step will act inside that thread. When bp_continue is called, the next BreakPoint to be hit regardless of the thread will be the StopPoint.

Note that even though a particular sequence of StopPoints is thread-specific, that doesn’t mean that some other generic BreakPoint might be hit and stop processing elsewhere. This can be mitigated by using the thread-specific BreakPoints.



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
# File 'lib/rti/manager.rb', line 169

def bp_add( key )
    if bp = @breakpoints[key]
        return "Breakpoint already held as #{bp.id} by " +
            "#{bp.state.socket}"
    end

    unless m = key.match( BP_RE )
        return "Unable to parse #{key}"
    end

    file = m[2]
    line = m[3]
    cont = m[5]
    meth = m[6]
    thread = m[8]

    if meth and meth.empty?
        meth = nil
    end

    if cont
        ObjectSpace.each_object( Class ) do |c|
            if c.name == cont
                cont = c
                break
            end
        end
        if cont.kind_of?( String )
            ObjectSpace.each_object( Module ) do |m|
                if m.name == cont
                    cont = m
                    break
                end
            end
        end
        if cont.kind_of?( String )
            return "Unable to find matching module or class " +
                "for #{cont}"
        end
    end

    if meth
        if cont
            unless( cont.instance_methods.include?( meth ) or
                    cont.methods.include?( meth ))
                return "Unable to find #{meth} method in #{cont}"
            end
        else
            ObjectSpace.each_object( Class ) do |c|
                if( c.instance_methods.include?( meth ) or
                    c.methods.include?( meth ))
                    cont = c
                    break
                end
            end
            unless cont
                ObjectSpace.each_object( Module ) do |m|
                    if( m.instance_methods.include?( meth ) or
                        m.methods.include?( meth ))
                        cont = m
                        break
                    end
                end
            end
            unless cont
                return "Unable to find containing class or module " +
                    "for #{meth}"
            end
        end
    end

    if thread
        key.sub!( />.+$/, ">#{get_thread_id(thread)}" )
    end

    return real_bp_add( key )
end

#bp_attach(thread) ⇒ Object

Continue tracing waiting for the very next call inside the specified thread. The thread value to use is either the thread’s name (as available in the thread’s :thread_name key) or the thread’s object_id.



304
305
306
307
308
309
310
311
312
# File 'lib/rti/manager.rb', line 304

def bp_attach( thread )
    # Need to stop any current tracing that may be running.
    bp_stop
    ret = bp_add( ">#{thread}" )
    unless ret.match( /^Added / )
        return ret
    end
    bp_start
end

#bp_continueObject

Continue tracing from current position waiting for next breakpoint to hit.



317
318
319
320
321
322
323
324
# File 'lib/rti/manager.rb', line 317

def bp_continue
    unless @state.bp_portal
        return "Not stopped at a breakpoint"
    end
    @state.bp_portal.cmds << :continue
    @state.bp_portal = @state.bp_waiting.pop
    return nil
end

#bp_delete(id) ⇒ Object

Delete a breakpoint.



249
250
251
252
253
254
255
256
257
258
259
# File 'lib/rti/manager.rb', line 249

def bp_delete( id )
    id = id.to_i
    ret = "Unable to find breakpoint #{id}"
    @breakpoints.delete_if do |key, bp|
        if bp.id == id and bp.state == @state
            ret = "Deleted breakpoint #{id}"
            true
        end
    end
    return ret
end

#bp_finishObject

Finish the current frame and stop in the return.



328
329
330
331
332
333
334
335
# File 'lib/rti/manager.rb', line 328

def bp_finish
    unless @state.bp_portal
        return "Not stopped at a breakpoint"
    end
    @state.bp_portal.cmds << :fin
    @state.bp_portal = @state.bp_waiting.pop
    return nil
end

#bp_list(show_all = false) ⇒ Object

List all the breakpoints.



263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/rti/manager.rb', line 263

def bp_list( show_all=false )
    ordered = @breakpoints.sort do |a, b|
        a[1].id <=> b[1].id
    end
    ordered.collect do |key, bp|
        next unless show_all or bp.state == @state
        if show_all and bp.state != @state
            post = " (#{bp.state.socket})"
        end
        "#{bp.id}: #{key}#{post}"
    end.compact
end

#bp_nextObject

Break execution at the next line (without going into another class).



340
341
342
343
344
345
346
347
# File 'lib/rti/manager.rb', line 340

def bp_next
    unless @state.bp_portal
        return "Not stopped at a breakpoint"
    end
    @state.bp_portal.cmds << :next
    @state.bp_portal = @state.bp_waiting.pop
    return nil
end

#bp_startObject

Start tracing looking for breakpoints to stop at.



278
279
280
281
282
283
284
285
286
# File 'lib/rti/manager.rb', line 278

def bp_start
    if @breakpoints.empty?
        return "No breakpoints set"
    end
    bp_stop
    set_trace_func( @tracing_proc )
    @state.bp_portal = @state.bp_waiting.pop
    return nil
end

#bp_stepObject

Follow the next instruction–potentially into another class and/or method.



352
353
354
355
356
357
358
359
# File 'lib/rti/manager.rb', line 352

def bp_step
    unless @state.bp_portal
        return "Not stopped at a breakpoint"
    end
    @state.bp_portal.cmds << :step
    @state.bp_portal = @state.bp_waiting.pop
    return nil
end

#bp_stopObject

Stop tracing for breakpoints.



290
291
292
293
294
295
296
297
# File 'lib/rti/manager.rb', line 290

def bp_stop
    unless @state.bp_portal
        return "Not stopped at a breakpoint"
    end
    @state.bp_portal.cmds << :stop
    @state.delete( :bp_portal )
    return nil
end

#get_object(name) ⇒ Object

Very simple helper to assist caller in retrieving a specific object. This will return the first one found. Anything requiring more complicated decision logic about what object to return should use ObjectSpace directly.

name

The object’s class or name (uses #name2class for the latter).



89
90
91
92
93
94
95
96
97
# File 'lib/rti/manager.rb', line 89

def get_object( name )
    if name.kind_of? String
        return nil unless c = name2class(name)
    else
        c = name
    end
    ObjectSpace.each_object( c ) {|obj| return obj}
    return nil
end

#listObject

Get the list of RTI commands available.



54
55
56
# File 'lib/rti/manager.rb', line 54

def list
    RTI_METHODS.sort
end

#name2class(name) ⇒ Object

Given a name, return the real class object. Returns the

first one found unless an explicit “path” is provided via

designators.



68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/rti/manager.rb', line 68

def name2class( name )
    if name.include?( '::' )
        ObjectSpace.each_object( Class ) do |c|
            return c if name == c.name
        end
    else
        ObjectSpace.each_object( Class ) do |c|
            return c if c.name.match( /#{name}$/ )
        end
    end
    return nil
end

#start_worldObject

Start all other threads up again.



109
110
111
# File 'lib/rti/manager.rb', line 109

def start_world
    ::Thread.critical = false
end

#stateObject

Get the state information for this client.



60
61
62
# File 'lib/rti/manager.rb', line 60

def state
    @state
end

#stop_worldObject

Stop all other threads except the RuntimeInsepction::Thread. Be careful. This will stop any new connections as well.



103
104
105
# File 'lib/rti/manager.rb', line 103

def stop_world
    ::Thread.critical = true
end

#threadsObject

Convenience method for listing threads in the system. The first value reported is the thread’s object_id which can be used in bp_attach and bp_add. Only threads that do not have :thread_name keys that start with ‘rti_’ are shown.



125
126
127
128
129
130
131
132
133
134
# File 'lib/rti/manager.rb', line 125

def threads
    tl = Thread.list.collect do |th|
        unless( th[:thread_name].to_s.match( /^rti_/ ) or
                th.kind_of?( Thread ))
            th
        end
    end
    tl.compact!
    return tl
end