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.
@@ -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 */