{"id":23393339,"url":"https://github.com/osyoyu/pf2","last_synced_at":"2026-01-18T14:18:24.377Z","repository":{"id":198080447,"uuid":"680137066","full_name":"osyoyu/pf2","owner":"osyoyu","description":"A sampling-based profiler for Ruby","archived":false,"fork":false,"pushed_at":"2026-01-10T10:12:30.000Z","size":1056,"stargazers_count":64,"open_issues_count":9,"forks_count":3,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-01-11T03:06:28.820Z","etag":null,"topics":["profiler","ruby"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/pf2","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/osyoyu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-08-18T12:34:54.000Z","updated_at":"2026-01-10T10:12:33.000Z","dependencies_parsed_at":"2024-01-07T17:35:40.887Z","dependency_job_id":"89e93928-073d-4815-b314-7a7e83df1793","html_url":"https://github.com/osyoyu/pf2","commit_stats":null,"previous_names":["osyoyu/pf2"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/osyoyu/pf2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osyoyu%2Fpf2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osyoyu%2Fpf2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osyoyu%2Fpf2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osyoyu%2Fpf2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/osyoyu","download_url":"https://codeload.github.com/osyoyu/pf2/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/osyoyu%2Fpf2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28537502,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T13:04:05.990Z","status":"ssl_error","status_checked_at":"2026-01-18T13:01:44.092Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["profiler","ruby"],"created_at":"2024-12-22T05:20:16.584Z","updated_at":"2026-01-18T14:18:24.363Z","avatar_url":"https://github.com/osyoyu.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"Pf2\n===========\n\nA experimental sampling-based profiler for Ruby 3.3+.\n\n- GitHub: https://github.com/osyoyu/pf2\n- Documentation: https://osyoyu.github.io/pf2/\n\n\nNotable Capabilites\n--------\n\n- Can accurately track multiple Ruby Threads' activity\n- Sampling interval can be set based on per-Thread CPU usage\n- Can record native (C-level) stack traces side-by-side with Ruby traces\n\nUsage\n--------\n\n### Installation\n\nYou will need a C compiler to build the native extension.\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'pf2'\n\n# When using the main branch, specify submodules: true\ngem 'pf2', git: 'https://github.com/osyoyu/pf2.git', submodules: true\n```\n\nPf2 can be installed as a standalone CLI tool as well.\n\n```console\ngem install pf2\n```\n\n### Quickstart\n\nRun your Ruby program through `pf2 serve`.\nWait a while until Pf2 collects profiles (or until the target program exits), then open the displayed link for visualization.\n\n```\n$ pf2 serve -- ruby target.rb\n[Pf2] Listening on localhost:51502.\n[Pf2] Open https://profiler.firefox.com/from-url/http%3A%2F%2Flocalhost%3A51502%2Fprofile for visualization.\n\nI'm the target program!\n```\n\n### Profiling\n\nPf2 will collect samples every 10 ms of wall time by default.\n\n```ruby\n# Threads in `threads` will be tracked\nPf2.start(threads: [Thread.current])\n\nyour_code_here\n\n# Stop profiling and save the profile for visualization\nprofile = Pf2.stop\nFile.write(\"my_program.pf2profile\", profile)\n```\n\nAlternatively, you may provide a code block to profile.\n\n```ruby\nprofile = Pf2.profile do\n  your_code_here() # will be profiled\n  Thread.new { threaded_code() } # will also be profiled\nend\n\n# Save the profile for visualization\nFile.write(\"my_program.pf2profile\", profile)\n```\n\n### Reporting / Visualization\n\nProfiles can be visualized using the [Firefox Profiler](https://profiler.firefox.com/).\n\n```console\n$ pf2 report -o report.json my_program.pf2profile\n```\n\nAlternatively, `pf2 annotate` can be used to display hit counts side-by-side with source code.\n\n```console\n$ pf2 annotate my_program.pf2prof\n```\n\n### Configuration\n\nPf2 accepts the following configuration keys:\n\n```rb\nPf2.start(\n  interval_ms: 9,        # Integer: The sampling interval in milliseconds (default: 9)\n  time_mode: :cpu,       # `:cpu` or `:wall`: The sampling timer's mode\n)\n```\n\n\nOverhead\n--------\n\nWhile Pf2 aims to be non-disturbulent as much as possible, a small overhead still is incured.\n\n(TBD)\n\nLimitations\n--------\n\nPf2 cannot properly track program activity in some known cases. I'm working to remove these limtations, so stay tuned.\n\n- Program execution in forked processes\n  - Workarounds available for Puma\n- Program execution in Fibers\n- Program execution when MaNy (`RUBY_MN_THREADS`) is enabled\n\nInternals\n--------\n\n### Sampling\n\nPf2 is a _sampling profiler_. This means that Pf2 collects _samples_ of program execution periodically, instead of tracing every action (e.g. method invocations and returns).\n\nPf2 uses the `rb_profile_thread_frames()` API for sampling. When to do so is controlled by _Schedulers_, described in the following section.\n\n### Scheduling\n\nSchedulers determine when to execute sample collection, based on configuration (time mode and interval).\n\n#### Signal-based scheduling\n\nPf2 schedules sample collection using POSIX timers. It creates a POSIX timer using `timer_create(3)` where available, or otherwise `setitimer(3)`. This leaves the actual time-keeping to the operating system kernel, which is capable of tracking accurate per-thread CPU time usage.\n\nWhen the specified interval has arrived (the timer has _expired_), the OS delivers us a SIGPROF signal.\n\nSignals are directed to Ruby Threads' underlying pthread, effectively \"pausing\" the Thread's activity. This routing is done using `SIGEV_THREAD_ID`, which is a Linux-only feature. Sample collection is done in the signal handler, which is expected to be more _accurate_, capturing the paused Thread's activity.\n\nThis scheduler heavily relies on Ruby's 1:N Thread model (1 Ruby Threads is strongly tied to a native pthread). It will not work properly in MaNy (`RUBY_MN_THREADS=1`).\n\n#### ~~Timer-thread based scheduling~~\n\nNote: Timer thread-based scheduling has been removed in v0.10.0, when the profiling backend has been rewritten in C. This may come back in the future if needed.\n\nAnother scheduler is the `TimerThreadScheduler`, which maintains a time-keeping thread by itself. A new native thread (pthread on Linux/macOS) will be created, and an infinite loop will be run inside. After `sleep(2)`-ing for the specified interval time, sampling will be queued using Ruby's Postponed Job API.\n\nThis scheduler is wall-time only, and does not support CPU-time based profiling.\n\n\nWishlist\n--------\n\n- [Flame Scopes](https://www.brendangregg.com/flamescope.html)\n- more\n\nDevelopment\n--------\n\nSee [doc/development.md](doc/development.md).\n\n\nLicense\n--------\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\nSee [LICENSE.txt](/LICENSE.txt) and [THIRD_PARTY_LICENSES.txt](/THIRD_PARTY_LICENSES.txt) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosyoyu%2Fpf2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fosyoyu%2Fpf2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fosyoyu%2Fpf2/lists"}