vernier 1.4.0 → 1.5.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/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);
|