readapt 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,5 +3,5 @@
3
3
 
4
4
  void initialize_normalize(VALUE);
5
5
  char *normalize_path_new_cstr(char *str);
6
-
6
+ void normalize_path(char *str);
7
7
  #endif
@@ -3,6 +3,7 @@
3
3
  #include "monitor.h"
4
4
  #include "normalize.h"
5
5
  #include "breakpoints.h"
6
+ #include "frame.h"
6
7
 
7
8
  static VALUE m_Readapt;
8
9
 
@@ -12,5 +13,6 @@ void Init_readapt()
12
13
 
13
14
  initialize_normalize(m_Readapt);
14
15
  initialize_breakpoints(m_Readapt);
16
+ initialize_frame(m_Readapt);
15
17
  initialize_monitor(m_Readapt);
16
18
  }
@@ -0,0 +1,86 @@
1
+ #include <stdlib.h>
2
+ #include "stack.h"
3
+
4
+ #define STACK_CAPACITY 20
5
+
6
+ /**
7
+ * Allocate a stack. The `elem_size` is the expected size of each element,
8
+ * e.g., `sizeof(some_struct)`. The optional `free_func` argument is a pointer
9
+ * to a function that will be called when an element is popped off the stack.
10
+ */
11
+ stack_t *stack_alloc(size_t elem_size, void (*free_func)(void *))
12
+ {
13
+ stack_t *s = malloc(sizeof(stack_t));
14
+ s->elem_size = elem_size;
15
+ s->free_func = free_func;
16
+ s->size = 0;
17
+ s->capacity = 0;
18
+ return s;
19
+ }
20
+
21
+ /**
22
+ * Add an element to the end of the stack
23
+ */
24
+ void stack_push(stack_t *stack, void *element)
25
+ {
26
+ if (stack->size == stack->capacity)
27
+ {
28
+ if (stack->capacity == 0)
29
+ {
30
+ stack->capacity = STACK_CAPACITY;
31
+ stack->elements = malloc(stack->elem_size * stack->capacity);
32
+ }
33
+ else
34
+ {
35
+ stack->capacity += STACK_CAPACITY;
36
+ stack->elements = realloc(stack->elements, stack->elem_size * stack->capacity);
37
+ }
38
+ }
39
+ stack->elements[stack->size] = element;
40
+ stack->size++;
41
+ }
42
+
43
+ /**
44
+ * Get a pointer to the last element in the stack.
45
+ */
46
+ void *stack_peek(stack_t *stack)
47
+ {
48
+ return stack->size == 0 ? NULL : stack->elements[stack->size - 1];
49
+ }
50
+
51
+ /**
52
+ * Pop the last element off the stack and pass it to free_func.
53
+ */
54
+ void stack_pop(stack_t *stack)
55
+ {
56
+ void *e;
57
+
58
+ if (stack->size > 0)
59
+ {
60
+ e = stack->elements[stack->size - 1];
61
+ if (stack->free_func)
62
+ {
63
+ stack->free_func(e);
64
+ }
65
+ // stack->elements[stack->size - 1] = NULL;
66
+ stack->size--;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Free the stack from memory. If it still contains any elements, pass them to
72
+ * free_func.
73
+ */
74
+ void stack_free(stack_t *stack)
75
+ {
76
+ int i;
77
+
78
+ if (stack->free_func)
79
+ {
80
+ for (i = 0; i < stack->size; i++)
81
+ {
82
+ stack->free_func(stack->elements[i]);
83
+ }
84
+ }
85
+ free(stack);
86
+ }
@@ -0,0 +1,20 @@
1
+ #ifndef STACK_H_
2
+ #define STACK_H_
3
+
4
+ #include "stddef.h"
5
+
6
+ typedef struct stack_struct {
7
+ int size;
8
+ size_t elem_size;
9
+ void (*free_func)(void *);
10
+ int capacity;
11
+ void **elements;
12
+ } stack_t;
13
+
14
+ stack_t *stack_alloc(size_t elem_size, void(*free_func)(void*));
15
+ void stack_push(stack_t *stack, void *element);
16
+ void *stack_peek(stack_t *stack);
17
+ void stack_pop(stack_t *stack);
18
+ void stack_free(stack_t *stack);
19
+
20
+ #endif
@@ -1,15 +1,23 @@
1
1
  #include "ruby.h"
2
2
  #include "ruby/debug.h"
3
3
  #include "threads.h"
4
+ #include "frame.h"
5
+ #include "inspector.h"
4
6
 
7
+ static VALUE c_Thread;
5
8
  static VALUE threads;
6
9
 
7
- void thread_reference_free(void* data)
10
+ static void thread_reference_free(void* data)
8
11
  {
9
- free(data);
12
+ thread_reference_t* thr;
13
+
14
+ thr = data;
15
+ // stack_free(thr->calls);
16
+ stack_free(thr->frames);
17
+ free(thr);
10
18
  }
11
19
 
12
- size_t thread_reference_size(const void* data)
20
+ static size_t thread_reference_size(const void* data)
13
21
  {
14
22
  return sizeof(thread_reference_t);
15
23
  }
@@ -25,20 +33,16 @@ static const rb_data_type_t thread_reference_type = {
25
33
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
26
34
  };
27
35
 
28
- void initialize_threads()
29
- {
30
- threads = rb_hash_new();
31
- rb_global_variable(&threads);
32
- }
33
-
34
- VALUE thread_reference_new(VALUE thr)
36
+ static VALUE thread_reference_new(VALUE thr)
35
37
  {
36
38
  thread_reference_t *data = malloc(sizeof(thread_reference_t));
37
- VALUE obj = TypedData_Make_Struct(rb_cData, thread_reference_t, &thread_reference_type, data);
39
+ VALUE obj = TypedData_Make_Struct(c_Thread, thread_reference_t, &thread_reference_type, data);
38
40
  data->id = NUM2LONG(rb_funcall(thr, rb_intern("object_id"), 0));
39
- data->depth = 0;
40
41
  data->cursor = 0;
42
+ data->depth = 0;
41
43
  data->control = rb_intern("continue");
44
+ data->frames = stack_alloc(sizeof(frame_t), frame_free);
45
+ // data->calls = stack_alloc(sizeof(frame_t), NULL);
42
46
  return obj;
43
47
  }
44
48
 
@@ -75,8 +79,7 @@ VALUE thread_add_reference(VALUE thr)
75
79
 
76
80
  VALUE thread_delete_reference(VALUE thr)
77
81
  {
78
- rb_hash_delete(threads, thr);
79
- return Qnil;
82
+ return rb_hash_delete(threads, rb_obj_id(thr));
80
83
  }
81
84
 
82
85
  void thread_pause()
@@ -95,7 +98,94 @@ void thread_pause()
95
98
  }
96
99
  }
