diff --git a/packages/browser/src/profiling/cache.ts b/packages/browser/src/profiling/cache.ts index ee62538e60cb..34b5da5d12fa 100644 --- a/packages/browser/src/profiling/cache.ts +++ b/packages/browser/src/profiling/cache.ts @@ -1,72 +1,4 @@ import type { Event } from '@sentry/types'; +import { makeFifoCache } from '@sentry/utils'; -/** - * Creates a cache that evicts keys in fifo order - * @param size {Number} - */ -export function makeProfilingCache( - size: number, -): { - get: (key: Key) => Value | undefined; - add: (key: Key, value: Value) => void; - delete: (key: Key) => boolean; - clear: () => void; - size: () => number; -} { - // Maintain a fifo queue of keys, we cannot rely on Object.keys as the browser may not support it. - let evictionOrder: Key[] = []; - let cache: Record = {}; - - return { - add(key: Key, value: Value) { - while (evictionOrder.length >= size) { - // shift is O(n) but this is small size and only happens if we are - // exceeding the cache size so it should be fine. - const evictCandidate = evictionOrder.shift(); - - if (evictCandidate !== undefined) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete cache[evictCandidate]; - } - } - - // in case we have a collision, delete the old key. - if (cache[key]) { - this.delete(key); - } - - evictionOrder.push(key); - cache[key] = value; - }, - clear() { - cache = {}; - evictionOrder = []; - }, - get(key: Key): Value | undefined { - return cache[key]; - }, - size() { - return evictionOrder.length; - }, - // Delete cache key and return true if it existed, false otherwise. - delete(key: Key): boolean { - if (!cache[key]) { - return false; - } - - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete cache[key]; - - for (let i = 0; i < evictionOrder.length; i++) { - if (evictionOrder[i] === key) { - evictionOrder.splice(i, 1); - break; - } - } - - return true; - }, - }; -} - -export const PROFILING_EVENT_CACHE = makeProfilingCache(20); +export const PROFILING_EVENT_CACHE = makeFifoCache(20); diff --git a/packages/browser/src/profiling/jsSelfProfiling.ts b/packages/browser/src/profiling/jsSelfProfiling.ts index af9ebada8cbf..efa4a0a0a0bc 100644 --- a/packages/browser/src/profiling/jsSelfProfiling.ts +++ b/packages/browser/src/profiling/jsSelfProfiling.ts @@ -53,63 +53,3 @@ declare global { export interface RawThreadCpuProfile extends JSSelfProfile { profile_id: string; } -export interface ThreadCpuProfile { - samples: { - stack_id: number; - thread_id: string; - elapsed_since_start_ns: string; - }[]; - stacks: number[][]; - frames: { - function: string; - file: string | undefined; - line: number | undefined; - column: number | undefined; - }[]; - thread_metadata: Record; - queue_metadata?: Record; -} - -export interface SentryProfile { - event_id: string; - version: string; - os: { - name: string; - version: string; - build_number: string; - }; - runtime: { - name: string; - version: string; - }; - device: { - architecture: string; - is_emulator: boolean; - locale: string; - manufacturer: string; - model: string; - }; - timestamp: string; - release: string; - environment: string; - platform: string; - profile: ThreadCpuProfile; - debug_meta?: { - images: { - debug_id: string; - image_addr: string; - code_file: string; - type: string; - image_size: number; - image_vmaddr: string; - }[]; - }; - transactions: { - name: string; - trace_id: string; - id: string; - active_thread_id: string; - relative_start_ns: string; - relative_end_ns: string; - }[]; -} diff --git a/packages/browser/src/profiling/utils.ts b/packages/browser/src/profiling/utils.ts index 9fc068f7c0ee..7b2e1b60e848 100644 --- a/packages/browser/src/profiling/utils.ts +++ b/packages/browser/src/profiling/utils.ts @@ -6,19 +6,15 @@ import type { EventEnvelope, EventEnvelopeHeaders, EventItem, + Profile as SentryProfile, SdkInfo, SdkMetadata, + ThreadCpuProfile, } from '@sentry/types'; import { createEnvelope, dropUndefinedKeys, dsnToString, logger, uuid4 } from '@sentry/utils'; import { WINDOW } from '../helpers'; -import type { - JSSelfProfile, - JSSelfProfileStack, - RawThreadCpuProfile, - SentryProfile, - ThreadCpuProfile, -} from './jsSelfProfiling'; +import type { JSSelfProfile, JSSelfProfileStack, RawThreadCpuProfile } from './jsSelfProfiling'; const MS_TO_NS = 1e6; // Use 0 as main thread id which is identical to threadId in node:worker_threads diff --git a/packages/hub/package.json b/packages/hub/package.json index ca34143369db..bc795aa6bee5 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -40,7 +40,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 1da1778d2012..ccabae59a995 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -49,6 +49,16 @@ export type { ExtractedNodeRequestData, HttpHeaderValue, Primitive, WorkerLocati export type { ClientOptions, Options } from './options'; export type { Package } from './package'; export type { PolymorphicEvent, PolymorphicRequest } from './polymorphics'; +export type { + ThreadId, + FrameId, + StackId, + ThreadCpuSample, + ThreadCpuStack, + ThreadCpuFrame, + ThreadCpuProfile, + Profile, +} from './profiling'; export type { ReplayEvent, ReplayRecordingData, ReplayRecordingMode } from './replay'; export type { QueryParams, Request, SanitizedRequestData } from './request'; export type { Runtime } from './runtime'; diff --git a/packages/types/src/profiling.ts b/packages/types/src/profiling.ts new file mode 100644 index 000000000000..1b6dac566901 --- /dev/null +++ b/packages/types/src/profiling.ts @@ -0,0 +1,76 @@ +export type ThreadId = string; +export type FrameId = number; +export type StackId = number; + +export interface ThreadCpuSample { + stack_id: StackId; + thread_id: ThreadId; + elapsed_since_start_ns: string; +} + +export type ThreadCpuStack = FrameId[]; + +export type ThreadCpuFrame = { + function: string; + file?: string; + line?: number; + column?: number; +}; + +export interface ThreadCpuProfile { + samples: ThreadCpuSample[]; + stacks: ThreadCpuStack[]; + frames: ThreadCpuFrame[]; + thread_metadata: Record; + queue_metadata?: Record; +} + +export interface Profile { + event_id: string; + version: string; + os: { + name: string; + version: string; + build_number?: string; + }; + runtime: { + name: string; + version: string; + }; + device: { + architecture: string; + is_emulator: boolean; + locale: string; + manufacturer: string; + model: string; + }; + timestamp: string; + release: string; + environment: string; + platform: string; + profile: ThreadCpuProfile; + debug_meta?: { + images: { + debug_id: string; + image_addr: string; + code_file: string; + type: string; + image_size: number; + image_vmaddr: string; + }[]; + }; + transaction?: { + name: string; + id: string; + trace_id: string; + active_thread_id: string; + }; + transactions?: { + name: string; + id: string; + trace_id: string; + active_thread_id: string; + relative_start_ns: string; + relative_end_ns: string; + }[]; +} diff --git a/packages/utils/src/cache.ts b/packages/utils/src/cache.ts new file mode 100644 index 000000000000..412970e77c76 --- /dev/null +++ b/packages/utils/src/cache.ts @@ -0,0 +1,68 @@ +/** + * Creates a cache that evicts keys in fifo order + * @param size {Number} + */ +export function makeFifoCache( + size: number, +): { + get: (key: Key) => Value | undefined; + add: (key: Key, value: Value) => void; + delete: (key: Key) => boolean; + clear: () => void; + size: () => number; +} { + // Maintain a fifo queue of keys, we cannot rely on Object.keys as the browser may not support it. + let evictionOrder: Key[] = []; + let cache: Record = {}; + + return { + add(key: Key, value: Value) { + while (evictionOrder.length >= size) { + // shift is O(n) but this is small size and only happens if we are + // exceeding the cache size so it should be fine. + const evictCandidate = evictionOrder.shift(); + + if (evictCandidate !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete cache[evictCandidate]; + } + } + + // in case we have a collision, delete the old key. + if (cache[key]) { + this.delete(key); + } + + evictionOrder.push(key); + cache[key] = value; + }, + clear() { + cache = {}; + evictionOrder = []; + }, + get(key: Key): Value | undefined { + return cache[key]; + }, + size() { + return evictionOrder.length; + }, + // Delete cache key and return true if it existed, false otherwise. + delete(key: Key): boolean { + if (!cache[key]) { + return false; + } + + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete cache[key]; + + for (let i = 0; i < evictionOrder.length; i++) { + if (evictionOrder[i] === key) { + evictionOrder.splice(i, 1); + break; + } + } + + return true; + }, + }; +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index c5631559a9aa..6b9426c22149 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -28,3 +28,4 @@ export * from './ratelimit'; export * from './baggage'; export * from './url'; export * from './userIntegrations'; +export * from './cache';