Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/yjit-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,12 @@ jobs:
if: ${{ matrix.test_task == 'check' && matrix.skipped_tests }}
continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}

- if: ${{ failure() }}
- name: Dump crash logs
if: ${{ failure() }}
continue-on-error: true
run: tail --verbose --lines=+1 rb_crash_*.txt
run: |
tail --verbose --lines=+1 rb_crash_*.txt
exit 1

- uses: ./.github/actions/slack
with:
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/yjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ jobs:
- name: Dump crash logs
if: ${{ failure() }}
continue-on-error: true
run: tail --verbose --lines=+1 rb_crash_*.txt
run: |
tail --verbose --lines=+1 rb_crash_*.txt
exit 1

- uses: ./.github/actions/slack
with:
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/zjit-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ jobs:
- name: Dump crash logs
if: ${{ failure() }}
continue-on-error: true
run: tail --verbose --lines=+1 rb_crash_*.txt
run: |
tail --verbose --lines=+1 rb_crash_*.txt
exit 1

- uses: ./.github/actions/slack
with:
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/zjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ jobs:
- name: Dump crash logs
if: ${{ failure() }}
continue-on-error: true
run: tail --verbose --lines=+1 rb_crash_*.txt
run: |
tail --verbose --lines=+1 rb_crash_*.txt
exit 1

- uses: ./.github/actions/slack
with:
Expand Down
52 changes: 36 additions & 16 deletions doc/string/partition.rdoc
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
Returns a 3-element array of substrings of +self+.

Matches a pattern against +self+, scanning from the beginning.
The pattern is:
If +pattern+ is matched, returns the array:

- +string_or_regexp+ itself, if it is a Regexp.
- <tt>Regexp.quote(string_or_regexp)</tt>, if +string_or_regexp+ is a string.
[pre_match, first_match, post_match]

If the pattern is matched, returns pre-match, first-match, post-match:
where:

'hello'.partition('l') # => ["he", "l", "lo"]
'hello'.partition('ll') # => ["he", "ll", "o"]
'hello'.partition('h') # => ["", "h", "ello"]
'hello'.partition('o') # => ["hell", "o", ""]
'hello'.partition(/l+/) #=> ["he", "ll", "o"]
'hello'.partition('') # => ["", "", "hello"]
'тест'.partition('т') # => ["", "т", "ест"]
'こんにちは'.partition('に') # => ["こん", "に", "ちは"]
- +first_match+ is the first-found matching substring.
- +pre_match+ and +post_match+ are the preceding and following substrings.

If the pattern is not matched, returns a copy of +self+ and two empty strings:
If +pattern+ is not matched, returns the array:

'hello'.partition('x') # => ["hello", "", ""]
[self.dup, "", ""]

Related: String#rpartition, String#split.
Note that in the examples below, a returned string <tt>'hello'</tt>
is a copy of +self+, not +self+.

If +pattern+ is a Regexp, performs the equivalent of <tt>self.match(pattern)</tt>
(also setting {pattern-matching global variables}[rdoc-ref:globals.md@Pattern+Matching]):

