8000 Merge pull request #182 from Shopify/tracepoint-support · jhawthorn/ruby@5d0c343 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5d0c343

Browse files
authored
Merge pull request ruby#182 from Shopify/tracepoint-support
TracePoint support
2 parents ed691bb + ce498cc commit 5d0c343

File tree

11 files changed

+560
-83
lines changed

11 files changed

+560
-83
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ To cite this repository in your publications, please use this bibtex snippet:
3232

3333
YJIT is a work in progress and as such may not yet be mature enough for mission-critical software. Below is a list of known limitations, all of which we plan to eventually address:
3434

35-
- No support for the `TracePoint` API (see [#54](https://github.com/Shopify/yjit/issues/54)).
3635
- No garbage collection for generated code.
3736

3837
Because there is no GC for generated code yet, your software could run out of executable memory if it is large enough. You can change how much executable memory is allocated using [YJIT's command-line options](https://github.com/Shopify/yjit#command-line-options).

bootstraptest/test_yjit.rb

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,3 +1612,217 @@ def bar(baz, quux)
16121612
bar(123, 1.1)
16131613
bar(123, 1.1)
16141614
}
1615+
1616+
# test enabling a line TracePoint in a C method call
1617+
assert_equal '[[:line, true]]', %q{
1618+
events = []
1619+
events.instance_variable_set(
1620+
:@tp,
1621+
TracePoint.new(:line) { |tp| events << [tp.event, tp.lineno] if tp.path == __FILE__ }
1622+
)
1623+
def events.to_str
1624+
@tp.enable; ''
1625+
end
1626+
1627+
# Stay in generated code while enabling tracing
1628+
def events.compiled(obj)
1629+
String(obj)
1630+
@tp.disable; __LINE__
1631+
end
1632+
1633+
line = events.compiled(events)
1634+
events[0][-1] = (events[0][-1] == line)
1635+
1636+
events
1637+
}
1638+
1639+
# test enabling a c_return TracePoint in a C method call
1640+
assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{
1641+
events = []
1642+
events.instance_variable_set(:@tp, TracePoint.new(:c_return) { |tp| events << [tp.event, tp.method_id, tp.callee_id, tp.return_value] })
1643+
def events.to_str
1644+
@tp.enable; 'events_to_str'
1645+
end
1646+
1647+
# Stay in generated code while enabling tracing
1648+
alias string_alias String
1649+
def events.compiled(obj)
1650+
string_alias(obj)
1651+
@tp.disable
1652+
end
1653+
1654+
events.compiled(events)
1655+
1656+
events
1657+
}
1658+
1659+
# test enabling a TracePoint that targets a particular line in a C method call
1660+
assert_equal '[true]', %q{
1661+
events = []
1662+
events.instance_variable_set(:@tp, TracePoint.new(:line) { |tp| events << tp.lineno })
1663+
def events.to_str
1664+
@tp.enable(target: method(:compiled))
1665+
''
1666+
end
1667+
1668+
# Stay in generated code while enabling tracing
1669+
def events.compiled(obj)
1670+
String(obj)
1671+
__LINE__
1672+
end
1673+
1674+
line = events.compiled(events)
1675+
events[0] = (events[0] == line)
1676+
1677+
events
1678+
}
1679+
1680+
# test enabling tracing in the middle of splatarray
1681+
assert_equal '[true]', %q{
1682+
events = []
1683+
obj = Object.new
1684+
obj.instance_variable_set(:@tp, TracePoint.new(:line) { |tp| events << tp.lineno })
1685+
def obj.to_a
1686+
@tp.enable(target: method(:compiled))
1687+
[]
1688+
end
1689+
1690+
# Enable tracing in the middle of the splatarray instruction
1691+
def obj.compiled(obj)
1692+
* = *obj
1693+
__LINE__
1694+
end
1695+
1696+
obj.compiled([])
1697+
line = obj.compiled(obj)
1698+
events[0] = (events[0] == line)
1699+
1700+
events
1701+
}
1702+
1703+
# test enabling tracing in the middle of opt_aref. Different since the codegen
1704+
# for it ends in a jump.
1705+
assert_equal '[true]', %q{
1706+
def lookup(hash, tp)
1707+
hash[42]
1708+
tp.disable; __LINE__
1709+
end
1710+
1711+
lines = []
1712+
tp = TracePoint.new(:line) { lines << _1.lineno if _1.path == __FILE__ }
1713+
1714+
lookup(:foo, tp)
1715+
lookup({}, tp)
1716+
1717+
enable_tracing_on_missing = Hash.new { tp.enable }
1718+
1719+
expected_line = lookup(enable_tracing_on_missing, tp)
1720+
1721+
lines[0] = true if lines[0] == expected_line
1722+
1723+
lines
1724+
}
1725+
1726+
# test enabling c_call tracing before compiling
1727+
assert_equal '[[:c_call, :itself]]', %q{
1728+
def shouldnt_compile
1729+
itself
1730+
end
1731+
1732+
events = []
1733+
tp = TracePoint.new(:c_call) { |tp| events << [tp.event, tp.method_id] }
1734+
1735+
# assume first call compiles
1736+
tp.enable { shouldnt_compile }
1737+
1738+
events
1739+
}
1740+
1741+
# test enabling c_return tracing before compiling
1742+
assert_equal '[[:c_return, :itself, main]]', %q{
1743+
def shouldnt_compile
1744+
itself
1745+
end
1746+
1747+
events = []
1748+
tp = TracePoint.new(:c_return) { |tp| events << [tp.event, tp.method_id, tp.return_value] }
1749+
1750+
# assume first call compiles
1751+
tp.enable { shouldnt_compile }
1752+
1753+
events
1754+
}
1755+
1756+
# test enabling tracing for a suspended fiber
1757+
assert_equal '[[:return, 42]]', %q{
1758+
def traced_method
1759+
Fiber.yield
1760+
42
1761+
end
1762+
1763+
events = []
1764+
tp = TracePoint.new(:return) { events << [_1.event, _1.return_value] }
1765+
# assume first call compiles
1766+
fiber = Fiber.new { traced_method }
1767+
fiber.resume
1768+
tp.enable(target: method(:traced_method))
1769+
fiber.resume
1770+
1771+
events
1772+
}
1773+
1774+
# test compiling on non-tracing ractor then running on a tracing one
1775+
assert_equal '[:itself]', %q{
1776+
def traced_method
1777+
itself
1778+
end
1779+
1780+
1781+
tracing_ractor = Ractor.new do
1782+
# 1: start tracing
1783+
events = []
1784+
tp = TracePoint.new(:c_call) { events << _1.method_id }
1785+
tp.enable
1786+
Ractor.yield(nil)
1787+
1788+
# 3: run compiled method on tracing ractor
1789+
Ractor.yield(nil)
1790+
traced_method
1791+
1792+
events
1793+
ensure
1794+
tp&.disable
1795+
end
1796+
1797+
tracing_ractor.take
1798+
1799+
# 2: compile on non tracing ractor
1800+
traced_method
1801+
1802+
tracing_ractor.take
1803+
tracing_ractor.take
1804+
}
1805+
1806+
# Try to hit a lazy branch stub while another ractor enables tracing
1807+
assert_equal '42', %q{
1808+
def compiled(arg)
1809+
if arg
1810+
arg + 1
1811+
else
1812+
itself
1813+
itself
1814+
end
1815+
end
1816+
1817+
ractor = Ractor.new do
1818+
compiled(false)
1819+
Ractor.yield(nil)
1820+
compiled(41)
1821+
end
1822+
1823+
tp = TracePoint.new(:line) { itself }
1824+
ractor.tak 6D4E e
1825+
tp.enable
1826+
1827+
ractor.take
1828+
}

common.mk

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7050,7 +7050,6 @@ iseq.$(OBJEXT): {$(VPATH)}vm_callinfo.h
70507050
iseq.$(OBJEXT): {$(VPATH)}vm_core.h
70517051
iseq.$(OBJEXT): {$(VPATH)}vm_opts.h
70527052
iseq.$(OBJEXT): {$(VPATH)}yjit.h
7053-
iseq.$(OBJEXT): {$(VPATH)}yjit_asm.h
70547053
load.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
70557054
load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
70567055
load.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -16641,6 +16640,7 @@ yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/gc.h
1664116640
yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/imemo.h
1664216641
yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/object.h
1664316642
yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/re.h
16643+
yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
1664416644
yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/serial.h
1664516645
yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
1664616646
yjit_codegen.$(OBJEXT): $(top_srcdir)/internal/string.h
@@ -16665,6 +16665,7 @@ yjit_codegen.$(OBJEXT): {$(VPATH)}darray.h
1666516665
yjit_codegen.$(OBJEXT): {$(VPATH)}debug_counter.h
1666616666
yjit_codegen.$(OBJEXT): {$(VPATH)}defines.h
1666716667
yjit_codegen.$(OBJEXT): {$(VPATH)}encoding.h
16668+
yjit_codegen.$(OBJEXT): {$(VPATH)}gc.h
1666816669
yjit_codegen.$(OBJEXT): {$(VPATH)}id.h
1666916670
yjit_codegen.$(OBJEXT): {$(VPATH)}id_table.h
1667016671
yjit_codegen.$(OBJEXT): {$(VPATH)}insns.def
@@ -16817,6 +16818,9 @@ yjit_codegen.$(OBJEXT): {$(VPATH)}missing.h
1681716818
yjit_codegen.$(OBJEXT): {$(VPATH)}node.h
1681816819
yjit_codegen.$(OBJEXT): {$(VPATH)}onigmo.h
1681916820
yjit_codegen.$(OBJEXT): {$(VPATH)}oniguruma.h
16821+
yjit_codegen.$(OBJEXT): {$(VPATH)}probes.dmyh
16822+
yjit_codegen.$(OBJEXT): {$(VPATH)}probes.h
16823+
yjit_codegen.$(OBJEXT): {$(VPATH)}probes_helper.h
1682016824
yjit_codegen.$(OBJEXT): {$(VPATH)}ruby_assert.h
1682116825
yjit_codegen.$(OBJEXT): {$(VPATH)}ruby_atomic.h
1682216826
yjit_codegen.$(OBJEXT): {$(VPATH)}st.h

iseq.c

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3181,14 +3181,6 @@ typedef struct insn_data_struct {
31813181
} insn_data_t;
31823182
static insn_data_t insn_data[VM_INSTRUCTION_SIZE/2];
31833183

3184-
3185-
3186-
3187-
#include "yjit_asm.h"
3188-
3189-
3190-
3191-
31923184
void
31933185
rb_vm_encoded_insn_data_table_init(void)
31943186
{
@@ -3305,10 +3297,6 @@ iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events,
33053297

33063298
VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
33073299

3308-
#if USE_MJIT
3309-
// Force write the jit function to NULL
3310-
*((jit_func_t *)(&body->jit_func)) = 0;
3311-
#endif
33123300

33133301
for (pc=0; pc<body->iseq_size;) {
33143302
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pc);
@@ -3445,10 +3433,6 @@ rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events)
34453433
rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
34463434
pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & enabled_events, true);
34473435
}
3448-
#if USE_MJIT
3449-
// Force write the jit function to NULL
3450-
*((jit_func_t *)(&body->jit_func)) = 0;
3451-
#endif
34523436
}
34533437
}
34543438

