Permalink
Browse files

Initial version of DTrace probes support

Adds probes for method invocation, method jitting, GC and thread start /
stop. More probes can be added in the future.
  • Loading branch information...
1 parent f99d4bd commit 682ceae8b6b3e0a6528b49e5c391b29ab1f04fef @dbussink dbussink committed Feb 13, 2013
View
@@ -88,3 +88,5 @@ doc/generated/vm
web/doc/*/*.bak
web/doc/*/*.new
+# Ignore generated DTrace header
+vm/dtrace/probes.h
View
@@ -53,7 +53,7 @@ end
load_configuration
-unless BUILD_CONFIG[:config_version] == 167
+unless BUILD_CONFIG[:config_version] == 168
STDERR.puts "Your configuration is outdated, please run ./configure first"
exit 1
end
View
@@ -86,6 +86,7 @@ class Configure
@x86_32 = false
@x86_64 = false
@fibers = false
+ @dtrace = false
@include_dirs = []
@lib_dirs = []
@@ -123,7 +124,7 @@ class Configure
@patch_version = "0"
@release = "rc1"
@release_date = "yyyy-mm-dd"
- @config_version = 167
+ @config_version = 168
end
# Set up system commands to run in cmd.exe on Windows. Either Windows
@@ -426,6 +427,7 @@ class Configure
feature "vendor-zlib", false
feature "alloc-tracking", false
feature "fibers", true
+ feature "dtrace", true
o.doc "\n Help!"
@@ -1079,6 +1081,27 @@ int main() { return tgetnum(""); }
success
end
+ def has_dtrace
+ @log.print "Checking for dtrace: "
+
+ begin
+ basename = "rbx-configure-dtrace-test"
+ source = basename + ".d"
+ File.open source, "wb" do |f|
+ f.write "provider conftest{ probe fire(); };"
+ end
+
+ cmd = "dtrace -h -o #{basename}.h -s #{source}"
+ @log.log cmd
+ system cmd
+ @dtrace = $?.exitstatus == 0
+ puts @dtrace ? "yes" : "no"
+ @dtrace
+ ensure
+ File.delete(*Dir["#{basename}*"])
+ end
+ end
+
def compile_check(logpart, &block)
@log.print "Checking for #{logpart}: "
@@ -1135,6 +1158,10 @@ int main() { return tgetnum(""); }
@fibers = true if @x86_32 or @x86_64
end
+ if @features["dtrace"].value and has_dtrace
+ @defines << "HAVE_DTRACE"
+ end
+
# Default on Windows is vendor-zlib
if @windows and @features["vendor-zlib"].configured.nil?
@features["vendor-zlib"].configured = true
@@ -1428,6 +1455,7 @@ module Rubinius
:sizeof_long => #{sizeof("long")},
:x86_32 => #{@x86_32},
:x86_64 => #{@x86_64},
+ :dtrace => #{@dtrace},
:fibers => #{@fibers},
:sourcedir => "#{@sourcedir}",
:stagingdir => #{@stagingdir.inspect},
@@ -206,6 +206,11 @@ def self.run_script(compiled_code)
raise PrimitiveFailure, "Rubinius.run_script primitive failed"
end
+ def self.dtrace_fire(payload)
+ Rubinius.primitive :vm_dtrace_fire
+ raise PrimitiveFailure, "Rubinius.dtrace_fire primitive failed"
+ end
+
module Tooling
def self.raw_load(str)
Rubinius.primitive :vm_load_tool
View
@@ -48,6 +48,7 @@ TYPE_GEN = %w[ vm/gen/includes.hpp
GENERATED = %W[ vm/gen/revision.h
vm/gen/config_variables.h
vm/gen/signature.h
+ vm/dtrace/probes.h
#{encoding_database}
#{transcoders_database}
] + TYPE_GEN + INSN_GEN
@@ -251,6 +252,12 @@ file 'vm/gen/config_variables.h' => %w[lib/rubinius/configuration.rb config.rb]
ruby 'vm/codegen/config_vars.rb', t.name
end
+file 'vm/dtrace/probes.h' do |t|
+ if Rubinius::BUILD_CONFIG[:dtrace]
+ sh %[dtrace -h -o vm/dtrace/probes.h -s vm/dtrace/probes.d]
+ end
+end
+
require 'projects/daedalus/daedalus'
if jobs = ENV['JOBS']
@@ -42,6 +42,8 @@
#include "builtin/nativefunction.hpp"
#include "instruments/tooling.hpp"
+#include "call_frame.hpp"
+#include "dtrace/dtrace.h"
namespace rubinius {
@@ -90,17 +92,25 @@ namespace rubinius {
state->set_call_frame(call_frame);
try {
-
+ OnStack<2> os(state, exec, mod);
#ifdef RBX_PROFILER
if(unlikely(state->vm()->tooling())) {
- OnStack<2> os(state, exec, mod);
tooling::MethodEntry method(state, exec, mod, args);
- return nfunc->call(state, args, call_frame);
+ RUBINIUS_METHOD_FFI_ENTRY_HOOK(state, mod, args.name(), call_frame);
+ Object* ret = nfunc->call(state, args, call_frame);
+ RUBINIUS_METHOD_FFI_RETURN_HOOK(state, mod, args.name(), call_frame);
+ return ret;
} else {
- return nfunc->call(state, args, call_frame);
+ RUBINIUS_METHOD_FFI_ENTRY_HOOK(state, mod, args.name(), call_frame);
+ Object* ret = nfunc->call(state, args, call_frame);
+ RUBINIUS_METHOD_FFI_RETURN_HOOK(state, mod, args.name(), call_frame);
+ return ret;
}
#else
- return nfunc->call(state, args, msg, call_frame);
+ RUBINIUS_METHOD_FFI_ENTRY_HOOK(state, mod, args.name(), call_frame);
+ Object* ret = nfunc->call(state, args, call_frame);
+ RUBINIUS_METHOD_FFI_RETURN_HOOK(state, mod, args.name(), call_frame);
+ return ret;
#endif
} catch(TypeError &e) {
@@ -109,6 +119,7 @@ namespace rubinius {
exc->locations(state, Location::from_call_stack(state, call_frame));
state->raise_exception(exc);
+ RUBINIUS_METHOD_FFI_RETURN_HOOK(state, mod, args.name(), call_frame);
return NULL;
}
}
@@ -30,6 +30,8 @@
#include "capi/capi.hpp"
#include "capi/handle.hpp"
+#include "dtrace/dtrace.h"
+
#ifdef RBX_WINDOWS
#include <malloc.h>
#endif
@@ -694,41 +696,50 @@ namespace rubinius {
// MethodEntry. It's duplicated, but it's much easier to understand than
// trying to de-dup it.
+ OnStack<2> os(state, exec, mod);
if(unlikely(state->vm()->tooling())) {
- OnStack<2> os(state, exec, mod);
tooling::MethodEntry method(state, exec, mod, args);
+ RUBINIUS_METHOD_NATIVE_ENTRY_HOOK(state, mod, args.name(), call_frame);
+
PLACE_EXCEPTION_POINT(ep);
if(unlikely(ep.jumped_to())) {
ret = NULL;
} else {
ret = ArgumentHandler::invoke(state, nm, env, args);
}
+ RUBINIUS_METHOD_NATIVE_RETURN_HOOK(state, mod, args.name(), call_frame);
} else {
+ RUBINIUS_METHOD_NATIVE_ENTRY_HOOK(state, mod, args.name(), call_frame);
+
PLACE_EXCEPTION_POINT(ep);
if(unlikely(ep.jumped_to())) {
ret = NULL;
} else {
ret = ArgumentHandler::invoke(state, nm, env, args);
}
+ RUBINIUS_METHOD_NATIVE_RETURN_HOOK(state, mod, args.name(), call_frame);
}
#else
+ RUBINIUS_METHOD_NATIVE_ENTRY_HOOK(state, mod, args.name(), call_frame);
+
PLACE_EXCEPTION_POINT(ep);
if(unlikely(ep.jumped_to())) {
ret = NULL;
} else {
ret = ArgumentHandler::invoke(state, nm, env, args);
}
+ RUBINIUS_METHOD_NATIVE_RETURN_HOOK(state, mod, args.name(), call_frame);
#endif
env->set_current_call_frame(saved_frame);
env->set_current_native_frame(nmf.previous());
ep.pop(env);
LEAVE_CAPI(state);
- OnStack<1> os(state, ret);
+ OnStack<1> os_ret(state, ret);
// Handle any signals that occurred while the native method
// was running.
View
@@ -69,6 +69,7 @@
#include "util/sha1.h"
#include "instruments/tooling.hpp"
+#include "dtrace/dtrace.h"
#include "gc/walker.hpp"
@@ -1725,4 +1726,16 @@ namespace rubinius {
setproctitle("%s", title->c_str_null_safe(state));
return title;
}
+
+ Object* System::vm_dtrace_fire(STATE, String* payload) {
+#if HAVE_DTRACE
+ if(RUBINIUS_RUBY_PROBE_ENABLED()) {
+ RUBINIUS_RUBY_PROBE((const char*)payload->byte_address(), payload->byte_size());
+ return cTrue;
+ }
+ return cFalse;
+#else
+ return cNil;
+#endif
+ }
}
@@ -388,6 +388,9 @@ namespace rubinius {
// Rubinius.primitive :vm_set_process_title
static String* vm_set_process_title(STATE, String* name);
+ // Rubinius.primitive :vm_dtrace_fire
+ static Object* vm_dtrace_fire(STATE, String* payload);
+
public: /* Type info */
class Info : public TypeInfo {
@@ -18,6 +18,7 @@
#include "environment.hpp"
#include "instruments/tooling.hpp"
+#include "dtrace/dtrace.h"
#include "vm/object_utils.hpp"
#include "vm.hpp"
@@ -227,6 +228,8 @@ namespace rubinius {
VM::set_current(vm, tn.str());
+ RUBINIUS_THREAD_START(tn.str().c_str(), vm->thread_id(), 0);
+
state->set_call_frame(0);
if(cDebugThreading) {
@@ -285,6 +288,7 @@ namespace rubinius {
std::cerr << "[LOCK thread " << vm->thread_id() << " exited]\n";
}
+ RUBINIUS_THREAD_STOP(tn.str().c_str(), vm->thread_id(), 0);
return 0;
}
View
@@ -173,6 +173,14 @@ namespace rubinius {
}
+ Symbol* CallFrame::file(STATE) {
+ if(compiled_code) {
+ return compiled_code->file();
+ } else {
+ return nil<Symbol>();
+ }
+ }
+
int CallFrame::line(STATE) {
if(!compiled_code) return -2; // trampoline context
return compiled_code->line(state, ip());
View
@@ -242,6 +242,7 @@ namespace rubinius {
void print_backtrace(STATE, int count=0, bool filter=false);
void print_backtrace(STATE, std::ostream& stream, int count=0, bool filter=false);
+ Symbol* file(STATE);
int line(STATE);
bool scope_still_valid(VariableScope* scope);
@@ -68,21 +68,28 @@ def prim_return(str, indent=2)
def output_call(str, call, args)
str << "\n"
str << " try {\n"
+ str << " OnStack<2> os(state, exec, mod);\n"
str << "#ifdef RBX_PROFILER\n"
str << " if(unlikely(state->vm()->tooling())) {\n"
- str << " OnStack<2> os(state, exec, mod);\n"
str << " tooling::MethodEntry method(state, exec, mod, args);\n"
+ str << " RUBINIUS_METHOD_PRIMITIVE_ENTRY_HOOK(state, mod, args.name(), call_frame);\n"
str << " ret = #{call}(#{args.join(', ')});\n"
+ str << " RUBINIUS_METHOD_PRIMITIVE_RETURN_HOOK(state, mod, args.name(), call_frame);\n"
str << " } else {\n"
+ str << " RUBINIUS_METHOD_PRIMITIVE_ENTRY_HOOK(state, mod, args.name(), call_frame);\n"
str << " ret = #{call}(#{args.join(', ')});\n"
+ str << " RUBINIUS_METHOD_PRIMITIVE_RETURN_HOOK(state, mod, args.name(), call_frame);\n"
str << " }\n"
str << "#else\n"
+ str << " RUBINIUS_METHOD_PRIMITIVE_ENTRY_HOOK(state, mod, args.name(), call_frame);\n"
str << " ret = #{call}(#{args.join(', ')});\n"
+ str << " RUBINIUS_METHOD_PRIMITIVE_RETURN_HOOK(state, mod, args.name(), call_frame);\n"
str << "#endif\n"
str << " } catch(const RubyException& exc) {\n"
str << " exc.exception->locations(state,\n"
str << " Location::from_call_stack(state, call_frame));\n"
str << " state->raise_exception(exc.exception);\n"
+ str << " RUBINIUS_METHOD_PRIMITIVE_RETURN_HOOK(state, mod, args.name(), call_frame);\n"
str << " return NULL;\n"
str << " }\n"
str << "\n"
View
@@ -0,0 +1,54 @@
+#ifndef RUBINIUS_DTRACE_H
+#define RUBINIUS_DTRACE_H
+
+#ifdef HAVE_DTRACE
+#include "dtrace/probes.h"
+
+#define RUBINIUS_METHOD_HOOK(probe, state, mod, method, previous) \
+{ \
+ if (RUBINIUS_METHOD_##probe##_ENABLED()) { \
+ const char* module_name = mod->debug_str(state).c_str(); \
+ const char* code_name = method->debug_str(state).c_str(); \
+ const char* file_name = "<unknown>"; \
+ int line = 0; \
+ if(previous) { \
+ Symbol* file = previous->file(state); \
+ if(!file->nil_p()) { \
+ file_name = file->debug_str(state).c_str(); \
+ } \
+ line = previous->line(state); \
+ } \
+ RUBINIUS_METHOD_##probe(module_name, code_name, file_name, line); \
+ } \
+} \
+
+#else
+#include "dtrace/probes_dummy.h"
+#define RUBINIUS_METHOD_HOOK(probe, state, mod, method, previous) do { } while(0)
+#endif
+
+#define RUBINIUS_METHOD_ENTRY_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(ENTRY, state, module, method, previous)
+
+#define RUBINIUS_METHOD_RETURN_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(RETURN, state, module, method, previous)
+
+#define RUBINIUS_METHOD_NATIVE_ENTRY_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(NATIVE_ENTRY, state, module, method, previous)
+
+#define RUBINIUS_METHOD_NATIVE_RETURN_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(NATIVE_RETURN, state, module, method, previous)
+
+#define RUBINIUS_METHOD_FFI_ENTRY_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(FFI_ENTRY, state, module, method, previous)
+
+#define RUBINIUS_METHOD_FFI_RETURN_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(FFI_RETURN, state, module, method, previous)
+
+#define RUBINIUS_METHOD_PRIMITIVE_ENTRY_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(PRIMITIVE_ENTRY, state, module, method, previous)
+
+#define RUBINIUS_METHOD_PRIMITIVE_RETURN_HOOK(state, module, method, previous) \
+ RUBINIUS_METHOD_HOOK(PRIMITIVE_RETURN, state, module, method, previous)
+
+#endif
Oops, something went wrong.

0 comments on commit 682ceae

Please sign in to comment.