Skip to content

Commit

Permalink
Fixes block form of #start
Browse files Browse the repository at this point in the history
* Because the ensure calls `_stop` instead of `stop`, the block form produces invalid json

* Perfetto is pretty permissive about this and still accepts the file - but other tools break and JSON parsing is broken

* Also adds first specs for the project, covering a few different scenarios 🎉
  • Loading branch information
jpcamara committed Dec 3, 2023
1 parent 49d2665 commit 64a9ac8
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ gems.locked
*.bundle
example*json
thread_name_with_extension.json
.DS_Store
.DS_Store
.rspec_status
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby-3.2.0
ruby-3.2.2
4 changes: 3 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ This way you can actually link from your dashboards and similar pages directly t

== Development

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to https://rubygems.org[rubygems.org].
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to https://rubygems.org[rubygems.org]. To run specs, run `bundle exec rake spec`.

To run all actions (build the extension, check linting, and run specs), run `bundle exec rake`.

== Contributing

Expand Down
4 changes: 3 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
require "bundler/gem_tasks"
require "standard/rake"
require "rake/extensiontask"
require "rspec/core/rake_task"

Rake::ExtensionTask.new("gvl_tracing_native_extension")
RSpec::Core::RakeTask.new(:spec)

task default: [:compile, :"standard:fix"]
task default: [:compile, :"standard:fix", :spec]
2 changes: 2 additions & 0 deletions gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@
gem "rake", "~> 13.0"
gem "rake-compiler", "~> 1.2"
gem "pry"
gem "pry-byebug"
gem "rspec"
gem "standard", "~> 1.12"
gem "concurrent-ruby"
2 changes: 1 addition & 1 deletion lib/gvl-tracing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def start(file)
begin
yield
ensure
_stop
stop
end
end

Expand Down
69 changes: 69 additions & 0 deletions spec/gvl_tracing_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

require "spec_helper"
require "json"
require "net/http"
require "perfetto_trace"

RSpec.describe GvlTracing do
let(:trace_path) { "tmp/gvl.json" }

it "has a version number" do
expect(GvlTracing::VERSION).not_to be nil
end

describe "#start" do
it "fails if already started" do
expect {
GvlTracing.start(trace_path) { GvlTracing.start(trace_path) }
}.to raise_error(/Already started/)
end

describe "with a block" do
before do
GvlTracing.start(trace_path) {}
end

it "stops tracing after the block" do
expect(File.exist?(trace_path)).to be_truthy
end

it "creates valid json" do
expect { JSON.parse(File.read(trace_path)) }.to_not raise_error
end
end
end

describe "#stop" do
it "fails if not started" do
expect { GvlTracing.stop }.to raise_error(/Tracing not running/)
end

it "closes out the json" do
GvlTracing.start(trace_path)
expect { JSON.parse(File.read(trace_path)) }.to raise_error(JSON::ParserError)

GvlTracing.stop
expect(JSON.parse(File.read(trace_path))).to be_an(Array)
end
end

describe "order of events" do
it "writes events in valid order" do
GvlTracing.start(trace_path) do
Thread.new {}.join
Thread.new {}.join
end

trace = PerfettoTrace.new(trace_path)
threads = trace.non_main_threads

threads.each do |thread|
events = trace.events_by_thread[thread.tid]
names = events.map(&:name).reject { |n| !n }

expect(names).to match_array(%w[thread_name running wants_gvl waiting died])
end
end
end
end
51 changes: 51 additions & 0 deletions spec/perfetto_trace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

class PerfettoTrace
def initialize(file_path)
@trace = JSON.parse(File.read(file_path))
end

def threads
@trace
.select { |j| j["ph"] == "M" && j["name"] == "thread_name" }
.uniq { |j| j["args"]["tid"] }
.map { |j| Row.new(j) }
end

def non_main_threads
threads.select { |t| t.thread_name != "Main Thread" }
end

def events_by_thread
@trace
.group_by { |j| j["tid"] }
.map { |tid, events| [tid, events.map { |j| Row.new(j) }] }
.to_h
end

class Row
def initialize(row)
@row = row
end

def meta? = ph == "M"

def phase_begin? = ph == "B"

def phase_end? = ph == "E"

def phase = @row["ph"]

def pid = @row["pid"]

def tid = @row["tid"]

def ts = @row["ts"]

def name = @row["name"]

def args = @row["args"]

def thread_name = @row.dig("args", "name")
end
end
16 changes: 16 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require "bundler/setup"
require "gvl-tracing"
require "pry"
require "pry-byebug"

RSpec.configure do |config|
config.example_status_persistence_file_path = ".rspec_status"

config.disable_monkey_patching!

config.expect_with :rspec do |c|
c.syntax = :expect
end
end

0 comments on commit 64a9ac8

Please sign in to comment.