Slide 1

Slide 1 text

Ruby & JVM: A (Jruby) Love Story Yarden Laifenfeld @yardenlaif

Slide 2

Slide 2 text

2 Who Am I? • Software Engineer at Rookout • Background in low level C programming in linux IOT environments • Ruby, Java, Go • C#, Python, JavaScript, C++

Slide 3

Slide 3 text

What is JRuby? 3 “ JRuby is a 100% Java implementation of the Ruby programming language. It is Ruby for the JVM. ” - the JRuby wiki

Slide 4

Slide 4 text

4 Should you use JRuby?

Slide 5

Slide 5 text

JRuby Advantages 5 ● Integration with Java libraries ● Running atop the JVM ○ Threads ○ Long running applications

Slide 6

Slide 6 text

Why JRuby.md - Jordan Sissel, 2010 https://gist.github.com/jordansissel/978956 6 ● core and stdlib ruby changes violently and without notice and without backwards compatibility. I want nothing of that. ● need a cross-ruby date library that isn't part of stdlib (see previous point) and is also good. ● need an easy way to use multiple cpus that is cross-ruby (threads are not it)

Slide 7

Slide 7 text

CRuby Advantages 7 ● Larger community ● More features

Slide 8

Slide 8 text

8 Should you use JRuby? I don’t know, you choose.

Slide 9

Slide 9 text

Supporting JRuby in one of Rookout’s debuggers 9

Slide 10

Slide 10 text

Supporting JRuby 10 ❏ Running an app ❏ Adding a breakpoint ❏ Collecting data

Slide 11

Slide 11 text

Decision Time 11 Java or Ruby

Slide 12

Slide 12 text

Decision Time 12 Java or Ruby

Slide 13

Slide 13 text

Running an app 13 ● rails server -p 8080 -e development

Slide 14

Slide 14 text

Running an app 14 rvm install jruby rvm use jruby bundle install

Slide 15

Slide 15 text

Running an app 15

Slide 16

Slide 16 text

Running an app 16 3 business days later…

Slide 17

Slide 17 text

Running an app 17 bundle config unset force_ruby_platform

Slide 18

Slide 18 text

Running an app 18 ➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development => Booting Puma => Rails 6.0.3.4 application starting in development => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.7 (jruby 9.2.9.0 - ruby 2.5.7), codename: Mysterious Traveller * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://[::1]:8080 * Listening on tcp://127.0.0.1:8080 Use Ctrl-C to stop

Slide 19

Slide 19 text

Running an app 19 ➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development => Booting Puma => Rails 6.0.3.4 application starting in development => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.7 (jruby 9.2.9.0 - ruby 2.5.7), codename: Mysterious Traveller * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://[::1]:8080 * Listening on tcp://127.0.0.1:8080 Use Ctrl-C to stop

Slide 20

Slide 20 text

Running an app 20 ➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development => Booting Puma => Rails 6.0.3.4 application starting in development => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.7 (jruby 9.2.9.0 - ruby 2.5.7), codename: Mysterious Traveller * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://[::1]:8080 * Listening on tcp://127.0.0.1:8080 Use Ctrl-C to stop

Slide 21

Slide 21 text

Supporting JRuby 21 ? Running an app ❏ Adding a breakpoint ❏ Collecting data

Slide 22

Slide 22 text

Running an app 22 ● bundle add rookout ● require 'rookout' ::Rookout.start

Slide 23

Slide 23 text

Running an app 23 ➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development

Slide 24

Slide 24 text

Running an app 24 ➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development [Rookout] Successfully connected to controller.

Slide 25

Slide 25 text

Running an app 25 ➜ tutorial-ruby git:(master) ✗ jruby -S rails server -p 8080 -e development [Rookout] Successfully connected to controller. => Booting Puma => Rails 6.0.6 application starting in development => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.12 (jruby 9.3.8.0 - ruby 2.6.8), codename: Mysterious Traveller * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://[::1]:8080 * Listening on tcp://127.0.0.1:8080 Use Ctrl-C to stop

Slide 26

Slide 26 text

Supporting JRuby 26 ✓ Running an app ❏ Adding a breakpoint ❏ Collecting data

Slide 27

Slide 27 text

TracePoints 27

Slide 28

Slide 28 text

TracePoints 28 tracepoint = Tracepoint.new(:line) do ... end

Slide 29

Slide 29 text

TracePoints 29 tracepoint = Tracepoint.new(:line) do ... end tracepoint.enable

Slide 30

Slide 30 text

TracePoints 30 tracepoint = Tracepoint.new(:line) do ... end tracepoint.enable(target: method(:m1), target_line: 14)

Slide 31

Slide 31 text

TracePoints in JRuby 31

Slide 32

