vernier 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/fiber_stalls.rb +51 -0
- data/exe/vernier +11 -52
- data/ext/vernier/extconf.rb +2 -0
- data/ext/vernier/memory.cc +144 -0
- data/ext/vernier/periodic_thread.hh +141 -0
- data/ext/vernier/signal_safe_semaphore.hh +72 -0
- data/ext/vernier/timestamp.hh +138 -0
- data/ext/vernier/vernier.cc +138 -338
- data/ext/vernier/vernier.hh +4 -0
- data/lib/vernier/autorun.rb +17 -1
- data/lib/vernier/collector.rb +37 -9
- data/lib/vernier/hooks/memory_usage.rb +37 -0
- data/lib/vernier/hooks.rb +1 -0
- data/lib/vernier/marker.rb +2 -0
- data/lib/vernier/output/file_listing.rb +113 -0
- data/lib/vernier/output/filename_filter.rb +30 -0
- data/lib/vernier/output/firefox.rb +29 -20
- data/lib/vernier/output/top.rb +60 -8
- data/lib/vernier/parsed_profile.rb +102 -0
- data/lib/vernier/result.rb +4 -92
- data/lib/vernier/stack_table.rb +3 -42
- data/lib/vernier/stack_table_helpers.rb +129 -0
- data/lib/vernier/version.rb +1 -1
- data/lib/vernier.rb +3 -0
- metadata +12 -2
data/ext/vernier/vernier.cc
CHANGED
@@ -14,20 +14,11 @@
|
|
14
14
|
|
15
15
|
#include <sys/time.h>
|
16
16
|
#include <signal.h>
|
17
|
-
#if defined(__APPLE__)
|
18
|
-
/* macOS */
|
19
|
-
#include <dispatch/dispatch.h>
|
20
|
-
#elif defined(__FreeBSD__)
|
21
|
-
/* FreeBSD */
|
22
|
-
#include <pthread_np.h>
|
23
|
-
#include <semaphore.h>
|
24
|
-
#else
|
25
|
-
/* Linux */
|
26
|
-
#include <semaphore.h>
|
27
|
-
#include <sys/syscall.h> /* for SYS_gettid */
|
28
|
-
#endif
|
29
17
|
|
30
18
|
#include "vernier.hh"
|
19
|
+
#include "timestamp.hh"
|
20
|
+
#include "periodic_thread.hh"
|
21
|
+
#include "signal_safe_semaphore.hh"
|
31
22
|
|
32
23
|
#include "ruby/ruby.h"
|
33
24
|
#include "ruby/encoding.h"
|
@@ -49,6 +40,7 @@
|
|
49
40
|
|
50
41
|
#define RUBY_NORMAL_EVENTS \
|
51
42
|
RUBY_EVENT_THREAD_BEGIN | \
|
43
|
+
RUBY_EVENT_FIBER_SWITCH | \
|
52
44
|
RUBY_EVENT_THREAD_END
|
53
45
|
|
54
46
|
#define sym(name) ID2SYM(rb_intern_const(name))
|
@@ -58,13 +50,13 @@ extern "C" size_t rb_obj_memsize_of(VALUE);
|
|
58
50
|
|
59
51
|
using namespace std;
|
60
52
|
|
61
|
-
|
53
|
+
VALUE rb_mVernier;
|
62
54
|
static VALUE rb_cVernierResult;
|
63
55
|
static VALUE rb_mVernierMarkerType;
|
64
56
|
static VALUE rb_cVernierCollector;
|
65
57
|
static VALUE rb_cStackTable;
|
66
58
|
|
67
|
-
static VALUE sym_state, sym_gc_by;
|
59
|
+
static VALUE sym_state, sym_gc_by, sym_fiber_id;
|
68
60
|
|
69
61
|
static const char *gvl_event_name(rb_event_flag_t event) {
|
70
62
|
switch (event) {
|
@@ -82,129 +74,6 @@ static const char *gvl_event_name(rb_event_flag_t event) {
|
|
82
74
|
return "no-event";
|
83
75
|
}
|
84
76
|
|
85
|
-
class TimeStamp {
|
86
|
-
static const uint64_t nanoseconds_per_second = 1000000000;
|
87
|
-
uint64_t value_ns;
|
88
|
-
|
89
|
-
TimeStamp(uint64_t value_ns) : value_ns(value_ns) {}
|
90
|
-
|
91
|
-
public:
|
92
|
-
TimeStamp() : value_ns(0) {}
|
93
|
-
|
94
|
-
static TimeStamp Now() {
|
95
|
-
struct timespec ts;
|
96
|
-
clock_gettime(CLOCK_MONOTONIC, &ts);
|
97
|
-
return TimeStamp(ts.tv_sec * nanoseconds_per_second + ts.tv_nsec);
|
98
|
-
}
|
99
|
-
|
100
|
-
static TimeStamp Zero() {
|
101
|
-
return TimeStamp(0);
|
102
|
-
}
|
103
|
-
|
104
|
-
// SleepUntil a specified timestamp
|
105
|
-
// Highly accurate manual sleep time
|
106
|
-
static void SleepUntil(const TimeStamp &target_time) {
|
107
|
-
if (target_time.zero()) return;
|
108
|
-
struct timespec ts = target_time.timespec();
|
109
|
-
|
110
|
-
int res;
|
111
|
-
do {
|
112
|
-
// do nothing until it's time :)
|
113
|
-
sleep(0);
|
114
|
-
} while (target_time > TimeStamp::Now());
|
115
|
-
}
|
116
|
-
|
117
|
-
static TimeStamp from_seconds(uint64_t s) {
|
118
|
-
return TimeStamp::from_milliseconds(s * 1000);
|
119
|
-
}
|
120
|
-
|
121
|
-
static TimeStamp from_milliseconds(uint64_t ms) {
|
122
|
-
return TimeStamp::from_microseconds(ms * 1000);
|
123
|
-
}
|
124
|
-
|
125
|
-
static TimeStamp from_microseconds(uint64_t us) {
|
126
|
-
return TimeStamp::from_nanoseconds(us * 1000);
|
127
|
-
}
|
128
|
-
|
129
|
-
static TimeStamp from_nanoseconds(uint64_t ns) {
|
130
|
-
return TimeStamp(ns);
|
131
|
-
}
|
132
|
-
|
133
|
-
TimeStamp operator-(const TimeStamp &other) const {
|
134
|
-
TimeStamp result = *this;
|
135
|
-
return result -= other;
|
136
|
-
}
|
137
|
-
|
138
|
-
TimeStamp &operator-=(const TimeStamp &other) {
|
139
|
-
if (value_ns > other.value_ns) {
|
140
|
-
value_ns = value_ns - other.value_ns;
|
141
|
-
} else {
|
142
|
-
// underflow
|
143
|
-
value_ns = 0;
|
144
|
-
}
|
145
|
-
return *this;
|
146
|
-
}
|
147
|
-
|
148
|
-
TimeStamp operator+(const TimeStamp &other) const {
|
149
|
-
TimeStamp result = *this;
|
150
|
-
return result += other;
|
151
|
-
}
|
152
|
-
|
153
|
-
TimeStamp &operator+=(const TimeStamp &other) {
|
154
|
-
uint64_t new_value = value_ns + other.value_ns;
|
155
|
-
value_ns = new_value;
|
156
|
-
return *this;
|
157
|
-
}
|
158
|
-
|
159
|
-
bool operator<(const TimeStamp &other) const {
|
160
|
-
return value_ns < other.value_ns;
|
161
|
-
}
|
162
|
-
|
163
|
-
bool operator<=(const TimeStamp &other) const {
|
164
|
-
return value_ns <= other.value_ns;
|
165
|
-
}
|
166
|
-
|
167
|
-
bool operator>(const TimeStamp &other) const {
|
168
|
-
return value_ns > other.value_ns;
|
169
|
-
}
|
170
|
-
|
171
|
-
bool operator>=(const TimeStamp &other) const {
|
172
|
-
return value_ns >= other.value_ns;
|
173
|
-
}
|
174
|
-
|
175
|
-
bool operator==(const TimeStamp &other) const {
|
176
|
-
return value_ns == other.value_ns;
|
177
|
-
}
|
178
|
-
|
179
|
-
bool operator!=(const TimeStamp &other) const {
|
180
|
-
return value_ns != other.value_ns;
|
181
|
-
}
|
182
|
-
|
183
|
-
uint64_t nanoseconds() const {
|
184
|
-
return value_ns;
|
185
|
-
}
|
186
|
-
|
187
|
-
uint64_t microseconds() const {
|
188
|
-
return value_ns / 1000;
|
189
|
-
}
|
190
|
-
|
191
|
-
bool zero() const {
|
192
|
-
return value_ns == 0;
|
193
|
-
}
|
194
|
-
|
195
|
-
struct timespec timespec() const {
|
196
|
-
struct timespec ts;
|
197
|
-
ts.tv_sec = nanoseconds() / nanoseconds_per_second;
|
198
|
-
ts.tv_nsec = (nanoseconds() % nanoseconds_per_second);
|
199
|
-
return ts;
|
200
|
-
}
|
201
|
-
};
|
202
|
-
|
203
|
-
std::ostream& operator<<(std::ostream& os, const TimeStamp& info) {
|
204
|
-
os << info.nanoseconds() << "ns";
|
205
|
-
return os;
|
206
|
-
}
|
207
|
-
|
208
77
|
// TODO: Rename FuncInfo
|
209
78
|
struct FrameInfo {
|
210
79
|
static const char *label_cstr(VALUE frame) {
|
@@ -269,58 +138,6 @@ namespace std {
|
|
269
138
|
};
|
270
139
|
}
|
271
140
|
|
272
|
-
// A basic semaphore built on sem_wait/sem_post
|
273
|
-
// post() is guaranteed to be async-signal-safe
|
274
|
-
class SignalSafeSemaphore {
|
275
|
-
#ifdef __APPLE__
|
276
|
-
dispatch_semaphore_t sem;
|
277
|
-
#else
|
278
|
-
sem_t sem;
|
279
|
-
#endif
|
280
|
-
|
281
|
-
public:
|
282
|
-
|
283
|
-
SignalSafeSemaphore(unsigned int value = 0) {
|
284
|
-
#ifdef __APPLE__
|
285
|
-
sem = dispatch_semaphore_create(value);
|
286
|
-
#else
|
287
|
-
sem_init(&sem, 0, value);
|
288
|
-
#endif
|
289
|
-
};
|
290
|
-
|
291
|
-
~SignalSafeSemaphore() {
|
292
|
-
#ifdef __APPLE__
|
293
|
-
dispatch_release(sem);
|
294
|
-
#else
|
295
|
-
sem_destroy(&sem);
|
296
|
-
#endif
|
297
|
-
};
|
298
|
-
|
299
|
-
void wait() {
|
300
|
-
#ifdef __APPLE__
|
301
|
-
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
|
302
|
-
#else
|
303
|
-
// Use sem_timedwait so that we get a crash instead of a deadlock for
|
304
|
-
// easier debugging
|
305
|
-
auto ts = (TimeStamp::Now() + TimeStamp::from_seconds(5)).timespec();
|
306
|
-
|
307
|
-
int ret;
|
308
|
-
do {
|
309
|
-
ret = sem_wait(&sem);
|
310
|
-
} while (ret && errno == EINTR);
|
311
|
-
assert(ret == 0);
|
312
|
-
#endif
|
313
|
-
}
|
314
|
-
|
315
|
-
void post() {
|
316
|
-
#ifdef __APPLE__
|
317
|
-
dispatch_semaphore_signal(sem);
|
318
|
-
#else
|
319
|
-
sem_post(&sem);
|
320
|
-
#endif
|
321
|
-
}
|
322
|
-
};
|
323
|
-
|
324
141
|
class RawSample {
|
325
142
|
public:
|
326
143
|
|
@@ -854,25 +671,35 @@ union MarkerInfo {
|
|
854
671
|
VALUE gc_by;
|
855
672
|
VALUE gc_state;
|
856
673
|
} gc_data;
|
674
|
+
struct {
|
675
|
+
VALUE fiber_id;
|
676
|
+
} fiber_data;
|
857
677
|
};
|
858
678
|
|
679
|
+
#define EACH_MARKER(XX) \
|
680
|
+
XX(GVL_THREAD_STARTED) \
|
681
|
+
XX(GVL_THREAD_EXITED) \
|
682
|
+
\
|
683
|
+
XX(GC_START) \
|
684
|
+
XX(GC_END_MARK) \
|
685
|
+
XX(GC_END_SWEEP) \
|
686
|
+
XX(GC_ENTER) \
|
687
|
+
XX(GC_EXIT) \
|
688
|
+
XX(GC_PAUSE) \
|
689
|
+
\
|
690
|
+
XX(THREAD_RUNNING) \
|
691
|
+
XX(THREAD_STALLED) \
|
692
|
+
XX(THREAD_SUSPENDED) \
|
693
|
+
\
|
694
|
+
XX(FIBER_SWITCH)
|
695
|
+
|
696
|
+
|
859
697
|
class Marker {
|
860
698
|
public:
|
861
699
|
enum Type {
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
MARKER_GC_START,
|
866
|
-
MARKER_GC_END_MARK,
|
867
|
-
MARKER_GC_END_SWEEP,
|
868
|
-
MARKER_GC_ENTER,
|
869
|
-
MARKER_GC_EXIT,
|
870
|
-
MARKER_GC_PAUSE,
|
871
|
-
|
872
|
-
MARKER_THREAD_RUNNING,
|
873
|
-
MARKER_THREAD_STALLED,
|
874
|
-
MARKER_THREAD_SUSPENDED,
|
875
|
-
|
700
|
+
#define XX(name) MARKER_ ## name,
|
701
|
+
EACH_MARKER(XX)
|
702
|
+
#undef XX
|
876
703
|
MARKER_MAX,
|
877
704
|
};
|
878
705
|
|
@@ -894,29 +721,33 @@ class Marker {
|
|
894
721
|
|
895
722
|
MarkerInfo extra_info;
|
896
723
|
|
897
|
-
VALUE to_array() {
|
898
|
-
VALUE record[
|
899
|
-
record[0] =
|
900
|
-
record[1] = INT2NUM(
|
901
|
-
record[2] =
|
902
|
-
record[3] = ULL2NUM(timestamp.nanoseconds());
|
724
|
+
VALUE to_array() const {
|
725
|
+
VALUE record[6] = {0};
|
726
|
+
record[0] = INT2NUM(type);
|
727
|
+
record[1] = INT2NUM(phase);
|
728
|
+
record[2] = ULL2NUM(timestamp.nanoseconds());
|
903
729
|
|
904
730
|
if (phase == Marker::Phase::INTERVAL) {
|
905
|
-
record[
|
731
|
+
record[3] = ULL2NUM(finish.nanoseconds());
|
906
732
|
}
|
907
733
|
else {
|
908
|
-
record[
|
734
|
+
record[3] = Qnil;
|
909
735
|
}
|
910
|
-
record[
|
736
|
+
record[4] = stack_index == -1 ? Qnil : INT2NUM(stack_index);
|
911
737
|
|
912
738
|
if (type == Marker::MARKER_GC_PAUSE) {
|
913
739
|
VALUE hash = rb_hash_new();
|
914
|
-
record[
|
740
|
+
record[5] = hash;
|
915
741
|
|
916
742
|
rb_hash_aset(hash, sym_gc_by, extra_info.gc_data.gc_by);
|
917
743
|
rb_hash_aset(hash, sym_state, extra_info.gc_data.gc_state);
|
744
|
+
} else if (type == Marker::MARKER_FIBER_SWITCH) {
|
745
|
+
VALUE hash = rb_hash_new();
|
746
|
+
record[5] = hash;
|
747
|
+
|
748
|
+
rb_hash_aset(hash, sym_fiber_id, extra_info.fiber_data.fiber_id);
|
918
749
|
}
|
919
|
-
return rb_ary_new_from_values(
|
750
|
+
return rb_ary_new_from_values(6, record);
|
920
751
|
}
|
921
752
|
};
|
922
753
|
|
@@ -934,7 +765,15 @@ class MarkerTable {
|
|
934
765
|
void record(Marker::Type type, int stack_index = -1, MarkerInfo extra_info = {}) {
|
935
766
|
const std::lock_guard<std::mutex> lock(mutex);
|
936
767
|
|
937
|
-
list.push_back({ type, Marker::INSTANT, TimeStamp::Now(), TimeStamp(), stack_index });
|
768
|
+
list.push_back({ type, Marker::INSTANT, TimeStamp::Now(), TimeStamp(), stack_index, extra_info });
|
769
|
+
}
|
770
|
+
|
771
|
+
VALUE to_array() const {
|
772
|
+
VALUE ary = rb_ary_new();
|
773
|
+
for (auto& marker: list) {
|
774
|
+
rb_ary_push(ary, marker.to_array());
|
775
|
+
}
|
776
|
+
return ary;
|
938
777
|
}
|
939
778
|
};
|
940
779
|
|
@@ -1139,6 +978,15 @@ class Thread {
|
|
1139
978
|
}
|
1140
979
|
}
|
1141
980
|
|
981
|
+
void record_fiber(VALUE fiber, StackTable &frame_list) {
|
982
|
+
RawSample sample;
|
983
|
+
sample.sample();
|
984
|
+
|
985
|
+
int stack_idx = translator.translate(frame_list, sample);
|
986
|
+
VALUE fiber_id = rb_obj_id(fiber);
|
987
|
+
markers->record(Marker::Type::MARKER_FIBER_SWITCH, stack_idx, { .fiber_data = { .fiber_id = fiber_id } });
|
988
|
+
}
|
989
|
+
|
1142
990
|
void set_state(State new_state) {
|
1143
991
|
if (state == Thread::State::STOPPED) {
|
1144
992
|
return;
|
@@ -1380,10 +1228,6 @@ class BaseCollector {
|
|
1380
1228
|
|
1381
1229
|
virtual void compact() {
|
1382
1230
|
};
|
1383
|
-
|
1384
|
-
virtual VALUE get_markers() {
|
1385
|
-
return rb_ary_new();
|
1386
|
-
};
|
1387
1231
|
};
|
1388
1232
|
|
1389
1233
|
class CustomCollector : public BaseCollector {
|
@@ -1658,6 +1502,19 @@ class GlobalSignalHandler {
|
|
1658
1502
|
LiveSample *GlobalSignalHandler::live_sample;
|
1659
1503
|
|
1660
1504
|
class TimeCollector : public BaseCollector {
|
1505
|
+
class TimeCollectorThread : public PeriodicThread {
|
1506
|
+
TimeCollector &time_collector;
|
1507
|
+
|
1508
|
+
void run_iteration() {
|
1509
|
+
time_collector.run_iteration();
|
1510
|
+
}
|
1511
|
+
|
1512
|
+
public:
|
1513
|
+
|
1514
|
+
TimeCollectorThread(TimeCollector &tc, TimeStamp interval) : PeriodicThread(interval), time_collector(tc) {
|
1515
|
+
};
|
1516
|
+
};
|
1517
|
+
|
1661
1518
|
GCMarkerTable gc_markers;
|
1662
1519
|
ThreadTable threads;
|
1663
1520
|
|
@@ -1680,8 +1537,10 @@ class TimeCollector : public BaseCollector {
|
|
1680
1537
|
collector->record_newobj(obj);
|
1681
1538
|
}
|
1682
1539
|
|
1540
|
+
TimeCollectorThread collector_thread;
|
1541
|
+
|
1683
1542
|
public:
|
1684
|
-
TimeCollector(VALUE stack_table, TimeStamp interval, unsigned int allocation_interval) : BaseCollector(stack_table), interval(interval), allocation_interval(allocation_interval), threads(*get_stack_table(stack_table)) {
|
1543
|
+
TimeCollector(VALUE stack_table, TimeStamp interval, unsigned int allocation_interval) : BaseCollector(stack_table), interval(interval), allocation_interval(allocation_interval), threads(*get_stack_table(stack_table)), collector_thread(*this, interval) {
|
1685
1544
|
}
|
1686
1545
|
|
1687
1546
|
void record_newobj(VALUE obj) {
|
@@ -1703,6 +1562,18 @@ class TimeCollector : public BaseCollector {
|
|
1703
1562
|
|
1704
1563
|
}
|
1705
1564
|
|
1565
|
+
void record_fiber(VALUE th, VALUE fiber) {
|
1566
|
+
threads.mutex.lock();
|
1567
|
+
for (auto &threadptr : threads.list) {
|
1568
|
+
auto &thread = *threadptr;
|
1569
|
+
if (th == thread.ruby_thread) {
|
1570
|
+
thread.record_fiber(fiber, threads.frame_list);
|
1571
|
+
break;
|
1572
|
+
}
|
1573
|
+
}
|
1574
|
+
threads.mutex.unlock();
|
1575
|
+
}
|
1576
|
+
|
1706
1577
|
void write_meta(VALUE meta, VALUE result) {
|
1707
1578
|
BaseCollector::write_meta(meta, result);
|
1708
1579
|
rb_hash_aset(meta, sym("interval"), ULL2NUM(interval.microseconds()));
|
@@ -1723,103 +1594,55 @@ class TimeCollector : public BaseCollector {
|
|
1723
1594
|
}
|
1724
1595
|
}
|
1725
1596
|
|
1726
|
-
|
1727
|
-
|
1728
|
-
VALUE main_thread = rb_thread_main();
|
1729
|
-
VALUE main_thread_id = rb_obj_id(main_thread);
|
1730
|
-
|
1731
|
-
for (auto& marker: this->gc_markers.list) {
|
1732
|
-
VALUE ary = marker.to_array();
|
1597
|
+
void run_iteration() {
|
1598
|
+
TimeStamp sample_start = TimeStamp::Now();
|
1733
1599
|
|
1734
|
-
RARRAY_ASET(ary, 0, main_thread_id);
|
1735
|
-
rb_ary_push(list, ary);
|
1736
|
-
}
|
1737
|
-
for (auto &thread : threads.list) {
|
1738
|
-
for (auto& marker: thread->markers->list) {
|
1739
|
-
VALUE ary = marker.to_array();
|
1740
|
-
RARRAY_ASET(ary, 0, thread->ruby_thread_id);
|
1741
|
-
rb_ary_push(list, ary);
|
1742
|
-
}
|
1743
|
-
}
|
1744
|
-
|
1745
|
-
return list;
|
1746
|
-
}
|
1747
|
-
|
1748
|
-
void sample_thread_run() {
|
1749
1600
|
LiveSample sample;
|
1750
1601
|
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
threads.mutex.lock();
|
1756
|
-
for (auto &threadptr : threads.list) {
|
1757
|
-
auto &thread = *threadptr;
|
1602
|
+
threads.mutex.lock();
|
1603
|
+
for (auto &threadptr : threads.list) {
|
1604
|
+
auto &thread = *threadptr;
|
1758
1605
|
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
} else {
|
1773
|
-
record_sample(sample.sample, sample_start, thread, CATEGORY_NORMAL);
|
1774
|
-
}
|
1775
|
-
} else if (thread.state == Thread::State::SUSPENDED) {
|
1776
|
-
thread.samples.record_sample(
|
1777
|
-
thread.stack_on_suspend_idx,
|
1778
|
-
sample_start,
|
1779
|
-
CATEGORY_IDLE);
|
1780
|
-
} else if (thread.state == Thread::State::READY) {
|
1781
|
-
thread.samples.record_sample(
|
1782
|
-
thread.stack_on_suspend_idx,
|
1783
|
-
sample_start,
|
1784
|
-
CATEGORY_STALLED);
|
1606
|
+
//if (thread.state == Thread::State::RUNNING) {
|
1607
|
+
//if (thread.state == Thread::State::RUNNING || (thread.state == Thread::State::SUSPENDED && thread.stack_on_suspend_idx < 0)) {
|
1608
|
+
if (thread.state == Thread::State::RUNNING) {
|
1609
|
+
//fprintf(stderr, "sampling %p on tid:%i\n", thread.ruby_thread, thread.native_tid);
|
1610
|
+
bool signal_sent = GlobalSignalHandler::get_instance()->record_sample(sample, thread.pthread_id);
|
1611
|
+
|
1612
|
+
if (!signal_sent) {
|
1613
|
+
// The thread has died. We probably should have caught
|
1614
|
+
// that by the GVL instrumentation, but let's try to get
|
1615
|
+
// it to a consistent state and stop profiling it.
|
1616
|
+
thread.set_state(Thread::State::STOPPED);
|
1617
|
+
} else if (sample.sample.empty()) {
|
1618
|
+
// fprintf(stderr, "skipping GC sample\n");
|
1785
1619
|
} else {
|
1620
|
+
record_sample(sample.sample, sample_start, thread, CATEGORY_NORMAL);
|
1786
1621
|
}
|
1622
|
+
} else if (thread.state == Thread::State::SUSPENDED) {
|
1623
|
+
thread.samples.record_sample(
|
1624
|
+
thread.stack_on_suspend_idx,
|
1625
|
+
sample_start,
|
1626
|
+
CATEGORY_IDLE);
|
1627
|
+
} else if (thread.state == Thread::State::READY) {
|
1628
|
+
thread.samples.record_sample(
|
1629
|
+
thread.stack_on_suspend_idx,
|
1630
|
+
sample_start,
|
1631
|
+
CATEGORY_STALLED);
|
1632
|
+
} else {
|
1787
1633
|
}
|
1788
|
-
|
1789
|
-
threads.mutex.unlock();
|
1790
|
-
|
1791
|
-
TimeStamp sample_complete = TimeStamp::Now();
|
1792
|
-
|
1793
|
-
next_sample_schedule += interval;
|
1794
|
-
|
1795
|
-
// If sampling falls behind, restart, and check in another interval
|
1796
|
-
if (next_sample_schedule < sample_complete) {
|
1797
|
-
next_sample_schedule = sample_complete + interval;
|
1798
|
-
}
|
1799
|
-
|
1800
|
-
TimeStamp::SleepUntil(next_sample_schedule);
|
1801
1634
|
}
|
1802
1635
|
|
1803
|
-
|
1804
|
-
}
|
1805
|
-
|
1806
|
-
static void *sample_thread_entry(void *arg) {
|
1807
|
-
#if HAVE_PTHREAD_SETNAME_NP
|
1808
|
-
#ifdef __APPLE__
|
1809
|
-
pthread_setname_np("Vernier profiler");
|
1810
|
-
#else
|
1811
|
-
pthread_setname_np(pthread_self(), "Vernier profiler");
|
1812
|
-
#endif
|
1813
|
-
#endif
|
1814
|
-
TimeCollector *collector = static_cast<TimeCollector *>(arg);
|
1815
|
-
collector->sample_thread_run();
|
1816
|
-
return NULL;
|
1636
|
+
threads.mutex.unlock();
|
1817
1637
|
}
|
1818
1638
|
|
1819
1639
|
static void internal_thread_event_cb(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass) {
|
1820
1640
|
TimeCollector *collector = static_cast<TimeCollector *>((void *)NUM2ULL(data));
|
1821
1641
|
|
1822
1642
|
switch (event) {
|
1643
|
+
case RUBY_EVENT_FIBER_SWITCH:
|
1644
|
+
collector->record_fiber(rb_thread_current(), rb_fiber_current());
|
1645
|
+
break;
|
1823
1646
|
case RUBY_EVENT_THREAD_BEGIN:
|
1824
1647
|
collector->threads.started(self);
|
1825
1648
|
break;
|
@@ -1913,11 +1736,7 @@ class TimeCollector : public BaseCollector {
|
|
1913
1736
|
|
1914
1737
|
running = true;
|
1915
1738
|
|
1916
|
-
|
1917
|
-
if (ret != 0) {
|
1918
|
-
perror("pthread_create");
|
1919
|
-
rb_bug("VERNIER: pthread_create failed");
|
1920
|
-
}
|
1739
|
+
collector_thread.start();
|
1921
1740
|
|
1922
1741
|
// Set the state of the current Ruby thread to RUNNING, which we know it
|
1923
1742
|
// is as it must have held the GVL to start the collector. We want to
|
@@ -1936,8 +1755,7 @@ class TimeCollector : public BaseCollector {
|
|
1936
1755
|
VALUE stop() {
|
1937
1756
|
BaseCollector::stop();
|
1938
1757
|
|
1939
|
-
|
1940
|
-
thread_stopped.wait();
|
1758
|
+
collector_thread.stop();
|
1941
1759
|
|
1942
1760
|
GlobalSignalHandler::get_instance()->uninstall();
|
1943
1761
|
|
@@ -1962,6 +1780,8 @@ class TimeCollector : public BaseCollector {
|
|
1962
1780
|
VALUE build_collector_result() {
|
1963
1781
|
VALUE result = BaseCollector::build_collector_result();
|
1964
1782
|
|
1783
|
+
rb_ivar_set(result, rb_intern("@gc_markers"), this->gc_markers.to_array());
|
1784
|
+
|
1965
1785
|
VALUE threads = rb_hash_new();
|
1966
1786
|
rb_ivar_set(result, rb_intern("@threads"), threads);
|
1967
1787
|
|
@@ -1969,8 +1789,7 @@ class TimeCollector : public BaseCollector {
|
|
1969
1789
|
VALUE hash = rb_hash_new();
|
1970
1790
|
thread->samples.write_result(hash);
|
1971
1791
|
thread->allocation_samples.write_result(hash);
|
1972
|
-
|
1973
|
-
rb_hash_aset(threads, thread->ruby_thread_id, hash);
|
1792
|
+
rb_hash_aset(hash, sym("markers"), thread->markers->to_array());
|
1974
1793
|
rb_hash_aset(hash, sym("tid"), ULL2NUM(thread->native_tid));
|
1975
1794
|
rb_hash_aset(hash, sym("started_at"), ULL2NUM(thread->started_at.nanoseconds()));
|
1976
1795
|
if (!thread->stopped_at.zero()) {
|
@@ -1979,6 +1798,7 @@ class TimeCollector : public BaseCollector {
|
|
1979
1798
|
rb_hash_aset(hash, sym("is_main"), thread->is_main() ? Qtrue : Qfalse);
|
1980
1799
|
rb_hash_aset(hash, sym("is_start"), thread->is_start(BaseCollector::start_thread) ? Qtrue : Qfalse);
|
1981
1800
|
|
1801
|
+
rb_hash_aset(threads, thread->ruby_thread_id, hash);
|
1982
1802
|
}
|
1983
1803
|
|
1984
1804
|
return result;
|
@@ -2051,13 +1871,6 @@ collector_stop(VALUE self) {
|
|
2051
1871
|
return result;
|
2052
1872
|
}
|
2053
1873
|
|
2054
|
-
static VALUE
|
2055
|
-
markers(VALUE self) {
|
2056
|
-
auto *collector = get_collector(self);
|
2057
|
-
|
2058
|
-
return collector->get_markers();
|
2059
|
-
}
|
2060
|
-
|
2061
1874
|
static VALUE
|
2062
1875
|
collector_sample(VALUE self) {
|
2063
1876
|
auto *collector = get_collector(self);
|
@@ -2111,24 +1924,10 @@ static VALUE collector_new(VALUE self, VALUE mode, VALUE options) {
|
|
2111
1924
|
|
2112
1925
|
static void
|
2113
1926
|
Init_consts(VALUE rb_mVernierMarkerPhase) {
|
2114
|
-
#define
|
2115
|
-
rb_define_const(rb_mVernierMarkerType, #name, INT2NUM(Marker::Type::MARKER_##name))
|
2116
|
-
|
2117
|
-
|
2118
|
-
MARKER_CONST(GVL_THREAD_EXITED);
|
2119
|
-
|
2120
|
-
MARKER_CONST(GC_START);
|
2121
|
-
MARKER_CONST(GC_END_MARK);
|
2122
|
-
MARKER_CONST(GC_END_SWEEP);
|
2123
|
-
MARKER_CONST(GC_ENTER);
|
2124
|
-
MARKER_CONST(GC_EXIT);
|
2125
|
-
MARKER_CONST(GC_PAUSE);
|
2126
|
-
|
2127
|
-
MARKER_CONST(THREAD_RUNNING);
|
2128
|
-
MARKER_CONST(THREAD_STALLED);
|
2129
|
-
MARKER_CONST(THREAD_SUSPENDED);
|
2130
|
-
|
2131
|
-
#undef MARKER_CONST
|
1927
|
+
#define XX(name) \
|
1928
|
+
rb_define_const(rb_mVernierMarkerType, #name, INT2NUM(Marker::Type::MARKER_##name));
|
1929
|
+
EACH_MARKER(XX)
|
1930
|
+
#undef XX
|
2132
1931
|
|
2133
1932
|
#define PHASE_CONST(name) \
|
2134
1933
|
rb_define_const(rb_mVernierMarkerPhase, #name, INT2NUM(Marker::Phase::name))
|
@@ -2140,11 +1939,12 @@ Init_consts(VALUE rb_mVernierMarkerPhase) {
|
|
2140
1939
|
#undef PHASE_CONST
|
2141
1940
|
}
|
2142
1941
|
|
2143
|
-
extern "C" void
|
1942
|
+
extern "C" __attribute__ ((__visibility__("default"))) void
|
2144
1943
|
Init_vernier(void)
|
2145
1944
|
{
|
2146
1945
|
sym_state = sym("state");
|
2147
1946
|
sym_gc_by = sym("gc_by");
|
1947
|
+
sym_fiber_id = sym("fiber_id");
|
2148
1948
|
rb_gc_latest_gc_info(sym_state); // HACK: needs to be warmed so that it can be called during GC
|
2149
1949
|
|
2150
1950
|
rb_mVernier = rb_define_module("Vernier");
|
@@ -2160,7 +1960,6 @@ Init_vernier(void)
|
|
2160
1960
|
rb_define_method(rb_cVernierCollector, "sample", collector_sample, 0);
|
2161
1961
|
rb_define_method(rb_cVernierCollector, "stack_table", collector_stack_table, 0);
|
2162
1962
|
rb_define_private_method(rb_cVernierCollector, "finish", collector_stop, 0);
|
2163
|
-
rb_define_private_method(rb_cVernierCollector, "markers", markers, 0);
|
2164
1963
|
|
2165
1964
|
rb_cStackTable = rb_define_class_under(rb_mVernier, "StackTable", rb_cObject);
|
2166
1965
|
rb_undef_alloc_func(rb_cStackTable);
|
@@ -2179,6 +1978,7 @@ Init_vernier(void)
|
|
2179
1978
|
rb_define_method(rb_cStackTable, "func_count", StackTable::stack_table_func_count, 0);
|
2180
1979
|
|
2181
1980
|
Init_consts(rb_mVernierMarkerPhase);
|
1981
|
+
Init_memory();
|
2182
1982
|
|
2183
1983
|
//static VALUE gc_hook = Data_Wrap_Struct(rb_cObject, collector_mark, NULL, &_collector);
|
2184
1984
|
//rb_global_variable(&gc_hook);
|