97
100
 
98
- void thread_reset()
101
+ void thread_clear()
99
102
  {
100
103
  rb_funcall(threads, rb_intern("clear"), 0);
101
104
  }
105
+
106
+ static VALUE thread_allocate_s(VALUE self)
107
+ {
108
+ thread_reference_t *data = malloc(sizeof(thread_reference_t));
109
+ data->control = rb_intern("continue");
110
+ data->depth = 0;
111
+ data->cursor = 0;
112
+ data->frames = stack_alloc(sizeof(frame_t), frame_free);
113
+ // data->calls = stack_alloc(sizeof(frame_t), NULL);
114
+ data->id = 0;
115
+ return TypedData_Wrap_Struct(self, &thread_reference_type, data);
116
+ }
117
+
118
+ static VALUE thread_all_s(VALUE self)
119
+ {
120
+ return rb_funcall(threads, rb_intern("values"), 0);
121
+ }
122
+
123
+ static VALUE thread_find_s(VALUE self, VALUE id)
124
+ {
125
+ return thread_reference_id(id);
126
+ }
127
+
128
+ static VALUE thread_include_s(VALUE self, VALUE id)
129
+ {
130
+ return rb_funcall(threads, rb_intern("include?"), 1, id);
131
+ }
132
+
133
+ static VALUE thread_id_m(VALUE self)
134
+ {
135
+ thread_reference_t *data = thread_reference_pointer(self);
136
+ return LONG2NUM(data->id);
137
+ }
138
+
139
+ static VALUE frames_m(VALUE self)
140
+ {
141
+ thread_reference_t *data;
142
+ VALUE ary;
143
+ VALUE frm;
144
+ int i;
145
+ frame_t *fd;
146
+
147
+ ary = rb_ary_new();
148
+ data = thread_reference_pointer(self);
149
+ for (i = data->frames->size - 1; i >= 0; i--)
150
+ {
151
+ fd = data->frames->elements[i];
152
+ // TODO This condition should probably not be necessary.
153
+ if (fd->binding != Qnil)
154
+ {
155
+ frm = frame_new_from_data(fd);
156
+ rb_ary_push(ary, frm);
157
+ }
158
+ }
159
+
160
+ return ary;
161
+ }
162
+
163
+ void thread_reference_build_frames(thread_reference_t *ptr)
164
+ {
165
+ inspector_inspect(ptr);
166
+ }
167
+
168
+ void thread_reference_clear_frames(thread_reference_t *ptr)
169
+ {
170
+ // TODO This is probably suboptimal
171
+ // while (ptr->frames->size > 0)
172
+ // {
173
+ // stack_pop(ptr->frames);
174
+ // }
175
+ stack_free(ptr->frames);
176
+ ptr->frames = stack_alloc(sizeof(frame_t), frame_free);
177
+ }
178
+
179
+ void initialize_threads(VALUE m_Readapt)
180
+ {
181
+ c_Thread = rb_define_class_under(m_Readapt, "Thread", rb_cData);
182
+ rb_define_alloc_func(c_Thread, thread_allocate_s);
183
+ rb_define_method(c_Thread, "id", thread_id_m, 0);
184
+ rb_define_method(c_Thread, "frames", frames_m, 0);
185
+ rb_define_singleton_method(c_Thread, "all", thread_all_s, 0);
186
+ rb_define_singleton_method(c_Thread, "find", thread_find_s, 1);
187
+ rb_define_singleton_method(c_Thread, "include?", thread_include_s, 1);
188
+
189
+ threads = rb_hash_new();
190
+ rb_global_variable(&threads);
191
+ }
@@ -1,21 +1,28 @@
1
1
  #ifndef THREADS_H_
