Skip to content
Browse files

Introduce tooling API, convert profiler to it.

  • Loading branch information...
1 parent 16f0478 commit 73fb4869e098cecd30d19822896aaedd1de7e012 Evan Phoenix committed
Showing with 2,040 additions and 1,044 deletions.
  1. +2 −0 .gitignore
  2. +1 −1 Rakefile
  3. +32 −1 configure
  4. +22 −0 kernel/bootstrap/rubinius.rb
  5. +0 −1 kernel/delta/load_order.txt
  6. +8 −0 kernel/loader.rb
  7. +162 −0 lib/tooling/profiler/Makefile
  8. +3 −0 lib/tooling/profiler/extconf.rb
  9. +4 −8 {kernel/delta → lib/tooling/profiler}/profiler.rb
  10. +826 −0 lib/tooling/profiler/profiler_vm.cpp
  11. +27 −6 projects/daedalus/daedalus.rb
  12. +4 −1 rakelib/extensions.rake
  13. +3 −5 vm/builtin/block_environment.cpp
  14. +3 −3 vm/builtin/nativefunction.cpp
  15. +3 −3 vm/builtin/nativemethod.cpp
  16. +12 −10 vm/builtin/system.cpp
  17. +12 −12 vm/builtin/system.hpp
  18. +117 −0 vm/capi/include/rbxti.hpp
  19. +4 −6 vm/codegen/field_extract.rb
  20. +13 −0 vm/config_parser.cpp
  21. +1 −0 vm/config_parser.hpp
  22. +6 −1 vm/configuration.hpp
  23. +41 −0 vm/environment.cpp
  24. +1 −0 vm/environment.hpp
  25. +0 −1 vm/instructions.cpp
  26. +0 −484 vm/instruments/profiler.cpp
  27. +0 −295 vm/instruments/profiler.hpp
  28. +86 −0 vm/instruments/rbxti-internal.hpp
  29. +317 −0 vm/instruments/rbxti.cpp
  30. +139 −0 vm/instruments/tooling.cpp
  31. +131 −0 vm/instruments/tooling.hpp
  32. +0 −2 vm/llvm/jit.cpp
  33. +2 −2 vm/llvm/jit_block.cpp
  34. +0 −1 vm/llvm/jit_compiler.cpp
  35. +2 −2 vm/llvm/jit_method.cpp
  36. +4 −4 vm/llvm/jit_util.cpp
  37. +1 −1 vm/primitives.cpp
  38. +2 −31 vm/shared_state.cpp
  39. +7 −9 vm/shared_state.hpp
  40. +0 −116 vm/test/test_profiler.hpp
  41. +4 −1 vm/util/configuration.hpp
  42. +12 −25 vm/vm.cpp
  43. +23 −9 vm/vm.hpp
  44. +3 −3 vm/vmmethod.cpp
