Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Tooling can be enabled anytime, anywhere.

  • Loading branch information...
commit 8133e094ce50fe449ad1a9491b7b217a2946b76d 1 parent 4aa76a4
authored April 04, 2011
20  kernel/bootstrap/rubinius.rb
@@ -44,6 +44,12 @@ def self.deoptimize_inliners(exec)
44 44
     raise PrimitiveFailure, "Rubinius.vm_deoptimize_inliners failed"
45 45
   end
46 46
 
  47
+  # Deoptimize all methods in the system.
  48
+  def self.deoptimize_all(disable)
  49
+    Ruby.primitive :vm_deoptimize_all
  50
+    raise PrimitiveFailure, "Rubinius.vm_deoptimize_all failed"
  51
+  end
  52
+
47 53
   def self.raise_exception(exc)
48 54
     Ruby.primitive :vm_raise_exception
49 55
     raise PrimitiveFailure, "Rubinius.vm_raise_exception failed"
@@ -120,6 +126,20 @@ def self.run_script(cm)
120 126
   end
121 127
 
122 128
   module Tooling
  129
+    def self.raw_load(str)
  130
+      Ruby.primitive :vm_load_tool
  131
+      raise PrimitiveFailure, "Tooling.raw_load failed"
  132
+    end
  133
+
  134
+    def self.load(str)
  135
+      error, reason = raw_load(str)
  136
+      unless error == true
  137
+        raise ArgumentError, reason
  138
+      end
  139
+
  140
+      return true
  141
+    end
  142
+
123 143
     def self.available?
124 144
       Ruby.primitive :vm_tooling_available_p
125 145
       raise PrimitiveFailure, "Tooling.available? failed"
2  lib/profiler.rb
... ...
@@ -1,3 +1,5 @@
  1
+require 'tooling/profiler/profiler'
  2
+
1 3
 module Profiler__
2 4
   def start_profile
3 5
     @p = Rubinius::Profiler::Instrumenter.new
9  lib/tooling/profiler/profiler.rb
@@ -17,7 +17,16 @@ def self.active?
17 17
         Rubinius::Tooling.active?
18 18
       end
19 19
 
  20
+      @loaded = false
  21
+      def self.load
  22
+        return if @loaded
  23
+        Rubinius::Tooling.load File.expand_path("../profiler_vm", __FILE__)
  24
+        @loaded = true
  25
+      end
  26
+
20 27
       def initialize(options = {})
  28
+        Instrumenter.load
  29
+
21 30
         @options = { :sort => :percent }
22 31
         set_options options
23 32
         set_options :full_report => true if Config["profiler.full_report"]
37  lib/tooling/profiler/profiler_vm.cpp
@@ -361,6 +361,10 @@ namespace profiler {
361 361
       attached_ = false;
362 362
     }
363 363
 
  364
+    bool attached_p() {
  365
+      return attached_;
  366
+    }
  367
+
364 368
     int id() {
365 369
       return id_;
366 370
     }
@@ -700,8 +704,13 @@ namespace profiler {
700 704
   }
701 705
 