Slide 32 text

Workaround 32 tracepoint.enable

Slide 33

Slide 33 text

Supporting JRuby 33 ✓ Running an app ✗ Adding a breakpoint ❏ Collecting data

Slide 34

Slide 34 text

Decision Time 34 Java or Ruby

Slide 35

Slide 35 text

Decision Time 35 Java or Ruby

Slide 36

Slide 36 text

Supporting JRuby 36 ❏ Running an app ❏ Adding a breakpoint ❏ Collecting data

Slide 37

Slide 37 text

Running an app - Java Agents 37 ● -javaagent flag ● premain entrypoint ● Instrumentation API App JavaAgent Instrumentation API

Slide 38

Slide 38 text

Running an app 38 ➜ tutorial-ruby git:(master) ✗ jruby -J-javaagent:rook.jar -S rails server -p 8080 -e development

Slide 39

Slide 39 text

Running an app 39 ➜ tutorial-ruby git:(master) ✗ jruby -J-javaagent:rook.jar -S rails server -p 8080 -e development [Rookout] Successfully connected to controller.

Slide 40

Slide 40 text

Running an app 40 ➜ tutorial-ruby git:(master) ✗ jruby -J-javaagent:rook.jar -S rails server -p 8080 -e development [Rookout] Successfully connected to controller. => Booting Puma => Rails 6.0.6 application starting in development => Run `rails server --help` for more startup options Puma starting in single mode... * Version 4.3.12 (jruby 9.3.8.0 - ruby 2.6.8), codename: Mysterious Traveller * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://[::1]:8080 * Listening on tcp://127.0.0.1:8080 Use Ctrl-C to stop

Slide 41

Slide 41 text

Supporting JRuby 41 ✓ Running an app ❏ Adding a breakpoint ❏ Collecting data

Slide 42

Slide 42 text

Adding a breakpoint - Class Loaders 42 JVM Compiler Code Bytecode

Slide 43

Slide 43 text

Adding a breakpoint - Class Loaders 43 JVM 32 | … 33 | new Item() 34 | … Class Loader Get Item class bytecode Return Item class bytecode

Slide 44

Slide 44 text

Adding a breakpoint - Class Loaders 44 JVM 32 | … 33 | new Item() 34 | … Class Loader Get Item class bytecode Return Item class bytecode Class Transformer Return transformed Item class bytecode

Slide 45

Slide 45 text

Adding a breakpoint 45 Expectation: Works straight away! Reality: Doesn’t work at all.

Slide 46

Slide 46 text

46

Slide 47

Slide 47 text

47

Slide 48

Slide 48 text

48

Slide 49

Slide 49 text

49

Slide 50

Slide 50 text

50

Slide 51

Slide 51 text

51

Slide 52

Slide 52 text

52

Slide 53

Slide 53 text

53 A few, spaced out invocations Many, rapid invocations Class isn’t loaded Class is loaded

Slide 54

Slide 54 text

54 A few, spaced out invocations Many, rapid invocations Class isn’t loaded Class is loaded JIT

Slide 55

Slide 55 text

JRuby Compilation Modes 55 ● Interpretation ● JIT ● Compilation

Slide 56

Slide 56 text

JRuby Compilation Modes 56 compile.mode=FORCE

Slide 57

Slide 57 text

57

Slide 58

Slide 58 text

58

Slide 59

Slide 59 text

59

Slide 60

Slide 60 text

Supporting JRuby 60 ✓ Running an app ✓ Adding a breakpoint ❏ Collecting data

Slide 61

Slide 61 text

Collecting data 61 class TasksController def create params.require(:task).permit(:task) task = Task.new(params[:task][:title]) $tasks_storage.push(task) redirect_back(fallback_location: 'tasks') end end

Slide 62

Slide 62 text

Collecting data 62 class TasksController def create params.require(:task).permit(:task) task = Task.new(params[:task][:title]) $tasks_storage.push(task) redirect_back(fallback_location: 'tasks') end end

Slide 63

Slide 63 text

Collecting data 63 class TasksController def create params.require(:task).permit(:task) task = Task.new(params[:task][:title]) $tasks_storage.push(task) redirect_back(fallback_location: 'tasks') end end

Slide 64

Slide 64 text

Collecting data 64

Slide 65

Slide 65 text

Collecting data - self 65

Slide 66

Slide 66 text

Collecting data - locals 66

Slide 67

Slide 67 text

Supporting JRuby 67 ✓ Running an app ✓ Adding a breakpoint ✓ Collecting data

Slide 68

Slide 68 text

What Next? 68 ● Ruby TracePoint API ● Java Agents ● Class Loaders @yardenlaif

Slide 69

Slide 69 text

Thank You! @yardenlaif 69