vm_trace.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "ruby/debug.h"
3131
#include "vm_core.h"
3232
#include "ruby/ractor.h"
33+
#include "yjit.h"
3334

3435
#include "builtin.h"
3536

@@ -95,6 +96,13 @@ update_global_event_hook(rb_event_flag_t vm_events)
9596
ruby_vm_event_flags = vm_events;
9697
ruby_vm_event_enabled_global_flags |= vm_events;
9798
rb_objspace_set_event_hook(vm_events);
99+
100+
if (vm_events & RUBY_EVENT_TRACEPOINT_ALL) {
101+
// Invalidate all code if listening for any TracePoint event.
102+
// Internal events fire inside C routines so don't need special handling.
103+
// Do this last so other ractors see updated vm events when they wake up.
104+
yjit_tracing_invalidate_all();
105+
}
98106
}
99107

100108
/* add/remove hooks */
@@ -1208,6 +1216,8 @@ rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
12081216
rb_raise(rb_eArgError, "can not enable any hooks");
12091217
}
12101218

1219+
yjit_tracing_invalidate_all();
1220+
12111221
ruby_vm_event_local_num++;
12121222

12131223
tp->tracing = 1;

yjit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,6 @@ void rb_yjit_iseq_update_references(const struct rb_iseq_constant_body *body);
7676
void rb_yjit_iseq_free(const struct rb_iseq_constant_body *body);
7777
void rb_yjit_before_ractor_spawn(void);
7878
void yjit_constant_ic_update(const rb_iseq_t *iseq, IC ic);
79+
void yjit_tracing_invalidate_all(void);
7980

8081
#endif // #ifndef YJIT_H

0 commit comments

Comments
 (0)
0