Bạn có biết rằng có thể ghi lại tất cả các cuộc gọi phương thức khi chúng xảy ra trong một quá trình đang chạy trong thời gian thực không? Làm thế nào về việc tiêm mã để được thực thi bên trong một tiến trình đang chạy? Bạn có thể - thông qua sự kỳ diệu của rbtrace
đá quý.
rbtrace
đá quý đi kèm với hai phần. Đầu tiên là một thư viện mà bạn đưa vào mã mà bạn muốn theo dõi. Thứ hai là tiện ích dòng lệnh để truy vấn dữ liệu theo dõi.
Hãy xem một ví dụ đơn giản. Đoạn mã mà chúng ta sẽ theo dõi rất đơn giản. Tất cả những gì chúng tôi phải làm là yêu cầu rbtrace
đá quý.
require 'rbtrace'
require 'digest'
require 'securerandom'
# An infinite loop
while true
# Do some work.
Digest::SHA256.digest SecureRandom.random_bytes(2**8)
# Sleep for one second on every iteration.
sleep 1
end
Bây giờ hãy chạy chương trình này:
$ ruby trace.rb &
[1] 12345
Chúng tôi lấy ID quy trình này và cung cấp cho rbtrace
công cụ dòng lệnh. -f
tùy chọn cho biết chế độ "firehose", chế độ này sẽ in mọi thứ ra màn hình.
$ rbtrace -p 12345 -f
*** attached to process 12345
Fixnum#** <0.000010>
SecureRandom.random_bytes
Integer#to_int <0.000005>
SecureRandom.gen_random
OpenSSL::Random.random_bytes <0.002223>
SecureRandom.gen_random <0.002243>
SecureRandom.random_bytes <0.002290>
Digest::SHA256.digest
Digest::Class#initialize <0.000004>
Digest::Instance#digest
Digest::Base#reset <0.000005>
Digest::Base#update <0.000210>
Digest::Base#finish <0.000006>
Digest::Base#reset <0.000005>
Digest::Instance#digest <0.000267>
Digest::SHA256.digest <0.000308>
Kernel#rand
Kernel#respond_to_missing? <0.000008>
Kernel#rand <0.000071>
Kernel#sleep <1.003233>
Điều này thực sự tuyệt vời! Chúng ta có thể thấy mọi phương thức được gọi cùng với thời gian dành cho phương thức đó.
Nếu chúng ta muốn sử dụng một phương pháp cụ thể, chúng ta có thể sử dụng -m
tùy chọn.
$ rbtrace -p 12345 -m digest
*** attached to process 12345
Digest::SHA256.digest
Digest::Instance#digest <0.000201>
Digest::SHA256.digest <0.000220>
Digest::SHA256.digest
Digest::Instance#digest <0.000287>
Digest::SHA256.digest <0.000343>
Có lẽ cách sử dụng thú vị nhất của viên ngọc này là lấy một kết xuất đống từ một máy chủ web đang chạy. Kết xuất đống chứa mọi đối tượng trong bộ nhớ cùng với một loạt siêu dữ liệu và rất hữu ích để gỡ lỗi rò rỉ bộ nhớ trong quá trình sản xuất.
Để có được kết xuất đống, hãy sử dụng một lệnh như bên dưới, được lấy từ bài đăng này của Sam Saffron.
$ bundle exec rbtrace -p <SERVER PID HERE> -e 'Thread.new{GC.start;require "objspace";io=File.open("/tmp/ruby-heap.dump", "w"); ObjectSpace.dump_all(output: io); io.close}'
Tuy nhiên, hãy cảnh báo rằng, đống đổ đống có thể rất lớn - theo thứ tự vài trăm megabyte cho một quy trình đường ray. Đây là một mẫu rất nhỏ:
[
{
"type": "ROOT",
"root": "vm",
"references": [
"0x7fb7d38bc3f0",
"0x7fb7d38b79b8",
"0x7fb7d38dff80",
"0x7fb7d38bff50",
"0x7fb7d38bff00",
"0x7fb7d38b4bf0",
"0x7fb7d38bfe88",
"0x7fb7d38bfe60",
"0x7fb7d38ddc80",
"0x7fb7d38dffa8",
"0x7fb7d382fbd0",
"0x7fb7d382fbf8"
]
},
{
"type": "ROOT",
"root": "machine_context",
"references": [
"0x7fb7d382fbf8",
"0x7fb7d382fbf8",
"0x7fb7d3827d40",
"0x7fb7d3827a70",
"0x7fb7d38becb8",
"0x7fb7d38bed08",
"0x7fb7d38ddc80",
"0x7fb7d3827e58",
"0x7fb7d3827e58",
"0x7fb7d38becb8",
"0x7fb7d38becb8",
"0x7fb7d38bc328",
"0x7fb7d38bc378",
"0x7fb7d38ddc80",
"0x7fb7d3835008",
"0x7fb7d3835008",
"0x7fb7d3835008",
"0x7fb7d3835008",
"0x7fb7d3835008"
]
},
{
"type": "ROOT",
"root": "global_list",
"references": [
"0x7fb7d38dff58"
]
},
{
"type": "ROOT",
"root": "global_tbl",
"references": [
"0x7fb7d38c6dc8",
"0x7fb7d38c6dc8",
"0x7fb7d38c65f8",
"0x7fb7d38c6580",
"0x7fb7d38c6508",
"0x7fb7d38c6580",
"0x7fb7d38c6288",
"0x7fb7d38c6288",
"0x7fb7d38c6288",
"0x7fb7d38c6288",
"0x7fb7d38c6288",
"0x7fb7d38bc418",
"0x7fb7d38bc418",
"0x7fb7d38bc418",
"0x7fb7d3835328",
"0x7fb7d3835328"
]
},
{
"address": "0x7fb7d300c5e8",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 10,
"value": "@exit_code",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300c7a0",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 9,
"value": "exit_code",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300c908",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 19,
"value": "SystemExitException",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300cb60",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 17,
"value": "VerificationError",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300cd90",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 19,
"value": "RubyVersionMismatch",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300cfe8",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 21,
"value": "RemoteSourceException",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300d1f0",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"fstring": true,
"bytesize": 25,
"value": "RemoteInstallationSkipped",
"encoding": "US-ASCII",
"memsize": 66,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300d3a8",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"fstring": true,
"bytesize": 27,
"value": "RemoteInstallationCancelled",
"encoding": "US-ASCII",
"memsize": 68,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300d560",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 11,
"value": "RemoteError",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300d830",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"fstring": true,
"bytesize": 26,
"value": "OperationNotSupportedError",
"encoding": "US-ASCII",
"memsize": 67,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300dbc8",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 12,
"value": "InstallError",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300e898",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 9,
"value": "requester",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300eaa0",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 13,
"value": "build_message",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300ec30",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 8,
"value": "@request",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300ede8",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"embedded": true,
"fstring": true,
"bytesize": 7,
"value": "request",
"encoding": "US-ASCII",
"memsize": 40,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
},
{
"address": "0x7fb7d300efa0",
"type": "STRING",
"class": "0x7fb7d38dcee8",
"frozen": true,
"fstring": true,
"bytesize": 27,
"value": "ImpossibleDependenciesError",
"encoding": "US-ASCII",
"memsize": 68,
"flags": {
"wb_protected": true,
"old": true,
"long_lived": true,
"marked": true
}
}
]