2
2
  #define THREADS_H_
3
3
 
4
+ #include "ruby.h"
5
+ #include "frame.h"
6
+ #include "stack.h"
7
+
4
8
  typedef struct thread_reference_struct {
5
9
  long id;
6
- int depth;
7
10
  int cursor;
11
+ int depth;
8
12
  ID control;
13
+ stack_t *frames;
9
14
  } thread_reference_t;
10
15
 
11
- void initialize_threads();
16
+ void initialize_threads(VALUE);
12
17
  VALUE thread_current_reference();
13
18
  VALUE thread_reference(VALUE);
14
19
  VALUE thread_reference_id(VALUE);
15
20
  VALUE thread_add_reference(VALUE);
16
21
  VALUE thread_delete_reference(VALUE);
17
22
  thread_reference_t *thread_reference_pointer(VALUE);
23
+ void thread_reference_build_frames(thread_reference_t *);
24
+ void thread_reference_clear_frames(thread_reference_t *);
18
25
  void thread_pause();
19
- void thread_reset();
26
+ void thread_clear();
20
27
 
21
28
  #endif
@@ -1,8 +1,8 @@
1
1
  require 'backport'
2
2
 
3
3
  require 'readapt/version'
4
+ require 'readapt/readapt'
4
5
  require 'readapt/breakpoint'
