@datadog/pprof
Advanced tools
Comparing version 4.0.0-pre-68b0b7b to 4.0.0-pre-70d3943
@@ -13,2 +13,3 @@ import * as heapProfiler from './heap-profiler'; | ||
isStarted: typeof timeProfiler.isStarted; | ||
v8ProfilerStuckEventLoopDetected: typeof timeProfiler.v8ProfilerStuckEventLoopDetected; | ||
getState: typeof timeProfiler.getState; | ||
@@ -15,0 +16,0 @@ constants: { |
@@ -59,2 +59,3 @@ "use strict"; | ||
isStarted: timeProfiler.isStarted, | ||
v8ProfilerStuckEventLoopDetected: timeProfiler.v8ProfilerStuckEventLoopDetected, | ||
getState: timeProfiler.getState, | ||
@@ -61,0 +62,0 @@ constants: timeProfiler.constants, |
@@ -18,3 +18,3 @@ /** | ||
import { SourceMapper } from './sourcemapper/sourcemapper'; | ||
import { AllocationProfileNode, LabelSet, TimeProfile } from './v8-types'; | ||
import { AllocationProfileNode, LabelSet, TimeProfile, TimeProfileNodeContext } from './v8-types'; | ||
/** | ||
@@ -27,3 +27,3 @@ * Converts v8 time profile into into a profile proto. | ||
*/ | ||
export declare function serializeTimeProfile(prof: TimeProfile, intervalMicros: number, sourceMapper?: SourceMapper, recomputeSamplingInterval?: boolean, generateLabels?: (context: object) => LabelSet): Profile; | ||
export declare function serializeTimeProfile(prof: TimeProfile, intervalMicros: number, sourceMapper?: SourceMapper, recomputeSamplingInterval?: boolean, generateLabels?: (context: TimeProfileNodeContext) => LabelSet): Profile; | ||
/** | ||
@@ -30,0 +30,0 @@ * Converts v8 heap profile into into a profile proto. |
@@ -179,4 +179,3 @@ "use strict"; | ||
if (totalHitCount > 0) { | ||
intervalMicros = Math.min(Math.max(Math.floor((prof.endTime - prof.startTime) / | ||
computeTotalHitCount(prof.topDownRoot)), intervalMicros), 2 * intervalMicros); | ||
intervalMicros = Math.min(Math.max(Math.floor((prof.endTime - prof.startTime) / totalHitCount), intervalMicros), 2 * intervalMicros); | ||
} | ||
@@ -188,3 +187,3 @@ } | ||
for (const context of entry.node.contexts || []) { | ||
const labels = generateLabels ? generateLabels(context) : context; | ||
const labels = generateLabels ? generateLabels(context) : context.context; | ||
if (Object.keys(labels).length > 0) { | ||
@@ -225,12 +224,17 @@ const sample = new pprof_format_1.Sample({ | ||
for (const [key, value] of Object.entries(labelSet)) { | ||
if (typeof value === 'number' || typeof value === 'string') { | ||
const label = new pprof_format_1.Label({ | ||
key: stringTable.dedup(key), | ||
num: typeof value === 'number' ? value : undefined, | ||
str: typeof value === 'string' | ||
? stringTable.dedup(value) | ||
: undefined, | ||
}); | ||
labels.push(label); | ||
const labelInput = { | ||
key: stringTable.dedup(key), | ||
}; | ||
switch (typeof value) { | ||
case 'string': | ||
labelInput.str = stringTable.dedup(value); | ||
break; | ||
case 'number': | ||
case 'bigint': | ||
labelInput.num = value; | ||
break; | ||
default: | ||
continue; | ||
} | ||
labels.push(new pprof_format_1.Label(labelInput)); | ||
} | ||
@@ -237,0 +241,0 @@ return labels; |
@@ -17,3 +17,3 @@ /** | ||
import { SourceMapper } from './sourcemapper/sourcemapper'; | ||
import { LabelSet } from './v8-types'; | ||
import { LabelSet, TimeProfileNodeContext } from './v8-types'; | ||
type Microseconds = number; | ||
@@ -35,9 +35,11 @@ type Milliseconds = number; | ||
withContexts?: boolean; | ||
workaroundV8Bug?: boolean; | ||
} | ||
export declare function profile({ intervalMicros, durationMillis, sourceMapper, lineNumbers, withContexts, }: TimeProfilerOptions): Promise<import("pprof-format").Profile>; | ||
export declare function start({ intervalMicros, durationMillis, sourceMapper, lineNumbers, withContexts, }: TimeProfilerOptions): void; | ||
export declare function stop(restart?: boolean, generateLabels?: (context: object) => LabelSet): import("pprof-format").Profile; | ||
export declare function profile({ intervalMicros, durationMillis, sourceMapper, lineNumbers, withContexts, workaroundV8Bug, }: TimeProfilerOptions): Promise<import("pprof-format").Profile>; | ||
export declare function start({ intervalMicros, durationMillis, sourceMapper, lineNumbers, withContexts, workaroundV8Bug, }: TimeProfilerOptions): void; | ||
export declare function stop(restart?: boolean, generateLabels?: (context: TimeProfileNodeContext) => LabelSet): import("pprof-format").Profile; | ||
export declare function getState(): any; | ||
export declare function setContext(context?: object): void; | ||
export declare function isStarted(): boolean; | ||
export declare function v8ProfilerStuckEventLoopDetected(): number; | ||
export declare const constants: { | ||
@@ -44,0 +46,0 @@ kSampleCount: any; |
@@ -21,3 +21,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.constants = exports.isStarted = exports.setContext = exports.getState = exports.stop = exports.start = exports.profile = void 0; | ||
exports.constants = exports.v8ProfilerStuckEventLoopDetected = exports.isStarted = exports.setContext = exports.getState = exports.stop = exports.start = exports.profile = void 0; | ||
const delay_1 = __importDefault(require("delay")); | ||
@@ -32,2 +32,3 @@ const profile_serializer_1 = require("./profile-serializer"); | ||
let gIntervalMicros; | ||
let gV8ProfilerStuckEventLoopDetected = 0; | ||
/** Make sure to stop profiler before node shuts down, otherwise profiling | ||
@@ -39,3 +40,3 @@ * signal might cause a crash if it occurs during shutdown */ | ||
}); | ||
async function profile({ intervalMicros = DEFAULT_INTERVAL_MICROS, durationMillis = DEFAULT_DURATION_MILLIS, sourceMapper, lineNumbers = false, withContexts = false, }) { | ||
async function profile({ intervalMicros = DEFAULT_INTERVAL_MICROS, durationMillis = DEFAULT_DURATION_MILLIS, sourceMapper, lineNumbers = false, withContexts = false, workaroundV8Bug = true, }) { | ||
start({ | ||
@@ -47,2 +48,3 @@ intervalMicros, | ||
withContexts, | ||
workaroundV8Bug, | ||
}); | ||
@@ -54,9 +56,10 @@ await (0, delay_1.default)(durationMillis); | ||
// Temporarily retained for backwards compatibility with older tracer | ||
function start({ intervalMicros = DEFAULT_INTERVAL_MICROS, durationMillis = DEFAULT_DURATION_MILLIS, sourceMapper, lineNumbers = false, withContexts = false, }) { | ||
function start({ intervalMicros = DEFAULT_INTERVAL_MICROS, durationMillis = DEFAULT_DURATION_MILLIS, sourceMapper, lineNumbers = false, withContexts = false, workaroundV8Bug = true, }) { | ||
if (gProfiler) { | ||
throw new Error('Wall profiler is already started'); | ||
} | ||
gProfiler = new time_profiler_bindings_1.TimeProfiler(intervalMicros, durationMillis * 1000, lineNumbers, withContexts); | ||
gProfiler = new time_profiler_bindings_1.TimeProfiler(intervalMicros, durationMillis * 1000, lineNumbers, withContexts, workaroundV8Bug); | ||
gSourceMapper = sourceMapper; | ||
gIntervalMicros = intervalMicros; | ||
gV8ProfilerStuckEventLoopDetected = 0; | ||
gProfiler.start(); | ||
@@ -70,2 +73,16 @@ } | ||
const profile = gProfiler.stop(restart); | ||
if (restart) { | ||
gV8ProfilerStuckEventLoopDetected = | ||
gProfiler.v8ProfilerStuckEventLoopDetected(); | ||
// Workaround for v8 bug, where profiler event processor thread is stuck in | ||
// a loop eating 100% CPU, leading to empty profiles. | ||
// Fully stop and restart the profiler to reset the profile to a valid state. | ||
if (gV8ProfilerStuckEventLoopDetected > 0) { | ||
gProfiler.stop(false); | ||
gProfiler.start(); | ||
} | ||
} | ||
else { | ||
gV8ProfilerStuckEventLoopDetected = 0; | ||
} | ||
const serialized_profile = (0, profile_serializer_1.serializeTimeProfile)(profile, gIntervalMicros, gSourceMapper, true, generateLabels); | ||
@@ -97,3 +114,8 @@ if (!restart) { | ||
exports.isStarted = isStarted; | ||
// Return 0 if no issue detected, 1 if possible issue, 2 if issue detected for certain | ||
function v8ProfilerStuckEventLoopDetected() { | ||
return gV8ProfilerStuckEventLoopDetected; | ||
} | ||
exports.v8ProfilerStuckEventLoopDetected = v8ProfilerStuckEventLoopDetected; | ||
exports.constants = { kSampleCount }; | ||
//# sourceMappingURL=time-profiler.js.map |
@@ -31,5 +31,9 @@ /** | ||
} | ||
export interface TimeProfileNodeContext { | ||
context: object; | ||
timestamp: bigint; | ||
} | ||
export interface TimeProfileNode extends ProfileNode { | ||
hitCount: number; | ||
contexts?: object[]; | ||
contexts?: TimeProfileNodeContext[]; | ||
} | ||
@@ -36,0 +40,0 @@ export interface AllocationProfileNode extends ProfileNode { |
{ | ||
"name": "@datadog/pprof", | ||
"version": "4.0.0-pre-68b0b7b", | ||
"version": "4.0.0-pre-70d3943", | ||
"description": "pprof support for Node.js", | ||
@@ -36,3 +36,3 @@ "repository": "datadog/pprof-nodejs", | ||
"@types/mocha": "^10.0.1", | ||
"@types/node": ">=12", | ||
"@types/node": ">=14", | ||
"@types/sinon": "^10.0.15", | ||
@@ -78,4 +78,4 @@ "@types/tmp": "^0.2.3", | ||
"engines": { | ||
"node": ">=12" | ||
"node": ">=14" | ||
} | ||
} |
@@ -10,18 +10,12 @@ # pprof support for Node.js | ||
## Prerequisites | ||
1. Your application will need to be using Node.js 10.4.1 or greater, or any | ||
version of Node 12 or 14. | ||
1. Your application will need to be using Node.js 14 or greater. | ||
The profiler should not be enabled when using earlier versions of Node 10, | ||
since versions of Node.js 10 prior to 10.4.1 are impacted by | ||
[this](https://bugs.chromium.org/p/chromium/issues/detail?id=847863) issue, | ||
which can cause garbage collection to take several minutes when heap profiling | ||
is enabled. | ||
2. The `pprof` module has a native component that is used to collect profiles | ||
with v8's CPU and Heap profilers. You may need to install additional | ||
dependencies to build this module. | ||
* For Linux: `pprof` has prebuilt binaries available for Linux and Alpine | ||
Linux for Node 10, 12 and 14. No additional dependencies are required. | ||
* For other environments: when using `@google-cloud/profiler` on environments | ||
that `pprof` does not have prebuilt binaries for, the module | ||
* For Linux: `pprof` has prebuilt binaries available for Linux arm64/x64, | ||
Alpine Linux x64, macOS arm64/x64, windows x64 for Node 14/16/18/20. | ||
No additional dependencies are required. | ||
* For other environments: on environments that `pprof` does not have | ||
prebuilt binaries for, the module | ||
[`node-gyp`](https://www.npmjs.com/package/node-gyp) will be used to | ||
@@ -32,3 +26,3 @@ build binaries. See `node-gyp`'s | ||
3. The [`pprof`][pprof-url] CLI can be used to view profiles collected with | ||
3. The [`pprof`][pprof-url] CLI can be used to view profiles collected with | ||
this module. Instructions for installing the `pprof` CLI can be found | ||
@@ -42,3 +36,3 @@ [here][pprof-install-url]. | ||
# Install through npm while saving to the local 'package.json' | ||
npm install --save pprof | ||
npm install --save @datadog/pprof | ||
``` | ||
@@ -72,3 +66,3 @@ | ||
```sh | ||
node --require pprof app.js | ||
node --require @datadog/pprof app.js | ||
``` | ||
@@ -75,0 +69,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
1477
53
8249982
86
114