rperf 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/docs/help.md +20 -0
- data/exe/rperf +28 -2
- data/lib/rperf/meta.rb +8 -1
- data/lib/rperf/table.rb +7 -2
- data/lib/rperf/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3240adc0c0b301a502b663cd9b044e02661c64e072e5a6582ac30d273b349d54
|
|
4
|
+
data.tar.gz: a49b1fab25b96e80f17f942acba4a07aef745a61e565587f791f99c878a0d84e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e5c770faca5dfe03cb87eb83d3021f12b0bbf23433eac923656910fe7275ebe20c08aec7046ab82975ccf2b7b1574bb062493712bee6e2adb5d772e9f60beb6
|
|
7
|
+
data.tar.gz: 776f023349f55073383d78c81736a578cb31f8d1973c49a57b3eef596a132edb1a03e89734fad69f53395c91578a431c27e4952fd6c272b3a069b04dc577c0e7
|
data/README.md
CHANGED
|
@@ -82,6 +82,12 @@ rperf report ./profiles/ # sidebar: per-commit list,
|
|
|
82
82
|
|
|
83
83
|
# Flat tables for AI analysis (no Go required)
|
|
84
84
|
rperf diff --format table base.json.gz head.json.gz | claude -p "analyze the regression"
|
|
85
|
+
|
|
86
|
+
# Drive rperf without wrapping the command (CI/tooling): source the env, then
|
|
87
|
+
# run your command unchanged — `bundle exec …` stays bundler-managed, plain
|
|
88
|
+
# `ruby …` stays plain. (Run `rperf help` for details.)
|
|
89
|
+
eval "$(rperf record --snapshot-dir ./profiles --print-env)"
|
|
90
|
+
export RPERF_ROOT_PROCESS=$$ && exec ruby app.rb
|
|
85
91
|
```
|
|
86
92
|
|
|
87
93
|
On `rperf report`, you can see the profile result like this page: [rperf viewer](https://ko1.github.io/rperf/examples/cpu_intensive_profile.html)
|
data/docs/help.md
CHANGED
|
@@ -31,8 +31,28 @@ POSIX systems (Linux, macOS). Requires Ruby >= 3.4.0.
|
|
|
31
31
|
--label KEY=VALUE Add a label to profile metadata (repeatable)
|
|
32
32
|
--no-inherit Do not profile forked/spawned child processes
|
|
33
33
|
--no-aggregate Disable C-level sample aggregation (raw per-sample data)
|
|
34
|
+
--print-env Print the env rperf would set (export KEY=VALUE lines)
|
|
35
|
+
and exit WITHOUT running a command — see below
|
|
34
36
|
-v, --verbose Print sampling statistics to stderr
|
|
35
37
|
|
|
38
|
+
#### --print-env: drive rperf without wrapping the command
|
|
39
|
+
|
|
40
|
+
Instead of `rperf record [options] -- command`, `--print-env` prints the
|
|
41
|
+
environment a profiled process auto-starts from and exits without running
|
|
42
|
+
anything. A wrapper (e.g. a CI script) sources it, makes the process that runs
|
|
43
|
+
the command the session root, and runs the command unchanged — so a
|
|
44
|
+
`bundle exec …` command stays bundler-managed and a plain `ruby …` stays plain;
|
|
45
|
+
rperf is never inserted into the command line.
|
|
46
|
+
|
|
47
|
+
eval "$(rperf record --snapshot-dir ./profiles --print-env)"
|
|
48
|
+
export RPERF_ROOT_PROCESS=$$ # the pid that will exec the command is the root
|
|
49
|
+
exec ruby app.rb # runs verbatim; rperf auto-starts via RUBYOPT
|
|
50
|
+
|
|
51
|
+
`RPERF_ROOT_PROCESS` is not emitted (the caller sets it to the pid that execs
|
|
52
|
+
the command); `RPERF_META_LABELS` is not emitted (set it yourself if you want
|
|
53
|
+
per-run labels). Everything else (`RUBYOPT=-rrperf`, output path, mode,
|
|
54
|
+
frequency, fork session dir, …) is included.
|
|
55
|
+
|
|
36
56
|
JSON output embeds `meta` (git commit, host, Ruby/rperf versions, labels)
|
|
37
57
|
and `summary` (time, GC, allocation, top methods) — see "Profile metadata"
|
|
38
58
|
under OUTPUT FORMATS.
|
data/exe/rperf
CHANGED
|
@@ -368,6 +368,7 @@ stat_report = (subcommand == "exec")
|
|
|
368
368
|
inherit = true
|
|
369
369
|
meta_labels = {}
|
|
370
370
|
snapshot_dir = nil
|
|
371
|
+
print_env = false
|
|
371
372
|
|
|
372
373
|
parser = OptionParser.new do |opts|
|
|
373
374
|
opts.banner = case subcommand
|
|
@@ -441,6 +442,13 @@ parser = OptionParser.new do |opts|
|
|
|
441
442
|
inherit = false
|
|
442
443
|
end
|
|
443
444
|
|
|
445
|
+
opts.on("--print-env",
|
|
446
|
+
"Print the environment rperf would set (one `export KEY=VALUE` per line), then exit " \
|
|
447
|
+
"WITHOUT running a command. The caller sources it, sets RPERF_ROOT_PROCESS to the pid " \
|
|
448
|
+
"that will exec the command, and runs the command itself — so rperf never wraps it.") do
|
|
449
|
+
print_env = true
|
|
450
|
+
end
|
|
451
|
+
|
|
444
452
|
opts.on("-v", "--verbose", "Print sampling statistics to stderr") do
|
|
445
453
|
verbose = true
|
|
446
454
|
end
|
|
@@ -461,7 +469,7 @@ rescue OptionParser::ParseError => e
|
|
|
461
469
|
exit 1
|
|
462
470
|
end
|
|
463
471
|
|
|
464
|
-
if ARGV.empty?
|
|
472
|
+
if ARGV.empty? && !print_env
|
|
465
473
|
$stderr.puts "No command specified."
|
|
466
474
|
$stderr.puts parser
|
|
467
475
|
exit 1
|
|
@@ -557,9 +565,27 @@ session_dir = nil
|
|
|
557
565
|
if inherit
|
|
558
566
|
session_dir = Rperf.send(:_create_session_dir, clean_stale: true)
|
|
559
567
|
if session_dir
|
|
560
|
-
ENV["RPERF_ROOT_PROCESS"] = Process.pid.to_s
|
|
561
568
|
ENV["RPERF_SESSION_DIR"] = session_dir
|
|
569
|
+
# In --print-env mode this short-lived process does not run the command, so
|
|
570
|
+
# it must not claim rootship: the caller sets RPERF_ROOT_PROCESS to the pid
|
|
571
|
+
# that will exec the command. Otherwise (normal exec) we are the root.
|
|
572
|
+
ENV["RPERF_ROOT_PROCESS"] = Process.pid.to_s unless print_env
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
# --print-env: emit the environment a profiled process inherits to auto-start
|
|
577
|
+
# rperf, then exit without running anything. RPERF_ROOT_PROCESS and
|
|
578
|
+
# RPERF_META_LABELS are deliberately omitted — the caller owns those (it sets
|
|
579
|
+
# the root pid, and labels per measurement run).
|
|
580
|
+
if print_env
|
|
581
|
+
require "shellwords"
|
|
582
|
+
%w[RUBYLIB RUBYOPT RPERF_ENABLED RPERF_OUTPUT RPERF_FREQUENCY RPERF_MODE
|
|
583
|
+
RPERF_FORMAT RPERF_VERBOSE RPERF_SIGNAL RPERF_AGGREGATE RPERF_META_GIT
|
|
584
|
+
RPERF_SESSION_DIR RPERF_STAT RPERF_STAT_COMMAND RPERF_STAT_REPORT].each do |k|
|
|
585
|
+
v = ENV[k]
|
|
586
|
+
puts "export #{k}=#{Shellwords.escape(v)}" unless v.nil?
|
|
562
587
|
end
|
|
588
|
+
exit 0
|
|
563
589
|
end
|
|
564
590
|
|
|
565
591
|
begin
|
data/lib/rperf/meta.rb
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
# first two top-level keys, so Meta.read can decompress only the head of the
|
|
7
7
|
# file and stop as soon as both are extracted.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# NOTE: json is NOT required here. rperf loads via `ruby -rrperf` at interpreter
|
|
10
|
+
# boot, before the profiled app runs `bundler/setup`. Requiring json eagerly
|
|
11
|
+
# would activate the default json gem and then clash with a bundle that pins a
|
|
12
|
+
# different json (Gem::LoadError). json is used only at profile write/read time
|
|
13
|
+
# (after the app has set up its bundle), so each user requires it lazily.
|
|
10
14
|
require "time"
|
|
11
15
|
require "zlib"
|
|
12
16
|
|
|
@@ -103,6 +107,7 @@ module Rperf
|
|
|
103
107
|
v = ENV["RPERF_META_GIT"].to_s
|
|
104
108
|
return nil if v.empty? || v == "null"
|
|
105
109
|
begin
|
|
110
|
+
require "json"
|
|
106
111
|
JSON.parse(v, symbolize_names: true)
|
|
107
112
|
rescue JSON::ParserError
|
|
108
113
|
nil
|
|
@@ -119,6 +124,7 @@ module Rperf
|
|
|
119
124
|
v = ENV["RPERF_META_LABELS"]
|
|
120
125
|
return nil unless v
|
|
121
126
|
begin
|
|
127
|
+
require "json"
|
|
122
128
|
labels = JSON.parse(v)
|
|
123
129
|
labels.is_a?(Hash) ? labels : nil
|
|
124
130
|
rescue JSON::ParserError
|
|
@@ -224,6 +230,7 @@ module Rperf
|
|
|
224
230
|
# Returns { meta:, summary: }, nil (old format / malformed),
|
|
225
231
|
# or :incomplete (need more input).
|
|
226
232
|
def scan_prefix(buf)
|
|
233
|
+
require "json" # lazy: see the note at the top of this file
|
|
227
234
|
n = buf.bytesize
|
|
228
235
|
i = skip_ws(buf, 0, n)
|
|
229
236
|
return :incomplete if i >= n
|
data/lib/rperf/table.rb
CHANGED
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
# TSV: header row, data rows, then a trailing "# summary" line of
|
|
6
6
|
# tab-separated key=value pairs.
|
|
7
7
|
# JSON: an array of row objects; the last element is { "summary": { ... } }.
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
#
|
|
9
|
+
# json is required lazily in the format methods, not here: rperf loads at
|
|
10
|
+
# interpreter boot via `-rrperf`, before the profiled app's bundler/setup, and
|
|
11
|
+
# eagerly activating the default json gem would clash with a bundle that pins a
|
|
12
|
+
# different json (see the note in meta.rb).
|
|
10
13
|
|
|
11
14
|
module Rperf
|
|
12
15
|
module Table
|
|
@@ -67,6 +70,7 @@ module Rperf
|
|
|
67
70
|
end
|
|
68
71
|
|
|
69
72
|
def report_json(data)
|
|
73
|
+
require "json"
|
|
70
74
|
JSON.generate(report_rows(data) + [{ summary: report_summary(data) }])
|
|
71
75
|
end
|
|
72
76
|
|
|
@@ -117,6 +121,7 @@ module Rperf
|
|
|
117
121
|
end
|
|
118
122
|
|
|
119
123
|
def diff_json(base, head)
|
|
124
|
+
require "json"
|
|
120
125
|
JSON.generate(diff_rows(base, head) + [{ summary: diff_summary(base, head) }])
|
|
121
126
|
end
|
|
122
127
|
|
data/lib/rperf/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rperf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Koichi Sasada
|
|
@@ -80,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '0'
|
|
82
82
|
requirements: []
|
|
83
|
-
rubygems_version: 4.0.
|
|
83
|
+
rubygems_version: 4.0.10
|
|
84
84
|
specification_version: 4
|
|
85
85
|
summary: Safepoint-based sampling performance profiler for Ruby
|
|
86
86
|
test_files: []
|