5
- require 'readapt/location'
6
6
  require 'readapt/thread'
7
7
  require 'readapt/frame'
8
8
  require 'readapt/monitor'
@@ -12,7 +12,6 @@ require 'readapt/debugger'
12
12
  require 'readapt/message'
13
13
  require 'readapt/variable'
14
14
  require 'readapt/adapter'
15
- require 'readapt/readapt'
16
15
  require 'readapt/shell'
17
16
 
18
17
  module Readapt
@@ -69,9 +69,9 @@ module Readapt
69
69
  envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
70
70
  write envelope
71
71
  end
72
- rescue Exception => e
73
- STDERR.puts e.message
74
- STDERR.puts e.backtrace
72
+ rescue RuntimeError => e
73
+ STDERR.puts "[#{e.class}] #{e.message}"
74
+ STDERR.puts e.backtrace.join
75
75
  end
76
76
  end
77
77
 
@@ -3,11 +3,18 @@ module Readapt
3
3
  attr_reader :source
4
4
  attr_reader :line
5
5
  attr_reader :condition
6
+ attr_reader :hit_condition
7
+ attr_writer :hit_cursor
6
8
 
7
- def initialize source, line, condition
9
+ def initialize source, line, condition, hit_condition
8
10
  @source = source
9
11
  @line = line
10
12
  @condition = condition
13
+ @hit_condition = hit_condition
14
+ end
15
+
16
+ def hit_cursor
17
+ @hit_cursor ||= 0
11
18
  end
12
19
  end
13
20
  end
@@ -15,7 +15,6 @@ module Readapt
15
15
 
16
16
  def initialize machine = Machine.new
17
17
  @stack = []
18
- @threads = {}
19
18
  @frames = {}
20
19
  @running = false
21
20
  @attached = false
@@ -37,11 +36,11 @@ module Readapt
37
36
 
38
37
  # @return [Readapt::Thread]
39
38
  def thread id
40
- @threads[id] || Thread::NULL_THREAD
39
+ Thread.find(id)
41
40
  end
42
41
 
43
42
  def threads
44
- @threads.values
43
+ Thread.all
45
44
  end
46
45
 
47
46
  def frame id
@@ -74,7 +73,7 @@ module Readapt
74
73
  end
75
74
  yield if block_given?
76
75
  rescue StandardError => e
77
- STDERR.puts e.message
76
+ STDERR.puts "[#{e.class}] #{e.message}"
78
77
  STDERR.puts e.backtrace.join("\n")
79
78
  rescue SystemExit
80
79
  # Ignore
@@ -82,6 +81,8 @@ module Readapt
82
81
  Monitor.stop
83
82
  @running = false
84
83
  set_original_args
84
+ STDOUT.flush
85
+ STDERR.flush
85
86
  changed
86
87
  send_event 'terminated', nil
87
88
  end
@@ -94,11 +95,11 @@ module Readapt
94
95
  end
95
96
 
96
97
  def get_breakpoint source, line
97
- @breakpoints["#{source}:#{line}"] || Breakpoint.new(source, line, nil)
98
+ @breakpoints["#{source}:#{line}"] || Breakpoint.new(source, line, nil, 0)
98
99
  end
99
100
 
100
- def set_breakpoint source, line, condition
101
- @breakpoints["#{source}:#{line}"] = Breakpoint.new(source, line, condition)
101
+ def set_breakpoint source, line, condition, hitcount
102
+ @breakpoints["#{source}:#{line}"] = Breakpoint.new(source, line, condition, hitcount)
102
103
  end
103
104
 
104
105
  def clear_breakpoints source
@@ -121,9 +122,9 @@ module Readapt
121
122
  # @param [Snapshot]
122
123
  # return [void]
123
124
  def debug snapshot
125
+ sleep 0.001 # @todo Trying to let thread data sync
124
126
  if snapshot.event == :thread_begin || snapshot.event == :entry
