rubinius-profiler 2.0.2 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module Rubinius
2
2
  module Profiler
3
- VERSION = "2.0.2"
3
+ VERSION = "2.1"
4
4
  end
5
5
  end
@@ -13,7 +13,6 @@ Gem::Specification.new do |spec|
13
13
 
14
14
  spec.files = `git ls-files`.split($/)
15
15
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
- spec.extensions = ["ext/rubinius/profiler/extconf.rb"]
17
16
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
17
  spec.require_paths = ["lib"]
19
18
 
metadata CHANGED
@@ -1,22 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubinius-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: '2.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Shirai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-15 00:00:00.000000000 Z
11
+ date: 2016-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
18
  version: '1.3'
19
+ name: bundler
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -25,12 +25,12 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
32
  version: '10.0'
33
+ name: rake
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,8 +42,7 @@ description: Rubinius profiler.
42
42
  email:
43
43
  - brixen@gmail.com
44
44
  executables: []
45
- extensions:
46
- - ext/rubinius/profiler/extconf.rb
45
+ extensions: []
47
46
  extra_rdoc_files: []
48
47
  files:
49
48
  - ".gitignore"
@@ -51,8 +50,6 @@ files:
51
50
  - LICENSE
52
51
  - README.md
53
52
  - Rakefile
54
- - ext/rubinius/profiler/extconf.rb
55
- - ext/rubinius/profiler/profiler.cpp
56
53
  - lib/rubinius/profiler.rb
57
54
  - lib/rubinius/profiler/profiler.rb
58
55
  - lib/rubinius/profiler/version.rb
@@ -77,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
74
  version: '0'
78
75
  requirements: []
79
76
  rubyforge_project:
80
- rubygems_version: 2.4.5
77
+ rubygems_version: 2.6.2
81
78
  signing_key:
82
79
  specification_version: 4
83
80
  summary: Rubinius profiler.
