vernier 0.1.999 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdb4d7d32dcaf189d999cfaee665b20a57e97db378b46dd45c73c82a1c4376c3
4
- data.tar.gz: c72210b3fc60d65e0fd6e64af43cb1ac828879d4555151c89d43560a8a4e3f75
3
+ metadata.gz: da6d8fafb453c25b5f1127a33c53401150cb91bf604a37db971c952ec490d900
4
+ data.tar.gz: 97cc5b3435335fe97c531122d5692a3af3264c5abeb34eafb1a1808743b017fe
5
5
  SHA512:
6
- metadata.gz: c337257e4808c6170701c2078d29e93df73c6b4855e02d079e8d7a7b49344180e9b6a184cc01b18d23d3038e60a36c0c80816a711c2270bfde9ba14d18f4a45f
7
- data.tar.gz: b141496282462bdfb536947d7667c71922ad885b5a6bcb4c14b2b681904b72d32f3d4423eb08cd1afaa632d039fd040cb3655f35e1d3bad49271fd6895d3bdec
6
+ metadata.gz: 92529d7331c8a015c900227f1d28b093322dfb386ce99213c4a22dea168d433f9e0d9ae221c3171ea7e3c91fd67753a9da668140af97f62e0edec08341cb4f63
7
+ data.tar.gz: bdd0baaf24abdbed97f603da42e827fa10442f4f702f15a5ee2f01e2b25eb893465d8b313bcd3dec84a402d76554aa58d28547e2bad401db2c4dbb24797a2414
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vernier
2
2
 
3
- Experimental Ruby profiling tool
3
+ Experimental next-generation Ruby profiler.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,16 +10,23 @@ gem 'vernier'
10
10
 
11
11
  ## Usage
12
12
 
13
- Record a flamegraph of all **retained** allocations from requiring `irb`.
13
+ ### Retained memory
14
+
15
+ Record a flamegraph of all **retained** allocations from loading `irb`.
14
16
 
15
17
  ```
16
- ruby -r vernier -e 'Vernier.trace_retained(out: "irb.stackcollapse") { require "irb" }'
18
+ ruby -r vernier -e 'Vernier.trace_retained(out: "irb_profile.json") { require "irb" }'
17
19
  ```
18
20
 