125
- @threads[snapshot.thread_id] ||= Thread.new(snapshot.thread_id)
126
- thr = @threads[snapshot.thread_id]
127
+ thr = Thread.find(snapshot.thread_id)
127
128
  thr.control = :continue
128
129
  send_event('thread', {
129
130
  reason: 'started',
@@ -133,47 +134,66 @@ module Readapt
133
134
  elsif snapshot.event == :thread_end
134
135
  thr = thread(snapshot.thread_id)
135
136
  thr.control = :continue
136
- @threads.delete snapshot.thread_id
137
+ # @threads.delete snapshot.thread_id
137
138
  send_event('thread', {
138
139
  reason: 'exited',
139
140
  threadId: snapshot.thread_id
140
141
  })
141
142
  snapshot.control = :continue
142
- # elsif snapshot.event == :entry
143
- # snapshot.control = :continue
144
143
  else
144
+ confirmed_pause = true
145
+ thread = self.thread(snapshot.thread_id)
145
146
  if snapshot.event == :breakpoint
146
147
  bp = get_breakpoint(snapshot.file, snapshot.line)
147
148
  unless bp.condition.nil? || bp.condition.empty?
148
149
  # @type [Binding]
149
- bnd = ObjectSpace._id2ref(snapshot.binding_id)
150
+ bnd = thread.frames.first.frame_binding
150
151
  begin
151
152
  unless bnd.eval(bp.condition)
152
- snapshot.control = :continue
153
- return
153
+ confirmed_pause = false
154
154
  end
155
155
  rescue Exception => e
156
156
  STDERR.puts "Breakpoint condition raised an error"
157
157
  STDERR.puts "#{snapshot.file}:#{snapshot.line} - `#{bp.condition}`"
158
158
  STDERR.puts "[#{e.class}] #{e.message}"
159
- snapshot.control = :continue
160
- return
159
+ confirmed_pause = false
160
+ end
161
+ end
162
+ unless !confirmed_pause || bp.hit_condition.nil? || bp.hit_condition.empty?
163
+ bp.hit_cursor += 1
164
+ bnd = thread.frames.first.frame_binding
165
+ begin
166
+ hit_count = bnd.eval(bp.hit_condition)
167
+ if bp.hit_cursor == hit_count
168
+ bp.hit_cursor = 0
169
+ else
170
+ confirmed_pause = false
171
+ end
172
+ rescue Exception => e
173
+ STDERR.puts "Breakpoint condition raised an error"
174
+ STDERR.puts "#{snapshot.file}:#{snapshot.line} - `#{bp.condition}`"
175
+ STDERR.puts "[#{e.class}] #{e.message}"
176
+ confirmed_pause = false
161
177
  end
162
178
  end
163
179
  end
164
- changed
165
- thread = self.thread(snapshot.thread_id)
166
- thread.control = :pause
167
- frame = Frame.new(Location.new(snapshot.file, snapshot.line), snapshot.binding_id)
168
- thread.frames.push frame
169
- @frames[frame.local_id] = frame
170
- send_event('stopped', {
171
- reason: snapshot.event,
172
- threadId: ::Thread.current.object_id
173
- })
174
- sleep 0.01 until thread.control != :pause || !@threads.key?(thread.id)
175
- @frames.delete frame.local_id
176
- thread.frames.delete frame
180
+ if confirmed_pause
181
+ changed
182
+ thread.control = :pause
183
+ thread.frames.each do |frm|
184
+ @frames[frm.local_id] = frm
185
+ end
186
+ send_event('stopped', {
187
+ reason: snapshot.event,
188
+ threadId: thread.id
189
+ })
190
+ sleep 0.01 until thread.control != :pause || !Thread.include?(thread.id)
191
+ thread.frames.each do |frm|
192
+ @frames.delete frm.local_id
193
+ end
194
+ else
195
+ thread.control = :continue
196
+ end
177
197
  snapshot.control = thread.control
178
198
  end
179
199
  end