npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@magnaboy/cli-trace

v0.0.3

Published

Perfetto traces, simpleperf profiles, cumulative counters and paired-measurement statistics for Node.js CLIs.

Readme

@magnaboy/cli-trace

Perfetto traces, simpleperf profiles, cumulative hardware counters, and the statistics that turn repeated measurements into a defensible number.

Install

npm i @magnaboy/cli-trace

Requires Node 25+ and ESM. Import the whole package or one area:

import { integrateEnergy } from '@magnaboy/cli-trace/counters';
import { logRatioInterval } from '@magnaboy/cli-trace/statistics';

Cumulative counters

import { integrateEnergy, timeWeightedMean, windowPoints } from '@magnaboy/cli-trace/counters';

const window = integrateEnergy(railSamples, startSeconds, endSeconds, { maxGapSeconds: 2.5 });
console.log(window.watts, window.duplicates);

Counter samples are { time, value } with time in seconds on whatever timebase the caller uses consistently. Timestamps must strictly increase and values must be finite.

windowPoints clips a series to [start, end] and interpolates both endpoints. The series must bracket the window on both sides; extrapolating past the observed samples would invent measurement where none exists. A gap longer than maxGapSeconds (default 2.5) inside the window is rejected for the same reason, because part of the window then went unobserved.

timeWeightedMean trapezoid-integrates an instantaneous series, such as sampled watts, so a level that held for three seconds counts three times as much as one that held for one. cumulativeDelta and cumulativeRate read a monotonically rising counter and reject one that was reset mid-series. cumulativeRate takes a scale to convert units: microjoules need 1e-6 to report watts.

integrateEnergy combines these for a cumulative microjoule rail counter and reports the sample count and how many duplicates it dropped. A power HAL can return the same cached snapshot to two consecutive polls; those carry no additional energy, and keeping them would create zero-duration intervals that are not real measurements. dedupeCounterPoints exposes that step on its own.

Statistics for repeated measurements

import { blockContrast, confidenceInterval, logRatioInterval, studentTQuantile } from '@magnaboy/cli-trace/statistics';

const interval = logRatioInterval(pairs.map(pair => Math.log(pair.baseline / pair.treatment)));
console.log(`${interval.mean.toFixed(1)}% ±${interval.halfWidthPercentagePoints.toFixed(1)}pp`);

studentTQuantile(probability, degreesOfFreedom) is the t inverse CDF, computed from the regularized incomplete beta function rather than a lookup table, so there is no upper limit on the number of blocks and no dependency on a statistics library. It reproduces the published critical values to better than 1e-6. incompleteBeta is exported for callers that need it directly.

confidenceInterval(values, confidence) is a two-sided Student-t interval; confidence is the total coverage, so 0.95 leaves 2.5% in each tail. It returns the critical value and standard error alongside the bounds, for reporting.

logRatioInterval is for ratios. Power and runtime comparisons are multiplicative, so the analysis runs on log ratios and converts back with expm1. The interval endpoints are converted rather than the percentages averaged, which keeps the result correct and asymmetric.

blockContrast takes one balanced ABBA or BAAB block of { treatment, time, value } and returns the treatment effect as a log ratio with linear drift over time regressed out. A device warms over a long session and its power climbs with it, so a single ordering makes whatever ran last look worse. Even spacing already cancels linear drift, and unadjustedLogRatio reports that naive contrast for comparison; the adjustment earns its place when the windows are unevenly spaced. information reports how well the block's timing separates treatment from trend, out of 4.

Perfetto

import { assertTraceIsComplete, perfettoConfig, readCounterSeries } from '@magnaboy/cli-trace/perfetto';

const config = perfettoConfig(await readFile('configs/full.pbtx', 'utf8'), { durationMs: 20_000 });
await assertTraceIsComplete(runner, { executable: traceProcessor, trace });
const rails = await readCounterSeries(runner, { executable: traceProcessor, trace, tracks: ['power.rails.*'] });

perfettoConfig strips CRLF, which the text config parser rejects and which a repository checked out on Windows produces. It appends duration_ms so one config serves any window length, and refuses to append a duration the config already sets.

queryTrace runs one SQL query through the trace_processor CLI and returns rows as string maps. parseTraceProcessorCsv does the parsing and honors RFC 4180 quoting, so a value containing a comma, a newline or a doubled quote survives — which a regular expression per row cannot do.

assertTraceIsComplete runs TRACE_ERROR_QUERY and rejects a trace that reported errors or dropped data. This has to be explicit: a lossy trace still answers every query, just with quietly incomplete numbers. readCounterSeries reads counter tracks by GLOB pattern and converts Perfetto's nanosecond timestamps to the seconds the counter helpers expect.

Each queryTrace call loads the trace, so a report needing many queries should combine them. A persistent session over trace_processor_shell --httpd is not implemented here.

simpleperf

import { parseSampleCounts, rankFoldedStacks, SimpleperfTools } from '@magnaboy/cli-trace/simpleperf';

const tools = new SimpleperfTools({ ndkPath, python: 'python3' });
await runner.run(tools.binaryCacheCommand({ perfData, cacheDirectory, libraryDirectories }));

SimpleperfTools builds CommandSpecs for the simpleperf Python tools rather than running them, so the arguments can be tested. Every command requires cacheDirectory, which becomes its working directory: all of these tools read and write binary_cache relative to the working directory and the path is not an option on any of them. Left at the caller's directory they litter wherever the script was launched from and, in a multi-device session, symbolize one phone's profile against the other's libraries. Use one cache directory per physical device.

binaryCacheCommand takes libraryDirectories for -lib. The packaged .so files are stripped, so without the unstripped build output native frames symbolize to raw addresses instead of names.

parseSampleCounts reads the Samples recorded: … Samples lost: … line; lost samples silently bias a profile toward whatever was cheap to record, so the count is worth asserting on. parseFoldedStacks and rankFoldedStacks turn stackcollapse.py output into rankings by thread, self cost, inclusive cost, and frames matching ownedPrefixes. Inclusive weight counts each frame once per stack, so a recursive frame is not multiplied by its depth.