@@ -1,3 +0,0 @@
1
- require 'mkmf'
2
-
3
- create_makefile 'rubinius/profiler/ext/profiler'
@@ -1,1059 +0,0 @@
1
- #include <rbxti.hpp>
2
- #include <rbxti/atomic.hpp>
3
- #include <rbx_config.h>
4
-
5
- #include <stdint.h>
6
- #include <stdio.h>
7
-
8
- #include <list>
9
- #include <stack>
10
- #include <map>
11
- #ifdef RBX_HAVE_TR1
12
- #include <tr1/unordered_map>
13
- #define std_unordered_map std::tr1::unordered_map
14
- #else
15
- #include <unordered_map>
16
- #define std_unordered_map std::unordered_map
17
- #endif
18
-
19
- #include <time.h>
20
-
21
- #include <sstream>
22
- #include <iostream>
23
- #include <vector>
24
- #include <algorithm>
25
-
26
- typedef uint64_t method_id;
27
-
28
- #if defined(RBX_HAVE_TR1) && !defined(RBX_HAVE_TR1_HASH)
29
- namespace std {
30
- namespace tr1 {
31
- template <>
32
- struct hash<method_id> {
33
- size_t operator()(const method_id id) const {
34
- return id;
35
- }
36
- };
37
- }
38
- }
39
- #endif
40
-
41
- using namespace rbxti;
42
-
43
- namespace profiler {
44
-
45
- enum Kind {
46
- kNormal,
47
- kSingleton,
48
- kBlock,
49
- kYoungGC,
50
- kMatureGC,
51
- kNormalJIT,
52
- kSingletonJIT,
53
- kBlockJIT,
54
- kFinalizers,
55
- kScript,
56
- kRoot
57
- };
58
-
59
- class VM;
60
- class Profiler;
61
- class Method;
62
- class Node;
63
- typedef std_unordered_map<Method*, rinteger> KeyMap;
64
- typedef std_unordered_map<method_id, Method*> MethodMap;
65
-
66
- /* An accumulating increment timer. Keeps track of the maximum and minimum
67
- * intervals recorded. Calculates a cumulative moving average according to
68
- * the formula:
69
- *
70
- * x(i+1) + i*CA(i)
71
- * CA(i+1) = -----------------
72
- * i + 1
73
- *
74
- * where:
75
- *
76
- * CA(n) is the nth cumulative average
77
- * x(n) is the nth measured value of x
78
- * i is the number of measurements
79
- */
80
- class Timer {
81
- protected:
82
-
83
- uint64_t total_;
84
- uint64_t timings_;
85
- uint64_t max_;
86
- uint64_t min_;
87
- uint64_t last_;
88
- uint64_t start_;
89
- double moving_average_;
90
- bool started_;
91
-
92
- public:
93
-
94
- Timer()
95
- : total_(0)
96
- , timings_(0)
97
- , max_(0)
98
- , min_(0)
99
- , last_(0)
100
- , start_(0)
101
- , moving_average_(0.0)
102
- , started_(false)
103
- { }
104
-
105
- bool started() {
106
- return started_;
107
- }
108
-
109
- uint64_t total() {
110
- return total_;
111
- }
112
-
113
- uint64_t timings() {
114
- return timings_;
115
- }
116
-
117
- uint64_t max() {
118
- return max_;
119
- }
120
-
121
- uint64_t min() {
122
- return min_;
123
- }
124
-
125
- double moving_average() {
126
- return moving_average_;
127
- }
128
-
129
- void start(Env* env) {
130
- if(started_) return;
131
-
132
- started_ = true;
133
- start_ = env->time_current_ns();
134
- }
135
-
136
- void stop(Env* env, uint64_t cur) {
137
- if(!started_) return;
138
-
139
- started_ = false;
140
-
141
- last_ = cur - start_;
142
- total_ += last_;
143
-
144
- if(min_ == 0 || min_ > last_) min_ = last_;
145
- if(max_ == 0 || max_ < last_) max_ = last_;
146
-
147
- moving_average_ = (last_ + timings_ * moving_average_) / (timings_ + 1);
148
- ++timings_;
149
- }
150
- };
151
-
152
- class StackTimer : public Timer {
153
- size_t entered_;
154
- uint64_t count_;
155
-
156
- public:
157
- StackTimer()
158
- : entered_(0)
159
- , count_(0)
160
- { }
161
-
162
- uint64_t count() {
163
- return count_;
164
- }
165
-
166
- void start(Env* env) {
167
- ++entered_;
168
- Timer::start(env);
169
- }
170
-
171
- void stop(Env* env, uint64_t cur) {
172
- if(!started_) return;
173
-
174
- ++count_;
175
- if(--entered_ == 0) Timer::stop(env, cur);
176
- }
177
- };
178
-
179
- class Node {
180
- int id_;
181
- int called_;
182
- uint64_t total_;
183
- Method* method_;
184
-
185
- Node* sibling_;
186
- Node* first_sub_node_;
187
-
188
- public:
189
- Node(Method* method, int id)
190
- : id_(id)
191
- , called_(0)
192
- , total_(0)
193
- , method_(method)
194
- , sibling_(0)
195
- , first_sub_node_(0)
196
- { }
197
-
198
- int id() {
199
- return id_;
200
- }
201
-
202
- Method* method() {
203
- return method_;
204
- }
205
-
206
- uint64_t total() {
207
- return total_;
208
- }
209
-
210
- int called() {
211
- return called_;
212
- }
213
-
214
- Node* sub_nodes() {
215
- return first_sub_node_;
216
- }
217
-
218
- int count_sub_nodes() {
219
- int count = 0;
220
- Node* node = first_sub_node_;
221
- while(node) {
222
- ++count;
223
- node = node->sibling();
224
- }
225
-
226
- return count;
227
- }
228
-
229
- Node* sibling() {
230
- return sibling_;
231
- }
232
-
233
- void set_sibling(Node* node) {
234
- sibling_ = node;
235
- }
236
-
237
- void accumulate(uint64_t time) {
238
- total_ += time;
239
- called_++;
240
- }
241
-
242
- Node* find_sub_node(Profiler* profiler, Method* method);
243
- };
244
-
245
- class Method {
246
- private:
247
- method_id id_;
248
- rsymbol name_;
249
- rsymbol container_;
250
- Kind kind_;
251
- rsymbol file_;
252
- int line_;
253
- uint64_t total_;
254
-
255
- public:
256
- StackTimer timer;
257
-
258
- public:
259
- Method(method_id id, rsymbol name, rsymbol container, Kind kind=kNormal)
260
- : id_(id)
261
- , name_(name)
262
- , container_(container)
263
- , kind_(kind)
264
- , file_(0)
265
- , line_(0)
266
- , total_(0)
267
- { }
268
-
269
- method_id id() {
270
- return id_;
271
- }
272
-
273
- rsymbol container() {
274
- return container_;
275
- }
276
-
277
- rsymbol name() {
278
- return name_;
279
- }
280
-
281
- Kind kind() {
282
- return kind_;
283
- }
284
-
285
- rstring to_s(Env* env);
286
-
287
- rsymbol file() {
288
- return file_;
289
- }
290
-
291
- int line() {
292
- return line_;
293
- }
294
-
295
- void set_position(rsymbol file, int line) {
296
- file_ = file;
297
- line_ = line;
298
- }
299
-
300
- uint64_t total() {
301
- return total_;
302
- }
303
-
304
- void accumulate(uint64_t time) {
305
- total_ += time;
306
- }
307
- };
308
-
309
- class Profiler;
310
-
311
- /** Created when a method is being called. Contains a timer that tracks
312
- * how much time is spent in the method. When the MethodEntry instance
313
- * goes out of scope, the destructor records the elapsed time and updates
314
- * the Method and Node objects.
315
- */
316
- class MethodEntry {
317
- Method* method_;
318
- Node* node_;
319
- MethodEntry* previous_me_;
320
- Timer timer_;
321
-
322
- public:
323
-
324
- MethodEntry(Method* method, Node* node=0)
325
- : method_(method)
326
- , node_(node)
327
- , previous_me_(0)
328
- {}
329
-
330
- void start(Profiler* profiler, Env* env);
331
- void stop(Profiler* profiler, Env* env);
332
- void stop_all(Profiler* profiler, Env* env, uint64_t end_time);
333
- };
334
-
335
- class Profiler {
336
- MethodMap methods_;
337
- Node* root_;
338
- MethodEntry* current_me_;
339
- int nodes_;
340
- uint32_t threshold_;
341
- uint64_t start_time_;
342
- uint64_t end_time_;
343
- int id_;
344
- bool attached_;
345
-
346
- rbxti::SpinLock lock_;
347
-
348
- public:
349
- Profiler(Env* env);
350
-
351
- ~Profiler();
352
-
353
- void lock() {
354
- lock_.lock();
355
- }
356
-
357
- void unlock() {
358
- lock_.unlock();
359
- }
360
-
361
- MethodEntry* current_me() {
362
- return current_me_;
363
- }
364
-
365
- void set_current_me(MethodEntry* me) {
366
- current_me_ = me;
367
- }
368
-
369
- int next_node_id() {
370
- return nodes_++;
371
- }
372
-
373
- uint64_t start_time() {
374
- return start_time_;
375
- }
376
-
377
- uint64_t end_time() {
378
- return end_time_;
379
- }
380
-
381
- uint64_t runtime() {
382
- return end_time_ - start_time_;
383
- }
384
-
385
- void finish(Env* env, uint64_t end_time) {
386
- if(end_time_ == 0) {
387
- end_time_ = end_time;
388
-
389
- current_me_->stop_all(this, env, end_time);
390
- }
391
- }
392
-
393
- void detach(Env* env, uint64_t end_time) {
394
- finish(env, end_time);
395
- attached_ = false;
396
- }
397
-
398
- bool attached_p() {
399
- return attached_;
400
- }
401
-
402
- int id() {
403
- return id_;
404
- }
405
-
406
- method_id create_id(Env* env, rcompiled_code ccode,
407
- rsymbol container, rsymbol name, Kind kind);
408
- Method* find_method(Env* env, rcompiled_code ccode,
409
- rsymbol container, rsymbol name, Kind kind);
410
-
411
-
412
- Method* enter_method(Env* env, robject recv, rsymbol name, rmodule mod,
413
- rcompiled_code ccode);
414
- Method* enter_block(Env* env, rsymbol name, rmodule module,
415
- rcompiled_code ccode);
416
- Method* get_method(Env* env, rcompiled_code ccode, rsymbol name,
417
- rsymbol container, Kind kind);
418
-
419
- void results(Env* env, rtable profile, rtable nodes, rtable methods,
420
- KeyMap& keys, uint64_t runtime);
421
- };
422
-
423
- rstring Method::to_s(Env* env) {
424
- std::stringstream ss;
425
- char data[1024];
426
-
427
- if(kind() == kScript) {
428
- ss << "script:";
429
- if(file_) {
430
- env->symbol_cstr(file_, data, 1024);
431
- ss << data;
432
- } else {
433
- ss << "--unknown-file--";
434
- ss << ":" << line_;
435
- }
436
-
437
- return env->string_new(ss.str().c_str());
438
- }
439
-
440
- if(!env->is_nil(container())) {
441
- env->symbol_cstr(container_, data, 1024);
442
- ss << data;
443
- } else {
444
- ss << "<anonymous>";
445
- }
446
-
447
- env->symbol_cstr(name(), data, 1024);
448
-
449
- switch(kind()) {
450
- case kNormal:
451
- ss << "#" << data;
452
- break;
453
- case kNormalJIT:
454
- ss << "#" << data << " <jit>";
455
- break;
456
- case kSingleton:
457
- case kYoungGC:
458
- case kMatureGC:
459
- case kFinalizers:
460
- ss << "." << data;
461
- break;
462
- case kSingletonJIT:
463
- ss << "." << data << " <jit>";
464
- break;
465
- case kBlock:
466
- ss << "::" << data << "<" << line_ << "> {}";
467
- break;
468
- case kBlockJIT:
469
- ss << "::" << data << " {" << line_ << "} <jit>";
470
- break;
471
- case kRoot:
472
- break;
473
- case kScript:
474
- // handled above, just here to make gcc happy.
475
- abort();
476
- }
477
-
478
- return env->string_new(ss.str().c_str());
479
- }
480
-
481
- Node* Node::find_sub_node(Profiler* profiler, Method* method) {
482
- Node* sub = first_sub_node_;
483
-
484
- while(sub) {
485
- if(sub->method() == method) return sub;
486
- sub = sub->sibling();
487
- }
488
-
489
- Node* node = new Node(method, profiler->next_node_id());
490
- node->set_sibling(first_sub_node_);
491
- first_sub_node_ = node;
492
-
493
- return node;
494
- }
495
-
496
- void MethodEntry::start(Profiler* profiler, Env* env) {
497
- previous_me_ = profiler->current_me();
498
-
499
- node_ = previous_me_->node_->find_sub_node(profiler, method_);
500
-
501
- profiler->set_current_me(this);
502
-
503
- method_->timer.start(env);
504
- timer_.start(env);
505
- }
506
-
507
- void MethodEntry::stop(Profiler* profiler, Env* env) {
508
- uint64_t cur = env->time_current_ns();
509
-
510
- method_->timer.stop(env, cur);
511
- timer_.stop(env, cur);
512
-
513
- method_->accumulate(timer_.total());
514
- node_->accumulate(timer_.total());
515
-
516
- if(previous_me_) {
517
- profiler->set_current_me(previous_me_);
518
- }
519
- }
520
-
521
- void MethodEntry::stop_all(Profiler* profiler, Env* env, uint64_t end_time) {
522
- method_->timer.stop(env, end_time);
523
- timer_.stop(env, end_time);
524
-
525
- method_->accumulate(timer_.total());
526
- node_->accumulate(timer_.total());
527
-
528
- if(previous_me_) previous_me_->stop_all(profiler, env, end_time);
529
- }
530
-
531
- Profiler::Profiler(Env* env)
532
- : root_(0)
533
- , current_me_(0)
534
- , nodes_(0)
535
- , threshold_((uint32_t)env->config_get_int("profiler.threshold"))
536
- , start_time_(env->time_current_ns())
537
- , end_time_(0)
538
- , id_(env->current_thread_id())
539
- , attached_(true)
540
- {
541
- Method* root_me = new Method(0, 0, 0, kRoot);
542
- root_ = new Node(root_me, next_node_id());
543
- current_me_ = new MethodEntry(root_me, root_);
544
- }
545
-
546
- Method* Profiler::enter_block(Env* env, rsymbol name, rmodule module,
547
- rcompiled_code ccode)
548
- {
549
- return get_method(env, ccode, name, env->module_name(module), kBlock);
550
- }
551
-
552
- Method* Profiler::enter_method(Env* env, robject recv, rsymbol name, rmodule mod,
553
- rcompiled_code ccode)
554
- {
555
- if(env->module_is_metaclass(mod)) {
556
- robject attached = env->metaclass_attached_instance(mod);
557
-
558
- rmodule as_module = env->cast_to_rmodule(attached);
559
- if(as_module) {
560
- return get_method(env, ccode, name, env->module_name(as_module), kSingleton);
561
- } else {
562
- rstring str = env->to_s(mod);
563
- return get_method(env, ccode, name, env->string_to_symbol(str), kSingleton);
564
- }
565
- } else {
566
- return get_method(env, ccode, name, env->module_name(mod), kNormal);
567
- }
568
- }
569
-
570
- Method* Profiler::get_method(Env* env, rcompiled_code ccode, rsymbol name,
571
- rsymbol container, Kind kind)
572
- {
573
- Method* method = find_method(env, ccode, container, name, kind);
574
-
575
- if(!method->file() && ccode && !env->is_nil(ccode)) {
576
- method->set_position(env->method_file(ccode), env->method_line(ccode));
577
- }
578
-
579
- return method;
580
- }
581
-
582
- method_id Profiler::create_id(Env* env, rcompiled_code ccode, rsymbol name,
583
- rsymbol container, Kind kind)
584
- {
585
- // If we have a CompiledCode, use it's method id.
586
- if(ccode && !env->is_nil(ccode)) {
587
- r_mint i = env->method_id(ccode);
588
- if(i) return i;
589
- }
590
-
591
- // | -- 32 bits of container -- | -- 29 bits of name -- | -- 2 bits of kind -- | 0
592
-
593
- uint32_t c = env->symbol_id(container) & 0xffffffff;
594
- uint32_t n = env->symbol_id(name) & 0x1fffffff;
595
- uint32_t k = kind & 0x3;
596
-
597
- return (((uint64_t)c) << 32) |
598
- (n << 3) |
599
- k << 1;
600
- }
601
-
602
- Method* Profiler::find_method(Env* env, rcompiled_code ccode, rsymbol container,
603
- rsymbol name, Kind kind)
604
- {
605
- method_id id = create_id(env, ccode, container, name, kind);
606
-
607
- Method* method;
608
-
609
- MethodMap::iterator iter = methods_.find(id);
610
-
611
- if(iter == methods_.end()) {
612
- method = new Method(id, name, container, kind);
613
- methods_[method->id()] = method;
614
- } else {
615
- method = iter->second;
616
- }
617
-
618
- return method;
619
- }
620
-
621
- typedef std::vector<Node*> WorkList;
622
-
623
- static rinteger make_key(Env* env, Method* meth, KeyMap& keys) {
624
- KeyMap::iterator iter = keys.find(meth);
625
-
626
- if(iter == keys.end()) {
627
- rinteger key = env->integer_new(keys.size());
628
- keys[meth] = key;
629
- return key;
630
- }
631
-
632
- return iter->second;
633
- }
634
-
635
- static rinteger add_method(Env* env, rtable methods, Method* meth,
636
- KeyMap& keys)
637
- {
638
- rinteger key = make_key(env, meth, keys);
639
-
640
- // We already have the method, skip this.
641
- bool fetched = false;
642
- env->table_fetch(methods, key, &fetched);
643
- if(fetched) return env->cast_to_rinteger(key);
644
-
645
- rsymbol cumulative_sym = env->symbol("cumulative");
646
- rsymbol total_sym = env->symbol("total");
647
- rsymbol called_sym = env->symbol("called");
648
-
649
- rtable method = env->table_new();
650
- env->table_store(methods, key, method);
651
-
652
- env->table_store(method, env->symbol("name"), meth->to_s(env));
653
- env->table_store(method, cumulative_sym, env->integer_new(meth->timer.total()));
654
- env->table_store(method, total_sym, env->integer_new(meth->total()));
655
- env->table_store(method, called_sym, env->integer_new(meth->timer.count()));
656
-
657
- if(meth->file()) {
658
- if(env->is_nil(meth->file())) {
659
- env->table_store(method, env->symbol("file"), env->string_new("unknown file"));
660
- } else {
661
- env->table_store(method, env->symbol("file"),
662
- env->symbol_to_string(meth->file()));
663
- }
664
-
665
- env->table_store(method, env->symbol("line"), env->integer_new(meth->line()));
666
- }
667
-
668
- return key;
669
- }
670
-
671
- static void add_node(Env* env, rtable nodes, rtable methods, Node* node,
672
- WorkList& work, KeyMap& keys, uint32_t threshold)
673
- {
674
- // We haven't exited this method yet, so its stats won't be accurate
675
- if(node->method()->timer.started()) return;
676
-
677
- rinteger key = env->integer_new(node->id());
678
-
679
- rarray tbl = env->array_new(5);
680
-
681
- env->table_store(nodes, key, tbl);
682
-
683
- robject meth_key = add_method(env, methods, node->method(), keys);
684
-
685
- env->array_set(tbl, 0, meth_key);
686
- env->array_set(tbl, 1, env->integer_new(node->total()));
687
- env->array_set(tbl, 2, env->integer_new(node->called()));
688
-
689
- int count = node->count_sub_nodes();
690
- env->array_set(tbl, 3, env->integer_new(count));
691
-
692
- rarray ary = env->array_new(count);
693
-
694
- int idx = 0;
695
-
696
- Node* sub = node->sub_nodes();
697
-
698
- while(sub) {
699
- if(sub->total() >= threshold) {
700
- env->array_set(ary, idx++, env->integer_new(sub->id()));
701
- work.push_back(sub);
702
- }
703
-
704
- sub = sub->sibling();
705
- }
706
-
707
- env->array_set(tbl, 4, ary);
708
- }
709
-
710
- void Profiler::results(Env* env, rtable profile, rtable nodes, rtable methods,
711
- KeyMap& keys, uint64_t runtime)
712
- {
713
-
714
- WorkList work;
715
-
716
- // If we haven't even gone for a total of longer than 10x the threshold,
717
- // just disable the threshold.
718
- if(runtime < 10 * threshold_) threshold_ = 0;
719
-
720
- env->table_store(profile, env->symbol("total_nodes"), env->integer_new(nodes_));
721
-
722
- rarray roots = env->array_new(root_->count_sub_nodes());
723
- env->table_store(profile, env->symbol("roots"), roots);
724
-
725
- int idx = 0;
726
- Node* sub = root_->sub_nodes();
727
-
728
- while(sub) {
729
- if(sub->total() >= threshold_) {
730
- env->array_set(roots, idx++, env->integer_new(sub->id()));
731
- work.push_back(sub);
732
- }
733
-
734
- sub = sub->sibling();
735
- }
736
-
737
- while(work.size() > 0) {
738
- Node* node = work.back();
739
- work.pop_back();
740
-
741
- add_node(env, nodes, methods, node, work, keys, threshold_);
742
- }
743
- }
744
-
745
- Profiler::~Profiler() {
746
- for(MethodMap::iterator i = methods_.begin();
747
- i != methods_.end();
748
- i++) {
749
- delete i->second;
750
- }
751
-
752
- WorkList work;
753
-
754
- work.push_back(root_);
755
-
756
- while(work.size() > 0) {
757
- Node* node = work.back();
758
- work.pop_back();
759
-
760
- Node* sub = node->sub_nodes();
761
-
762
- while(sub) {
763
- work.push_back(sub);
764
- sub = sub->sibling();
765
- }
766
-
767
- delete node;
768
- }
769
- }
770
-
771
- struct GlobalState {
772
- Profiler* main_profiler;
773
- std::list<Profiler*> profilers;
774
-
775
- GlobalState()
776
- : main_profiler(0)
777
- {}
778
-
779
- void add(Profiler* prof) {
780
- profilers.push_back(prof);
781
- }
782
- };
783
-
784
- static int cProfileToolID = -1;
785
-
786
- namespace {
787
- void tool_enable(Env* env) {
788
- // Ignore if we are already enabled
789
- if(env->global_tool_data()) return;
790
-
791
- GlobalState* st = new GlobalState;
792
- env->set_global_tool_data(st);
793
-
794
- Profiler* profiler = new Profiler(env);
795
- st->main_profiler = profiler;
796
-
797
- env->thread_tool_set_data(cProfileToolID, profiler);
798
- st->add(profiler);
799
-
800
- env->enable_thread_tooling();
801
- }
802
-
803
- void* tool_enter_method(Env* env, robject recv, rsymbol name, rmodule mod,
804
- rcompiled_code ccode)
805
- {
806
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
807
- if(!profiler) return 0;
808
-
809
- profiler->lock();
810
-
811
- Method* method = profiler->enter_method(env, recv, name, mod, ccode);
812
- MethodEntry* me = new MethodEntry(method);
813
- me->start(profiler, env);
814
-
815
- profiler->unlock();
816
-
817
- return me;
818
- }
819
-
820
- void tool_leave_entry(Env* env, void* tag) {
821
- MethodEntry* me = (MethodEntry*)tag;
822
-
823
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
824
- if(!profiler) return;
825
-
826
- profiler->lock();
827
-
828
- me->stop(profiler, env);
829
-
830
- profiler->unlock();
831
-
832
- delete me;
833
- }
834
-
835
- void* tool_enter_block(Env* env, rsymbol name, rmodule module, rcompiled_code ccode) {
836
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
837
- if(!profiler) return 0;
838
-
839
- profiler->lock();
840
-
841
- Method* method = profiler->enter_block(env, name, module, ccode);
842
- MethodEntry* me = new MethodEntry(method);
843
- me->start(profiler, env);
844
-
845
- profiler->unlock();
846
-
847
- return me;
848
- }
849
-
850
- void* tool_enter_gc(Env* env, int level) {
851
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
852
- if(!profiler) return 0;
853
-
854
- rsymbol container = env->symbol("GC");
855
- rsymbol name;
856
-
857
- Kind kind;
858
-
859
- switch(level) {
860
- case GCYoung:
861
- kind = kYoungGC;
862
- name = env->symbol("collect_young");
863
- break;
864
- case GCMature:
865
- kind = kMatureGC;
866
- name = env->symbol("collect_mature");
867
- break;
868
- case GCFinalizer:
869
- kind = kFinalizers;
870
- name = env->symbol("run_finalizers");
871
- break;
872
- default:
873
- kind = kFinalizers;
874
- name = env->symbol("unknown");
875
- break;
876
- }
877
-
878
- profiler->lock();
879
-
880
- Method* method = profiler->get_method(env, NULL, name, container, kind);
881
- MethodEntry* me = new MethodEntry(method);
882
- me->start(profiler, env);
883
-
884
- profiler->unlock();
885
-
886
- return me;
887
- }
888
-
889
- void* tool_enter_script(Env* env, rcompiled_code ccode) {
890
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
891
- if(!profiler) return 0;
892
-
893
- profiler->lock();
894
-
895
- Kind kind = kScript;
896
- rsymbol container = env->symbol("unknown");
897
- rsymbol name = container;
898
-
899
- Method* method = profiler->get_method(env, ccode, name, container, kind);
900
- MethodEntry* me = new MethodEntry(method);
901
- me->start(profiler, env);
902
-
903
- profiler->unlock();
904
-
905
- return me;
906
- }
907
-
908
- void tool_shutdown(Env* env) {
909
- GlobalState* st = (GlobalState*)env->global_tool_data();
910
- if(!st) return;
911
-
912
- env->set_global_tool_data(0);
913
-
914
- for(std::list<Profiler*>::iterator i = st->profilers.begin();
915
- i != st->profilers.end();
916
- ++i) {
917
-
918
- Profiler* prof = *i;
919
- if(!prof->attached_p()) delete prof;
920
- }
921
-
922
- delete st;
923
- }
924
-
925
- void tool_start_thread(Env* env) {
926
- GlobalState* st = (GlobalState*)env->global_tool_data();
927
-
928
- // No GlobalState means that the tool isn't currently enabled.
929
- if(!st) return;
930
-
931
- Profiler* profiler = new Profiler(env);
932
- profiler->lock();
933
-
934
- st->add(profiler);
935
-
936
- env->thread_tool_set_data(cProfileToolID, profiler);
937
-
938
- env->enable_thread_tooling();
939
-
940
- profiler->unlock();
941
- }
942
-
943
- void tool_stop_thread(Env* env) {
944
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
945
- if(!profiler) return;
946
-
947
- profiler->lock();
948
-
949
- env->thread_tool_set_data(cProfileToolID, 0);
950
- profiler->detach(env, env->time_current_ns());
951
-
952
- env->disable_thread_tooling();
953
-
954
- profiler->unlock();
955
- }
956
-
957
- void tool_at_gc(Env* env) {
958
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
959
- if(!profiler) return;
960
-
961
- profiler->lock();
962
-
963
- // The main thread finished profiling, so we've already stopped
964
- // doing our work, so we can safely bail.
965
- if(profiler->end_time() > 0) {
966
- env->thread_tool_set_data(cProfileToolID, 0);
967
- delete profiler;
968
- return;
969
- }
970
-
971
- profiler->unlock();
972
- }
973
-
974
- robject tool_results(Env* env) {
975
- uint64_t fin = env->time_current_ns();
976
-
977
- GlobalState* st = (GlobalState*)env->global_tool_data();
978
-
979
- // If we are already shutting down, ignore this
980
- if(!st) return env->nil();
981
-
982
- Profiler* profiler = (Profiler*)env->thread_tool_data(cProfileToolID);
983
-
984
- // Ignore results requests that don't come from the thread that
985
- // started profiling.
986
- if(st->main_profiler != profiler) return env->nil();
987
-
988
- profiler->detach(env, fin);
989
-
990
- env->thread_tool_set_data(cProfileToolID, 0);
991
-
992
- rtable profile = env->table_new();
993
-
994
- for(std::list<Profiler*>::iterator i = st->profilers.begin();
995
- i != st->profilers.end();
996
- ++i) {
997
-
998
- Profiler* prof = *i;
999
-
1000
- prof->lock();
1001
-
1002
- prof->finish(env, fin);
1003
-
1004
- rtable thread = env->table_new();
1005
-
1006
- env->table_store(profile, env->integer_new(prof->id()), thread);
1007
-
1008
- rtable methods = env->table_new();
1009
- rtable nodes = env->table_new();
1010
-
1011
- env->table_store(thread, env->symbol("methods"), methods);
1012
- env->table_store(thread, env->symbol("nodes"), nodes);
1013
-
1014
- uint64_t runtime = prof->runtime();
1015
- env->table_store(thread, env->symbol("runtime"), env->integer_new(runtime));
1016
-
1017
- KeyMap keys;
1018
- prof->results(env, thread, nodes, methods, keys, runtime);
1019
-
1020
- prof->unlock();
1021
- }
1022
-
1023
- tool_shutdown(env);
1024
-
1025
- env->disable_thread_tooling();
1026
- return profile;
1027
- }
1028
-
1029
- }
1030
-
1031
- extern "C" int Tool_Init(Env* env) {
1032
- env->config_set("tool.require", "tooling/profiler/profiler.rb");
1033
-
1034
- cProfileToolID = env->thread_tool_new_id();
1035
-
1036
- env->set_tool_results(tool_results);
1037
- env->set_tool_enable(tool_enable);
1038
-
1039
- env->set_tool_enter_method(tool_enter_method);
1040
- env->set_tool_leave_method(tool_leave_entry);
1041
-
1042
- env->set_tool_enter_block(tool_enter_block);
1043
- env->set_tool_leave_block(tool_leave_entry);
1044
-
1045
- env->set_tool_enter_gc(tool_enter_gc);
1046
- env->set_tool_leave_gc(tool_leave_entry);
1047
-
1048
- env->set_tool_enter_script(tool_enter_script);
1049
- env->set_tool_leave_script(tool_leave_entry);
1050
-
1051
- env->set_tool_shutdown(tool_shutdown);
1052
-
1053
- env->set_tool_thread_start(tool_start_thread);
1054
- env->set_tool_thread_stop(tool_stop_thread);
1055
-
1056
- return 1;
1057
- }
1058
-
1059
- }