rubinius-profiler 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1059 @@
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
+ }