19
- The output can then be viewed in speedscope or other flamegraph tools
21
+ The output can then be viewed in the [Firefox Profiler (demo)](https://share.firefox.dev/3DhLsFa)
22
+
23
+ ![Screenshot 2023-07-16 at 21-06-19 Ruby_Vernier – 1970-01-01 12 00 00 a m UTC – Firefox Profiler](https://github.com/jhawthorn/vernier/assets/131752/9ca0b593-70fb-4c8b-aed9-cb33e0e0bc06)
20
24
 
21
- <img width="1082" alt="Screen Shot 2022-04-26 at 8 11 19 PM" src="https://user-images.githubusercontent.com/131752/165440422-3a11f5cc-3018-4455-8918-887c2afa6d6e.png">
25
+ ### Time
22
26
 
27
+ ```
28
+ Vernier.trace(out: "time_profile.json") { some_slow_method }
29
+ ```
23
30
 
24
31
  ## Development
25
32
 
@@ -31,6 +38,13 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
31
38
 
32
39
  Bug reports and pull requests are welcome on GitHub at https://github.com/jhawthorn/vernier. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/jhawthorn/vernier/blob/main/CODE_OF_CONDUCT.md).
33
40
 
41
+ ### Resources
42
+
43
+ * https://profiler.firefox.com/docs/#/
44
+ * https://github.com/firefox-devtools/profiler/tree/main/docs-developer
45
+ * https://github.com/tmm1/stackprof
46
+ * https://github.com/ruby/ruby/pull/5500
47
+
34
48
  ## License
35
49
 
36
50
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -9,6 +9,10 @@ Rake::TestTask.new(:test) do |t|
9
9
  t.test_files = FileList["test/**/test_*.rb"]
10
10
  end
11
11
 
12
+ task :console => :compile do
13
+ sh "irb -r vernier"
14
+ end
15
+
12
16
  require "rake/extensiontask"
13
17
 
14
18
  task build: :compile
@@ -0,0 +1,38 @@
1
+ require "vernier"
2
+
3
+ Vernier.trace(out: "http_requests.json") do
4
+
5
+ require "net/http"
6
+ require "uri"
7
+ require "openssl"
8
+
9
+ urls = Queue.new
10
+ received = Queue.new
11
+
12
+ threads = 2.times.map do
13
+ Thread.new do
14
+ while url = urls.pop
15
+ uri = URI.parse(url)
16
+ response = Net::HTTP.get_response(uri)
17
+
18
+ received << [url, response.code]
19
+ end
20
+ end
21
+ end
22
+
23
+ Thread.new do
24
+ threads.each(&:join)
25
+ received.close
26
+ end
27
+
28
+ urls << "http://example.com"
29
+ urls << "https://www.johnhawthorn.com"
30
+ urls << "https://tenderlovemaking.com/"
31
+ urls.close
32
+
33
+ while x = received.pop
34
+ url, code = *x
35
+ puts "#{url} #{code}"
36
+ end
37
+
38
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "mkmf"
4
4
 
5
- $CXXFLAGS += " -std=c++14 "
5
+ $CXXFLAGS += " -std=c++17 "
6
6
  $CXXFLAGS += " -ggdb3 -Og "
7
7
 
8
8
  create_makefile("vernier/vernier")
@@ -0,0 +1,44 @@
1
+ #pragma once
2
+
3
+ inline const char *
4
+ ruby_object_type_name(VALUE obj) {
5
+ enum ruby_value_type type = rb_type(obj);
6
+
7
+ #define TYPE_CASE(x) case (x): return (#x)
8
+
9
+ // Many of these are impossible, but it's easier to just include them
10
+ switch (type) {
11
+ TYPE_CASE(T_OBJECT);
12
+ TYPE_CASE(T_CLASS);
13
+ TYPE_CASE(T_MODULE);
14
+ TYPE_CASE(T_FLOAT);
15
+ TYPE_CASE(T_STRING);
16
+ TYPE_CASE(T_REGEXP);
17
+ TYPE_CASE(T_ARRAY);
18
+ TYPE_CASE(T_HASH);
19
+ TYPE_CASE(T_STRUCT);
20
+ TYPE_CASE(T_BIGNUM);
21
+ TYPE_CASE(T_FILE);
22
+ TYPE_CASE(T_DATA);
23
+ TYPE_CASE(T_MATCH);
24
+ TYPE_CASE(T_COMPLEX);
25
+ TYPE_CASE(T_RATIONAL);
26
+
27
+ TYPE_CASE(T_NIL);
28
+ TYPE_CASE(T_TRUE);
29
+ TYPE_CASE(T_FALSE);
30
+ TYPE_CASE(T_SYMBOL);
31
+ TYPE_CASE(T_FIXNUM);
32
+ TYPE_CASE(T_UNDEF);
33
+
34
+ TYPE_CASE(T_IMEMO);
35
+ TYPE_CASE(T_NODE);
36
+ TYPE_CASE(T_ICLASS);
37
+ TYPE_CASE(T_ZOMBIE);
38
+ TYPE_CASE(T_MOVED);
39
+
40
+ default:
41
+ return "unknown type";
42
+ }
43
+ #undef TYPE_CASE
44
+ }
data/ext/vernier/stack.hh CHANGED
@@ -7,33 +7,87 @@
7
7
  #include <memory>
8
8
  #include <algorithm>
9
9
 
10
- struct Frame {
11
- VALUE frame;
12
- int line;
10
+ struct FrameInfo {
11
+ static const char *label_cstr(VALUE frame) {
12
+ VALUE label = rb_profile_frame_full_label(frame);
13
+ return StringValueCStr(label);
14
+ }
13
15
 
14
- VALUE full_label() const {
15
- return rb_profile_frame_full_label(frame);
16
+ static const char *file_cstr(VALUE frame) {
17
+ VALUE file = rb_profile_frame_absolute_path(frame);
18
+ if (NIL_P(file))
19
+ file = rb_profile_frame_path(frame);
20
+ if (NIL_P(file)) {
21
+ return "";
22
+ } else {
23
+ return StringValueCStr(file);
24
+ }
16
25
  }
17
26
 
18
- VALUE absolute_path() const {
19
- return rb_profile_frame_absolute_path(frame);
27
+ static int first_lineno_int(VALUE frame) {
28
+ VALUE first_lineno = rb_profile_frame_first_lineno(frame);
29
+ return NIL_P(first_lineno) ? 0 : FIX2INT(first_lineno);
20
30
  }
21
31
 
22
- VALUE path() const {
23
- return rb_profile_frame_path(frame);
32
+ FrameInfo(VALUE frame) :
33
+ label(label_cstr(frame)),
34
+ file(file_cstr(frame)),
35
+ first_lineno(first_lineno_int(frame)) { }
36
+
37
+ std::string label;
38
+ std::string file;
39
+ int first_lineno;
40
+ };
41
+
42
+ bool operator==(const FrameInfo& lhs, const FrameInfo& rhs) noexcept {
43
+ return
44
+ lhs.label == rhs.label &&
45
+ lhs.file == rhs.file &&
46
+ lhs.first_lineno == rhs.first_lineno;
47
+ }
48
+
49
+ template<>
50
+ struct std::hash<FrameInfo>
51
+ {
52
+ std::size_t operator()(FrameInfo const& f) const noexcept
53
+ {
54
+ return
55
+ std::hash<std::string>{}(f.label) ^
56
+ std::hash<std::string>{}(f.file) ^
57
+ f.first_lineno;
24
58
  }
59
+ };
25
60
 
26
- VALUE file() const {
27
- VALUE file = absolute_path();
28
- return NIL_P(file) ? path() : file;
61
+
62
+ struct Frame {
63
+ VALUE frame;
64
+ int line;
65
+
66
+ FrameInfo info() const {
67
+ return FrameInfo(frame);
29
68
  }
69
+ };
30
70
 
31
- VALUE first_lineno() const {
32
- return rb_profile_frame_first_lineno(frame);
71
+ bool operator==(const Frame& lhs, const Frame& rhs) noexcept {
72
+ return lhs.frame == rhs.frame && lhs.line == rhs.line;
73
+ }
74
+
75
+ template<>
76
+ struct std::hash<Frame>
77
+ {
78
+ std::size_t operator()(Frame const& s) const noexcept
79
+ {
80
+ return s.frame ^ s.line;
33
81
  }
34
82
  };
35
83
 
36
- struct Stack {
84
+ struct BaseStack {
85
+ virtual ~BaseStack() {};
86
+
87
+ virtual int size() const = 0;
88
+ };
89
+
90
+ struct Stack : public BaseStack {
37
91
  std::unique_ptr<VALUE[]> frames;
38
92
  std::unique_ptr<int[]> lines;
39
93
  int _size = 0;
@@ -51,28 +105,51 @@ struct Stack {
51
105
  std::copy_n(_lines, size, &lines[0]);
52
106
  }
53
107
 
108
+ Stack(const Stack &s) :
109
+ _size(s.size()),
110
+ frames(std::make_unique<VALUE[]>(s.size())),
111
+ lines(std::make_unique<int[]>(s.size()))
112
+ {
113
+ std::copy_n(&s.frames[0], s.size(), &frames[0]);
114
+ std::copy_n(&s.lines[0], s.size(), &lines[0]);
115
+ }
116
+
54
117
  Frame frame(int i) const {
55
118
  if (i >= size()) throw std::out_of_range("nope");
56
119
  return Frame{frames[i], lines[i]};
57
120
  }
58
121
  };
59
122
 
60
- std::ostream& operator<<(std::ostream& os, const Frame& frame)
61
- {
62
- VALUE label = frame.full_label();
63
- VALUE file = frame.absolute_path();
64
- const char *file_cstr = NIL_P(file) ? "" : StringValueCStr(file);
65
- os << file_cstr << ":" << frame.line << ":in `" << StringValueCStr(label) << "'";
66
- return os;
123
+ bool operator==(const Stack& lhs, const Stack& rhs) noexcept {
124
+ return lhs.size() == rhs.size() &&
125
+ std::equal(&lhs.frames[0], &lhs.frames[lhs.size()], &rhs.frames[0]) &&
126
+ std::equal(&lhs.lines[0], &lhs.lines[lhs.size()], &rhs.lines[0]);
67
127
  }
68
128
 
69
- std::ostream& operator<<(std::ostream& os, const Stack& stack)
129
+ // https://xoshiro.di.unimi.it/splitmix64.c
130
+ // https://nullprogram.com/blog/2018/07/31/
131
+ uint64_t
132
+ hash64(uint64_t x)
70
133
  {
71
- for (int i = 0; i < stack.size(); i++) {
72
- Frame frame = stack.frame(i);
73
- os << frame << "\n";
74
- }
75
-
76
- return os;
134
+ x ^= x >> 16;
135
+ x *= 0x7feb352dU;
136
+ x ^= x >> 15;
137
+ x *= 0x846ca68bU;
138
+ x ^= x >> 16;
139
+ return x;
77
140
  }
78
141
 
142
+ template<>
143
+ struct std::hash<Stack>
144
+ {
145
+ std::size_t operator()(Stack const& s) const noexcept
146
+ {
147
+ size_t hash = 0;
148
+ for (int i = 0; i < s.size(); i++) {
149
+ VALUE frame = s.frames[i];
150
+ hash ^= frame;
151
+ hash = hash64(hash);
152
+ }
153
+ return hash;
154
+ }
155
+ };