Steve Golton
90d · built 2026-09-08
Performance
What Steve Golton shipped in the selected window, measured in ETV, and how it compares with the 90 days before it.
Effective capacity
+0.8engineers
delivers like 1.8 (1.8x pre-AI)
Output (ETV)
25.6ETV
+35.1% vs 19.0 prior
Features share
46.0%
+0.5 pp vs prior window
Fixes share
5.0%
−0.4 pp vs prior window
Work mix
46% Features33.9% Maintenance12.5% Tests2.6% Docs5% Fixes
111 commits over 90 days, ending 2026-09-08.
Where this dev ranks
Percentile against the global top-100 leaderboard (all-time totals).
- By commits
- Top 27 %
- By Features share
- Top 30 %
Daily performance
Daily ETV, stacked by Features, Maintenance, Tests, Docs and Fixes.
Repository spread
Where this developer's commits land. Concentrated work (top1 > 80%) vs polymath spread (top1 < 30%).
Most impactful commits
Top 10 by ETV in the last 90 days.
- 2.1ETVui: Simplify DataGrid schema models (#6710) Currently the schema is a record of tables keyed by name, like this: ```js { slice: { id: {}, ts: {}, dur: {}, track: {ref: 'track', ...}, // Ref to the foreign table }, track: { id: {}, name: {} }, parent: {ref: 'slice', ...}, // Recursive reference } ``` This works but is messy: - Can reference non-existent tables - must check for undefined everywhere. - The vast majority of schemas in practice are flat (single table) which means we have to nest every single schema inside a dummy table name when its totally redundant. - Field paths don't specify a 'root' table name so we must pass the 'root' (default) table name around as a string everywhere. ## The fix This patch replaces this nominal structure with a structured definition which solves all of these problems: E.g. ```js const sliceSchema = { id: {}, ts: {}, dur: {}, table: { schema: { // <-- Nested table schema id: {}, name: {} }, }, parent: { get schema() { // <-- recursive references can exist - you just have to use a getter. return sliceSchema; } }, } ``` Notes: - The recursive via getter trick is borrowed from [zod](https://zod.dev/api?id=recursive-objects#recursive-objects). - The sqlSchema (the separate schema passed to the SQLDataSource has been restructured in a similar way. - This is a big change due to the number of DataGrid call-sites, but it's mainly mechanical. Should be a no-op. Key changes: - SchemaRef.ref (string) → SchemaRef.schema (ColumnSchema), with get schema() for self-referential tables - SQLTableSchema.table → SQLTableSchema.tableOrSubquery, always wrapped in parens in generated SQL - DataGridAttrs.schema is now ColumnSchema (was SchemaRegistry), rootSchema removed - SQLDataSource config extends SQLTableSchema directly (was sqlSchema + rootSchemaName) - Add getQuery() to SQLDataSource and sub-engines for SQL inspectiongithub.com-google-perfetto · c4002e38 · 2026-07-16
- 1.8ETVui: Bulk columnar decode for QueryResult (#7277) Currently all queries must be iterated out row by row which involves mutating the row object for every iteration. On heavy queries with many rows (such as tracks) this overhead can add up. In addition, most tracks ultimately require the data in columnar TypedArrays in order to load into WebGL buffers, so round tripping to a row object is wasteful. This patch introduces a new QueryResult API - `decodeColumns(spec)` - which takes the same spec object as `.iter(spec)` but returns all rows in one go as a set of columnar based `TypedArray`s. Building arrays directly allows certain shortcuts to be taken such as avoiding creating bigints and simply copying bytes. `decodeColumns()` runs around 2-4x faster than `iter()`, depending on the row spec. This patch also migrates SliceTrack, CounterTrack, GroupSummaryTrack, and CpuFreqTrack over to `decodeColumns()`. For compatibility, some row oriented work is still done in SliceTrack which leaves some performance on the table but changing this would involve changing track API, and this is out of scope of this PR. Added a benchmark utility to compare the relative performance of `iter()` and `decodeColumns()`, which can be run using the following command: ```bash RUN_BENCH=1 ui/run-unittests -f 'QueryResultBenchmark' -n ``` Note: Changes in unrelated parts of the codebase are related to the fact that the type of the row spec has been decoupled from the type of the row itself. This was a neat trick but doesn't work for decodeColumns as we need to translate the spec to columnar TypedArrays - e.g. NUM -> Float64Array. Unfortunately Typescript cannot tell the difference between the NUM and NUM_NULL types in type based meta-programming. The spec types (NUM, STR, LONG, etc...) how now been changed to objects - so that each one is distinct and can be translated to the equivalent TypedArray. Another advantage is we can make the various specs extend one another properly so that spec subtyping works. E.g. `{foo: NUM}` can be used where `{foo: UNKNOWN}` or `{foo: NUM_NULL}` is expected, but not where `{foo: STR}` is expected.github.com-google-perfetto · 3abc8b53 · 2026-09-01
- 1.3ETVui: Fix fuzzy search matching by replacing MiniSearch with fuzzysort (#6852) Fix odd fuzzy search behavior where search terms like 'gpucompute' did not match 'GpuCompute'. Replace MiniSearch with fuzzysort, which is built for omniboxes, and adds some better support for other things such as multi key seraching. Summary of changes: - Switch MiniSearch for fuzzysort (0 deps, bundle size reduced by 35K). - Modify the interface to allow multiple key lookup functions to be passed which is now handled natively by fuzzysort. - Add fuzzy demo page in the widgets page (#!/widgets/fuzzy-search) - Sprinkly readonly on arrays liberally where appropriate. Update FuzzyFinder to support single and multi-key lookups, allowing Omnibox command search to filter across both command names and sources with per-field match highlights. Add a Fuzzy Search demo to WidgetsPage.github.com-google-perfetto · d08db71b · 2026-07-23
- 1.3ETVui: Clean up memoization primitives (#6877) - Rename `QuerySlot` to `AsyncMemo` because it has nothing to do with queries and everything to do with memoization with async tasks. - Add `Memo` for synchronous memoization. - Rename `SerialTaskQueue` to `AtomicTaskQueue` because it is used to make tasks run atomically without interleaving one another. While in the area, do some cleanup: - Remove `isFresh` from `AsyncMemo.use()` because the value wasn't consumed anywhere. - Enforce `data` field returned by `AsyncMemo.use()` to be defined when `isPending` is false at the type system level. This avoids double checking. - Move shared JSON types and disposable type predicates to base utility files.github.com-google-perfetto · 093d5387 · 2026-07-24
- 1.1ETVui: Open aggregation drilldowns in new tab (#6866) Clicking the drill-down button in an aggregator now copies the entire pivot model + selection to a new tab with the drill-down applied. The original aggregation tab's model stays as-is. Also add 'Add debug track' menu button to the drill-down view, and add 'ts' columns to relevant aggregators to make debug tracks work. This means that when clicking when subiquently clicking on a slice link to reveal the slice on the timeline from one of these drilldown tabs, the drill-down list is not lost the selection changes. Fixes: #6817github.com-google-perfetto · a7bed649 · 2026-08-10
- 1.1ETVui: Add LLM core gateway plugin + chrome API provider + test chat page (#6509) This patch adds the following plugins: - **dev.perfetto.Llm**: A common entry point for which other plugins can access language model functionality, and protocol provider plugins can register their protocols against. - **dev.perfetto.LlmProtocolChromePrompt**: A language model provider exposing the Chrome Prompt API (on-device gemini nano model) as an LLM provider. This plugin also registers gemini-nano as a *static* provider & model, which will appear as an ever present option not available to user configuration (this is the same way that extension servers will register hard coded configurations). In the future we may disable this entire plugin, but for now it makes sense to keep it enabled and present for testing. - **dev.perfetto.LlmTestChat**: A very simple test chatbot style page used for testing the API. No tools, just allows chatting with the model for the sake of testing. RFC: https://github.com/google/perfetto/discussions/6241 Note - the LLM core registries are kept as plugins for now rather than merge into the UI core. This is to keep things cleaner while we're still working out the kinks in the protocol, and will be subsumed into the core at some point in the future. Testing: - In chrome, enable the prompt API `chrome://flags/#prompt-api-for-gemini-nano` if not enabled by default. - Enable the dev.perfetto.LlmTestChat plugin - Under `Support` in the sidebar, click the LLM test chat page <img width="303" height="236" alt="image" src="https://github.com/user-attachments/assets/037e3408-1e16-4b18-bf45-875990b4dbd2" />github.com-google-perfetto · e68c84d0 · 2026-07-03
- 1.1ETVui: Extract reusable components and utils out of memscope (#6435) Widgets: - Panel - Hero - Page - Billboard - ProgressBar - ColorChip - Callout (special memscope themed version) Utils: - Byte formatters (bytes_formate.ts) - Switch to using IEC units (e.g. MiB, GiB, etc) for memory Tweak styling: - In light mode make the panels white and the background gray (invert). - Add staggered fade-in animation.github.com-google-perfetto · d9c6f174 · 2026-06-30
- 0.9ETVui: Add Intelletto assistant plugin (#6512) Adds dev.perfetto.Intelletto, the conversational assistant that sits in the sidebar and uses the LLM gateway plugin. It owns the agent loop (multi-step tool use with lazy tool loading), the trace-scoped chat session and context registry, the core query/schema tools, and the sidebar chat panel. The agent drives the gateway directly: it picks the first configured model advertising the 'agentic' role and streams a turn via gateway.createStream(). The chat panel header shows that active model. RFC: https://github.com/google/perfetto/discussions/6240 Note: Plugin is not enabled while in development - so for testing, enable `dev.perfetto.Intelletto` and open the sidebar. Tools: 1. run_query — run PerfettoSQL, return rows as JSON 2. get_schema — list tables/views or a table's columns 3. get_selection — read the current UI selection 4. show_query — open a query in the Query page (mutating) 5. navigate — switch to a route, e.g. /viewer (mutating) 6. list_tools / more_tools — lazy-loading meta-tools (off by default) Context injections: 1. page — current page route 2. viewport — visible time range (start/end ns) 3. selection — current selection, if anygithub.com-google-perfetto · 1c27245e · 2026-07-04
- 0.6ETVui: Return getGridConfig from aggregator probe step (#6984) Move getGridConfig() from the root Aggregator interface to be returned on the Aggregation object by probe() (or AggregationData by prepareData()). This is much more convenient when data from the probe step needs to be cached and used for the grid config - e.g when track lineage needs to be retained. Instead of storing this data in mutable member variables on the class, this data can now be const and simply captured in a closure.github.com-google-perfetto · 22544564 · 2026-08-05
- 0.6ETVui: Add memory over time chart + mini memory breakdown flamegraph (#6445) Add smaps based memory composition over time chart to the summary tab of the memscope landing page. This shows smaps composition over time broken down into various categories, and also doubles as a snapshot selector. <img width="1086" height="580" alt="image" src="https://github.com/user-attachments/assets/394dfc14-aa50-4fd0-9841-6d7f61b546c1" />github.com-google-perfetto · d2efecaf · 2026-07-01