Created
April 17, 2009 23:48
-
-
Save methodmissing/97353 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| mri: | |
| thread: | |
| thread_switch: | |
| - char: 'Thread Status' | |
| - int: 'Restore' | |
| cache: | |
| clear_cache: | |
| clear_cache_by_class: | |
| - char: 'Class' | |
| clear_cache_by_id: | |
| - int: 'Id' | |
| marshal: | |
| marshal_dump: | |
| marshal_load: | |
| load: | |
| require: | |
| - char: 'File' | |
| - int: 'Safe level' | |
| parse: | |
| compile: | |
| - char: 'File' | |
| - int: 'Line' | |
| lex: | |
| variable: | |
| global_var_get: | |
| - char: 'Name' | |
| global_var_set: | |
| - char: 'Name' | |
| class_var_get: | |
| - char: 'Class' | |
| - char: 'Name' | |
| class_var_set: | |
| - char: 'Class' | |
| - char: 'Name' | |
| instance_var_get: | |
| - char: 'Class' | |
| - char: 'Name' | |
| instance_var_set: | |
| - char: 'Class' | |
| - char: 'Name' | |
| const_get: | |
| - char: 'Class' | |
| - char: 'Name' | |
| const_set: | |
| - char: 'Class' | |
| - char: 'Name' | |
| gc: | |
| gc_malloc: | |
| gc_calloc: | |
| gc_realloc: | |
| gc_free: | |
| gc_register_address: | |
| gc_unregister_address: | |
| gc_add_heap: | |
| gc_new_object: | |
| gc_data_object_alloc: | |
| gc_source_filename: | |
| - char: 'Class' | |
| gc_mark_source_filename: | |
| - char: 'Class' | |
| gc_sweep_source_filename: | |
| - char: 'Class' | |
| gc_mark_all: | |
| gc_mark_rest: | |
| gc_is_pointer_to_heap: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 213-138-234-81:matzruby lourens$ ruby instrument/runtime.rb | |
| provider mri { | |
| probe marshal_dump_start( ); | |
| probe marshal_dump_end( ); | |
| probe marshal_load_start( ); | |
| probe marshal_load_end( ); | |
| probe thread_switch_start( char*, int ); | |
| probe thread_switch_end( char*, int ); | |
| probe clear_cache_by_id_start( int ); | |
| probe clear_cache_by_id_end( int ); | |
| probe clear_cache_by_class_start( char* ); | |
| probe clear_cache_by_class_end( char* ); | |
| probe clear_cache_start( ); | |
| probe clear_cache_end( ); | |
| probe gc_sweep_source_filename_start( char* ); | |
| probe gc_sweep_source_filename_end( char* ); | |
| probe gc_calloc_start( ); | |
| probe gc_calloc_end( ); | |
| probe gc_unregister_address_start( ); | |
| probe gc_unregister_address_end( ); | |
| probe gc_mark_rest_start( ); | |
| probe gc_mark_rest_end( ); | |
| probe gc_new_object_start( ); | |
| probe gc_new_object_end( ); | |
| probe gc_add_heap_start( ); | |
| probe gc_add_heap_end( ); | |
| probe gc_mark_all_start( ); | |
| probe gc_mark_all_end( ); | |
| probe gc_register_address_start( ); | |
| probe gc_register_address_end( ); | |
| probe gc_mark_source_filename_start( char* ); | |
| probe gc_mark_source_filename_end( char* ); | |
| probe gc_free_start( ); | |
| probe gc_free_end( ); | |
| probe gc_data_object_alloc_start( ); | |
| probe gc_data_object_alloc_end( ); | |
| probe gc_is_pointer_to_heap_start( ); | |
| probe gc_is_pointer_to_heap_end( ); | |
| probe gc_realloc_start( ); | |
| probe gc_realloc_end( ); | |
| probe gc_malloc_start( ); | |
| probe gc_malloc_end( ); | |
| probe gc_source_filename_start( char* ); | |
| probe gc_source_filename_end( char* ); | |
| probe instance_var_set_start( char*, char* ); | |
| probe instance_var_set_end( char*, char* ); | |
| probe class_var_get_start( char*, char* ); | |
| probe class_var_get_end( char*, char* ); | |
| probe class_var_set_start( char*, char* ); | |
| probe class_var_set_end( char*, char* ); | |
| probe const_get_start( char*, char* ); | |
| probe const_get_end( char*, char* ); | |
| probe global_var_get_start( char* ); | |
| probe global_var_get_end( char* ); | |
| probe global_var_set_start( char* ); | |
| probe global_var_set_end( char* ); | |
| probe const_set_start( char*, char* ); | |
| probe const_set_end( char*, char* ); | |
| probe instance_var_get_start( char*, char* ); | |
| probe instance_var_get_end( char*, char* ); | |
| probe compile_start( char*, int ); | |
| probe compile_end( char*, int ); | |
| probe lex_start( ); | |
| probe lex_end( ); | |
| probe require_start( char*, int ); | |
| probe require_end( char*, int ); | |
| }; | |
| #!/usr/sbin/dtrace -Zs | |
| #pragma D option quiet | |
| dtrace:::BEGIN | |
| { | |
| depth = 0; | |
| } | |
| mri*:::lex_start | |
| { | |
| this->lex_start = timestamp; | |
| } | |
| mri*:::lex_end | |
| /lex_start/ | |
| { | |
| this->elapsed = timestamp - this->lex_start; | |
| this->lex_start = 0; | |
| this->type = "lex"; | |
| @num[this->type] = count(); | |
| @eavg[this->type] = avg(this->elapsed); | |
| @esum[this->type] = sum(this->elapsed); | |
| @edist[this->type] = quantize(this->elapsed); | |
| this->depth--; | |
| } | |
| mri*:::thread_switch_start | |
| { | |
| this->thread_switch_start = timestamp; | |
| } | |
| mri*:::thread_switch_end | |
| /thread_switch_start/ | |
| { | |
| this->elapsed = timestamp - this->thread_switch_start; | |
| this->thread_switch_start = 0; | |
| this->type = "thread_switch"; | |
| @num[this->type] = count(); | |
| @eavg[this->type] = avg(this->elapsed); | |
| @esum[this->type] = sum(this->elapsed); | |
| @edist[this->type] = quantize(this->elapsed); | |
| this->depth--; | |
| } | |
| dtrace:::END | |
| { | |
| normalize(@eavg, 1000); | |
| normalize(@esum, 1000); | |
| printf("ELAPSED TIME DISTRIBUTION, | |
| "); | |
| printa(@edist); | |
| setopt("aggsortpos", "2"); | |
| printf("%-33s %45s | |
| ", "___ OVERLAP TIMES: ___", "______ ELAPSED ______"); | |
| printf("%-24s %6s %10s %12s | |
| ", "TYPE", "COUNT", "AVG(us)", "SUM(us)"); | |
| printa("%-24.24s %@6d %@10d %@12d | |
| ", @num, @eavg, @esum); | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'yaml' | |
| module Instrument | |
| class Runtime | |
| # Default probe definition location | |
| # | |
| PROBES = File.join( File.dirname(__FILE__), 'mri_trace.yml' ) | |
| class Argument < Struct.new(:type, :description) | |
| # Dtrace definition | |
| # | |
| def to_probe | |
| char? ? 'char*' : 'int' | |
| end | |
| # String representation as description | |
| # | |
| def to_s | |
| self.description | |
| end | |
| private | |
| # Is this a character argument ? | |
| # | |
| def char? | |
| self.type == 'char' | |
| end | |
| end | |
| class Probe < Struct.new(:namespace, :group, :name, :arguments) | |
| # Are we the start probe ? | |
| # | |
| def start? | |
| self.name =~ /start/ | |
| end | |
| # Dtrace representation | |
| # | |
| def to_probe | |
| "probe #{self.name}( #{arguments_to_probe} );\n" | |
| end | |
| # D source, depending on start or end scope | |
| # | |
| def to_s | |
| start? ? to_s_start : to_s_end | |
| end | |
| # The total amount of columns required | |
| # | |
| def argument_size | |
| @argument_size ||= self.arguments.size | |
| end | |
| private | |
| # Probe arguments | |
| # | |
| def arguments_to_probe | |
| self.arguments.map{|a| a.to_probe }.join(', ') | |
| end | |
| # Probe type | |
| # | |
| def type | |
| @type ||= self.name.gsub( /_(end|start)$/, '' ) | |
| end | |
| # Start predicate | |
| # | |
| def start | |
| @start ||= self.name.gsub(/end$/, 'start') | |
| end | |
| # Start definition | |
| # | |
| def to_s_start | |
| %[ mri*:::#{self.name} | |
| { | |
| this->#{self.name} = timestamp; | |
| }\n ] | |
| end | |
| # End definition | |
| # | |
| def to_s_end | |
| %[ mri*:::#{self.name} | |
| /#{start}/ | |
| { | |
| this->elapsed = timestamp - this->#{start}; | |
| this->#{start} = 0; | |
| this->type = "#{type}"; | |
| @num[this->type] = count(); | |
| @eavg[this->type] = avg(this->elapsed); | |
| @esum[this->type] = sum(this->elapsed); | |
| @edist[this->type] = quantize(this->elapsed); | |
| this->depth--; | |
| }\n ] | |
| end | |
| end | |
| attr_reader :definition, | |
| :namespace, | |
| :probes | |
| # Accepts the path to a YAML probe definition | |
| # | |
| def initialize( definition = nil ) | |
| @definition = definition || PROBES | |
| @probes = [] | |
| setup_probes() | |
| end | |
| # Yield a D stream for instrumentation from given group or probe signatures | |
| # | |
| # TODO: regexes | |
| # | |
| def probe( *probes ) | |
| stream_with do | |
| probes.map do |probe| | |
| selected_probes( probe.to_s ) | |
| end.flatten.map{|p| p.to_s } | |
| end | |
| end | |
| # Dtrace definition | |
| # | |
| def to_definition | |
| %[ provider #{@namespace.to_s} { | |
| #{@probes.map{|p| p.to_probe }.join} | |
| }; ] | |
| end | |
| private | |
| # Dtrace template | |
| # | |
| def stream_with | |
| %[ #!/usr/sbin/dtrace -Zs | |
| #pragma D option quiet | |
| dtrace:::BEGIN | |
| { | |
| depth = 0; | |
| } | |
| #{yield} | |
| dtrace:::END | |
| { | |
| normalize(@eavg, 1000); | |
| normalize(@esum, 1000); | |
| printf("ELAPSED TIME DISTRIBUTION,\n"); | |
| printa(@edist); | |
| setopt("aggsortpos", "2"); | |
| printf("%-33s %45s\n", "___ OVERLAP TIMES: ___", "______ ELAPSED ______"); | |
| printf("%-24s %6s %10s %12s\n", "TYPE", "COUNT", "AVG(us)", "SUM(us)"); | |
| printa("%-24.24s %@6d %@10d %@12d\n", @num, @eavg, @esum); | |
| } | |
| ] | |
| end | |
| # Find related probes | |
| # | |
| def selected_probes( probe ) | |
| if group?( probe ) | |
| @probes.select{|p| p.group == probe } | |
| else | |
| @probes.select{|p| p.name =~ /#{probe}/ } | |
| end | |
| end | |
| # Is the given probe a reference to a group ? | |
| # | |
| def group?( probe ) | |
| @probes.detect{|p| p.group == probe } | |
| end | |
| # Read and parse the YAML data | |
| # | |
| def read | |
| YAML.load( IO.read( @definition ) ) | |
| end | |
| # Init probe definitions | |
| # | |
| def setup_probes | |
| read.each_pair do |namespace,groups| | |
| @namespace = namespace | |
| groups.each do |group| | |
| group.last.each_pair do |probe,arguments| | |
| setup_probe( namespace, group.first, probe, arguments ) | |
| end | |
| end | |
| end | |
| end | |
| # Setup a single probe ... spawn both start and end variations | |
| # | |
| def setup_probe( namespace, group, probe, arguments ) | |
| %w(start end).each do |suffix| | |
| @probes << Probe.new( namespace, group, "#{probe}_#{suffix}", setup_probe_arguments( arguments ) ) | |
| end | |
| end | |
| # Setup probe arguments | |
| # | |
| def setup_probe_arguments( arguments ) | |
| Array(arguments).compact.map do |argument| | |
| Argument.new( argument.keys.first, argument.values.first ) | |
| end | |
| end | |
| end | |
| end | |
| puts Instrument::Runtime.new.to_definition | |
| puts Instrument::Runtime.new.probe( :lex, :thread ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment