vernier 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- static VALUE rb_mVernier;
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
- MARKER_GVL_THREAD_STARTED,
863
- MARKER_GVL_THREAD_EXITED,
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[7] = {0};
899
- record[0] = Qnil; // FIXME
900
- record[1] = INT2NUM(type);
901
- record[2] = INT2NUM(phase);
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[4] = ULL2NUM(finish.nanoseconds());
731
+ record[3] = ULL2NUM(finish.nanoseconds());
906
732
  }
907
733
  else {
908
- record[4] = Qnil;
734
+ record[3] = Qnil;
909
735
  }
910
- record[5] = stack_index == -1 ? Qnil : INT2NUM(stack_index);
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[6] = hash;
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(7, record);
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
- VALUE get_markers() {
1727
- VALUE list = rb_ary_new();
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
- TimeStamp next_sample_schedule = TimeStamp::Now();
1752
- while (running) {
1753
- TimeStamp sample_start = TimeStamp::Now();
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
- //if (thread.state == Thread::State::RUNNING) {
1760
- //if (thread.state == Thread::State::RUNNING || (thread.state == Thread::State::SUSPENDED && thread.stack_on_suspend_idx < 0)) {
1761
- if (thread.state == Thread::State::RUNNING) {
1762
- //fprintf(stderr, "sampling %p on tid:%i\n", thread.ruby_thread, thread.native_tid);
1763
- bool signal_sent = GlobalSignalHandler::get_instance()->record_sample(sample, thread.pthread_id);
1764
-
1765
- if (!signal_sent) {
1766
- // The thread has died. We probably should have caught
1767
- // that by the GVL instrumentation, but let's try to get
1768
- // it to a consistent state and stop profiling it.
1769
- thread.set_state(Thread::State::STOPPED);
1770
- } else if (sample.sample.empty()) {
1771
- // fprintf(stderr, "skipping GC sample\n");
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
- thread_stopped.post();
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
- int ret = pthread_create(&sample_thread, NULL, &sample_thread_entry, this);
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
- running = false;
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 MARKER_CONST(name) \
2115
- rb_define_const(rb_mVernierMarkerType, #name, INT2NUM(Marker::Type::MARKER_##name))
2116
-
2117
- MARKER_CONST(GVL_THREAD_STARTED);
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);
@@ -3,4 +3,8 @@
3
3
 
4
4
  #include "ruby.h"
5
5
 
6
+ extern VALUE rb_mVernier;
7
+
8
+ void Init_memory();
9
+
6
10
  #endif /* VERNIER_H */