View
2 .gitignore
@@ -72,6 +72,8 @@ tmtags
/lib/compiler/opcodes.rb
/lib/compiler/generator_methods.rb
+vm/capi/include/rbx_config.h
+
/lib/etc.rb
/lib/kernel.rb
/lib/zlib.rb
View
2 Rakefile
@@ -33,7 +33,7 @@ end
require config_rb
BUILD_CONFIG = Rubinius::BUILD_CONFIG
-unless BUILD_CONFIG[:config_version] == 35
+unless BUILD_CONFIG[:config_version] == 36
STDERR.puts "Your configuration is outdated, please run ./configure first"
exit 1
end
View
33 configure
@@ -89,7 +89,7 @@ class Configure
@libversion = "1.2"
@version = "#{@libversion}.4dev"
@release_date = "yyyy-mm-dd"
- @config_version = 35
+ @config_version = 36
# TODO: add conditionals for platforms
if RbConfig::CONFIG["build_os"] =~ /darwin/
@@ -877,6 +877,37 @@ end
end
end
+ File.open "vm/capi/include/rbx_config.h", "w" do |f|
+ f.puts <<-EOC
+#define RBX_HOST "#{@host}"
+#define RBX_CPU "#{@cpu}"
+#define RBX_VENDOR "#{@vendor}"
+#define RBX_OS "#{@os}"
+#define RBX_BIN_PATH "#{@bindir}"
+#define RBX_GEMS_PATH "#{@gemsdir}"
+#define RBX_RUNTIME "#{@runtime}"
+#define RBX_LIB_PATH "#{@lib_path}"
+#define RBX_EXT_PATH "#{@ext_path}"
+#define RBX_HDR_PATH "#{@includedir}"
+#define RBX_SITE_PATH "#{@sitedir}"
+#define RBX_VENDOR_PATH "#{@vendordir}"
+#define RBX_VERSION "#{@version}"
+#define RBX_LIB_VERSION "#{@libversion}"
+#define RBX_LDSHARED "#{@ldshared}"
+#define RBX_RELEASE_DATE "#{@release_date}"
+#define RBX_SIZEOF_LONG #{@sizeof_long}
+#define RBX_LLVM_API_VER #{@llvm_api_version}
+ EOC
+
+ if @little_endian
+ f.puts "#define RBX_LITTLE_ENDIAN 1"
+ end
+
+ if @tr1_hash
+ f.puts "#define RBX_HAVE_TR1_HASH 1"
+ end
+ end
+
# Write a require file depending on which Readline library we use.
File.open "lib/readline.rb", "w" do |f|
if @rb_readline
View
22 kernel/bootstrap/rubinius.rb
@@ -118,4 +118,26 @@ def self.run_script(cm)
Ruby.primitive :vm_run_script
raise PrimitiveFailure, "Rubinius.run_script failed"
end
+
+ module Tooling
+ def self.available?
+ Ruby.primitive :vm_tooling_available_p
+ raise PrimitiveFailure, "Tooling.available? failed"
+ end
+
+ def self.active?
+ Ruby.primitive :vm_tooling_active_p
+ raise PrimitiveFailure, "Tooling.active? failed"
+ end
+
+ def self.enable
+ Ruby.primitive :vm_tooling_enable
+ raise PrimitiveFailure, "Tooling.enable failed"
+ end
+
+ def self.disable
+ Ruby.primitive :vm_tooling_disable
+ raise PrimitiveFailure, "Tooling.disable failed"
+ end
+ end
end
View
1 kernel/delta/load_order.txt
@@ -11,7 +11,6 @@ kernel.rbc
math.rbc
options.rbc
stats.rbc
-profiler.rbc
signal.rbc
struct.rbc
thread.rbc
View
8 kernel/loader.rb
@@ -429,6 +429,14 @@ def options(argv=ARGV)
handle_rubyopt(options)
+ if str = Rubinius::Config['tool.require']
+ begin
+ require str
+ rescue LoadError
+ STDERR.puts "Unable to require file for tool: '#{str}'"
+ end
+ end
+
if @profile
require 'profile'
end
View
162 lib/tooling/profiler/Makefile
@@ -0,0 +1,162 @@
+
+SHELL = /bin/sh
+
+#### Start of system configuration section. ####
+
+srcdir = .
+topdir = /Users/evan/git/rbx-release/vm/capi/include
+hdrdir = $(topdir)
+VPATH = $(srcdir):$(topdir):$(hdrdir)
+prefix = $(DESTDIR)/Users/evan/git/rbx-release
+exec_prefix = $(prefix)
+install_prefix = $(DESTDIR)
+includedir = $(prefix)/include
+bindir = $(DESTDIR)/Users/evan/git/rbx-release/bin
+sysconfdir = $(prefix)/etc
+localedir = $(datarootdir)/locale
+rubylibdir = $(DESTDIR)/Users/evan/git/rbx-release/lib/site
+sitedir = $(DESTDIR)/Users/evan/git/rbx-release/lib/site
+oldincludedir = $(DESTDIR)/usr/include
+libexecdir = $(exec_prefix)/libexec
+rubyhdrdir = $(DESTDIR)/Users/evan/git/rbx-release/vm/capi/include
+libdir = $(exec_prefix)/lib
+dvidir = $(docdir)
+docdir = $(datarootdir)/doc/$(PACKAGE)
+psdir = $(docdir)
+infodir = $(datarootdir)/info
+datadir = $(datarootdir)
+archdir = $(DESTDIR)/Users/evan/git/rbx-release/lib/site/x86_64-darwin10.7.0
+sharedstatedir = $(prefix)/com
+localstatedir = $(prefix)/var
+pdfdir = $(docdir)
+htmldir = $(docdir)
+datarootdir = $(prefix)/share
+sbindir = $(exec_prefix)/sbin
+sitelibdir = $(DESTDIR)/Users/evan/git/rbx-release/lib/site
+mandir = $(datarootdir)/man
+sitearchdir = $(DESTDIR)/Users/evan/git/rbx-release/lib/site/x86_64-darwin10.7.0
+
+CC = gcc
+LIBRUBY = $(LIBRUBY_SO)
+LIBRUBY_A =
+LIBRUBYARG_SHARED =
+LIBRUBYARG_STATIC =
+
+RUBY_EXTCONF_H =
+cflags =
+optflags =
+debugflags =
+warnflags =
+CFLAGS = -fPIC -ggdb3 -O2 -fPIC
+INCFLAGS = -I. -I$(topdir) -I$(hdrdir) -I$(srcdir)
+DEFS =
+CPPFLAGS =
+CXXFLAGS = $(CFLAGS)
+ldflags =
+dldflags =
+archflag =
+DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
+LDSHARED = gcc -dynamic -bundle -undefined suppress -flat_namespace
+AR = ar
+EXEEXT =
+
+RUBY_INSTALL_NAME = rbx
+RUBY_SO_NAME = rubinius-1.2.4dev
+arch = x86_64-darwin10.7.0
+sitearch = x86_64-darwin10.7.0
+ruby_version = 1.8
+ruby = /Users/evan/git/rbx-release/bin/rbx
+RUBY = $(ruby)
+RM = rm -f
+MAKEDIRS = mkdir -p
+INSTALL = install -c
+INSTALL_PROG = $(INSTALL) -m 0755
+INSTALL_DATA = $(INSTALL) -m 644
+COPY = cp
+
+#### End of system configuration section. ####
+
+preload =
+
+libpath = . $(libdir)
+LIBPATH = -L. -L$(libdir)
+DEFFILE =
+
+CLEANFILES = mkmf.log
+DISTCLEANFILES =
+
+extout =
+extout_prefix =
+target_prefix =
+LOCAL_LIBS =
+LIBS = $(LIBRUBYARG_STATIC)
+SRCS = profiler_vm.cpp
+OBJS = profiler_vm.o
+TARGET = profiler_vm
+DLLIB = $(TARGET).bundle
+EXTSTATIC =
+STATIC_LIB =
+
+BINDIR = $(bindir)
+RUBYCOMMONDIR = $(sitedir)$(target_prefix)
+RUBYLIBDIR = $(sitelibdir)$(target_prefix)
+RUBYARCHDIR = $(sitearchdir)$(target_prefix)
+
+TARGET_SO = $(DLLIB)
+CLEANLIBS = $(TARGET).bundle
+CLEANOBJS = *.o *.bak
+
+all: $(DLLIB)
+static: $(STATIC_LIB)
+.PHONY: all install static install-so install-rb
+.PHONY: clean clean-so clean-rb
+
+clean:
+ @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
+
+distclean: clean
+ @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+ @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
+
+realclean: distclean
+install: install-so install-rb
+
+install-so: $(RUBYARCHDIR)
+install-so: $(RUBYARCHDIR)/$(DLLIB)
+$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
+install-rb: pre-install-rb install-rb-default
+install-rb-default: pre-install-rb-default
+pre-install-rb: Makefile
+pre-install-rb-default: Makefile
+$(RUBYARCHDIR):
+ $(MAKEDIRS) $@
+
+site-install: site-install-so site-install-rb
+site-install-so: install-so
+site-install-rb: install-rb
+
+.SUFFIXES: .c .m .cc .cxx .cpp .C .o
+
+.cc.o:
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
+
+.cxx.o:
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
+
+.cpp.o:
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
+
+.C.o:
+ $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
+
+.c.o:
+ $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<
+
+$(DLLIB): $(OBJS) Makefile
+ @-$(RM) $@
+ $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
+
+
+
+$(OBJS): ruby.h defines.h
View
3 lib/tooling/profiler/extconf.rb
@@ -0,0 +1,3 @@
+require 'mkmf'
+
+create_makefile 'profiler_vm'
View
12 kernel/delta/profiler.rb → lib/tooling/profiler/profiler.rb
@@ -10,13 +10,11 @@ class Instrumenter
attr_reader :info, :options
def self.available?
- Ruby.primitive :vm_profiler_instrumenter_available_p
- raise PrimitiveFailure, "Profiler::Instrumenter.available? failed"
+ Rubinius::Tooling.available?
end
def self.active?
- Ruby.primitive :vm_profiler_instrumenter_active_p
- raise PrimitiveFailure, "Profiler::Instrumenter.active? failed"
+ Rubinius::Tooling.active?
end
def initialize(options = {})
@@ -57,13 +55,11 @@ def set_options(options)
end
def start
- Ruby.primitive :vm_profiler_instrumenter_start
- raise PrimitiveFailure, "Profiler::Instrumenter#start failed"
+ Rubinius::Tooling.enable
end
def __stop__
- Ruby.primitive :vm_profiler_instrumenter_stop
- raise PrimitiveFailure, "Profiler::Instrumenter#stop failed"
+ Rubinius::Tooling.disable
end
def stop
View
826 lib/tooling/profiler/profiler_vm.cpp
@@ -0,0 +1,826 @@
+#include <rbxti.hpp>
+#include <rbx_config.h>
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <stack>
+#include <tr1/unordered_map>
+#include <map>
+#include <iostream>
+
+#include <time.h>
+
+#include <sstream>
+#include <iostream>
+#include <vector>
+#include <algorithm>
+
+typedef uint64_t method_id;
+
+#ifndef RBX_HAVE_TR1_HASH
+namespace std {
+ namespace tr1 {
+ template <>
+ struct hash<method_id> {
+ size_t operator()(const method_id id) const {
+ return id;
+ }
+ };
+ }
+}
+#endif
+
+using namespace rbxti;
+
+namespace profiler {
+
+ enum Kind {
+ kNormal,
+ kSingleton,
+ kBlock,
+ kYoungGC,
+ kMatureGC,
+ kNormalJIT,
+ kSingletonJIT,
+ kBlockJIT,
+ kFinalizers,
+ kScript
+ };
+
+ class VM;
+ class Profiler;
+ class Method;
+ class Node;
+ typedef std::tr1::unordered_map<Method*, rinteger> KeyMap;
+ typedef std::tr1::unordered_map<method_id, Method*> MethodMap;
+
+ /* An accumulating increment timer. Keeps track of the maximum and minimum
+ * intervals recorded. Calculates a cumulative moving average according to
+ * the formula:
+ *
+ * x(i+1) + i*CA(i)
+ * CA(i+1) = -----------------
+ * i + 1
+ *
+ * where:
+ *
+ * CA(n) is the nth cumulative average
+ * x(n) is the nth measured value of x
+ * i is the number of measurements
+ */
+ class Timer {
+ protected:
+
+ uint64_t total_;
+ uint64_t timings_;
+ uint64_t max_;
+ uint64_t min_;
+ uint64_t last_;
+ uint64_t start_;
+ double moving_average_;
+ bool started_;
+
+ public:
+
+ Timer()
+ : total_(0)
+ , timings_(0)
+ , max_(0)
+ , min_(0)
+ , last_(0)
+ , start_(0)
+ , moving_average_(0.0)
+ , started_(false)
+ { }
+
+ bool started() {
+ return started_;
+ }
+
+ uint64_t total() {
+ return total_;
+ }
+
+ uint64_t timings() {
+ return timings_;
+ }
+
+ uint64_t max() {
+ return max_;
+ }
+
+ uint64_t min() {
+ return min_;
+ }
+
+ double moving_average() {
+ return moving_average_;
+ }
+
+ void start(Env* env) {
+ if(started_) return;
+
+ started_ = true;
+ start_ = env->time_current_ns();
+ }
+
+ void stop(Env* env) {
+ if(!started_) return;
+
+ started_ = false;
+
+ last_ = env->time_current_ns() - start_;
+ total_ += last_;
+
+ if(min_ == 0 || min_ > last_) min_ = last_;
+ if(max_ == 0 || max_ < last_) max_ = last_;
+
+ moving_average_ = (last_ + timings_ * moving_average_) / (timings_ + 1);
+ ++timings_;
+ }
+ };
+
+ class StackTimer : public Timer {
+ size_t entered_;
+ uint64_t count_;
+
+ public:
+ StackTimer()
+ : entered_(0)
+ , count_(0)
+ { }
+
+ uint64_t count() {
+ return count_;
+ }
+
+ void start(Env* env) {
+ ++entered_;
+ Timer::start(env);
+ }
+
+ void stop(Env* env) {
+ if(!started_) return;
+
+ ++count_;
+ if(--entered_ == 0) Timer::stop(env);
+ }
+ };
+
+ class Node {
+ int id_;
+ int called_;
+ uint64_t total_;
+ Method* method_;
+
+ Node* sibling_;
+ Node* first_sub_node_;
+
+ public:
+ Node(Method* method, int id)
+ : id_(id)
+ , called_(0)
+ , total_(0)
+ , method_(method)
+ , sibling_(0)
+ , first_sub_node_(0)
+ { }
+
+ int id() {
+ return id_;
+ }
+
+ Method* method() {
+ return method_;
+ }
+
+ uint64_t total() {
+ return total_;
+ }
+
+ int called() {
+ return called_;
+ }
+
+ Node* sub_nodes() {
+ return first_sub_node_;
+ }
+
+ int count_sub_nodes() {
+ int count = 0;
+ Node* node = first_sub_node_;
+ while(node) {
+ ++count;
+ node = node->sibling();
+ }
+
+ return count;
+ }
+
+ Node* sibling() {
+ return sibling_;
+ }
+
+ void set_sibling(Node* node) {
+ sibling_ = node;
+ }
+
+ void accumulate(uint64_t time) {
+ total_ += time;
+ called_++;
+ }
+
+ Node* find_sub_node(Profiler* profiler, Method* method);
+ };
+
+ class Method {
+ private:
+ method_id id_;
+ rsymbol name_;
+ rsymbol container_;
+ Kind kind_;
+ rsymbol file_;
+ int line_;
+ uint64_t total_;
+
+ public:
+ StackTimer timer;
+
+ public:
+ Method(method_id id, rsymbol name, rsymbol container, Kind kind=kNormal)
+ : id_(id)
+ , name_(name)
+ , container_(container)
+ , kind_(kind)
+ , file_(0)
+ , line_(0)
+ , total_(0)
+ { }
+
+ method_id id() {
+ return id_;
+ }
+
+ rsymbol container() {
+ return container_;
+ }
+
+ rsymbol name() {
+ return name_;
+ }
+
+ Kind kind() {
+ return kind_;
+ }
+
+ rstring to_s(Env* env);
+
+ rsymbol file() {
+ return file_;
+ }
+
+ int line() {
+ return line_;
+ }
+
+ void set_position(rsymbol file, int line) {
+ file_ = file;
+ line_ = line;
+ }
+
+ uint64_t total() {
+ return total_;
+ }
+
+ void accumulate(uint64_t time) {
+ total_ += time;
+ }
+
+ Node* find_node(Method* method);
+ Method* find_callee(method_id id, rsymbol container,
+ rsymbol name, Kind kind);
+ };
+
+ class Profiler;
+
+ /** Created when a method is being called. Contains a timer that tracks
+ * how much time is spent in the method. When the MethodEntry instance
+ * goes out of scope, the destructor records the elapsed time and updates
+ * the Method and Node objects.
+ */
+ class MethodEntry {
+ Method* method_;
+ Node* node_;
+ Node* previous_;
+ Timer timer_;
+
+ public:
+
+ MethodEntry(Method* method)
+ : method_(method)
+ {}
+
+ void start(Profiler* profiler, Env* env);
+ void stop(Profiler* profiler, Env* env);
+ };
+
+ class Profiler {
+ MethodMap methods_;
+ Node* root_;
+ Node* current_;
+ int nodes_;
+ uint32_t threshold_;
+ uint64_t start_time_;
+
+ public:
+ Profiler(Env* env);
+
+ ~Profiler();
+
+ Node* current() {
+ return current_;
+ }
+
+ void set_current(Node* node) {
+ current_ = node;
+ }
+
+ int next_node_id() {
+ return nodes_++;
+ }
+
+ uint64_t start_time() {
+ return start_time_;
+ }
+
+ method_id create_id(Env* env, rmethod cm, rsymbol container, rsymbol name, Kind kind);
+ Method* find_method(Env* env, rmethod cm, rsymbol container, rsymbol name, Kind kind);
+
+
+ Method* enter_method(Env* env, robject recv, rsymbol name, rmodule mod,
+ rmethod cm);
+ Method* enter_block(Env* env, rsymbol name, rmodule module, rmethod cm);
+ Method* get_method(Env* env, rmethod cm, rsymbol name,
+ rsymbol container, Kind kind);
+
+ void results(Env* env, rtable profile, rtable nodes, rtable methods,
+ KeyMap& keys, uint64_t runtime);
+ };
+
+ rstring Method::to_s(Env* env) {
+ std::stringstream ss;
+ char data[1024];
+
+ if(kind() == kScript) {
+ ss << "script:";
+ if(file_) {
+ env->symbol_cstr(file_, data, 1024);
+ ss << data;
+ } else {
+ ss << "--unknown-file--";
+ ss << ":" << line_;
+ }
+
+ return env->string_new(ss.str().c_str());
+ }
+
+ if(!env->is_nil(container())) {
+ env->symbol_cstr(container_, data, 1024);
+ ss << data;
+ } else {
+ ss << "<anonymous>";
+ }
+
+ env->symbol_cstr(name(), data, 1024);
+
+ switch(kind()) {
+ case kNormal:
+ ss << "#" << data;
+ break;
+ case kNormalJIT:
+ ss << "#" << data << " <jit>";
+ break;
+ case kSingleton:
+ case kYoungGC:
+ case kMatureGC:
+ case kFinalizers:
+ ss << "." << data;
+ break;
+ case kSingletonJIT:
+ ss << "." << data << " <jit>";
+ break;
+ case kBlock:
+ ss << "::" << data << "<" << line_ << "> {}";
+ break;
+ case kBlockJIT:
+ ss << "::" << data << " {" << line_ << "} <jit>";
+ break;
+ case kScript:
+ // handled above, just here to make gcc happy.
+ abort();
+ }
+
+ return env->string_new(ss.str().c_str());
+ }
+
+ Node* Node::find_sub_node(Profiler* profiler, Method* method) {
+ Node* sub = first_sub_node_;
+
+ while(sub) {
+ if(sub->method() == method) return sub;
+ sub = sub->sibling();
+ }
+
+ Node* node = new Node(method, profiler->next_node_id());
+ node->set_sibling(first_sub_node_);
+ first_sub_node_ = node;
+
+ return node;
+ }
+
+ void MethodEntry::start(Profiler* profiler, Env* env) {
+ previous_ = profiler->current();
+ node_ = previous_->find_sub_node(profiler, method_);
+ profiler->set_current(node_);
+
+ method_->timer.start(env);
+ timer_.start(env);
+ }
+
+ void MethodEntry::stop(Profiler* profiler, Env* env) {
+ method_->timer.stop(env);
+ timer_.stop(env);
+ method_->accumulate(timer_.total());
+ node_->accumulate(timer_.total());
+ profiler->set_current(previous_);
+ }
+
+ Profiler::Profiler(Env* env)
+ : nodes_(0)
+ , start_time_(env->time_current_ns())
+ {
+ root_ = current_ = new Node(0, next_node_id());
+ threshold_ = (uint32_t)env->config_get_int("profiler.threshold");
+ }
+
+ Method* Profiler::enter_block(Env* env, rsymbol name, rmodule module, rmethod cm) {
+ return get_method(env, cm, name, env->module_name(module), kBlock);
+ }
+
+ Method* Profiler::enter_method(Env* env, robject recv, rsymbol name, rmodule mod,
+ rmethod cm)
+ {
+ if(env->module_is_metaclass(mod)) {
+ robject attached = env->metaclass_attached_instance(mod);
+
+ rmodule as_module = env->cast_to_rmodule(attached);
+ if(as_module) {
+ return get_method(env, cm, name, env->module_name(as_module), kSingleton);
+ } else {
+ rstring str = env->to_s(recv);
+ return get_method(env, cm, name, env->string_to_symbol(str), kSingleton);
+ }
+ } else {
+ return get_method(env, cm, name, env->module_name(mod), kNormal);
+ }
+ }
+
+ Method* Profiler::get_method(Env* env, rmethod cm, rsymbol name,
+ rsymbol container, Kind kind)
+ {
+ Method* method = find_method(env, cm, container, name, kind);
+
+ if(!method->file() && cm && !env->is_nil(cm)) {
+ method->set_position(env->method_file(cm), env->method_line(cm));
+ }
+
+ return method;
+ }
+
+ method_id Profiler::create_id(Env* env, rmethod cm, rsymbol name,
+ rsymbol container, Kind kind)
+ {
+ // If we have a CompiledMethod, use it's method id.
+ if(cm && !env->is_nil(cm)) {
+ r_mint i = env->method_id(cm);
+ if(i) return i;
+ }
+
+ // | -- 32 bits of container -- | -- 29 bits of name -- | -- 2 bits of kind -- | 0
+
+ uint32_t c = env->symbol_id(container) & 0xffffffff;
+ uint32_t n = env->symbol_id(name) & 0x1fffffff;
+ uint32_t k = kind & 0x3;
+
+ return (((uint64_t)c) << 32) |
+ (n << 3) |
+ k << 1;
+ }
+
+ Method* Profiler::find_method(Env* env, rmethod cm, rsymbol container,
+ rsymbol name, Kind kind)
+ {
+ method_id id = create_id(env, cm, container, name, kind);
+
+ Method* method;
+
+ MethodMap::iterator iter = methods_.find(id);
+
+ if(iter == methods_.end()) {
+ method = new Method(id, name, container, kind);
+ methods_[method->id()] = method;
+ } else {
+ method = iter->second;
+ }
+
+ return method;
+ }
+
+ typedef std::vector<Node*> WorkList;
+
+ static rinteger make_key(Env* env, Method* meth, KeyMap& keys) {
+ KeyMap::iterator iter = keys.find(meth);
+
+ if(iter == keys.end()) {
+ rinteger key = env->integer_new(keys.size());
+ keys[meth] = key;
+ return key;
+ }
+
+ return iter->second;
+ }
+
+ static rinteger add_method(Env* env, rtable methods, Method* meth,
+ KeyMap& keys)
+ {
+ rinteger key = make_key(env, meth, keys);
+
+ // We already have the method, skip this.
+ bool fetched = false;
+ env->table_fetch(methods, key, &fetched);
+ if(fetched) return env->cast_to_rinteger(key);
+
+ rsymbol cumulative_sym = env->symbol("cumulative");
+ rsymbol total_sym = env->symbol("total");
+ rsymbol called_sym = env->symbol("called");
+
+ rtable method = env->table_new();
+ env->table_store(methods, key, method);
+
+ env->table_store(method, env->symbol("name"), meth->to_s(env));
+ env->table_store(method, cumulative_sym, env->integer_new(meth->timer.total()));
+ env->table_store(method, total_sym, env->integer_new(meth->total()));
+ env->table_store(method, called_sym, env->integer_new(meth->timer.count()));
+
+ if(meth->file()) {
+ if(env->is_nil(meth->file())) {
+ env->table_store(method, env->symbol("file"), env->string_new("unknown file"));
+ } else {
+ env->table_store(method, env->symbol("file"),
+ env->symbol_to_string(meth->file()));
+ }
+
+ env->table_store(method, env->symbol("line"), env->integer_new(meth->line()));
+ }
+
+ return key;
+ }
+
+ static void add_node(Env* env, rtable nodes, rtable methods, Node* node,
+ WorkList& work, KeyMap& keys, uint32_t threshold)
+ {
+ // We haven't exited this method yet, so its stats won't be accurate
+ if(node->method()->timer.started()) return;
+
+ rinteger key = env->integer_new(node->id());
+
+ rarray tbl = env->array_new(5);
+
+ env->table_store(nodes, key, tbl);
+
+ robject meth_key = add_method(env, methods, node->method(), keys);
+
+ env->array_set(tbl, 0, meth_key);
+ env->array_set(tbl, 1, env->integer_new(node->total()));
+ env->array_set(tbl, 2, env->integer_new(node->called()));
+
+ int count = node->count_sub_nodes();
+ env->array_set(tbl, 3, env->integer_new(count));
+
+ rarray ary = env->array_new(count);
+
+ int idx = 0;
+
+ Node* sub = node->sub_nodes();
+
+ while(sub) {
+ if(sub->total() >= threshold) {
+ env->array_set(ary, idx++, env->integer_new(sub->id()));
+ work.push_back(sub);
+ }
+
+ sub = sub->sibling();
+ }
+
+ env->array_set(tbl, 4, ary);
+ }
+
+ void Profiler::results(Env* env, rtable profile, rtable nodes, rtable methods,
+ KeyMap& keys, uint64_t runtime)
+ {
+ WorkList work;
+
+ // If we haven't even gone for a total of longer than 10x the threshold,
+ // just disable the threshold.
+ if(runtime < 10 * threshold_) threshold_ = 0;
+
+ env->table_store(profile, env->symbol("total_nodes"), env->integer_new(nodes_));
+
+ rarray roots = env->array_new(root_->count_sub_nodes());
+ env->table_store(profile, env->symbol("roots"), roots);
+
+ int idx = 0;
+ Node* sub = root_->sub_nodes();
+
+ while(sub) {
+ if(sub->total() >= threshold_) {
+ env->array_set(roots, idx++, env->integer_new(sub->id()));
+ work.push_back(sub);
+ }
+
+ sub = sub->sibling();
+ }
+
+ while(work.size() > 0) {
+ Node* node = work.back();
+ work.pop_back();
+
+ add_node(env, nodes, methods, node, work, keys, threshold_);
+ }
+ }
+
+ Profiler::~Profiler() {
+ for(MethodMap::iterator i = methods_.begin();
+ i != methods_.end();
+ i++) {
+ delete i->second;
+ }
+
+ WorkList work;
+
+ work.push_back(root_);
+
+ while(work.size() > 0) {
+ Node* node = work.back();
+ work.pop_back();
+
+ Node* sub = node->sub_nodes();
+
+ while(sub) {
+ work.push_back(sub);
+ sub = sub->sibling();
+ }
+
+ delete node;
+ }
+ }
+
+ static int cProfileToolID = -1;
+
+ namespace {
+ robject tool_results(Env* env) {
+ Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
+ if(!profiler) return env->nil();
+
+ rtable profile = env->table_new();
+ rtable methods = env->table_new();
+ rtable nodes = env->table_new();
+
+ env->table_store(profile, env->symbol("methods"), methods);
+ env->table_store(profile, env->symbol("nodes"), nodes);
+
+ uint64_t runtime = env->time_current_ns() - profiler->start_time();
+ env->table_store(profile, env->symbol("runtime"), env->integer_new(runtime));
+
+ KeyMap keys;
+ profiler->results(env, profile, nodes, methods, keys, runtime);
+
+ return profile;
+ }
+
+ void tool_enable(Env* env) {
+ Profiler* profiler = new Profiler(env);
+ env->thread_tool_set_data(cProfileToolID, profiler);
+ }
+
+ void* tool_enter_method(Env* env, robject recv, rsymbol name, rmodule mod,
+ rmethod cm)
+ {
+ Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
+ if(!profiler) return 0;
+
+ Method* method = profiler->enter_method(env, recv, name, mod, cm);
+ MethodEntry* me = new MethodEntry(method);
+ me->start(profiler, env);
+
+ return me;
+ }
+
+ void tool_leave_entry(Env* env, void* tag) {
+ MethodEntry* me = (MethodEntry*)tag;
+
+ Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
+ if(!profiler) return;
+
+ me->stop(profiler, env);
+ delete me;
+ }
+
+ void* tool_enter_block(Env* env, rsymbol name, rmodule module, rmethod cm) {
+ Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
+ if(!profiler) return 0;
+
+ Method* method = profiler->enter_block(env, name, module, cm);
+ MethodEntry* me = new MethodEntry(method);
+ me->start(profiler, env);
+
+ return me;
+ }
+
+ void* tool_enter_gc(Env* env, int level) {
+ Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
+ if(!profiler) return 0;
+
+ rsymbol container = env->symbol("GC");
+ rsymbol name;
+
+ Kind kind;
+
+ switch(level) {
+ case GCYoung:
+ kind = kYoungGC;
+ name = env->symbol("collect_young");
+ break;
+ case GCMature:
+ kind = kMatureGC;
+ name = env->symbol("collect_mature");
+ break;
+ case GCFinalizer:
+ kind = kFinalizers;
+ name = env->symbol("run_finalizers");
+ break;
+ default:
+ kind = kFinalizers;
+ name = env->symbol("unknown");
+ break;
+ }
+
+ Method* method = profiler->get_method(env, NULL, name, container, kind);
+ MethodEntry* me = new MethodEntry(method);
+ me->start(profiler, env);
+
+ return me;
+ }
+
+ void* tool_enter_script(Env* env, rmethod cm) {
+ Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
+ if(!profiler) return 0;
+
+ Kind kind = kScript;
+ rsymbol container = env->symbol("unknown");
+ rsymbol name = container;
+
+ Method* method = profiler->get_method(env, cm, name, container, kind);
+ MethodEntry* me = new MethodEntry(method);
+ me->start(profiler, env);
+
+ return me;
+ }
+ }
+
+ extern "C" int Tool_Init(Env* env) {
+ env->config_set("tool.require", "tooling/profiler/profiler.rb");
+ env->config_set("int", "true");
+
+ cProfileToolID = env->thread_tool_new_id();
+
+ env->set_tool_results(tool_results);
+ env->set_tool_enable(tool_enable);
+
+ env->set_tool_enter_method(tool_enter_method);
+ env->set_tool_leave_method(tool_leave_entry);
+
+ env->set_tool_enter_block(tool_enter_block);
+ env->set_tool_leave_block(tool_leave_entry);
+
+ env->set_tool_enter_gc(tool_enter_gc);
+ env->set_tool_leave_gc(tool_leave_entry);
+
+ env->set_tool_enter_script(tool_enter_script);
+ env->set_tool_leave_script(tool_leave_entry);
+
+ return 1;
+ }
+
+}
View
33 projects/daedalus/daedalus.rb
@@ -295,21 +295,42 @@ def dependencies(ctx)
deps = @data[:deps]
if ctx.sha1(@path) != @data[:dep_sha1] or !deps
- deps = ctx.calculate_deps(@path)
-
- @data[:dep_sha1] = ctx.sha1(@path)
- @data[:deps] = deps
+ deps = recalc_depedencies(ctx)
end
return deps + @static_deps
end
+ def recalc_depedencies(ctx)
+ deps = ctx.calculate_deps(@path)
+
+ @data[:dep_sha1] = ctx.sha1(@path)
+ @data[:deps] = deps
+
+ return deps
+ end
+
def sha1(ctx)
sha1 = Digest::SHA1.new
sha1 << ctx.sha1(@path)
- dependencies(ctx).each do |d|
- sha1 << ctx.sha1(d)
+ begin
+ dependencies(ctx).each do |d|
+ sha1 << ctx.sha1(d)
+ end
+ rescue StandardError
+ recalc_depedencies(ctx)
+
+ sha1 = Digest::SHA1.new
+ sha1 << ctx.sha1(@path)
+
+ dependencies(ctx).each do |d|
+ begin
+ sha1 << ctx.sha1(d)
+ rescue StandardError => e
+ raise "Unable to find dependency '#{d}' from #{@path}"
+ end
+ end
end
sha1.hexdigest
View
5 rakelib/extensions.rake
@@ -53,7 +53,7 @@ end
def compile_ext(name, opts={})
names = name.split ":"
name = names.last
- ext_dir = File.join "lib/ext", names
+ ext_dir = opts[:dir] || File.join("lib/ext", names)
if t = opts[:task]
ext_task_name = "build:#{t}"
@@ -105,3 +105,6 @@ compile_ext "dl", :deps => ["Makefile", "dlconfig.h"]
compile_ext "dbm", :ignore_fail => true, :deps => ["Makefile"]
compile_ext "gdbm", :ignore_fail => true, :deps => ["Makefile"]
compile_ext "sdbm", :deps => ["Makefile"]
+
+compile_ext "profiler", :dir => "lib/tooling/profiler",
+ :deps => ["Makefile"]
View
8 vm/builtin/block_environment.cpp
@@ -21,7 +21,7 @@
#include "builtin/location.hpp"
#include "builtin/nativemethod.hpp"
-#include "instruments/profiler.hpp"
+#include "instruments/tooling.hpp"
#include "configuration.hpp"
#ifdef ENABLE_LLVM
@@ -140,7 +140,7 @@ namespace rubinius {
}
#ifdef RBX_PROFILER
- if(unlikely(state->shared.profiling())) {
+ if(unlikely(state->tooling())) {
Module* mod = scope->module();
if(MetaClass* mc = try_as<MetaClass>(mod)) {
if(Module* ma = try_as<Module>(mc->attached_instance())) {
@@ -148,9 +148,7 @@ namespace rubinius {
}
}
- profiler::MethodEntry method(state,
- env->top_scope_->method()->name(),
- mod, env->method_);
+ tooling::BlockEntry method(state, env, mod);
return (*vmm->run)(state, vmm, frame);
} else {
return (*vmm->run)(state, vmm, frame);
View
6 vm/builtin/nativefunction.cpp
@@ -30,7 +30,7 @@
#include "builtin/nativefunction.hpp"
-#include "instruments/profiler.hpp"
+#include "instruments/tooling.hpp"
namespace rubinius {
@@ -79,8 +79,8 @@ namespace rubinius {
try {
#ifdef RBX_PROFILER
- if(unlikely(state->shared.profiling())) {
- profiler::MethodEntry method(state, msg, args);
+ if(unlikely(state->tooling())) {
+ tooling::MethodEntry method(state, msg, args);
return nfunc->call(state, args, msg, call_frame);
} else {
return nfunc->call(state, args, msg, call_frame);
View
6 vm/builtin/nativemethod.cpp
@@ -21,7 +21,7 @@
#include "builtin/location.hpp"
#include "builtin/ffi_pointer.hpp"
-#include "instruments/profiler.hpp"
+#include "instruments/tooling.hpp"
#include "capi/capi.hpp"
#include "capi/handle.hpp"
@@ -652,8 +652,8 @@ namespace rubinius {
ret = NULL;
} else {
#ifdef RBX_PROFILER
- if(unlikely(state->shared.profiling())) {
- profiler::MethodEntry method(state, msg, args);
+ if(unlikely(state->tooling())) {
+ tooling::MethodEntry method(state, msg, args);
ret = ArgumentHandler::invoke(state, nm, env, args);
} else {
ret = ArgumentHandler::invoke(state, nm, env, args);
View
22 vm/builtin/system.cpp
@@ -58,7 +58,7 @@
#include "util/sha1.h"
-#include "instruments/profiler.hpp"
+#include "instruments/tooling.hpp"
#ifdef ENABLE_LLVM
#include "llvm/jit.hpp"
@@ -466,25 +466,27 @@ namespace rubinius {
return Qnil;
}
- Object* System::vm_profiler_instrumenter_available_p(STATE) {
+ Object* System::vm_tooling_available_p(STATE) {
#ifdef RBX_PROFILER
- return Qtrue;
+ return state->shared.tool_broker()->available(state) ? Qtrue : Qfalse;
#else
return Qfalse;
#endif
}
- Object* System::vm_profiler_instrumenter_active_p(STATE) {
- return state->shared.profiling() ? Qtrue : Qfalse;
+ Object* System::vm_tooling_active_p(STATE) {
+ return state->tooling() ? Qtrue : Qfalse;
}
- Object* System::vm_profiler_instrumenter_start(STATE) {
- state->shared.enable_profiling(state);
+ Object* System::vm_tooling_enable(STATE) {
+ state->shared.tool_broker()->enable(state);
+ state->enable_tooling();
return Qtrue;
}
- LookupTable* System::vm_profiler_instrumenter_stop(STATE) {
- return state->shared.disable_profiling(state);
+ Object* System::vm_tooling_disable(STATE) {
+ state->disable_tooling();
+ return state->shared.tool_broker()->results(state);
}
Object* System::vm_write_error(STATE, String* str) {
@@ -1131,7 +1133,7 @@ namespace rubinius {
#ifdef RBX_PROFILER
if(unlikely(state->shared.profiling())) {
- profiler::MethodEntry me(state, profiler::kScript, cm);
+ tooling::ScriptEntry me(state, cm);
return cm->backend_method()->execute_as_script(state, cm, calling_environment);
} else {
return cm->backend_method()->execute_as_script(state, cm, calling_environment);
View
24 vm/builtin/system.hpp
@@ -124,21 +124,21 @@ namespace rubinius {
// Ruby.primitive :vm_mri_backtrace
static Array* vm_mri_backtrace(STATE, Fixnum* skip, CallFrame* calling_environment);
- /** Return true if the profiler is available. */
- // Ruby.primitive :vm_profiler_instrumenter_available_p
- static Object* vm_profiler_instrumenter_available_p(STATE);
+ /** Return true if tooling is enabled */
+ // Ruby.primitive :vm_tooling_available_p
+ static Object* vm_tooling_available_p(STATE);
- /** Return true if the profiler is running. */
- // Ruby.primitive :vm_profiler_instrumenter_active_p
- static Object* vm_profiler_instrumenter_active_p(STATE);
+ /** Return true if tooling is running. */
+ // Ruby.primitive :vm_tooling_active_p
+ static Object* vm_tooling_active_p(STATE);
- /** Starts the instrumenting profiler. */
- // Ruby.primitive :vm_profiler_instrumenter_start
- static Object* vm_profiler_instrumenter_start(STATE);
+ /** Starts tooling. */
+ // Ruby.primitive :vm_tooling_enable
+ static Object* vm_tooling_enable(STATE);
- /** Stops the instrumenting profiler. */
- // Ruby.primitive :vm_profiler_instrumenter_stop
- static LookupTable* vm_profiler_instrumenter_stop(STATE);
+ /** Stops tooling. */
+ // Ruby.primitive :vm_tooling_disable
+ static Object* vm_tooling_disable(STATE);
/**
* Writes String to standard error stream.
View
117 vm/capi/include/rbxti.hpp
@@ -0,0 +1,117 @@
+#ifndef RUBINIUS_RBXTI_HPP
+#define RUBINIUS_RBXTI_HPP
+
+#include <stdint.h>
+
+namespace rbxti {
+
+ class InternalObject {};
+ class InternalSymbol : public InternalObject {};
+ class InternalTable : public InternalObject {};
+ class InternalMethod : public InternalObject {};
+ class InternalModule : public InternalObject {};
+ class InternalString : public InternalObject {};
+ class InternalInteger : public InternalObject {};
+ class InternalArray : public InternalObject {};
+
+
+ typedef InternalObject* robject;
+ typedef InternalSymbol* rsymbol;
+ typedef InternalTable* rtable;
+ typedef InternalMethod* rmethod;
+ typedef InternalModule* rmodule;
+ typedef InternalString* rstring;
+ typedef InternalInteger* rinteger;
+ typedef InternalArray* rarray;
+
+ typedef int64_t r_mint;
+
+ class Env;
+
+ class EnvPrivate;
+
+ const static int GCYoung = 1;
+ const static int GCMature = 2;
+ const static int GCFinalizer = 4;
+
+ typedef robject (*results_func)(Env* env);
+ typedef void (*enable_func)(Env* env);
+ typedef void* (*enter_method)(Env* env, robject recv, rsymbol name, rmodule mod, rmethod cm);
+ typedef void* (*enter_block)(Env* env, rsymbol name, rmodule module, rmethod cm);
+ typedef void (*leave_func)(Env* env, void* tag);
+ typedef void* (*enter_gc)(Env* env, int level);
+ typedef void* (*enter_script)(Env* env, rmethod cm);
+
+ class Env {
+ public:
+ EnvPrivate* private_;
+
+ long config_get_int(const char* name);
+ void config_set(const char* name, const char* val);
+
+ ////
+
+ rsymbol cast_to_rsymbol (robject obj);
+ rtable cast_to_rtable (robject obj);
+ rmethod cast_to_rmethod (robject obj);
+ rmodule cast_to_rmodule (robject obj);
+ rstring cast_to_rstring (robject obj);
+ rinteger cast_to_rinteger (robject obj);
+ rarray cast_to_rarray (robject obj);
+
+ int thread_tool_new_id();
+ void* thread_tool_data(int id);
+ void thread_tool_set_data(int id, void* data);
+
+ rinteger integer_new(r_mint val);
+ r_mint integer_value(rinteger i);
+
+ rsymbol symbol(const char* data);
+ void symbol_cstr(rsymbol sym, char* data, int size);
+ rstring symbol_to_string(rsymbol sym);
+ r_mint symbol_id(rsymbol sym);
+
+ rstring string_new(const char* data);
+ rstring to_s(robject obj);
+ rsymbol string_to_symbol(rstring str);
+
+ rsymbol module_name(rmodule mod);
+ bool module_is_metaclass(rmodule mod);
+ robject metaclass_attached_instance(rmodule mod);
+
+ bool is_nil(robject obj);
+ robject nil();
+
+ rsymbol method_file(rmethod cm);
+ r_mint method_line(rmethod cm);
+ r_mint method_id(rmethod cm);
+
+ rtable table_new();
+ robject table_fetch(rtable tbl, robject key, bool* fetched);
+ void table_store(rtable tbl, robject key, robject val);
+
+ rarray array_new(int size);
+ void array_set(rarray ary, int idx, robject obj);
+ robject array_get(rarray ary, int idx);
+
+ uint64_t time_current_ns();
+
+ ////
+ void set_tool_enter_method(enter_method func);
+ void set_tool_leave_method(leave_func func);
+
+ void set_tool_enter_block(enter_block func);
+ void set_tool_leave_block(leave_func func);
+
+ void set_tool_enter_gc(enter_gc func);
+ void set_tool_leave_gc(leave_func func);
+
+ void set_tool_enter_script(enter_script func);
+ void set_tool_leave_script(leave_func func);
+
+ void set_tool_results(results_func func);
+ void set_tool_enable(enable_func func);
+ };
+}
+
+#endif
View
10 vm/codegen/field_extract.rb
@@ -59,13 +59,12 @@ def prim_return(str, indent=2)
str << "#{' ' * indent}return ret;\n"
end
- # @todo Add profiler stuff back in. --rue
def output_call(str, call, args)
str << "\n"
str << " try {\n"
str << "#ifdef RBX_PROFILER\n"
- str << " if(unlikely(state->shared.profiling())) {\n"
- str << " profiler::MethodEntry method(state, msg, args);\n"
+ str << " if(unlikely(state->tooling())) {\n"
+ str << " tooling::MethodEntry method(state, msg, args);\n"
str << " ret = #{call}(#{args.join(', ')});\n"
str << " } else {\n"
str << " ret = #{call}(#{args.join(', ')});\n"
@@ -476,7 +475,6 @@ def add_kind(prim)
@kinds << prim
end
- # @todo Add profiler stuff back in. --rue
def generate_glue
str = ""
output_header str
@@ -495,8 +493,8 @@ def generate_glue
call = " ret = recv->#{@cpp_name}(arg);\n"
end
str << "#ifdef RBX_PROFILER\n"
- str << " if(unlikely(state->shared.profiling())) {\n"
- str << " profiler::MethodEntry method(state, msg, args);\n"
+ str << " if(unlikely(state->tooling())) {\n"
+ str << " tooling::MethodEntry method(state, msg, args);\n"
str << " " << call
str << " } else {\n"
str << " " << call
View
13 vm/config_parser.cpp
@@ -139,6 +139,19 @@ namespace rubinius {
return i->second;
}
+ void ConfigParser::set(const char* name, const char* val) {
+ ConfigParser::ConfigMap::iterator i = variables.find(name);
+ if(i == variables.end()) {
+ i->second->value = val;
+ }
+
+ Entry* entry = new ConfigParser::Entry();
+ entry->variable = name;
+ entry->value = val;
+
+ variables[entry->variable] = entry;
+ }
+
bool ConfigParser::Entry::is_number() {
return rubinius::is_number(value.c_str());
}
View
1 vm/config_parser.hpp
@@ -32,6 +32,7 @@ namespace rubinius {
bool load_file(std::string path);
Entry* parse_line(const char* line);
void import_line(const char* line);
+ void set(const char* name, const char* val);
void import_many(std::string string);
void import_stream(std::istream&);
Entry* find(std::string variable);
View
7 vm/configuration.hpp
@@ -34,6 +34,9 @@ namespace rubinius {
config::Bool jit_show_remove;
config::Bool jit_check_debugging;
+ // Tools
+ config::String tool_to_load;
+
// CAPI
config::Bool capi_global_flush;
@@ -89,6 +92,8 @@ namespace rubinius {
, jit_show_remove(this, "jit.removal.print", false)
, jit_check_debugging(this, "jit.check_debugging", false)
+ , tool_to_load(this, "tool")
+
, capi_global_flush(this, "capi.global_flush", false)
, qa_port(this, "agent.start")
@@ -200,7 +205,7 @@ namespace rubinius {
void finalize() {
if(profile) {
- jit_disabled.value = true;
+ tool_to_load.set("tooling/profiler/profiler_vm");
}
}
};
View
41 vm/environment.cpp
@@ -45,6 +45,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/param.h>
+#include <dlfcn.h>
namespace rubinius {
@@ -581,6 +582,44 @@ namespace rubinius {
}
}
+ void Environment::load_tool() {
+ if(!state->shared.config.tool_to_load.set_p()) return;
+ std::string path = std::string(state->shared.config.tool_to_load.value) + ".";
+
+#ifdef _WIN32
+ path += "dll";
+#else
+ #ifdef __APPLE_CC__
+ path += "bundle";
+ #else
+ path += "so";
+ #endif
+#endif
+
+ void* handle = dlopen(path.c_str(), RTLD_NOW);
+ if(!handle) {
+ path = std::string(RBX_LIB_PATH) + "/" + path;
+
+ handle = dlopen(path.c_str(), RTLD_NOW);
+ if(!handle) {
+ std::cerr << "Unable to load tool '" << path << "': " << dlerror() << "\n";
+ return;
+ }
+ }
+
+ void* sym = dlsym(handle, "Tool_Init");
+ if(!sym) {
+ std::cerr << "Failed to initialize tool '" << path << "': " << dlerror() << "\n";
+ } else {
+ typedef int (*init_func)(rbxti::Env* env);
+ init_func init = (init_func)sym;
+
+ if(!init(state->tooling_env())) {
+ std::cerr << "Tool '" << path << "' reported failure to init.\n";
+ }
+ }
+ }
+
void Environment::run_from_filesystem(std::string root) {
int i = 0;
state->set_stack_start(&i);
@@ -591,6 +630,8 @@ namespace rubinius {
state->initialize_config();
+ load_tool();
+
load_kernel(root);
start_signals();
View
1 vm/environment.hpp
@@ -57,6 +57,7 @@ namespace rubinius {
void load_conf(std::string path);
void load_string(std::string str);
void run_file(std::string path);
+ void load_tool();
void run_from_filesystem(std::string root);
void boot_vm();
View
1 vm/instructions.cpp
@@ -26,7 +26,6 @@
#include "arguments.hpp"
#include "dispatch.hpp"
#include "instructions.hpp"
-#include "instruments/profiler.hpp"
#include "configuration.hpp"
#include "helpers.hpp"
View
484 vm/instruments/profiler.cpp
@@ -1,484 +0,0 @@
-#include "instruments/profiler.hpp"
-
-#include "vm/object_utils.hpp"
-
-#include "builtin/array.hpp"
-#include "builtin/class.hpp"
-#include "builtin/compiledmethod.hpp"
-#include "builtin/integer.hpp"
-#include "builtin/lookuptable.hpp"
-#include "builtin/module.hpp"
-#include "builtin/string.hpp"
-#include "builtin/symbol.hpp"
-#include "detection.hpp"
-#include "arguments.hpp"
-#include "dispatch.hpp"
-#include "vmmethod.hpp"
-#include "configuration.hpp"
-
-#include "instruments/timing.hpp"
-
-#include <time.h>
-
-#include <iostream>
-#include <vector>
-#include <algorithm>
-
-namespace rubinius {
-
- namespace profiler {
-
- Method::~Method() { }
-
- String* Method::to_s(STATE) {
- std::stringstream ss;
-
- if(kind() == kScript) {
- ss << "script:";
- if(file_) {
- ss << file_->c_str(state);
- } else {
- ss << "--unknown-file--";
- ss << ":" << line_;
- }
-
- return String::create(state, ss.str().c_str());
- }
-
- const char *module = "<anonymous>";
- const char *method_name = name()->c_str(state);
-
- if(Symbol* klass = try_as<Symbol>(container())) {
- module = klass->c_str(state);
- }
-
- ss << module;
-
- switch(kind()) {
- case kNormal:
- ss << "#" << method_name;
- break;
- case kNormalJIT:
- ss << "#" << method_name << " <jit>";
- break;
- case kSingleton:
- case kYoungGC:
- case kMatureGC:
- case kFinalizers:
- ss << "." << method_name;
- break;
- case kSingletonJIT:
- ss << "." << method_name << " <jit>";
- break;
- case kBlock:
- ss << "::" << method_name << "<" << line_ << "> {}";
- break;
- case kBlockJIT:
- ss << "::" << method_name << " {" << line_ << "} <jit>";
- break;
- case kScript:
- // handled above, just here to make gcc happy.
- abort();
- }
-
- return String::create(state, ss.str().c_str());
- }
-
- Node* Node::find_sub_node(Profiler* profiler, Method* method) {
- Node* sub = first_sub_node_;
-
- while(sub) {
- if(sub->method() == method) return sub;
- sub = sub->sibling();
- }
-
- Node* node = new Node(method, profiler->next_node_id());
- node->set_sibling(first_sub_node_);
- first_sub_node_ = node;
-
- return node;
- }
-
- MethodEntry::MethodEntry(STATE, Dispatch& msg, Arguments& args)
- : state_(state)
- , node_(0)
- {
- method_ = state->profiler()->enter_method(
- msg, args, reinterpret_cast<CompiledMethod*>(Qnil), false);
- start();
- }
-
- MethodEntry::MethodEntry(STATE, Dispatch& msg, Arguments& args, CompiledMethod* cm, bool jit)
- : state_(state)
- , node_(0)
- {
- method_ = state->profiler()->enter_method(msg, args, cm, jit);
- start();
- }
-
- MethodEntry::MethodEntry(STATE, Symbol* name, Module* module, CompiledMethod* cm, bool jit)
- : state_(state)
- , node_(0)
- {
- method_ = state->profiler()->enter_block(name, module, cm, jit);
- start();
- }
-
- MethodEntry::MethodEntry(STATE, Kind kind, CompiledMethod* cm)
- : state_(state)
- , node_(0)
- {
- Symbol* container;
- Symbol* name;
-
- switch(kind) {
- case kYoungGC:
- container = state_->symbol("GC");
- name = state_->symbol("collect_young");
- break;
- case kMatureGC:
- container = state_->symbol("GC");
- name = state_->symbol("collect_mature");
- break;
- case kFinalizers:
- container = state_->symbol("GC");
- name = state_->symbol("run_finalizers");
- break;
- case kScript:
- container = state_->symbol("__script__");
- name = state_->symbol("__script__");
- break;
- default:
- container = state_->symbol("unknown");
- name = state_->symbol("unknown");
- }
-
- if(!cm) cm = reinterpret_cast<CompiledMethod*>(Qnil);
- method_ = state->profiler()->get_method(cm, name, container, kind);
- start();
- }
-
- void MethodEntry::start() {
- Profiler* profiler = state_->profiler();
-
- previous_ = profiler->current();
- node_ = previous_->find_sub_node(profiler, method_);
- profiler->set_current(node_);
-
- method_->timer.start();
- timer_.start();
- }
-
- MethodEntry::~MethodEntry() {
- if(!state_->shared.profiling()) return;
-
- method_->timer.stop();
- timer_.stop();
- method_->accumulate(timer_.total());
- node_->accumulate(timer_.total());
- state_->profiler()->set_current(previous_);
- }
-
- Profiler::Profiler(STATE)
- : state_(state)
- , nodes_(0)
- {
- root_ = current_ = new Node(0, next_node_id());
- threshold_ = (uint32_t)state->shared.config.profiler_threshold;
- }
-
- Symbol* Profiler::module_name(Module* module) {
- if(IncludedModule* im = try_as<IncludedModule>(module)) {
- return im->module()->name();
- } else {
- return module->name();
- }
- }
-
- Method* Profiler::enter_block(Symbol* name, Module* module, CompiledMethod* cm, bool jit) {
- return get_method(cm, name, module_name(module), jit ? kBlockJIT : kBlock);
- }
-
- Method* Profiler::enter_method(Dispatch &msg, Arguments& args, CompiledMethod* cm, bool jit) {
- if(MetaClass* mc = try_as<MetaClass>(msg.module)) {
- Object* attached = mc->attached_instance();
-
- if(Module* mod = try_as<Module>(attached)) {
- return get_method(cm, msg.name, mod->name(), jit ? kSingletonJIT : kSingleton);
- } else {
- Symbol* name = args.recv()->to_s(state_)->to_sym(state_);
- return get_method(cm, msg.name, name, jit ? kSingletonJIT : kSingleton);
- }
- } else {
- return get_method(cm, msg.name, module_name(msg.module), jit ? kNormalJIT : kNormal);
- }
- }
-
- Method* Profiler::get_method(CompiledMethod* cm, Symbol* name,
- Symbol* container, Kind kind) {
- Method* method = find_method(cm, container, name, kind);
-
- if(!method->file() && !cm->nil_p()) {
- method->set_position(cm->file(), cm->start_line(state_));
- }
-
- return method;
- }
-
- method_id Profiler::create_id(CompiledMethod* cm, Symbol* container,
- Symbol* name, Kind kind)
- {
- // If we have a CompiledMethod, use it's method id.
- if(!cm->nil_p()) {
- if(VMMethod* vmm = cm->backend_method()) {
- return (vmm->method_id() << 1) | 1;
- }
- }
-
- // | -- 32 bits of container -- | -- 29 bits of name -- | -- 2 bits of kind -- | 0
-
- uint32_t c = container->index() & 0xffffffff;
- uint32_t n = name->index() & 0x1fffffff;
- uint32_t k = kind & 0x3;
-
- return (((uint64_t)c) << 32) |
- (n << 3) |
- k << 1;
- }
-
- Method* Profiler::find_method(CompiledMethod* cm, Symbol* container,
- Symbol* name, Kind kind)
- {
- method_id id = create_id(cm, container, name, kind);
-
- Method* method;
-
- MethodMap::iterator iter = methods_.find(id);
-
- if(unlikely(iter == methods_.end())) {
- method = new Method(id, name, container, kind);
- methods_[method->id()] = method;
- } else {
- method = iter->second;
- }
-
- return method;
- }
-
- typedef std::vector<Node*> WorkList;
-
- static Fixnum* make_key(Method* meth, KeyMap& keys) {
- KeyMap::iterator iter = keys.find(meth);
-
- if(iter == keys.end()) {
- Fixnum* key = Fixnum::from(keys.size());
- keys[meth] = key;
- return key;
- }
-
- return iter->second;
- }
-
- static Fixnum* add_method(STATE, LookupTable* profile, Method* meth,
- KeyMap& keys)
- {
- LookupTable* methods = try_as<LookupTable>(profile->fetch(
- state, state->symbol("methods")));
-
- if(!methods) return 0;
-
- Fixnum* key = make_key(meth, keys);
-
- // We already have the method, skip this.
- if(!methods->fetch(state, key)->nil_p()) return key;
-
- Symbol* cumulative_sym = state->symbol("cumulative");
- Symbol* total_sym = state->symbol("total");
- Symbol* called_sym = state->symbol("called");
-
- LookupTable* method = LookupTable::create(state);
- methods->store(state, key, method);
-
- method->store(state, state->symbol("name"), meth->to_s(state));
- method->store(state, cumulative_sym, Integer::from(state, meth->timer.total()));
- method->store(state, total_sym, Integer::from(state, meth->total()));
- method->store(state, called_sym, Fixnum::from(meth->timer.count()));
-
- if(meth->file()) {
- const char *file;
- if(meth->file()->nil_p()) {
- file = "unknown file";
- } else {
- file = meth->file()->c_str(state);
- }
-
- method->store(state, state->symbol("file"), String::create(state, file));
- method->store(state, state->symbol("line"), Fixnum::from(meth->line()));
- }
-
- return key;
- }
-
- static void add_node(STATE, LookupTable* profile, Node* node,
- WorkList& work, KeyMap& keys, uint32_t threshold)
- {
- LookupTable* nodes = try_as<LookupTable>(profile->fetch(
- state, state->symbol("nodes")));
-
- if(!nodes) return;
-
- // We haven't exited this method yet, so its stats won't be accurate
- if(node->method()->timer.started()) return;
-
- Fixnum* key = Fixnum::from(node->id());
-
- Array* tbl = Array::create(state, 5);
-
- nodes->store(state, key, tbl);
-
- Fixnum* meth_key = add_method(state, profile, node->method(), keys);
-
- tbl->set(state, 0, meth_key);
- tbl->set(state, 1, Integer::from(state, node->total()));
- tbl->set(state, 2, Fixnum::from(node->called()));
-
- int count = node->count_sub_nodes();
- tbl->set(state, 3, Fixnum::from(count));
-
- Array* ary = Array::create(state, count);
-
- int idx = 0;
-
- Node* sub = node->sub_nodes();
-
- while(sub) {
- if(sub->total() >= threshold) {
- ary->set(state, idx++, Fixnum::from(sub->id()));
- work.push_back(sub);
- }
-
- sub = sub->sibling();
- }
-
- tbl->set(state, 4, ary);
- }
-
- void Profiler::results(LookupTable* profile, KeyMap& keys, uint64_t runtime) {
- WorkList work;
-
- // If we haven't even gone for a total of longer than 10x the threshold,
- // just disable the threshold.
- if(runtime < 10 * threshold_) threshold_ = 0;
-
- profile->store(state_, state_->symbol("total_nodes"), Fixnum::from(nodes_));
-
- Array* roots = Array::create(state_, root_->count_sub_nodes());
- profile->store(state_, state_->symbol("roots"), roots);
-
- int idx = 0;
- Node* sub = root_->sub_nodes();
-
- while(sub) {
- if(sub->total() >= threshold_) {
- roots->set(state_, idx++, Fixnum::from(sub->id()));
- work.push_back(sub);
- }
-
- sub = sub->sibling();
- }
-
- while(work.size() > 0) {
- Node* node = work.back();
- work.pop_back();
-
- add_node(state_, profile, node, work, keys, threshold_);
- }
- }
-
- Profiler::~Profiler() {
- for(MethodMap::iterator i = methods_.begin();
- i != methods_.end();
- i++) {
- delete i->second;
- }
-
- WorkList work;
-
- work.push_back(root_);
-
- while(work.size() > 0) {
- Node* node = work.back();
- work.pop_back();
-
- Node* sub = node->sub_nodes();
-
- while(sub) {
- work.push_back(sub);
- sub = sub->sibling();
- }
-
- delete node;
- }
- }
-
- ProfilerCollection::ProfilerCollection(STATE)
- : profile_(state, (LookupTable*)Qnil)
- {
- LookupTable* profile = LookupTable::create(state);
- LookupTable* methods = LookupTable::create(state);
- LookupTable* nodes = LookupTable::create(state);
- profile->store(state, state->symbol("methods"), methods);
- profile->store(state, state->symbol("nodes"), nodes);
- profile->store(state, state->symbol("method"),
- String::create(state, TIMING_METHOD));
-
- profile_.set(profile);
-
- start_time_ = get_current_time();
- }
-
- ProfilerCollection::~ProfilerCollection() {
- for(ProfilerMap::iterator iter = profilers_.begin();
- iter != profilers_.end();
- iter++) {
- iter->first->remove_profiler();
- delete iter->second;
- }
- }
-
- void ProfilerCollection::add_profiler(VM* vm, Profiler* profiler) {
- profilers_[vm] = profiler;
- }
-
- void ProfilerCollection::remove_profiler(VM* vm, Profiler* profiler) {
- uint64_t runtime = get_current_time() - start_time_;
-
- ProfilerMap::iterator iter = profilers_.find(vm);
- if(iter != profilers_.end()) {
- Profiler* profiler = iter->second;
- profiler->results(profile_.get(), keys_, runtime);
-
- iter->first->remove_profiler();
-
- delete iter->second;
- profilers_.erase(iter);
- }
- }
-
- LookupTable* ProfilerCollection::results(STATE) {
- LookupTable* profile = profile_.get();
-
- uint64_t runtime = get_current_time() - start_time_;
- profile->store(state, state->symbol("runtime"),
- Integer::from(state, runtime));
-
- for(ProfilerMap::iterator iter = profilers_.begin();
- iter != profilers_.end();
- iter++) {
- iter->second->results(profile, keys_, runtime);
- }
-
- return profile;
- }
- }
-}
View
295 vm/instruments/profiler.hpp
@@ -1,295 +0,0 @@
-#ifndef RBX_PROFILER_HPP
-#define RBX_PROFILER_HPP
-
-#include "vm/config.h"
-#include "vm.hpp"
-#include "instruments/stats.hpp"
-#include "testable.hpp"
-
-#include <stdint.h>
-#include <stdio.h>
-
-#include <stack>
-#include <tr1/unordered_map>
-#include <map>
-#include <iostream>
-
-namespace rubinius {
- namespace profiler {
- typedef uint64_t method_id;
- }
-}
-
-#ifndef RBX_HAVE_TR1_HASH
-namespace std {
- namespace tr1 {
- template <>
- struct hash<rubinius::profiler::method_id> {
- size_t operator()(const rubinius::profiler::method_id id) const {
- return id;
- }
- };
- }
-}
-#endif
-
-class TestProfiler;
-
-namespace rubinius {
- class VM;
- class Symbol;
- class Object;
- class LookupTable;
- class Fixnum;
- class String;
- class CompiledMethod;
- class Dispatch;
- class Arguments;
- class Message;
-
- namespace profiler {
-
- enum Kind {
- kNormal,
- kSingleton,
- kBlock,
- kYoungGC,
- kMatureGC,
- kNormalJIT,
- kSingletonJIT,
- kBlockJIT,
- kFinalizers,
- kScript
- };
-
- class Method;
- class Node;
- typedef std::tr1::unordered_map<Method*, Fixnum*> KeyMap;
- typedef std::tr1::unordered_map<method_id, Method*> MethodMap;
-
- class Node {
- int id_;
- int called_;
- uint64_t total_;
- Method* method_;
-
- Node* sibling_;
- Node* first_sub_node_;
-
- public:
- Node(Method* method, int id)
- : id_(id)
- , called_(0)
- , total_(0)
- , method_(method)
- , sibling_(0)