{"id":18656547,"url":"https://github.com/zendesk/ruby_memprofiler_pprof","last_synced_at":"2025-04-10T18:23:42.931Z","repository":{"id":37284626,"uuid":"443286954","full_name":"zendesk/ruby_memprofiler_pprof","owner":"zendesk","description":"Experimental memory profiler for Ruby that emits pprof files.","archived":false,"fork":false,"pushed_at":"2024-05-16T20:03:56.000Z","size":2817,"stargazers_count":37,"open_issues_count":8,"forks_count":5,"subscribers_count":66,"default_branch":"main","last_synced_at":"2025-03-24T16:04:27.191Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zendesk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-31T07:40:45.000Z","updated_at":"2024-10-25T21:15:03.000Z","dependencies_parsed_at":"2023-01-21T10:32:03.246Z","dependency_job_id":null,"html_url":"https://github.com/zendesk/ruby_memprofiler_pprof","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fruby_memprofiler_pprof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fruby_memprofiler_pprof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fruby_memprofiler_pprof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zendesk%2Fruby_memprofiler_pprof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zendesk","download_url":"https://codeload.github.com/zendesk/ruby_memprofiler_pprof/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248270615,"owners_count":21075795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-11-07T07:23:56.249Z","updated_at":"2025-04-10T18:23:42.912Z","avatar_url":"https://github.com/zendesk.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ruby_memprofiler_pprof: A heap profiler for Ruby applications.\n\n**⚠️⚠️🚧🚧 WARNING: THIS IS PRE-ALPHA SOFTWARE. IF YOU USE THIS IN PRODUCTION, YOU WILL BE THE FIRST ONE. ⚠️⚠️🚧🚧**\n\nRuby_memprofiler_pprof (RMP for short, in this README!) is a tool designed to help understand memory usage in large Ruby applications. It's intended to help answer questions like \"why is my app's memory usage so high?\", or \"why is my app leaking memory?\"; it's also designed to be usable in production environments, to help solve the dreaded \"...but only in production??\" part of those questions too.\n\nRMP is a gem that's intended to run continuously inside a running Ruby app from the very beginning. It periodically produces profile data in the [pprof](https://github.com/google/pprof/blob/master/doc/README.md) format. It's capable of gathering a few different kinds of information, but at its heart the profiles RMP produces contain one key piece of information:\n\n\u003e What codepaths (stack traces) allocated objects, that are still live as of when the profile was taken? (`retained_objects`/`retained_size`)\n\nBecause RMP is designed for production use, it also supports setting a sample rate; when doing this, the profiles contain some random subset of allocations \u0026 retained objects, rather than the whole picture themselves. However, the pprof data can also be combined; so, over an entire production environment, you should still be able to get a solid picture of what, on average, is using memory.\n\n## Quick start\n\nAdd the `ruby_memprofiler_pprof` gem to your Gemfile. Note that the gem is pre-alpha and has no stable interface yet, so you should pin to the exact version you want.\n\n```ruby\ngem 'ruby_memprofiler_pprof', '=0.0.4'\n```\n\nYou can profile an application with `ruby_memprofiler_pprof` in two ways; either by starting it via the `ruby_memprofiler_pprof_profile` wrapper, or by integrating `ruby_memprofiler_pprof` directly into your code.\n\n### Using the wrapper\n\n```\nbundle exec ruby_memprofiler_pprof_profile YOUR_APP ...\n```\n\nWith the default options, this will start your application, and periodically (every 30s) write pprof files to the `tmp/profiles` directory.\n\nThe `ruby_memprofiler_pprof_profile` wrapper application works by simply adding `-rruby_memprofiler_pprof/profile_app` to the `RUBYOPT` environment variable, so that when your application starts, it will automatically require the profiling machinery.\n\nWhen using the profiler in this way, some parts of its behaviour can be configured by environment variables:\n\n* `RUBY_MEMPROFILER_PPROF_SAMPLE_RATE`: The fraction (from 0 to 1) of allocated objects that should be sampled. Has the same effect as `MemprofilerPprof::Collector#sample_rate`. Defaults to 1.\n* `RUBY_MEMPROFILER_PPROF_ALLOC_RETAIN_RATE`: The fraction (from 0 to 1) of sampled allocations that should be profiled. Normally, when RMP samples an allocation, it will produce an entry in the `allocations` profile information recording where and when this object was allocated. If the object is still alive when the sample data is produced, it will also appear on the `retained_objects` section of the profile. Ruby programs usually have very many short-lived allocations, so the `allocations` section can turn out to be enormous; they're also often less interesting than analysing long-lived objects. So, the `RUBY_MEMPROFILER_PPROF_ALLOC_RETAIN_RATE` setting specifies a fraction of these allocation events to keep; setting this to zero would mean that _only_ information about retained objects is kept. Has the same effect as `MemprofilerPprof::Collector#allocation_retain_rate`. Defaults to 1.\n* `RUBY_MEMPROFILER_PPROF_MAX_ALLOC_SAMPLES`: The maximum number of allocation samples to keep in RMP's internal buffers; if more samples than this are collected before being periodically flushed to files, they will be dropped. Has the same effect as `MemprofilerPprof::Collector#max_allocation_samples`. Defaults to 10000.\n* `RUBY_MEMPROFILER_PPROF_MAX_HEAP_SAMPLES`: The maximum number of live objects to keep track of in RMP's internal buffers; if more object allocations than this are traced, they will be dropped. Has the same effect as `MemprofilerPprof::Collector#max_heap_samples`. Defaults to 50000.\n* `RUBY_MEMPROFILER_PPROF_FILE_PATTERN`: The path and pattern template to use for the written-out pprof files. See the documentation for `MemprofilerPprof::FileFlusher#pattern` for details of the interpolation options available here. Defaults to `tmp/profiles/mem-%{pid}-%{isotime}.pprof`.\n\n### Integrating into your code\n\nIntegrating `ruby_memprofiler_pprof` directly into your code gives you more control over flushing the generated profiles out of RMP's internal buffers. First, you need to construct a `MemprofilerPprof::Collector` object, and call `#start!` on it, as early as possible in your program, to begin tracking objects:\n\n```ruby\nrequire 'ruby_memprofiler_pprof'\n$rmp_collector = MemprofilerPprof::Collector.new\n\n# Configure the $rmp_collector object here, e.g.\n$rmp_collector.sample_rate = 0.1\n\n$rmp_collector.start!\n```\n\nThen, you will need to organise to periodically call `#flush` on this collector. Calling `#flush` clears out the internal buffers of the collector, and returns a pprof-encoded binary string containing the profile data. You might want to write this to disk, send it to cloud storage, or any number of other things. `MemprofilerPprof::BlockFlusher` contains a useful primitive for periodically calling `#flush` in a background thread and passing the profile data to a block you specify; for example:\n\n```ruby\n$rmp_flusher = MemprofilerPprof::BlockFlusher.new(\n  $rmp_collector,\n  interval: 15, # seconds\n  on_flush: -\u003e(pprof_data) {\n    write_profile_data_to_s3_somehow(pprof_data)\n  },\n)\n```\n\nHowever, you're free to organise the calls to `#flush` however makes sense for your application.\n\n### Visualising the output\n\nIt's part of this project's aim to build some tooling to easily aggregate profiles across different processes and guide app developers towards which things are having the biggest impact on memory usage. In particular, what kind of objects (and where were they allocated) increase over time, indicating a potential cause for a memory leak. However, right now, these tools don't exist yet.\n\nHowever, since RMP produces standard format pprof profiles, they can be viewed and analysed with any other pprof program. For example, you can get a flamegraph of allocation stacktraces for allocated or retained objects using the [Golang pprof viewer](https://pkg.go.dev/cmd/pprof), `go tool pprof`:\n\n```\ngo tool pprof -http :8987 \u003cpath-to-output.pprof\u003e\n```\n\nThis will open a web UI to analyise the profile. Perhaps the most useful view is the Flame graph view (\"View \u003e Flame graph\"), and you can select to view either allocated or live objects (Sample \u003e \"allocation_size\" or Sample \u003e \"retained_size\"). The following is an example for a profile collected after booting a large Rails app.\n\n![A big Rails profile](doc/images/go_flamegraph_example.png?raw=true \"A big Rails profile\")\n\nThere's a huge amount to wade through here! The intention of this project is to provide some more purpose-built tools for analyisng memory use for this kind of large app from these profiles.\n\n## How it works internally\n\nSee [IMPLEMENTATION.md](IMPLEMENTATION.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzendesk%2Fruby_memprofiler_pprof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzendesk%2Fruby_memprofiler_pprof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzendesk%2Fruby_memprofiler_pprof/lists"}