Skip to content

Use instrument hooks #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"**/dist/**",
"**/node_modules/**",
"**/rollup.config.ts",
"**/jest.config.js"
"**/jest.config.js",
"packages/core/src/native_core/instruments/hooks/**"
],
"settings": {
"import/parsers": {
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
- uses: "actions/checkout@v4"
with:
fetch-depth: 0
submodules: true
- name: Install valgrind
run: |
sudo apt-get update
Expand All @@ -32,6 +33,9 @@ jobs:
examples: ${{ steps.list-examples.outputs.examples }}
steps:
- uses: "actions/checkout@v4"
with:
fetch-depth: 0
submodules: true
# list the directories in ./examples and output them to a github action workflow variables as a JSON array
- run: |
examples=$(find ./examples -maxdepth 1 -mindepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n") | map(select(length > 0))')
Expand All @@ -51,6 +55,7 @@ jobs:
- uses: "actions/checkout@v4"
with:
fetch-depth: 0
submodules: true
- name: Install valgrind
run: |
sudo apt-get update
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
- uses: "actions/checkout@v4"
with:
fetch-depth: 0
submodules: true
- name: Install valgrind
run: |
sudo apt-get update
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v3
with:
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "packages/core/src/native_core/instruments/hooks"]
path = packages/core/src/native_core/instruments/hooks
url = [email protected]:CodSpeedHQ/instrument-hooks.git
9 changes: 9 additions & 0 deletions .moon/tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ tasks:
- "@globs(sources)"
- "@globs(tests)"
- "@globs(configs)"
fix-format:
local: true
command: "prettier --config @in(0) --ignore-path @in(1) --write ."
inputs:
- "/.prettierrc.json"
- "/.prettierignore"
- "@globs(sources)"
- "@globs(tests)"
- "@globs(configs)"
lint:
command: "eslint ."
inputs:
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
.rollup.cache
dist
generated
packages/core/src/native_core/instruments/hooks
86 changes: 86 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# CodSpeed Node Repository Layout

## Repository Structure

This is a monorepo containing CodSpeed plugins for various Node.js benchmarking frameworks.

### Root Level
- `package.json` - Root package configuration
- `pnpm-workspace.yaml` - PNPM workspace configuration
- `lerna.json` - Lerna monorepo configuration
- `tsconfig.base.json` - Base TypeScript configuration
- `rollup.options.js` - Rollup bundler configuration
- `scripts/` - Build and release scripts
- `docs/` - Documentation files
- `examples/` - Example projects using the plugins

### Packages (`packages/`)

#### Core Package (`packages/core/`)
- **Purpose**: Core measurement and instrumentation functionality
- **Key files**:
- `src/index.ts` - Main exports, setupCore/teardownCore functions
- `src/mongoMeasurement.ts` - MongoDB measurement handling
- `src/optimization.ts` - Function optimization utilities
- `src/native_core/` - Native C++ bindings for performance measurement
- `src/introspection.ts` - V8 flags and runtime introspection

#### Tinybench Plugin (`packages/tinybench-plugin/`)
- **Purpose**: CodSpeed integration for tinybench framework
- **Key files**:
- `src/index.ts` - Main plugin implementation with `withCodSpeed()` function
- `tests/index.integ.test.ts` - Integration tests
- `benches/` - Benchmark examples

#### Benchmark.js Plugin (`packages/benchmark.js-plugin/`)
- **Purpose**: CodSpeed integration for benchmark.js framework
- **Key files**:
- `src/index.ts` - Main plugin implementation
- `src/buildSuiteAdd.ts` - Suite building utilities

#### Vitest Plugin (`packages/vitest-plugin/`)
- **Purpose**: CodSpeed integration for Vitest framework
- **Key files**:
- `src/index.ts` - Main plugin implementation
- `src/runner.ts` - Custom test runner
- `src/globalSetup.ts` - Global setup configuration

### Examples Directory (`examples/`)
- `with-javascript-cjs/` - CommonJS JavaScript examples
- `with-javascript-esm/` - ESM JavaScript examples
- `with-typescript-cjs/` - CommonJS TypeScript examples
- `with-typescript-esm/` - ESM TypeScript examples
- `with-typescript-simple-cjs/` - Simple CommonJS TypeScript examples
- `with-typescript-simple-esm/` - Simple ESM TypeScript examples

## Tinybench Plugin Architecture

### Current Stats/Measurements Access

The tinybench plugin currently has **limited stats exposure**:

1. **No direct stats API**: The `withCodSpeed()` function wraps a tinybench instance but doesn't expose measurement results
2. **Console-only output**: Results are only printed to console via `console.log()`
3. **Core measurement**: Uses `@codspeed/core` for actual measurement via:
- `mongoMeasurement.start(uri)` / `mongoMeasurement.stop(uri)`
- `Measurement.startInstrumentation()` / `Measurement.stopInstrumentation(uri)`

### Current Workflow
1. User calls `withCodSpeed(new Bench())` to wrap their tinybench instance
2. Plugin intercepts `bench.run()` to add CodSpeed instrumentation
3. Each benchmark task runs with measurement instrumentation
4. Results are logged to console but not returned as structured data

### Key Functions in tinybench plugin
- `withCodSpeed(bench: Bench): Bench` - Main wrapper function
- `setupInstruments(body)` - Dynamic instrument setup
- `getCallingFile()` - Helper to generate unique URIs for benchmarks

## Potential Enhancement Areas

Based on the codebase analysis, to add stats access features:

1. **Extend return value**: Modify `bench.run()` to return structured measurement data
2. **Add stats methods**: Add methods like `getStats()`, `getResults()`, `getLastRunStats()`
3. **Integrate with core**: Leverage `@codspeed/core` measurement data
4. **Maintain tinybench compatibility**: Ensure existing `bench.table()` still works
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 52 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
description = "CodSpeed Node development environment";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
flake-utils.url = "github:numtide/flake-utils";
};

outputs =
{
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
commonBuildInputs = with pkgs; [
# Needed for node-gyp
(python314.withPackages (
ps: with ps; [
setuptools
]
))
];

in
{
devShells = {
default = pkgs.mkShell {
buildInputs = commonBuildInputs;
shellHook = ''
echo "CodSpeed Node development environment"
'';
};

lsp = pkgs.mkShell {
buildInputs =
with pkgs;
[
typescript-language-server
]
++ commonBuildInputs;
shellHook = ''
echo "CodSpeed Node development environment with LSP"
'';
};
};
}
);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"devDependencies": {
"@commitlint/cli": "^17.5.1",
"@commitlint/config-conventional": "^17.4.4",
"@moonrepo/cli": "^1.19.3",
"@moonrepo/cli": "1.37.3",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
Expand Down
18 changes: 10 additions & 8 deletions packages/benchmark.js-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Measurement,
InstrumentHooks,
mongoMeasurement,
optimizeFunction,
optimizeFunctionSync,
Expand Down Expand Up @@ -90,7 +90,7 @@ export function withCodSpeed(item: unknown): unknown {
}

function withCodSpeedBenchmark(bench: Benchmark): WithCodSpeedBenchmark {
if (!Measurement.isInstrumented()) {
if (!InstrumentHooks.isInstrumented()) {
const rawRun = bench.run;
bench.run = (options?: Benchmark.Options) => {
console.warn(
Expand Down Expand Up @@ -120,7 +120,7 @@ function withCodSpeedBenchmark(bench: Benchmark): WithCodSpeedBenchmark {
}

function withCodSpeedSuite(suite: Benchmark.Suite): WithCodSpeedSuite {
if (!Measurement.isInstrumented()) {
if (!InstrumentHooks.isInstrumented()) {
const rawRun = suite.run;
suite.run = (options?: Benchmark.Options) => {
console.warn(
Expand Down Expand Up @@ -198,18 +198,20 @@ async function runBenchmarks({
await mongoMeasurement.start(uri);
global.gc?.();
await (async function __codspeed_root_frame__() {
Measurement.startInstrumentation();
const benchmarkId = InstrumentHooks.startBenchmark();
await benchPayload();
Measurement.stopInstrumentation(uri);
InstrumentHooks.stopBenchmark();
InstrumentHooks.setExecutedBenchmark(benchmarkId, uri);
})();
await mongoMeasurement.stop(uri);
} else {
optimizeFunctionSync(benchPayload);
await mongoMeasurement.start(uri);
(function __codspeed_root_frame__() {
Measurement.startInstrumentation();
const benchmarkId = InstrumentHooks.startBenchmark();
benchPayload();
Measurement.stopInstrumentation(uri);
InstrumentHooks.stopBenchmark();
InstrumentHooks.setExecutedBenchmark(benchmarkId, uri);
})();
await mongoMeasurement.stop(uri);
}
Expand All @@ -231,7 +233,7 @@ async function runBenchmarks({
export async function setupInstruments(
body: SetupInstrumentsRequestBody
): Promise<SetupInstrumentsResponse> {
if (!Measurement.isInstrumented()) {
if (!InstrumentHooks.isInstrumented()) {
console.warn("[CodSpeed] No instrumentation found, using default mongoUrl");

return { remoteAddr: body.mongoUrl };
Expand Down
Loading
Loading