702 706
   struct GlobalState {
  707
+    Profiler* main_profiler;
703 708
     std::list<Profiler*> profilers;
704 709
 
  710
+    GlobalState()
  711
+      : main_profiler(0)
  712
+    {}
  713
+
705 714
     void add(Profiler* prof) {
706 715
       profilers.push_back(prof);
707 716
     }
@@ -715,8 +724,12 @@ namespace profiler {
715 724
       env->set_global_tool_data(st);
716 725
 
717 726
       Profiler* profiler = new Profiler(env);
  727
+      st->main_profiler = profiler;
  728
+
718 729
       env->thread_tool_set_data(cProfileToolID, profiler);
719 730
       st->add(profiler);
  731
+
  732
+      env->enable_thread_tooling();
720 733
     }
721 734
 
722 735
     void* tool_enter_method(Env* env, robject recv, rsymbol name, rmodule mod,
@@ -805,12 +818,16 @@ namespace profiler {
805 818
 
806 819
     void tool_shutdown(Env* env) {
807 820
       GlobalState* st = (GlobalState*)env->global_tool_data();
  821
+      if(!st) return;
  822
+
  823
+      env->set_global_tool_data(0);
  824
+
808 825
       for(std::list<Profiler*>::iterator i = st->profilers.begin();
809 826
           i != st->profilers.end();
810 827
           ++i) {
811 828
 
812 829
         Profiler* prof = *i;
813  
-        delete prof;
  830
+        if(!prof->attached_p()) delete prof;
814 831
       }
815 832
 
816 833
       delete st;
@@ -819,6 +836,9 @@ namespace profiler {
819 836
     void tool_start_thread(Env* env) {
820 837
       GlobalState* st = (GlobalState*)env->global_tool_data();
821 838
 
  839
+      // No GlobalState means that the tool isn't currently enabled.
  840
+      if(!st) return;
  841
+
822 842
       Profiler* profiler = new Profiler(env);
823 843
       st->add(profiler);
824 844
 
@@ -833,11 +853,21 @@ namespace profiler {
833 853
 
834 854
       env->thread_tool_set_data(cProfileToolID, 0);
835 855
       profiler->detach();
  856
+
  857
+      env->disable_thread_tooling();
836 858
     }
837 859
 
838 860
     robject tool_results(Env* env) {
839 861
       GlobalState* st = (GlobalState*)env->global_tool_data();
840 862
 
  863
+      Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
  864
+
  865
+      // Ignore results requests that don't come from the thread that
  866
+      // started profiling.
  867
+      if(st->main_profiler != profiler) return env->nil();
  868
+
  869
+      env->thread_tool_set_data(cProfileToolID, 0);
  870
+
841 871
       rtable profile = env->table_new();
842 872
 
843 873
       for(std::list<Profiler*>::iterator i = st->profilers.begin();
@@ -863,6 +893,10 @@ namespace profiler {
863 893
         prof->results(env, thread, nodes, methods, keys, runtime);
864 894
       }
865 895
 
  896
+      tool_shutdown(env);
  897
+      delete profiler;
  898
+
  899
+      env->disable_thread_tooling();
866 900
       return profile;
867 901
     }
868 902
 
@@ -870,7 +904,6 @@ namespace profiler {
870 904
 
871 905
   extern "C" int Tool_Init(Env* env) {
872 906
     env->config_set("tool.require", "tooling/profiler/profiler.rb");
873  
-    env->config_set("int", "true");
874 907
 
875 908
     cProfileToolID = env->thread_tool_new_id();
876 909
 
2  spec/custom/guards/profiler.rb
... ...
@@ -1,3 +1,5 @@
  1
+require 'tooling/profiler/profiler'
  2
+
1 3
 class ProfilerGuard < SpecGuard
2 4
   def match?
3 5
     Rubinius::Profiler::Instrumenter.available? and
73  vm/builtin/system.cpp
@@ -12,6 +12,8 @@
12 12
 #include <unistd.h>
13 13
 #include <pwd.h>
14 14
 
  15
+#include <dlfcn.h>
  16
+
15 17
 #include "vm/call_frame.hpp"
16 18
 #include "vm/helpers.hpp"
17 19
 
@@ -60,6 +62,8 @@
60 62
 
61 63
 #include "instruments/tooling.hpp"
62 64
 
  65
+#include "gc/walker.hpp"
  66
+
63 67
 #ifdef ENABLE_LLVM
64 68
 #include "llvm/jit.hpp"
65 69
 #include "llvm/jit_compiler.hpp"
@@ -480,15 +484,53 @@ namespace rubinius {
480 484
 
481 485
   Object* System::vm_tooling_enable(STATE) {
482 486
     state->shared.tool_broker()->enable(state);
483  
-    state->enable_tooling();
484 487
     return Qtrue;
485 488
   }
486 489
 
487 490
   Object* System::vm_tooling_disable(STATE) {
488  
-    state->disable_tooling();
489 491
     return state->shared.tool_broker()->results(state);
490 492
   }
491 493
 
  494
+  Object* System::vm_load_tool(STATE, String* str) {
  495
+    std::string path = std::string(str->c_str(state)) + ".";
  496
+
  497
+#ifdef _WIN32
  498
+    path += "dll";
  499
+#else
  500
+  #ifdef __APPLE_CC__
  501
+    path += "bundle";
  502
+  #else
  503
+    path += "so";
  504
+  #endif
  505
+#endif
  506
+
  507
+    void* handle = dlopen(path.c_str(), RTLD_NOW);
  508
+    if(!handle) {
  509
+      path = std::string(RBX_LIB_PATH) + "/" + path;
  510
+
  511
+      handle = dlopen(path.c_str(), RTLD_NOW);
  512
+      if(!handle) {
  513
+        return Tuple::from(state, 2, Qfalse, String::create(state, dlerror()));
  514
+      }
  515
+    }
  516
+
  517
+    void* sym = dlsym(handle, "Tool_Init");
  518
+    if(!sym) {
  519
+      dlclose(handle);
  520
+      return Tuple::from(state, 2, Qfalse, String::create(state, dlerror()));
  521
+    } else {
  522
+      typedef int (*init_func)(rbxti::Env* env);
  523
+      init_func init = (init_func)sym;
  524
+
  525
+      if(!init(state->tooling_env())) {
  526
+        dlclose(handle);
  527
+        return Tuple::from(state, 2, Qfalse, String::create(state, path.c_str()));
  528
+      }
  529
+    }
  530
+    
  531
+    return Tuple::from(state, 1, Qtrue);
  532
+  }
  533
+
492 534
   Object* System::vm_write_error(STATE, String* str) {
493 535
     std::cerr << str->c_str(state) << std::endl;
494 536
     return Qnil;
@@ -804,6 +846,33 @@ namespace rubinius {
804 846
     return Qtrue;
805 847
   }
806 848
 
  849
+  Object* System::vm_deoptimize_all(STATE, Object* o_disable) {
  850
+    ObjectWalker walker(state->om);
  851
+    GCData gc_data(state);
  852
+
  853
+    // Seed it with the root objects.
  854
+    walker.seed(gc_data);
  855
+
  856
+    Object* obj = walker.next();
  857
+
  858
+    int total = 0;
  859
+
  860
+    bool disable = RTEST(o_disable);
  861
+
  862
+    while(obj) {
  863
+      if(CompiledMethod* cm = try_as<CompiledMethod>(obj)) {
  864
+        if(VMMethod* vmm = cm->backend_method()) {
  865
+          vmm->deoptimize(state, cm, disable);
  866
+        }
  867
+        total++;
  868
+      }
  869
+
  870
+      obj = walker.next();
  871
+    }
  872
+
  873
+    return Integer::from(state, total);
  874
+  }
  875
+
807 876
   Object* System::vm_raise_exception(STATE, Exception* exc) {
808 877
     state->thread_state()->raise_exception(exc);
809 878
     return NULL;
9  vm/builtin/system.hpp
@@ -124,6 +124,10 @@ namespace rubinius {
124 124
     // Ruby.primitive :vm_mri_backtrace
125 125
     static Array* vm_mri_backtrace(STATE, Fixnum* skip, CallFrame* calling_environment);
126 126
 
  127
+    // Load a tool into the VM.
  128
+    // Ruby.primitive :vm_load_tool
  129
+    static Object* vm_load_tool(STATE, String* str);
  130
+
127 131
     /** Return true if tooling is enabled */
128 132
     // Ruby.primitive :vm_tooling_available_p
129 133
     static Object* vm_tooling_available_p(STATE);
@@ -223,6 +227,11 @@ namespace rubinius {
223 227
     // Ruby.primitive :vm_deoptimize_inliners
224 228
     static Object* vm_deoptimize_inliners(STATE, Executable* exec);
225 229
 
  230
+    // Deoptimize all methods.
  231
+    // +disable+ indicates if the methods should also be pulled from being
  232
+    // available for JIT.
  233
+    static Object* vm_deoptimize_all(STATE, Object* disable);
  234
+
226 235
     // Ruby.primitive :vm_raise_exception
227 236
     static Object* vm_raise_exception(STATE, Exception* exc);
228 237
 
1  vm/capi/include/rbxti.hpp
@@ -73,6 +73,7 @@ namespace rbxti {
73 73
     int current_thread_id();
74 74
 
75 75
     void enable_thread_tooling();
  76
+    void disable_thread_tooling();
76 77
 
77 78
     rinteger integer_new(r_mint val);
78 79
     r_mint integer_value(rinteger i);
6  vm/configuration.hpp
@@ -203,11 +203,7 @@ namespace rubinius {
203 203
           "Set a custom path to write crash reports");
204 204
     }
205 205
 
206  
-    void finalize() {
207  
-      if(profile) {
208  
-        tool_to_load.set("tooling/profiler/profiler_vm");
209  
-      }
210  
-    }
  206
+    void finalize() { }
211 207
   };
212 208
 }
213 209
 
4  vm/instruments/rbxti.cpp
@@ -151,6 +151,10 @@ namespace rbxti {
151 151
     private_->state()->enable_tooling();
152 152
   }
153 153
 
  154
+  void Env::disable_thread_tooling() {
  155
+    private_->state()->disable_tooling();
  156
+  }
  157
+
154 158
   int Env::current_thread_id() {
155 159
     return private_->state()->thread_id();
156 160
   }
13  vm/instruments/tooling.cpp
... ...
@@ -1,6 +1,7 @@
1 1
 #include "vm/vm.hpp"
2 2
 #include "arguments.hpp"
3 3
 #include "dispatch.hpp"
  4
+#include "configuration.hpp"
4 5
 
5 6
 #include "instruments/rbxti-internal.hpp"
6 7
 #include "instruments/tooling.hpp"
@@ -8,6 +9,7 @@
8 9
 #include "builtin/compiledmethod.hpp"
9 10
 #include "builtin/block_environment.hpp"
10 11
 #include "builtin/variable_scope.hpp"
  12
+#include "builtin/system.hpp"
11 13
 
12 14
 namespace rubinius {
13 15
 namespace tooling {
@@ -19,6 +21,10 @@ namespace tooling {
19 21
 
20 22
   void ToolBroker::enable(STATE) {
21 23
     if(!enable_func_) return;
  24
+
  25
+    state->shared.config.jit_disabled.set("true");
  26
+    System::vm_deoptimize_all(state, Qtrue);
  27
+
22 28
     enable_func_(state->tooling_env());
23 29
   }
24 30
 
@@ -29,6 +35,13 @@ namespace tooling {
29 35
 
30 36
   Object* ToolBroker::results(STATE) {
31 37
     if(!results_func_) return Qnil;
  38
+
  39
+    state->shared.config.jit_disabled.set("false");
  40
+
  41
+    // This finds all the methods again and this time makes them available
  42
+    // for JIT.
  43
+    System::vm_deoptimize_all(state, Qfalse);
  44
+
32 45
     return rbxti::s(results_func_(state->tooling_env()));
33 46
   }
34 47
 
32  vm/llvm/jit.cpp
@@ -334,23 +334,27 @@ namespace rubinius {
334 334
           jit.show_machine_code();
335 335
         }
336 336
 
337  
-        req->vmmethod()->set_jitted(jit.llvm_function(),
338  
-                                    jit.code_bytes(),
339  
-                                    func);
340  
-
341  
-        if(!req->is_block()) {
342  
-          req->method()->execute = reinterpret_cast<executor>(func);
343  
-        }
344  
-        assert(req->method()->jit_data());
  337
+        // If the method has had jit'ing request disabled since we started
  338
+        // JIT'ing it, discard our work.
  339
+        if(!req->vmmethod()->jit_disabled()) {
  340
+          req->vmmethod()->set_jitted(jit.llvm_function(),
  341
+                                      jit.code_bytes(),
  342
+                                      func);
  343
+
  344
+          if(!req->is_block()) {
  345
+            req->method()->execute = reinterpret_cast<executor>(func);
  346
+          }
  347
+          assert(req->method()->jit_data());
345 348
 
346  
-        req->method()->jit_data()->run_write_barrier(ls_->write_barrier(), req->method());
  349
+          req->method()->jit_data()->run_write_barrier(ls_->write_barrier(), req->method());
347 350
 
348  
-        ls_->shared().stats.jitted_methods++;
  351
+          ls_->shared().stats.jitted_methods++;
349 352
 
350  
-        if(ls_->config().jit_show_compiling) {
351  
-          llvm::outs() << "[[[ JIT finished background compiling "
352  
-                    << (req->is_block() ? " (block)" : " (method)")
353  
-                    << " ]]]\n";
  353
+          if(ls_->config().jit_show_compiling) {
  354
+            llvm::outs() << "[[[ JIT finished background compiling "
  355
+                      << (req->is_block() ? " (block)" : " (method)")
  356
+                      << " ]]]\n";
  357
+          }
354 358
         }
355 359
 
356 360
         // If someone was waiting on this, wake them up.
40  vm/vmmethod.cpp
@@ -680,23 +680,37 @@ namespace rubinius {
680 680
   /* This is a noop for this class. */
681 681
   void VMMethod::compile(STATE) { }
682 682
 
683  
-  void VMMethod::deoptimize(STATE, CompiledMethod* original) {
  683
+  // If +disable+ is set, then the method is tagged as not being
  684
+  // available for JIT.
  685
+  void VMMethod::deoptimize(STATE, CompiledMethod* original, bool disable) {
684 686
 #ifdef ENABLE_LLVM
685  
-    // This resets execute to use the interpreter
686  
-    setup_argument_handler(original);
  687
+    if(jitted_impl_) {
  688
+      // This resets execute to use the interpreter
  689
+      setup_argument_handler(original);
687 690
 
688  
-    // Don't call LLVMState::get(state)->remove(llvm_function_)
689  
-    // here. We let the CodeManager do that later, when we're sure
690  
-    // the llvm function is no longer used.
691  
-    llvm_function_ = 0;
692  
-    jitted_impl_ = 0;
693  
-    jitted_bytes_ = 0;
  691
+      // Don't call LLVMState::get(state)->remove(llvm_function_)
  692
+      // here. We let the CodeManager do that later, when we're sure
  693
+      // the llvm function is no longer used.
  694
+      llvm_function_ = 0;
694 695
 
695  
-    // Remove any JIT data, which will be cleanup by the CodeManager
696  
-    // later.
697  
-    original->set_jit_data(0);
  696
+      jitted_impl_ = 0;
698 697
 
699  
-    call_count = 0;
  698
+      if(disable) {
  699
+        jitted_bytes_ = -1;
  700
+      } else {
  701
+        jitted_bytes_ = 0;
  702
+      }
  703
+
  704
+      // Remove any JIT data, which will be cleanup by the CodeManager
  705
+      // later.
  706
+      original->set_jit_data(0);
  707
+    }
  708
+
  709
+    if(disable) {
  710
+      call_count = -1;
  711
+    } else {
  712
+      call_count = 0;
  713
+    }
700 714
 #endif
701 715
   }
702 716
 
10  vm/vmmethod.hpp
@@ -73,7 +73,7 @@ namespace rubinius {
73 73
 #ifdef ENABLE_LLVM
74 74
   private:
75 75
     llvm::Function* llvm_function_;
76  
-    size_t jitted_bytes_;
  76
+    int    jitted_bytes_;
77 77
     void*  jitted_impl_;
78 78
 #endif
79 79
 
@@ -94,7 +94,11 @@ namespace rubinius {
94 94
 
95 95
 #ifdef ENABLE_LLVM
96 96
     bool jitted() {
97  
-      return jitted_impl_ != 0;
  97
+      return jit_disabled() && jitted_impl_ != 0;
  98
+    }
  99
+
  100
+    bool jit_disabled() {
  101
+      return jitted_bytes_ == -1;
98 102
     }
99 103
 
100 104
     void set_jitted(llvm::Function* func, size_t bytes, void* impl) {
@@ -227,7 +231,7 @@ namespace rubinius {
227 231
     void initialize_caches(STATE, CompiledMethod* original, int sends);
228 232
     void find_super_instructions();
229 233
 
230  
-    void deoptimize(STATE, CompiledMethod* original);
  234
+    void deoptimize(STATE, CompiledMethod* original, bool disable=false);
231 235
 
232 236
     /*
233 237
      * Helper class for iterating over an Opcode array.  Used to convert a

0 notes on commit 8133e09

Please sign in to comment.
Something went wrong with that request. Please try again.