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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Rakefile +0 -11
- data/ext/readapt/breakpoints.c +0 -5
- data/ext/readapt/breakpoints.h +0 -1
- data/ext/readapt/frame.c +137 -0
- data/ext/readapt/frame.h +17 -0
- data/ext/readapt/hash_table.c +0 -1
- data/ext/readapt/hash_table.h +0 -2
- data/ext/readapt/inspector.c +51 -0
- data/ext/readapt/inspector.h +8 -0
- data/ext/readapt/monitor.c +39 -26
- data/ext/readapt/normalize.c +14 -8
- data/ext/readapt/normalize.h +1 -1
- data/ext/readapt/readapt.c +2 -0
- data/ext/readapt/stack.c +86 -0
- data/ext/readapt/stack.h +20 -0
- data/ext/readapt/threads.c +105 -15
- data/ext/readapt/threads.h +10 -3
- data/lib/readapt.rb +1 -2
- data/lib/readapt/adapter.rb +3 -3
- data/lib/readapt/breakpoint.rb +8 -1
- data/lib/readapt/debugger.rb +50 -30
- data/lib/readapt/frame.rb +16 -18
- data/lib/readapt/message.rb +10 -7
- data/lib/readapt/message/scopes.rb +2 -1
- data/lib/readapt/message/set_breakpoints.rb +1 -1
- data/lib/readapt/message/stack_trace.rb +16 -4
- data/lib/readapt/message/variables.rb +23 -19
- data/lib/readapt/shell.rb +2 -0
- data/lib/readapt/snapshot.rb +1 -13
- data/lib/readapt/thread.rb +11 -22
- data/lib/readapt/version.rb +1 -1
- metadata +8 -3
- data/lib/readapt/location.rb +0 -25
data/ext/readapt/normalize.h
CHANGED
data/ext/readapt/readapt.c
CHANGED
@@ -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
|
}
|
data/ext/readapt/stack.c
ADDED
@@ -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
|
+
}
|
data/ext/readapt/stack.h
ADDED
@@ -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
|
data/ext/readapt/threads.c
CHANGED
@@ -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
|
-
|
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
|
-
|
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(
|
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
|
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
|
+
}
|
data/ext/readapt/threads.h
CHANGED
@@ -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
|
26
|
+
void thread_clear();
|
20
27
|
|
21
28
|
#endif
|
data/lib/readapt.rb
CHANGED
@@ -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
|
data/lib/readapt/adapter.rb
CHANGED
@@ -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
|
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
|
|
data/lib/readapt/breakpoint.rb
CHANGED
@@ -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
|
data/lib/readapt/debugger.rb
CHANGED
@@ -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
|
-
|
39
|
+
Thread.find(id)
|
41
40
|
end
|
42
41
|
|
43
42
|
def threads
|
44
|
-
|
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
|
-
|
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 =
|
150
|
+
bnd = thread.frames.first.frame_binding
|
150
151
|
begin
|
151
152
|
unless bnd.eval(bp.condition)
|
152
|
-
|
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
|
-
|
160
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|