-
-
Notifications
You must be signed in to change notification settings - Fork 495
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Experimental support for multi-threaded profiling using Vernier (#2372)
* Add support for Vernier profiler - Introduce `Sentry::Vernier::Profiler` - Introduce `Sentry.config.profiler_class` that can be set to a class that should be used for profiling. By default it's set to `Sentry::Profiler`
- Loading branch information
Showing
17 changed files
with
1,472 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# frozen_string_literal: true | ||
|
||
require "securerandom" | ||
|
||
module Sentry | ||
class Profiler | ||
module Helpers | ||
def in_app?(abs_path) | ||
abs_path.match?(@in_app_pattern) | ||
end | ||
|
||
# copied from stacktrace.rb since I don't want to touch existing code | ||
# TODO-neel-profiler try to fetch this from stackprof once we patch | ||
# the native extension | ||
def compute_filename(abs_path, in_app) | ||
return nil if abs_path.nil? | ||
|
||
under_project_root = @project_root && abs_path.start_with?(@project_root) | ||
|
||
prefix = | ||
if under_project_root && in_app | ||
@project_root | ||
else | ||
longest_load_path = $LOAD_PATH.select { |path| abs_path.start_with?(path.to_s) }.max_by(&:size) | ||
|
||
if under_project_root | ||
longest_load_path || @project_root | ||
else | ||
longest_load_path | ||
end | ||
end | ||
|
||
prefix ? abs_path[prefix.to_s.chomp(File::SEPARATOR).length + 1..-1] : abs_path | ||
end | ||
|
||
def split_module(name) | ||
# last module plus class/instance method | ||
i = name.rindex("::") | ||
function = i ? name[(i + 2)..-1] : name | ||
mod = i ? name[0...i] : nil | ||
|
||
[function, mod] | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# frozen_string_literal: true | ||
|
||
require "json" | ||
require "rbconfig" | ||
|
||
module Sentry | ||
module Vernier | ||
class Output | ||
include Profiler::Helpers | ||
|
||
attr_reader :profile | ||
|
||
def initialize(profile, project_root:, in_app_pattern:, app_dirs_pattern:) | ||
@profile = profile | ||
@project_root = project_root | ||
@in_app_pattern = in_app_pattern | ||
@app_dirs_pattern = app_dirs_pattern | ||
end | ||
|
||
def to_h | ||
@to_h ||= { | ||
frames: frames, | ||
stacks: stacks, | ||
samples: samples, | ||
thread_metadata: thread_metadata | ||
} | ||
end | ||
|
||
private | ||
|
||
def thread_metadata | ||
profile.threads.map { |thread_id, thread_info| | ||
[thread_id, { name: thread_info[:name] }] | ||
}.to_h | ||
end | ||
|
||
def samples | ||
profile.threads.flat_map { |thread_id, thread_info| | ||
started_at = thread_info[:started_at] | ||
samples, timestamps = thread_info.values_at(:samples, :timestamps) | ||
|
||
samples.zip(timestamps).map { |stack_id, timestamp| | ||
elapsed_since_start_ns = timestamp - started_at | ||
|
||
next if elapsed_since_start_ns < 0 | ||
|
||
{ | ||
thread_id: thread_id.to_s, | ||
stack_id: stack_id, | ||
elapsed_since_start_ns: elapsed_since_start_ns.to_s | ||
} | ||
}.compact | ||
} | ||
end | ||
|
||
def frames | ||
funcs = stack_table_hash[:frame_table].fetch(:func) | ||
lines = stack_table_hash[:func_table].fetch(:first_line) | ||
|
||
funcs.map do |idx| | ||
function, mod = split_module(stack_table_hash[:func_table][:name][idx]) | ||
|
||
abs_path = stack_table_hash[:func_table][:filename][idx] | ||
in_app = in_app?(abs_path) | ||
filename = compute_filename(abs_path, in_app) | ||
|
||
{ | ||
function: function, | ||
module: mod, | ||
filename: filename, | ||
abs_path: abs_path, | ||
lineno: (lineno = lines[idx]) > 0 ? lineno : nil, | ||
in_app: in_app | ||
}.compact | ||
end | ||
end | ||
|
||
def stacks | ||
profile._stack_table.stack_count.times.map do |stack_id| | ||
profile.stack(stack_id).frames.map(&:idx) | ||
end | ||
end | ||
|
||
def stack_table_hash | ||
@stack_table_hash ||= profile._stack_table.to_h | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.