Module: Trepan::Frame

Includes:
Util
Defined in:
app/frame.rb

Overview

Call-Stack frame methods

Constant Summary collapse

DEFAULT_STACK_TRACE_SETTINGS =
{
  :basename     => false,   # Basename only for files?
  :maxstack     => 16,      # How many entries to show? nil means all
  :count        => nil,     # How many entries to show? nil means all
  :current_pos  => 0,       # Where are we in the stack?
  :show_pc      => false,   # Show PC offset?
}

Class Method Summary collapse

Methods included from Util

extract_expression, safe_repr, suppress_warnings, uniq_abbrev

Class Method Details

.all_param_names(iseq, delineate = true) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'app/frame.rb', line 21

def all_param_names(iseq, delineate=true)
  return '' unless iseq
  params = param_names(iseq, 0, iseq.argc-1, '')
  if iseq.arg_opts > 0
    opt_params = param_names(iseq, iseq.argc,
                             iseq.argc + iseq.arg_opts-2, '')
    opt_params[0] = "optional: #{opt_params[0]}" if delineate
    params += opt_params
  end
  params += param_names(iseq, iseq.arg_rest, iseq.arg_rest, '*') if
    iseq.arg_rest != -1
  if iseq.arg_post_len > 0
    # Manditory arguments after optional ones - new in Ruby 1.9
    post_params = param_names(iseq, iseq.arg_post_start,
                              iseq.post_start + iseq.arg_post_len, '')
    post_params[0] = "post: #{post_params[0]}" if delineate
    params += post_params
  end
  params += param_names(iseq, iseq.arg_block, iseq.arg_block, '&') if
    iseq.arg_block != -1

  return params
end

.c_params(frame, maxstring = 20) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/frame.rb', line 45

def c_params(frame, maxstring=20)
    argc = frame.argc
    # FIXME should figure out why exception is raised.
    begin
        args =
            if 0 == argc
                ''
            elsif frame
                1.upto(argc).map do
                |i|
                # FIXME: figure out why
                # Trepan::Frame:Module throws an error
                safe_repr(frame.sp(argc-i+3).inspect, 10) rescue ''
                end.join(', ')
            else
                '??'
            end
        safe_repr(args, maxstring) rescue ''
    rescue NotImplementedError
        '??'
    end
end

.eval_string(frame) ⇒ Object

Return the eval string. We get this as the parameter to the eval C call. A bit of checking is done to make sure everything is okay:

- we have to be in an EVAL type frame which has an iseq
- we have to have a prev frame which is a CFUNC
- the prev method name has to be 'eval'


74
75
76
77
78
79
80
81
# File 'app/frame.rb', line 74

def eval_string(frame)
  return nil unless 'EVAL' == frame.type && frame.iseq
  prev = frame.prev
  return nil unless prev && 'CFUNC' == prev.type && 'eval' == prev.method
  retval = prev.sp 3
  retval = $1 if retval =~ /^\(eval "(.+)"\)/
  retval
end

.fileObject



83
84
85
# File 'app/frame.rb', line 83

def file
  iseq.source_container[1]
end

.format_stack_call(frame, opts) ⇒ Object



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
# File 'app/frame.rb', line 87

def format_stack_call(frame, opts)
    # FIXME: prettify
    s = "#{frame.type}"
    # FIXME: Figure why frame.class can throw a NoMethodError
    # on to_s.
    s += if opts[:class]
             " #{opts[:class]}#"
         else
             " #{frame.klass}#" rescue ''
         end
    meth = frame.method
    if meth and frame.type != 'IFUNC'
        iseq = frame.iseq
        args = if 'CFUNC' == frame.type
                   c_params(frame)
               elsif iseq
                   all_param_names(iseq).join(', ')
               end
        s += meth
        if %w(CFUNC METHOD).member?(frame.type)
            s += "(#{args})"
        elsif %w(BLOCK LAMBDA TOP EVAL).member?(frame.type)
            s += " |#{args}|" unless args.nil? || args.empty?
        else
            s += "(#{all_param_names(iseq)})"
        end
    end
    s
end

.format_stack_entry(frame, opts = {}) ⇒ Object



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
# File 'app/frame.rb', line 117

