PProf Format Encoder/Decoder
pprof-format is a pure JavaScript library for encoding and decoding PProf (Profiling Protobuf) profiles. It distinguishes itself by providing a zero-dependency implementation that avoids the full Protobuf runtime, instead focusing on the subset of the PProf specification required for profiling data. This design choice contributes to a smaller bundle size and faster execution, making it highly suitable for both Node.js and browser environments. The library explicitly uses Uint8Arrays over Node.js Buffers to ensure universal compatibility. Currently at version 2.2.1, its release cadence is typically driven by new features or specification updates rather than a fixed schedule. Key differentiators include its browser-friendliness, lack of external dependencies, and performance optimizations. It ships with TypeScript types for enhanced developer experience.
Common errors
-
ReferenceError: require is not defined
cause Attempting to use CommonJS `require()` syntax in an ESM-only context or a browser environment that doesn't support it.fixUpdate your import statements to use ESM syntax: `import { Profile } from 'pprof-format'`. Ensure your Node.js project is configured for ESM (e.g., by adding `"type": "module"` to your `package.json`). -
TypeError: Cannot read properties of undefined (reading 'dedup')
cause A string field within a PProf component (e.g., `Function.name`, `ValueType.type`) was assigned a raw string instead of a string table index, or `stringTable` was not initialized or passed to the `Profile` constructor.fixEnsure `stringTable = new StringTable()` is initialized and used for all string values via `stringTable.dedup('my string')`. Also, confirm the `stringTable` instance is passed to the `Profile` constructor. -
TypeError: Type 'number' is not assignable to type 'bigint'.
cause A PProf field expecting a `BigInt` (like `timeNanos`, `durationNanos`, `address`, or sample values) received a standard JavaScript `number`.fixExplicitly convert numeric values to `BigInt` using the `BigInt()` constructor or by appending `n` to integer literals (e.g., `123n`, `BigInt(Date.now())`).
Warnings
- gotcha This library is designed for modern JavaScript environments and uses ESM for its module system. Attempting to use `require()` for imports will result in module resolution errors or runtime failures.
- gotcha All string values within the PProf structure (like function names, filenames, label keys/values, etc.) must be handled through an instance of `StringTable` for deduplication. Directly assigning string literals to fields expecting string table indices will lead to malformed profiles or runtime errors.
- gotcha PProf values such as `timeNanos`, `durationNanos`, `address`, and `sample.value` require `BigInt` types for precision, not standard JavaScript `number` primitives. Using `number` will lead to loss of precision, incorrect profile data, or `TypeError`.
Install
-
npm install pprof-format -
yarn add pprof-format -
pnpm add pprof-format
Imports
- Profile
const Profile = require('pprof-format').Profileimport { Profile } from 'pprof-format' - StringTable
import StringTable from 'pprof-format'
import { StringTable } from 'pprof-format' - Location
import * as pprof from 'pprof-format'; new pprof.Location()
import { Location } from 'pprof-format'
Quickstart
import {
Function,
Label,
Line,
Location,
Mapping,
Profile,
Sample,
ValueType,
StringTable
} from 'pprof-format'
const stringTable = new StringTable()
const periodType = new ValueType({
type: stringTable.dedup('cpu'),
unit: stringTable.dedup('nanoseconds')
})
const fun = new Function({
id: 1,
name: stringTable.dedup('main'),
systemName: stringTable.dedup('main'),
filename: stringTable.dedup('app.js'),
startLine: 1
})
const mapping = new Mapping({
id: 1,
memoryStart: 0n,
memoryLimit: 1000n,
fileOffset: 0n,
filename: stringTable.dedup('app.js')
})
const location = new Location({
id: 1,
mappingId: mapping.id,
address: 123n, // Addresses are BigInt
line: [
new Line({
functionId: fun.id,
line: 1
})
]
})
const profile = new Profile({
sampleType: [
new ValueType({
type: stringTable.dedup('samples'),
unit: stringTable.dedup('count')
})
],
sample: [
new Sample({
locationId: [location.id],
value: [1000n] // Values are BigInt
})
],
mapping: [mapping],
location: [location],
'function': [fun],
stringTable,
timeNanos: BigInt(Date.now()) * 1_000_000n,
durationNanos: 500_000_000n, // 500ms
periodType,
period: 1_000_000n, // 1ms period for CPU samples
comment: [
stringTable.dedup('Example PProf profile')
]
})
// Encode to Uint8Array
const encodedProfile = profile.encode()
console.log('Encoded profile length:', encodedProfile.length)
// Decode from Uint8Array
const copied = Profile.decode(encodedProfile)
console.log('Decoded profile sample count:', copied.sample.length)
// Verify string table content
console.log('Decoded string table entry 1:', copied.stringTable.at(1))