@sentry/profiling-node 8.9.2 → 8.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bindings/cpu_profiler.cc +7 -5
- package/lib/cjs/index.js +83 -33
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/index.js +85 -35
- package/lib/esm/index.js.map +1 -1
- package/lib/sentry_cpu_profiler-darwin-arm64-108.node +0 -0
- package/lib/sentry_cpu_profiler-darwin-arm64-115.node +0 -0
- package/lib/sentry_cpu_profiler-darwin-arm64-127.node +0 -0
- package/lib/sentry_cpu_profiler-darwin-arm64-93.node +0 -0
- package/lib/sentry_cpu_profiler-darwin-x64-108.node +0 -0
- package/lib/sentry_cpu_profiler-darwin-x64-115.node +0 -0
- package/lib/sentry_cpu_profiler-darwin-x64-127.node +0 -0
- package/lib/sentry_cpu_profiler-darwin-x64-93.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-glibc-108.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-glibc-115.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-glibc-127.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-glibc-93.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-musl-108.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-musl-115.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-musl-127.node +0 -0
- package/lib/sentry_cpu_profiler-linux-arm64-musl-93.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-glibc-108.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-glibc-115.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-glibc-127.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-glibc-93.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-musl-108.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-musl-115.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-musl-127.node +0 -0
- package/lib/sentry_cpu_profiler-linux-x64-musl-93.node +0 -0
- package/lib/sentry_cpu_profiler-win32-x64-108.node +0 -0
- package/lib/sentry_cpu_profiler-win32-x64-115.node +0 -0
- package/lib/sentry_cpu_profiler-win32-x64-127.node +0 -0
- package/lib/sentry_cpu_profiler-win32-x64-93.node +0 -0
- package/lib/types/integration.d.ts +18 -0
- package/lib/types/integration.d.ts.map +1 -1
- package/lib/types/spanProfileUtils.d.ts.map +1 -1
- package/lib/types/utils.d.ts +3 -1
- package/lib/types/utils.d.ts.map +1 -1
- package/lib/types-ts3.8/integration.d.ts +18 -0
- package/lib/types-ts3.8/utils.d.ts +3 -1
- package/package.json +5 -5
package/bindings/cpu_profiler.cc
CHANGED
|
@@ -546,7 +546,8 @@ CreateFrameNode(const napi_env &env, const v8::CpuProfileNode &node,
|
|
|
546
546
|
};
|
|
547
547
|
|
|
548
548
|
napi_value CreateSample(const napi_env &env, const enum ProfileFormat format,
|
|
549
|
-
const uint32_t stack_id,
|
|
549
|
+
const uint32_t stack_id,
|
|
550
|
+
const int64_t sample_timestamp_ns,
|
|
550
551
|
const double chunk_timestamp,
|
|
551
552
|
const uint32_t thread_id) {
|
|
552
553
|
napi_value js_node;
|
|
@@ -564,7 +565,7 @@ napi_value CreateSample(const napi_env &env, const enum ProfileFormat format,
|
|
|
564
565
|
switch (format) {
|
|
565
566
|
case ProfileFormat::kFormatThread: {
|
|
566
567
|
napi_value timestamp;
|
|
567
|
-
napi_create_int64(env,
|
|
568
|
+
napi_create_int64(env, sample_timestamp_ns, ×tamp);
|
|
568
569
|
napi_set_named_property(env, js_node, "elapsed_since_start_ns", timestamp);
|
|
569
570
|
} break;
|
|
570
571
|
case ProfileFormat::kFormatChunk: {
|
|
@@ -643,7 +644,8 @@ static void GetSamples(const napi_env &env, const v8::CpuProfile *profile,
|
|
|
643
644
|
uint64_t sample_offset_from_profile_start_ms =
|
|
644
645
|
(sample_timestamp_us - profile_start_time_us) * 1e-3;
|
|
645
646
|
double seconds_since_start =
|
|
646
|
-
profile_start_timestamp_ms + sample_offset_from_profile_start_ms
|
|
647
|
+
(profile_start_timestamp_ms + sample_offset_from_profile_start_ms) *
|
|
648
|
+
1e-3;
|
|
647
649
|
|
|
648
650
|
napi_value sample = nullptr;
|
|
649
651
|
sample = CreateSample(env, format, stack_index, sample_timestamp_ns,
|
|
@@ -759,7 +761,7 @@ static napi_value TranslateMeasurementsDouble(
|
|
|
759
761
|
} else if (format == ProfileFormat::kFormatChunk) {
|
|
760
762
|
napi_value ts;
|
|
761
763
|
napi_create_double(
|
|
762
|
-
env, profile_start_timestamp_ms + (timestamps_ns[i] * 1e-
|
|
764
|
+
env, profile_start_timestamp_ms + (timestamps_ns[i] * 1e-9), &ts);
|
|
763
765
|
napi_set_named_property(env, entry, "timestamp", ts);
|
|
764
766
|
}
|
|
765
767
|
|
|
@@ -818,7 +820,7 @@ TranslateMeasurements(const napi_env &env, const enum ProfileFormat format,
|
|
|
818
820
|
case ProfileFormat::kFormatChunk: {
|
|
819
821
|
napi_value ts;
|
|
820
822
|
napi_create_double(
|
|
821
|
-
env, profile_start_timestamp_ms + (timestamps_ns[i] * 1e-
|
|
823
|
+
env, profile_start_timestamp_ms + (timestamps_ns[i] * 1e-9), &ts);
|
|
822
824
|
napi_set_named_property(env, entry, "timestamp", ts);
|
|
823
825
|
} break;
|
|
824
826
|
default:
|
package/lib/cjs/index.js
CHANGED
|
@@ -202,12 +202,12 @@ const CpuProfilerBindings = new Bindings();
|
|
|
202
202
|
const NODE_VERSION = utils.parseSemver(process.versions.node) ;
|
|
203
203
|
const NODE_MAJOR = NODE_VERSION.major;
|
|
204
204
|
|
|
205
|
-
function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/* eslint-disable max-lines */
|
|
205
|
+
function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/* eslint-disable max-lines */
|
|
206
206
|
|
|
207
207
|
// We require the file because if we import it, it will be included in the bundle.
|
|
208
208
|
// I guess tsc does not check file contents when it's imported.
|
|
209
|
-
const
|
|
210
|
-
const
|
|
209
|
+
const PROFILER_THREAD_ID_STRING = String(worker_threads.threadId);
|
|
210
|
+
const PROFILER_THREAD_NAME = worker_threads.isMainThread ? 'main' : 'worker';
|
|
211
211
|
const FORMAT_VERSION = '1';
|
|
212
212
|
const CONTINUOUS_FORMAT_VERSION = '2';
|
|
213
213
|
|
|
@@ -253,8 +253,8 @@ function enrichWithThreadInformation(
|
|
|
253
253
|
frames: profile.frames,
|
|
254
254
|
stacks: profile.stacks,
|
|
255
255
|
thread_metadata: {
|
|
256
|
-
[
|
|
257
|
-
name:
|
|
256
|
+
[PROFILER_THREAD_ID_STRING]: {
|
|
257
|
+
name: PROFILER_THREAD_NAME,
|
|
258
258
|
},
|
|
259
259
|
},
|
|
260
260
|
} ;
|
|
@@ -272,12 +272,12 @@ function createProfilingEvent(client, profile, event) {
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
return createProfilePayload(client, profile, {
|
|
275
|
-
release: _nullishCoalesce(event.release, () => ( '')),
|
|
276
|
-
environment: _nullishCoalesce(event.environment, () => ( '')),
|
|
277
|
-
event_id: _nullishCoalesce(event.event_id, () => ( '')),
|
|
278
|
-
transaction: _nullishCoalesce(event.transaction, () => ( '')),
|
|
275
|
+
release: _nullishCoalesce$1(event.release, () => ( '')),
|
|
276
|
+
environment: _nullishCoalesce$1(event.environment, () => ( '')),
|
|
277
|
+
event_id: _nullishCoalesce$1(event.event_id, () => ( '')),
|
|
278
|
+
transaction: _nullishCoalesce$1(event.transaction, () => ( '')),
|
|
279
279
|
start_timestamp: event.start_timestamp ? event.start_timestamp * 1000 : Date.now(),
|
|
280
|
-
trace_id: _nullishCoalesce(_optionalChain$1([event, 'access', _ => _.contexts, 'optionalAccess', _2 => _2['trace'], 'optionalAccess', _3 => _3['trace_id']]), () => ( '')),
|
|
280
|
+
trace_id: _nullishCoalesce$1(_optionalChain$1([event, 'access', _ => _.contexts, 'optionalAccess', _2 => _2['trace'], 'optionalAccess', _3 => _3['trace_id']]), () => ( '')),
|
|
281
281
|
profile_id: profile.profile_id,
|
|
282
282
|
});
|
|
283
283
|
}
|
|
@@ -344,7 +344,7 @@ function createProfilePayload(
|
|
|
344
344
|
name: transaction,
|
|
345
345
|
id: event_id,
|
|
346
346
|
trace_id: trace_id || '',
|
|
347
|
-
active_thread_id:
|
|
347
|
+
active_thread_id: PROFILER_THREAD_ID_STRING,
|
|
348
348
|
},
|
|
349
349
|
};
|
|
350
350
|
|
|
@@ -363,7 +363,6 @@ function createProfileChunkPayload(
|
|
|
363
363
|
{
|
|
364
364
|
release,
|
|
365
365
|
environment,
|
|
366
|
-
start_timestamp,
|
|
367
366
|
trace_id,
|
|
368
367
|
profiler_id,
|
|
369
368
|
chunk_id,
|
|
@@ -383,7 +382,6 @@ function createProfileChunkPayload(
|
|
|
383
382
|
const profile = {
|
|
384
383
|
chunk_id: chunk_id,
|
|
385
384
|
profiler_id: profiler_id,
|
|
386
|
-
timestamp: new Date(start_timestamp).toISOString(),
|
|
387
385
|
platform: 'node',
|
|
388
386
|
version: CONTINUOUS_FORMAT_VERSION,
|
|
389
387
|
release: release,
|
|
@@ -402,7 +400,6 @@ function createProfileChunkPayload(
|
|
|
402
400
|
* Creates a profiling chunk envelope item, if the profile does not pass validation, returns null.
|
|
403
401
|
*/
|
|
404
402
|
function createProfilingChunkEvent(
|
|
405
|
-
start_timestamp,
|
|
406
403
|
client,
|
|
407
404
|
options,
|
|
408
405
|
profile,
|
|
@@ -413,10 +410,9 @@ function createProfilingChunkEvent(
|
|
|
413
410
|
}
|
|
414
411
|
|
|
415
412
|
return createProfileChunkPayload(client, profile, {
|
|
416
|
-
release: _nullishCoalesce(options.release, () => ( '')),
|
|
417
|
-
environment: _nullishCoalesce(options.environment, () => ( '')),
|
|
418
|
-
|
|
419
|
-
trace_id: _nullishCoalesce(identifiers.trace_id, () => ( '')),
|
|
413
|
+
release: _nullishCoalesce$1(options.release, () => ( '')),
|
|
414
|
+
environment: _nullishCoalesce$1(options.environment, () => ( '')),
|
|
415
|
+
trace_id: _nullishCoalesce$1(identifiers.trace_id, () => ( '')),
|
|
420
416
|
chunk_id: identifiers.chunk_id,
|
|
421
417
|
profiler_id: identifiers.profiler_id,
|
|
422
418
|
});
|
|
@@ -753,7 +749,6 @@ function stopSpanProfile(span, profile_id) {
|
|
|
753
749
|
}
|
|
754
750
|
|
|
755
751
|
const profile = CpuProfilerBindings.stopProfiling(profile_id, 0);
|
|
756
|
-
|
|
757
752
|
DEBUG_BUILD && utils.logger.log(`[Profiling] stopped profiling of transaction: ${core.spanToJSON(span).description}`);
|
|
758
753
|
|
|
759
754
|
// In case of an overlapping span, stopProfiling may return null and silently ignore the overlapping profile.
|
|
@@ -962,8 +957,10 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
|
|
|
962
957
|
utils.logger.log(`[Profiling] Failed to collect profile for: ${_optionalChain([this, 'access', _10 => _10._chunkData, 'optionalAccess', _11 => _11.id])}, the chunk_id is missing.`);
|
|
963
958
|
return;
|
|
964
959
|
}
|
|
965
|
-
|
|
966
|
-
|
|
960
|
+
|
|
961
|
+
const profile = this._stopChunkProfiling(this._chunkData);
|
|
962
|
+
|
|
963
|
+
if (!profile) {
|
|
967
964
|
DEBUG_BUILD && utils.logger.log(`[Profiling] _chunkiledStartTraceID to collect profile for: ${this._chunkData.id}`);
|
|
968
965
|
return;
|
|
969
966
|
}
|
|
@@ -972,17 +969,11 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
|
|
|
972
969
|
}
|
|
973
970
|
|
|
974
971
|
DEBUG_BUILD && utils.logger.log(`[Profiling] Profile chunk ${this._chunkData.id} sent to Sentry.`);
|
|
975
|
-
const chunk = createProfilingChunkEvent(
|
|
976
|
-
this._chunkData.
|
|
977
|
-
this.
|
|
978
|
-
this.
|
|
979
|
-
|
|
980
|
-
{
|
|
981
|
-
chunk_id: this._chunkData.id,
|
|
982
|
-
trace_id: this._chunkData.startTraceID,
|
|
983
|
-
profiler_id: this._profilerId,
|
|
984
|
-
},
|
|
985
|
-
);
|
|
972
|
+
const chunk = createProfilingChunkEvent(this._client, this._client.getOptions(), profile, {
|
|
973
|
+
chunk_id: this._chunkData.id,
|
|
974
|
+
trace_id: this._chunkData.startTraceID,
|
|
975
|
+
profiler_id: this._profilerId,
|
|
976
|
+
});
|
|
986
977
|
|
|
987
978
|
if (!chunk) {
|
|
988
979
|
DEBUG_BUILD && utils.logger.log(`[Profiling] Failed to create profile chunk for: ${this._chunkData.id}`);
|
|
@@ -1025,11 +1016,21 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
|
|
|
1025
1016
|
});
|
|
1026
1017
|
}
|
|
1027
1018
|
|
|
1019
|
+
/**
|
|
1020
|
+
* Stops the profile and clears chunk instrumentation from global scope
|
|
1021
|
+
* @returns void
|
|
1022
|
+
*/
|
|
1023
|
+
_stopChunkProfiling(chunk) {
|
|
1024
|
+
this._teardownSpanChunkInstrumentation();
|
|
1025
|
+
return CpuProfilerBindings.stopProfiling(chunk.id, ProfileFormat.CHUNK);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
1028
|
/**
|
|
1029
1029
|
* Starts the profiler and registers the flush timer for a given chunk.
|
|
1030
1030
|
* @param chunk
|
|
1031
1031
|
*/
|
|
1032
1032
|
_startChunkProfiling(chunk) {
|
|
1033
|
+
this._setupSpanChunkInstrumentation();
|
|
1033
1034
|
CpuProfilerBindings.startProfiling(chunk.id);
|
|
1034
1035
|
DEBUG_BUILD && utils.logger.log(`[Profiling] starting profiling chunk: ${chunk.id}`);
|
|
1035
1036
|
|
|
@@ -1044,6 +1045,32 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
|
|
|
1044
1045
|
chunk.timer.unref();
|
|
1045
1046
|
}
|
|
1046
1047
|
|
|
1048
|
+
/**
|
|
1049
|
+
* Attaches profiling information to spans that were started
|
|
1050
|
+
* during a profiling session.
|
|
1051
|
+
*/
|
|
1052
|
+
_setupSpanChunkInstrumentation() {
|
|
1053
|
+
if (!this._client) {
|
|
1054
|
+
DEBUG_BUILD &&
|
|
1055
|
+
utils.logger.log('[Profiling] Failed to collect profile, sentry client was never attached to the profiler.');
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
core.getGlobalScope().setContext('profile', {
|
|
1060
|
+
profiler_id: this._profilerId,
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
this._client.on('beforeSendEvent', e => this._assignThreadIdContext(e));
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Clear profiling information from global context when a profile is not running.
|
|
1068
|
+
*/
|
|
1069
|
+
_teardownSpanChunkInstrumentation() {
|
|
1070
|
+
const globalScope = core.getGlobalScope();
|
|
1071
|
+
globalScope.setContext('profile', {});
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1047
1074
|
/**
|
|
1048
1075
|
* Initializes new profile chunk metadata
|
|
1049
1076
|
*/
|
|
@@ -1051,11 +1078,34 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
|
|
|
1051
1078
|
this._chunkData = {
|
|
1052
1079
|
id: utils.uuid4(),
|
|
1053
1080
|
startTraceID: traceId,
|
|
1054
|
-
startTimestampMS: utils.timestampInSeconds(),
|
|
1055
1081
|
timer: undefined,
|
|
1056
1082
|
};
|
|
1057
1083
|
}
|
|
1058
1084
|
|
|
1085
|
+
/**
|
|
1086
|
+
* Assigns thread_id and thread name context to a profiled event.
|
|
1087
|
+
*/
|
|
1088
|
+
_assignThreadIdContext(event) {
|
|
1089
|
+
if (!_optionalChain([event, 'optionalAccess', _13 => _13['contexts'], 'optionalAccess', _14 => _14['profile']])) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
if (!event.contexts) {
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// @ts-expect-error the trace fallback value is wrong, though it should never happen
|
|
1098
|
+
// and in case it does, we dont want to override whatever was passed initially.
|
|
1099
|
+
event.contexts['trace'] = {
|
|
1100
|
+
...(_nullishCoalesce(_optionalChain([event, 'access', _15 => _15.contexts, 'optionalAccess', _16 => _16['trace']]), () => ( {}))),
|
|
1101
|
+
data: {
|
|
1102
|
+
...(_nullishCoalesce(_optionalChain([event, 'access', _17 => _17.contexts, 'optionalAccess', _18 => _18['trace'], 'optionalAccess', _19 => _19['data']]), () => ( {}))),
|
|
1103
|
+
['thread.id']: PROFILER_THREAD_ID_STRING,
|
|
1104
|
+
['thread.name']: PROFILER_THREAD_NAME,
|
|
1105
|
+
},
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1059
1109
|
/**
|
|
1060
1110
|
* Resets the current chunk state.
|
|
1061
1111
|
*/
|