def format_stack_entry(frame, opts={})
    return 'invalid frame' unless frame.valid?
    container_type = frame.source_container[0]
    s  = format_stack_call(frame, opts)
    s += " in #{container_type}"
    sep = if opts[:maxwidth] && s.size > opts[:maxwidth]
              "\n\t"
          else
              ' '
          end
    s +=
        if (eval_str = eval_string(frame))
            sep + safe_repr(eval_str.inspect, 15)
        else
            if 'file' == container_type && opts[:basename]
                sep + File.basename(frame.source_container[1])
            elsif 'binary' == container_type
                ''
            else
                sep + frame.source_container[1]
            end
        end
  if frame.source_location
      loc = ''
      loc +=
          if container_type == 'binary'
              "at address 0x%x" % frame.source_location[0]
          elsif frame.source_location.size == 1
              frame.source_location[0] != 0 ?
              "at line #{frame.source_location[0]}" : ''
          else
              "at lines #{frame.source_location}"
          end
      s +=
          if opts[:maxwidth] && s.size + loc.size > opts[:maxwidth]
              "\n\t"
          else
              ' '
          end
      s += loc
  end
    s += ", pc: #{frame.pc_offset}" if
        frame.pc_offset > 0 && opts[:show_pc]
    return s
end

.location_equal(frame1, frame2) ⇒ Object

Return true if frame1 and frame2 are at the same place. We use this for example in detecting tail recursion.



165
166
167
168
169
# File 'app/frame.rb', line 165

def location_equal(frame1, frame2)
  frame1 && frame2 && frame1.source_location == frame2.source_location &&
    frame1.pc_offset == frame2.pc_offset &&
    frame1.source_container == frame2.source_container
end

.offset_for_return(event) ⇒ Object

Raises:

  • (RuntimeError)


171
172
173
174
175
176
# File 'app/frame.rb', line 171

def offset_for_return(event)
  raise RuntimeError unless %w(return c-return).member?(event)
  # FIXME: C calls have a RubyVM::Env added to the stack.
  # Where? Why?
  'return' == event ? 1 : 4
end

.param_names(iseq, start, stop, prefix = '') ⇒ Object



178
179
180
181
182
183
184
185
186
# File 'app/frame.rb', line 178

def param_names(iseq, start, stop, prefix='')
  start.upto(stop).map do |i|
    begin
      prefix + iseq.local_name(i)
    rescue
      nil
    end
  end.compact
end

.print_stack_entry(frame, i, prefix = ' ', opts = {}) ⇒ Object



188
189
190
191
# File 'app/frame.rb', line 188

def print_stack_entry(frame, i, prefix='    ', opts={})
  opts[:class] = nil unless i == 0
  msg "%s%s" % [prefix, format_stack_entry(frame, opts)]
end

Print ‘count’ frame entries



216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'app/frame.rb', line 216

def print_stack_trace(frame, opts={})
    opts    = DEFAULT_STACK_TRACE_SETTINGS.merge(opts)
    halfstack = (opts[:maxstack]+1) / 2
    n       = frame.stack_size
    n       = [n, opts[:count]].min if opts[:count]
    if n > (halfstack * 2)
        print_stack_trace_from_to(0, halfstack-1, frame, opts)
        msg "... %d levels ..." % (n - halfstack*2)
        last_frame = print_stack_trace_from_to(n - halfstack, n-1, frame, opts)
    else
        last_frame = print_stack_trace_from_to(0, n-1, frame, opts)
    end
    msg "(More stack frames follow...)" if last_frame.type != 'TOP'
end


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'app/frame.rb', line 193

def print_stack_trace_from_to(from, to, frame, opts)
    last_frame = nil
    # TODO: handle indirect recursion.
    direct_recursion_count = 0
    from.upto(to) do |i|
        if location_equal(last_frame, frame)
            direct_recursion_count += 1
        else
            if direct_recursion_count > 0
                msg("... above line repeated #{direct_recursion_count} times")
                direct_recursion_count = 0
            end
            prefix = (i == opts[:current_pos]) ? '-->' : '   '
            prefix += ' #%d ' % [i]
            print_stack_entry(frame, i, prefix, opts)
        end
        last_frame = frame
        frame = frame.prev
    end
    return last_frame
end

.set_return_value(frame, event, value) ⇒ Object



231
232
233
234
# File 'app/frame.rb', line 231

def set_return_value(frame, event, value)
  offset = offset_for_return(event)
  frame.sp_set(offset, value)
end

.value_returned(frame, event) ⇒ Object



237
238
239
# File 'app/frame.rb', line 237

def value_returned(frame, event)
  frame.sp(offset_for_return(event))
end