{"id":31763999,"url":"https://github.com/dispatchrun/wzprof","last_synced_at":"2026-03-08T13:36:55.334Z","repository":{"id":164087541,"uuid":"632605371","full_name":"dispatchrun/wzprof","owner":"dispatchrun","description":"WebAssembly Profiler based on Wazero","archived":false,"fork":false,"pushed_at":"2025-01-13T22:11:04.000Z","size":1402,"stargazers_count":164,"open_issues_count":10,"forks_count":5,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-09-10T11:34:32.245Z","etag":null,"topics":["cpu","golang","memory","optimization","performance","profiling","wazero","webassembly"],"latest_commit_sha":null,"homepage":"","language":"Go","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/dispatchrun.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-25T18:54:52.000Z","updated_at":"2025-09-06T20:12:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"43fbf243-eb85-43cc-aaef-d4f46869d20a","html_url":"https://github.com/dispatchrun/wzprof","commit_stats":null,"previous_names":["dispatchrun/wzprof","stealthrocket/wzprof"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/dispatchrun/wzprof","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dispatchrun%2Fwzprof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dispatchrun%2Fwzprof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dispatchrun%2Fwzprof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dispatchrun%2Fwzprof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dispatchrun","download_url":"https://codeload.github.com/dispatchrun/wzprof/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dispatchrun%2Fwzprof/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002311,"owners_count":26083340,"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","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["cpu","golang","memory","optimization","performance","profiling","wazero","webassembly"],"created_at":"2025-10-09T23:39:21.570Z","updated_at":"2025-10-09T23:39:30.913Z","avatar_url":"https://github.com/dispatchrun.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build](https://github.com/stealthrocket/wzprof/actions/workflows/build.yml/badge.svg)](https://github.com/stealthrocket/wzprof/actions/workflows/build.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/stealthrocket/wzprof)](https://goreportcard.com/report/github.com/stealthrocket/wzprof)\n[![Go Reference](https://pkg.go.dev/badge/github.com/stealthrocket/wzprof.svg)](https://pkg.go.dev/github.com/stealthrocket/wzprof)\n[![Apache 2 License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)\n\n# wzprof\n\n`wzprof`, pronounced as you think it should, is a pprof based profiler for\nWebAssembly built on top of [**Wazero**](https://github.com/tetratelabs/wazero).\nIt offers the ability to collect CPU and Memory profiles during the execution of\nWebAssembly modules.\n\nIf you are interested in taking a deep-dive into how `wzprof` is built,\nyou might enjoy reading:\n\n👉 [**Performance in the spotlight: WebAssembly profiling for everyone**](https://blog.stealthrocket.tech/performance-in-the-spotlight-webassembly-profiling-for-everyone)\n\nWe have presented this project at international conferences, such as:\n\n- **[Civo Navigate Europe 2024](https://www.youtube.com/watch?v=9fxMbaHSSiI)**\n  \u003e slides: [https://github.com/iamrajiv/civo-navigate-europe-2024](https://github.com/iamrajiv/civo-navigate-europe-2024)\n- **[KubeCon + CloudNativeCon India 2024](https://www.youtube.com/watch?v=LaQZVxOixGY)**\n  \u003e slides: [https://github.com/iamrajiv/kubecon-cloudnativecon-india-2024](https://github.com/iamrajiv/kubecon-cloudnativecon-india-2024)\n\n## Motivation\n\nWebAssembly runtimes typically allow profiling guest code via an external\nprofiler such as `perf`, but in many cases the recording and analysis of\nprofiles remains a difficult task, especially due to features like JIT\ncompilation.\n\n`pprof` is the de-facto standard profiling tool for Go programs, and offers\nsome of the simplest and quickest ways to gather insight into the performance\nof an application.\n\n`wzprof` aims to combine the capabilities and user experience of `pprof`\nwith a [**wazero.Runtime**](https://pkg.go.dev/github.com/tetratelabs/wazero#Runtime),\nenabling the profiling of any application compiled to WebAssembly.\n\n## Features\n\n`wzprof` mimics the approach and workflow popularized by Go pprof, and extends\nit to collect profiles of WebAssembly programs compiled from any programming\nlanguage. The profiles produced are designed to be compatible with pprof,\nallowing developers to use the classic `go tool pprof` workflow to analyize\napplication performance.\n\n- CPU: calls sampling and on-CPU time.\n- Memory: allocations (see below).\n- DWARF support (demangling, source-level profiling).\n- Integrated pprof server.\n- Library and CLI interfaces.\n\n## Usage\n\nYou can either use `wzprof` as a CLI or as a library if you use the Wazero\nruntime libraries.\n\nTo install the latest version of `wzprof`:\n\n```sh\ngo install github.com/stealthrocket/wzprof/cmd/wzprof@latest\n```\n\nTo use the library as code in a Go program:\n\n```sh\ngo get github.com/stealthrocket/wzprof@latest\n```\n\n### Sampling\n\nBy default, wzprof will sample calls with a ratio of 1/19. Sampling is used to\nlimit the overhead of the profilers but the default rate might not be suitable\nin some cases.\nFor example, if your processes are short running and you don't see anything in the\nprofile, you might want to disable the sampling. To do so, use `-sample 1`.\n\n### Run program to completion with CPU or memory profiling\n\nIn those examples we set the sample rate to 1 to capture all samples because the\ntest programs complete quickly.\n\n```sh\nwzprof -sample 1 -memprofile /tmp/profile ./testdata/c/simple.wasm\n```\n\n```sh\nwzprof -sample 1 -cpuprofile /tmp/profile ./testdata/c/crunch_numbers.wasm\n```\n\n```sh\ngo tool pprof -http :4000 /tmp/profile\n```\n\n### Connect to running pprof server\n\nSimilarly to [`net/http/pprof`](https://pkg.go.dev/net/http/pprof), `wzprof`\ncan expose a pprof-compatible http endpoint on behalf of the guest application:\n\n```sh\nwzprof -pprof-addr :8080 ...\n```\n\n```sh\ngo tool pprof -http :3030 'http://localhost:8080/debug/pprof/profile?seconds=5'\n```\n\n```sh\ngo tool pprof -http :3030 'http://localhost:8080/debug/pprof/heap'\n```\n\n## Profilers\n\n⚠️ The `wzprof` Go APIs depend on Wazero's `experimental` package which makes no\nguarantees of backward compatilbity!\n\nThe following code snippet demonstrates how to integrate the profilers to a\nWazero runtime within a Go program:\n\n```go\nsampleRate := 1.0\n\np := wzprof.ProfilingFor(wasmCode)\ncpu := p.CPUProfiler()\nmem := p.MemoryProfiler()\n\nctx := context.WithValue(context.Background(),\n\texperimental.FunctionListenerFactoryKey{},\n\texperimental.MultiFunctionListenerFactory(\n\t\twzprof.Sample(sampleRate, cpu),\n\t\twzprof.Sample(sampleRate, mem),\n    ),\n)\n\nruntime := wazero.NewRuntime(ctx)\ndefer runtime.Close(ctx)\n\ncompiledModule, err := runtime.CompileModule(ctx, wasmCode)\nif err != nil {\n\tlog.Fatal(\"compiling wasm module:\", err)\n}\n\nerr = p.Prepare(compiledModule)\nif err != nil {\n\treturn fmt.Errorf(\"preparing wasm module: %w\", err)\n}\n\n// The CPU profiler collects records of module execution between two time\n// points, the program drives where the profiler is active by calling\n// StartProfile/StopProfile.\ncpu.StartProfile()\n\nmoduleInstance, err := runtime.InstantiateModule(ctx, compiledModule,\n\twazero.NewModuleConfig(),\n)\nif err != nil {\n\tlog.Fatal(\"instantiating wasm module:\", err)\n}\nif err := moduleInstance.Close(ctx); err != nil {\n\tlog.Fatal(\"closing wasm module:\", err)\n}\n\ncpuProfile := cpu.StopProfile(sampleRate, symbols)\nmemProfile := mem.NewProfile(sampleRate, symbols)\n\nif err := wzprof.WriteProfile(\"cpu.pprof\", cpuProfile); err != nil {\n\tlog.Fatal(\"writing CPU profile:\", err)\n}\nif err := wzprof.WriteProfile(\"mem.pprof\", memProfile); err != nil {\n\tlog.Fatal(\"writing memory profile:\", err)\n}\n```\n\nNote that the program must spearate the compilation and instantiation of\nWebAssembly modules in order to use the profilers, because the module must be\ncompiled first in order to build the list of symbols from the DWARF sections.\n\n### Memory\n\nMemory profiling works by tracing specific functions. Supported functions are:\n\n- `malloc`\n- `calloc`\n- `realloc`\n- `free`\n- `runtime.mallocgc`\n- `runtime.alloc`\n\nFeel free to open a pull request to support more memory-allocating functions!\n\n### CPU\n\n`wzprof` has two CPU profilers: CPU samples and CPU time.\n\nThe CPU samples profiler gives a repesentation of the guest execution by counting\nthe number of time it sees a unique stack trace.\n\nThe CPU time profiler measures the actual time spent on-CPU without taking into\naccount the off-CPU time (e.g waiting for I/O). For this profiler, all the\nhost-functions are considered off-CPU.\n\n## Language support\n\nwzprof runs some heuristics to assess what the guest module is running to adapt\nthe way it symbolizes and walks the stack. In all other cases, it defaults to\ninspecting the wasm stack and uses DWARF information if present in the module.\n\n### Golang\n\nIf the guest has been compiled by golang/go 1.21+, wzprof inspects the memory\nto walk the Go stack, which provides full call stacks, instead of the shortened\nversions you would get without this support.\n\nIn addition, wzprof parses pclntab to perform symbolization. This is the same\nmechanism the Go runtime itself uses to display meaningful stack traces when a\npanic occurs.\n\n### Python 3.11\n\nIf the guest is CPython 3.11 and has been compiled with debug symbols (such as\n[timecraft's][timecraft-python]), wzprof walks the Python interpreter call\nstack, not the C stack it would otherwise report. This provides more meaningful\nprofiling information on the script being executed.\n\nAt the moment it does not support merging the C extension calls into the Python\ninterpreter stack.\n\nNote that a current limitation of the implementation is that unloading or\nreloading modules may result in an incorrect profile. If that's a problem for\nyou please file an issue in the github tracker.\n\n[timecraft-python]: https://docs.timecraft.dev/getting-started/prep-application/compiling-python#preparing-python\n\n### DWARF (C, Rust, Zig...)\n\nAs a fallback, if DWARF sections are available, wzprof symbolizes the wasm stack\ntrace using the DWARF symbols stored in custom sections of the module. For this\nto work, you need to make sure your compiler generates those sections. For\nexample, use `-g` when compiling with clang:\n\n```\nclang code.c -o code.wasm -g -target wasm32\n```\n\n\u003e **Warning**\n\u003e When using clang with any optimization level other than `-O0`, it will\n\u003e automatically run `wasm-opt` if that program is in your `PATH`. It makes DWARF\n\u003e information unusable by wzprof. Make sure clang can't find `wasm-opt` during\n\u003e compilation. See [llvm/llvm-project#55781][llvm-bug].\n\n[llvm-bug]: https://github.com/llvm/llvm-project/issues/55781\n\n## Contributing\n\nPull requests are welcome! Anything that is not a simple fix would probably\nbenefit from being discussed in an issue first.\n\nRemember to be respectful and open minded!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdispatchrun%2Fwzprof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdispatchrun%2Fwzprof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdispatchrun%2Fwzprof/lists"}