Rubyã§å©ç¨ã§ããUSDT㯠probes.d
ã¨ãããã¡ã¤ã«ã«å®ç¾©ããã¦ããã
USDTã¨ã¯:
以ä¸ã®è¨äºã¯bpftraceã¨USDTãç解ãã¦ããåæã§æ¸ãã¦ããã¾ãã
Rubyã«ãããprobeã®å®ç¾©ç®æ
probes.d
ã¯ä»¥ä¸ã®ãããªè¨æ³ã
provider ruby { /* ruby:::method-entry(classname, methodname, filename, lineno); This probe is fired just before a method is entered. * `classname` name of the class (a string) * `methodname` name of the method about to be executed (a string) * `filename` the file name where the method is _being called_ (a string) * `lineno` the line number where the method is _being called_ (an int) */ probe method__entry(const char *classname, const char *methodname, const char *filename, int lineno); /* ruby:::method-return(classname, methodname, filename, lineno); This probe is fired just after a method has returned. The arguments are the same as "ruby:::method-entry". */ probe method__return(const char *classname, const char *methodname, const char *filename, int lineno); //... }
Linuxç°å¢ã®å ´åã dtrace
ã¨ããã³ãã³ãï¼systemtap-sdt-dev
ãããã¯ããã«æºããããã±ã¼ã¸ã¨ä¸ç·ã«å
¥ãï¼ãåã¾ãããã¨ã§Cã®ããããã¡ã¤ã«ã«å¤æãããã
$ dtrace -h -s probes.d -o /dev/stdout | head -24 /* Generated by the Systemtap dtrace wrapper */ #define _SDT_HAS_SEMAPHORES 1 #define STAP_HAS_SEMAPHORES 1 /* deprecated */ #include <sys/sdt.h> /* RUBY_METHOD_ENTRY ( const char *classname, const char *methodname, const char *filename, int lineno ) */ #if defined STAP_SDT_V1 #define RUBY_METHOD_ENTRY_ENABLED() __builtin_expect (method__entry_semaphore, 0) #define ruby_method__entry_semaphore method__entry_semaphore #else #define RUBY_METHOD_ENTRY_ENABLED() __builtin_expect (ruby_method__entry_semaphore, 0) #endif __extension__ extern unsigned short ruby_method__entry_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); #define RUBY_METHOD_ENTRY(arg1, arg2, arg3, arg4) \ DTRACE_PROBE4 (ruby, method__entry, arg1, arg2, arg3, arg4)
*.d
ãã¡ã¤ã«ãããsys/sdt.hã§å®ç¾©ããã DTRACE_PROBE*
ãã¯ããã©ãããããã¯ããå®ç¾©ãã¦ãããå®éã®Rubyã®ãã«ãã§ã¯ããã¯ãã®ååãsedã§ç½®ãæãã¦ããããã ã
./configure --enable-dtrace
ããRubyã®ä¸éçæç©ã§ãã probes.h
ã¯ä»¥ä¸ã®ããã«ãªã£ã¦ããã
//... /* RUBY_DTRACE_METHOD_ENTRY ( const char *classname, const char *methodname, const char *filename, int lineno ) */ #if defined STAP_SDT_V1 #define RUBY_DTRACE_METHOD_ENTRY_ENABLED() __builtin_expect (method__entry_semaphore, 0) #define ruby_method__entry_semaphore method__entry_semaphore #else #define RUBY_DTRACE_METHOD_ENTRY_ENABLED() __builtin_expect (ruby_method__entry_semaphore, 0) #endif __extension__ extern unsigned short ruby_method__entry_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); #define RUBY_DTRACE_METHOD_ENTRY(arg1, arg2, arg3, arg4) \ DTRACE_PROBE4 (ruby, method__entry, arg1, arg2, arg3, arg4) //... #define RUBY_DTRACE_METHOD_RETURN(arg1, arg2, arg3, arg4) \ DTRACE_PROBE4 (ruby, method__return, arg1, arg2, arg3, arg4) //... #define RUBY_DTRACE_CMETHOD_ENTRY(arg1, arg2, arg3, arg4) \ DTRACE_PROBE4 (ruby, cmethod__entry, arg1, arg2, arg3, arg4) //...
ãã ãå®éã«ã³ã¼ãã§ã¯ãã®ãã¯ããç´æ¥ä½¿ãããã¨ãããããããã«ä¸æ®µåã¾ãããã¯ãã使ã£ã¦ãã模æ§ã
$ grep RUBY_DTRACE_ -r *.c | head -20 array.c: RUBY_DTRACE_CREATE_HOOK(ARRAY, 0); array.c: RUBY_DTRACE_CREATE_HOOK(ARRAY, capa); array.c: RUBY_DTRACE_CREATE_HOOK(ARRAY, capa); eval.c: RUBY_DTRACE_HOOK(RAISE, rb_obj_classname(ec->errinfo)); gc.c:#define RUBY_DTRACE_GC_HOOK(name) \ gc.c: do {if (RUBY_DTRACE_GC_##name##_ENABLED()) RUBY_DTRACE_GC_##name();} while (0) gc.c: RUBY_DTRACE_GC_HOOK(MARK_BEGIN); gc.c: RUBY_DTRACE_GC_HOOK(MARK_END); gc.c: RUBY_DTRACE_GC_HOOK(SWEEP_BEGIN); gc.c: RUBY_DTRACE_GC_HOOK(SWEEP_END); hash.c: RUBY_DTRACE_CREATE_HOOK(HASH, 0); load.c: RUBY_DTRACE_HOOK(LOAD_ENTRY, RSTRING_PTR(orig_fname)); load.c: RUBY_DTRACE_HOOK(LOAD_RETURN, RSTRING_PTR(orig_fname)); load.c: RUBY_DTRACE_HOOK(REQUIRE_ENTRY, RSTRING_PTR(fname)); load.c: RUBY_DTRACE_HOOK(FIND_REQUIRE_ENTRY, RSTRING_PTR(fname)); load.c: RUBY_DTRACE_HOOK(FIND_REQUIRE_RETURN, RSTRING_PTR(fname)); load.c: RUBY_DTRACE_HOOK(REQUIRE_RETURN, RSTRING_PTR(fname)); object.c: RUBY_DTRACE_CREATE_HOOK(OBJECT, rb_class2name(klass)); parse.c:#define RUBY_DTRACE_PARSE_HOOK(name) \ parse.c: if (RUBY_DTRACE_PARSE_##name##_ENABLED()) { \
大ä½ä»¥ä¸ã®ãããªãã¯ããåã¾ãã¦ããã詳細㯠probes_helper.h
/ internal/vm.h
/ gc.c
ãªã©ã«ããã
- ã¡ã½ããã®entry, return -
RUBY_DTRACE_(C)?METHOD_(ENTRY|RETURN)_HOOK()
- ãªãã¸ã§ã¯ããç¹å®ã®ã¯ã©ã¹ï¼Arrayãªã©ï¼ã®ãªãã¸ã§ã¯ãã®çæ -
RUBY_DTRACE_CREATE_HOOK()
- ãã®ã»ãããã¬ã¼ã¹ãããã¤ãã³ã -
RUBY_DTRACE_HOOK()
- GCã«é¢ããã¤ãã³ã -
RUBY_DTRACE_GC_HOOK()
probes.d
ãããã£ã¦ã³ã³ãã¤ã«ãã¦ã¿ã
ã§ãä»å以ä¸ã®ãã㪠ruby::ractor__create
ã¨ããProbeã追å ããã
provider ruby { // ... /* ruby:::gc-sweep-end(); Fired at the end of a sweep phase. */ probe gc__sweep__end(); probe ractor__create(int ractor_id, const char *filename, int lineno); // added };
ãã®ç¶æ ã§makeãç´ãã¨probes.hã«æ°ãããã¯ããå®ç¾©ããã¦ããã
$ tail probes.h #if defined STAP_SDT_V1 #define RUBY_DTRACE_RACTOR_CREATE_ENABLED() __builtin_expect (ractor__create_semaphore, 0) #define ruby_ractor__create_semaphore ractor__create_semaphore #else #define RUBY_DTRACE_RACTOR_CREATE_ENABLED() __builtin_expect (ruby_ractor__create_semaphore, 0) #endif __extension__ extern unsigned short ruby_ractor__create_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); #define RUBY_DTRACE_RACTOR_CREATE(arg1, arg2, arg3) \ DTRACE_PROBE3 (ruby, ractor__create, arg1, arg2, arg3)
ãã ããã®ç¶æ ã§ã¯ãã¤ããªã«ã¯USDTã®æ å ±ã¯ãªãã
ä»ã®å®ç¾©ãããã RUBY_DTRACE_CREATE_HOOK(RACTOR, id)
ãé©åãªå ´æã«æ¿å
¥ãããå·çæç¹ã®masterã§ã¯ä»¥ä¸ã®é¢æ°å
ã«å®ç¾©ããã°è¯ãããã
ruby/ractor.c at 7b9476fbfab738d1eb01b4b4c4af9a1680513019 · ruby/ruby · GitHub
ããæ¸ãæããã
#include "probes.h" //... static VALUE ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block) { VALUE rv = ractor_alloc(self); rb_ractor_t *r = RACTOR_PTR(rv); ractor_init(r, name, loc); // can block here r->pub.id = ractor_next_id(); RUBY_DEBUG_LOG("r:%u", r->pub.id); RUBY_DTRACE_CREATE_HOOK(RACTOR, r->pub.id); // ããã«è¿½å ãã r->r_stdin = rb_io_prep_stdin(); r->r_stdout = rb_io_prep_stdout(); r->r_stderr = rb_io_prep_stderr(); rb_ractor_t *cr = rb_ec_ractor_ptr(ec); r->verbose = cr->verbose; r->debug = cr->debug; rb_thread_create_ractor(r, args, block); RB_GC_GUARD(rv); return rv; }
ããã§ã³ã³ãã¤ã«ãéãããã¤ããªå´ã§ã ruby:ractor__create
ã¨ããUSDTãå®ç¾©ãããã
$ readelf -n ./ruby | grep -C 2 ractor__create stapsdt 0x00000049 NT_STAPSDT (SystemTap probe descriptors) Provider: ruby Name: ractor__create Location: 0x000000000013d0f8, Base: 0x00000000003950c0, Semaphore: 0x00000000003fc2cc Arguments: 4@8(%rbp) 8@%rax -4@16(%rsp)
æ°ããProbeã®åä½ç¢ºèª
Ubuntu Groovy㧠bpftrace
ããã±ã¼ã¸ãå
¥ããããã¼ã¸ã§ã³ã¯ v0.11.0
ã
$ sudo apt install bpftrace
bpftraceãã㯠usdt:/path/to/miniruby:ruby:ractor__create
ã§ã¢ã¿ããã§ããããããã¡ãªã¿ã«minirubyã«ãåãUSDTãããã®ã§ããã£ããminirubyã§è©¦ãã
$ sudo bpftrace -l 'usdt:./miniruby' usdt:./miniruby:ruby:array__create usdt:./miniruby:ruby:raise usdt:./miniruby:ruby:gc__sweep__end usdt:./miniruby:ruby:gc__sweep__begin usdt:./miniruby:ruby:gc__mark__begin usdt:./miniruby:ruby:gc__mark__end usdt:./miniruby:ruby:hash__create usdt:./miniruby:ruby:load__return usdt:./miniruby:ruby:load__entry usdt:./miniruby:ruby:find__require__return usdt:./miniruby:ruby:require__entry usdt:./miniruby:ruby:find__require__entry usdt:./miniruby:ruby:require__return usdt:./miniruby:ruby:object__create usdt:./miniruby:ruby:parse__end usdt:./miniruby:ruby:parse__begin usdt:./miniruby:ruby:ractor__create usdt:./miniruby:ruby:string__create usdt:./miniruby:ruby:symbol__create usdt:./miniruby:ruby:cmethod__entry usdt:./miniruby:ruby:cmethod__return usdt:./miniruby:ruby:method__return usdt:./miniruby:ruby:method__entry
å ¬å¼ããã¥ã¡ã³ã ã®ãµã³ãã«ã«ãããããªä»¥ä¸ã®ã³ã¼ããå®è¡ããã
puts "Pid: #{$$}" gets # block until keyhit pipe = Ractor.new do loop do Ractor.yield Ractor.receive end end RN = 10 rs = RN.times.map{|i| Ractor.new pipe, rand(RN*128) do |pipe, i| pipe << i end } p RN.times.map{ pipe.take }.sort
ããã¿ã¼ããã«ã§ãµã³ãã«ã³ã¼ããå®è¡ãããPIDãåãåºãã¦ãããã¯ããã®ã§ã
$ ./miniruby ractor-sample.rb Pid: 80186
ãã®PIDã«å¯¾ãã¦å¥ã®ã¿ã¼ããã«ã§ã¢ã¿ããããã
$ sudo bpftrace -p 80186 \ -e 'usdt:./miniruby:ruby:ractor__create { printf("Ractor created: id=%d file=%s:L%d\n", arg0, str(arg1), arg2) }' Attaching 1 probe...
ãããã¯ããæ¹ã®ã¿ã¼ããã«ã§ãªã¿ã¼ã³ãã¼ãæ¼ããåããã¨ãçæãããRactorã®æ
å ±ãåå¾ã§ããããã ããã±ã¼ã·ã§ã³æ
å ±ã¯internalã§ã racrot.rb
ã® __builtin_ractor_create ãå¼ã°ããè¡çªå·ã«ãªã£ã¦ãã¾ããArrayãHashã¨æ¯ã¹åæåãç¹æ®ãªãããªã®ã§ä½ãèæ
®ããå¿
è¦ããã模æ§ã
Ractor created: id=2 file=<internal:ractor>:L267 Ractor created: id=3 file=<internal:ractor>:L267 Ractor created: id=4 file=<internal:ractor>:L267 Ractor created: id=5 file=<internal:ractor>:L267 Ractor created: id=6 file=<internal:ractor>:L267 Ractor created: id=7 file=<internal:ractor>:L267 Ractor created: id=8 file=<internal:ractor>:L267 Ractor created: id=9 file=<internal:ractor>:L267 Ractor created: id=10 file=<internal:ractor>:L267 Ractor created: id=11 file=<internal:ractor>:L267 Ractor created: id=12 file=<internal:ractor>:L267
ã¾ã¨ããææ
probes.d
ãå å·¥ããã°Rubyã«æ°ããUSDTã®Probeãå°å ¥ã§ãã- çæããããã¯ããã®ã¾ã¾ã§ã¯ãªãRubyå ã®è¦ç´ã«æ²¿ã£ã¦è¨è¿°ããå¿ è¦ãããï¼ããã¤ãã®è¦ç´ã¯undocumentedï¼ã
- ï¼è¦å´ãã¦ï¼USDTãããªãã¦ãå
é¨é¢æ°ã®ã¨ã³ããªãã¤ã³ãã¯æ®éã«uprobe/uretprobeãåå¨ãã模æ§ãªã®ã§ãããããã¬ã¼ã¹ãã¦ãããã
- ãã ãããã§ã¯ractor idãç°¡åã«åããªãããï¼
$ sudo bpftrace -l 'uprobe:./miniruby' | grep ':ractor' | head -20 uprobe:./miniruby:ractor_basket_accept uprobe:./miniruby:ractor_basket_accept.cold uprobe:./miniruby:ractor_close_outgoing.constprop.0 uprobe:./miniruby:ractor_create uprobe:./miniruby:ractor_create.cold uprobe:./miniruby:ractor_free uprobe:./miniruby:ractor_init uprobe:./miniruby:ractor_local_storage_free_i uprobe:./miniruby:ractor_local_storage_mark_i uprobe:./miniruby:ractor_local_value uprobe:./miniruby:ractor_local_value.cold uprobe:./miniruby:ractor_local_value_set uprobe:./miniruby:ractor_local_value_set.cold uprobe:./miniruby:ractor_mark uprobe:./miniruby:ractor_memsize uprobe:./miniruby:ractor_moved_missing uprobe:./miniruby:ractor_queue_enq.constprop.0 uprobe:./miniruby:ractor_receive_if uprobe:./miniruby:ractor_receive_if.cold uprobe:./miniruby:ractor_receive_wait.isra.0