'hello'.partition(/h/) # => ["", "h", "ello"]
'hello'.partition(/l/) # => ["he", "l", "lo"]
'hello'.partition(/l+/) # => ["he", "ll", "o"]
'hello'.partition(/o/) # => ["hell", "o", ""]
'hello'.partition(/^/) # => ["", "", "hello"]
'hello'.partition(//) # => ["", "", "hello"]
'hello'.partition(/$/) # => ["hello", "", ""]
'hello'.partition(/x/) # => ["hello", "", ""]

If +pattern+ is not a Regexp, converts it to a string (if it is not already one),
then performs the equivalet of <tt>self.index(pattern)</tt>
(and does _not_ set {pattern-matching global variables}[rdoc-ref:globals.md@Pattern+Matching]):

'hello'.partition('h') # => ["", "h", "ello"]
'hello'.partition('l') # => ["he", "l", "lo"]
'hello'.partition('ll') # => ["he", "ll", "o"]
'hello'.partition('o') # => ["hell", "o", ""]
'hello'.partition('') # => ["", "", "hello"]
'hello'.partition('x') # => ["hello", "", ""]
'тест'.partition('т') # => ["", "т", "ест"]
'こんにちは'.partition('に') # => ["こん", "に", "ちは"]

Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString].
14 changes: 14 additions & 0 deletions doc/zjit.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,20 @@ To build with stats support:
make -j
```

### Tracing side exits

Through [Stackprof](https://github.com/tmm1/stackprof), detailed information about the methods that the JIT side-exits from can be displayed after some execution of a program. Note that the use of `--zjit-trace-exits` must be used alongside `--zjit-stats`.

```bash
./miniruby --zjit-stats --zjit-trace-exits script.rb
```

A file called `zjit_exit_locations.dump` will be created in the same directory as `script.rb`. Viewing the side exited methods can be done with Stackprof:

```bash
stackprof path/to/zjit_exit_locations.dump
```

## ZJIT Glossary

This glossary contains terms that are helpful for understanding ZJIT.
Expand Down
8 changes: 8 additions & 0 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3070,6 +3070,14 @@ rb_gc_mark_roots(void *objspace, const char **categoryp)
}
#endif

#if USE_ZJIT
void rb_zjit_root_mark(void);
if (rb_zjit_enabled_p) {
MARK_CHECKPOINT("ZJIT");
rb_zjit_root_mark();
}
#endif

MARK_CHECKPOINT("machine_context");
mark_current_machine_context(ec);

Expand Down
10 changes: 7 additions & 3 deletions string.c
Original file line number Diff line number Diff line change
Expand Up @@ -6983,12 +6983,16 @@ rb_str_bytesplice(int argc, VALUE *argv, VALUE str)

/*
* call-seq:
* reverse -> string
* reverse -> new_string
*
* Returns a new string with the characters from +self+ in reverse order.
*
* 'stressed'.reverse # => "desserts"
* 'drawer'.reverse # => "reward"
* 'reviled'.reverse # => "deliver"
* 'stressed'.reverse # => "desserts"
* 'semordnilaps'.reverse # => "spalindromes"
*
* Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String].
*/

static VALUE
Expand Down Expand Up @@ -11217,7 +11221,7 @@ rb_str_center(int argc, VALUE *argv, VALUE str)

/*
* call-seq:
* partition(string_or_regexp) -> [head, match, tail]
* partition(pattern) -> [pre_match, first_match, post_match]
*
* :include: doc/string/partition.rdoc
*
Expand Down
6 changes: 6 additions & 0 deletions thread_none.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,10 @@ rb_thread_prevent_fork(void *(*func)(void *), void *data)
return func(data);
}

void
rb_thread_malloc_stack_set(rb_thread_t *th, void *stack)
{
// no-op
}

#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
7 changes: 7 additions & 0 deletions thread_pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -3492,4 +3492,11 @@ rb_thread_lock_native_thread(void)
return is_snt;
}

void
rb_thread_malloc_stack_set(rb_thread_t *th, void *stack)
{
th->sched.malloc_stack = true;
th->sched.context_stack = stack;
}

#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
6 changes: 6 additions & 0 deletions thread_win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -1020,4 +1020,10 @@ rb_thread_prevent_fork(void *(*func)(void *), void *data)
return func(data);
}

void
rb_thread_malloc_stack_set(rb_thread_t *th, void *stack)
{
// no-op
}

#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
7 changes: 4 additions & 3 deletions vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -3280,7 +3280,7 @@ ruby_vm_destruct(rb_vm_t *vm)

if (vm) {
rb_thread_t *th = vm->ractor.main_thread;
VALUE *stack = th->ec->vm_stack;

if (rb_free_at_exit) {
rb_free_encoded_insn_data();
rb_free_global_enc_table();
Expand Down Expand Up @@ -3345,7 +3345,6 @@ ruby_vm_destruct(rb_vm_t *vm)
rb_free_default_rand_key();
if (th && vm->fork_gen == 0) {
/* If we have forked, main_thread may not be the initial thread */
xfree(stack);
ruby_mimfree(th);
}
}
Expand Down Expand Up @@ -3827,7 +3826,9 @@ th_init(rb_thread_t *th, VALUE self, rb_vm_t *vm)

if (self == 0) {
size_t size = vm->default_params.thread_vm_stack_size / sizeof(VALUE);
rb_ec_initialize_vm_stack(th->ec, ALLOC_N(VALUE, size), size);
VALUE *stack = ALLOC_N(VALUE, size);
rb_ec_initialize_vm_stack(th->ec, stack, size);
rb_thread_malloc_stack_set(th, stack);
}
else {
VM_ASSERT(th->ec->cfp == NULL);
Expand Down
1 change: 1 addition & 0 deletions vm_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,7 @@ VALUE *rb_vm_svar_lep(const rb_execution_context_t *ec, const rb_control_frame_t
int rb_vm_get_sourceline(const rb_control_frame_t *);
void rb_vm_stack_to_heap(rb_execution_context_t *ec);
void ruby_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame);
void rb_thread_malloc_stack_set(rb_thread_t *th, void *stack);
rb_thread_t * ruby_thread_from_native(void);
int ruby_thread_set_native(rb_thread_t *th);
int rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, ID *called_idp, VALUE *klassp);
Expand Down
4 changes: 2 additions & 2 deletions yjit/src/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ fn rb_yjit_gen_stats_dict(key: VALUE) -> VALUE {
/// and line samples. Their length should be the same, however the data stored in
/// them is different.
#[no_mangle]
pub extern "C" fn rb_yjit_record_exit_stack(_exit_pc: *const VALUE)
pub extern "C" fn rb_yjit_record_exit_stack(exit_pc: *const VALUE)
{
// Return if YJIT is not enabled
if !yjit_enabled_p() {
Expand All @@ -920,7 +920,7 @@ pub extern "C" fn rb_yjit_record_exit_stack(_exit_pc: *const VALUE)
#[cfg(not(test))]
{
// Get the opcode from the encoded insn handler at this PC
let insn = unsafe { rb_vm_insn_addr2opcode((*_exit_pc).as_ptr()) };
let insn = unsafe { rb_vm_insn_addr2opcode((*exit_pc).as_ptr()) };

// Use the same buffer size as Stackprof.
const BUFF_LEN: usize = 2048;
Expand Down
91 changes: 91 additions & 0 deletions zjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,95 @@

#include <errno.h>

#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x)))

// For a given raw_sample (frame), set the hash with the caller's
// name, file, and line number. Return the hash with collected frame_info.
static void
rb_zjit_add_frame(VALUE hash, VALUE frame)
{
VALUE frame_id = PTR2NUM(frame);

if (RTEST(rb_hash_aref(hash, frame_id))) {
return;
}
else {
VALUE frame_info = rb_hash_new();
// Full label for the frame
VALUE name = rb_profile_frame_full_label(frame);
// Absolute path of the frame from rb_iseq_realpath
VALUE file = rb_profile_frame_absolute_path(frame);
// Line number of the frame
VALUE line = rb_profile_frame_first_lineno(frame);

// If absolute path isn't available use the rb_iseq_path
if (NIL_P(file)) {
file = rb_profile_frame_path(frame);
}

rb_hash_aset(frame_info, ID2SYM(rb_intern("name")), name);
rb_hash_aset(frame_info, ID2SYM(rb_intern("file")), file);

if (line != INT2FIX(0)) {
rb_hash_aset(frame_info, ID2SYM(rb_intern("line")), line);
}

rb_hash_aset(hash, frame_id, frame_info);
}
}

// Parses the ZjitExitLocations raw_samples and line_samples collected by
// rb_zjit_record_exit_stack and turns them into 3 hashes (raw, lines, and frames) to
// be used by RubyVM::ZJIT.exit_locations. zjit_raw_samples represents the raw frames information
// (without name, file, and line), and zjit_line_samples represents the line information
// of the iseq caller.
VALUE
rb_zjit_exit_locations_dict(VALUE *zjit_raw_samples, int *zjit_line_samples, int samples_len)
{
VALUE result = rb_hash_new();
VALUE raw_samples = rb_ary_new_capa(samples_len);
VALUE line_samples = rb_ary_new_capa(samples_len);
VALUE frames = rb_hash_new();
int idx = 0;

// While the index is less than samples_len, parse zjit_raw_samples and
// zjit_line_samples, then add casted values to raw_samples and line_samples array.
while (idx < samples_len) {
int num = (int)zjit_raw_samples[idx];
int line_num = (int)zjit_line_samples[idx];
idx++;

rb_ary_push(raw_samples, SIZET2NUM(num));
rb_ary_push(line_samples, INT2NUM(line_num));

// Loop through the length of samples_len and add data to the
// frames hash. Also push the current value onto the raw_samples
// and line_samples arrary respectively.
for (int o = 0; o < num; o++) {
rb_zjit_add_frame(frames, zjit_raw_samples[idx]);
rb_ary_push(raw_samples, SIZET2NUM(zjit_raw_samples[idx]));
rb_ary_push(line_samples, INT2NUM(zjit_line_samples[idx]));
idx++;
}

rb_ary_push(raw_samples, SIZET2NUM(zjit_raw_samples[idx]));
rb_ary_push(line_samples, INT2NUM(zjit_line_samples[idx]));
idx++;

rb_ary_push(raw_samples, SIZET2NUM(zjit_raw_samples[idx]));
rb_ary_push(line_samples, INT2NUM(zjit_line_samples[idx]));
idx++;
}

// Set add the raw_samples, line_samples, and frames to the results
// hash.
rb_hash_aset(result, ID2SYM(rb_intern("raw")), raw_samples);
rb_hash_aset(result, ID2SYM(rb_intern("lines")), line_samples);
rb_hash_aset(result, ID2SYM(rb_intern("frames")), frames);

return result;
}

void rb_zjit_profile_disable(const rb_iseq_t *iseq);

void
Expand Down Expand Up @@ -217,6 +306,8 @@ VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);
VALUE rb_zjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_print_stats_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_get_exit_locations(rb_execution_context_t *ec, VALUE self);

// Preprocessed zjit.rb generated during build
#include "zjit.rbinc"
Loading