readapt 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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