diff --git a/packages/scheduler/npm/umd/scheduler.development.js b/packages/scheduler/npm/umd/scheduler.development.js index b960dc91132e..490d72f9ac4b 100644 --- a/packages/scheduler/npm/umd/scheduler.development.js +++ b/packages/scheduler/npm/umd/scheduler.development.js @@ -144,9 +144,5 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED .Scheduler.unstable_UserBlockingPriority; }, - get unstable_Profiling() { - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .Scheduler.unstable_Profiling; - }, }); }); diff --git a/packages/scheduler/npm/umd/scheduler.production.min.js b/packages/scheduler/npm/umd/scheduler.production.min.js index 0c2584331b84..72b2abede07f 100644 --- a/packages/scheduler/npm/umd/scheduler.production.min.js +++ b/packages/scheduler/npm/umd/scheduler.production.min.js @@ -138,9 +138,5 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED .Scheduler.unstable_UserBlockingPriority; }, - get unstable_Profiling() { - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .Scheduler.unstable_Profiling; - }, }); }); diff --git a/packages/scheduler/npm/umd/scheduler.profiling.min.js b/packages/scheduler/npm/umd/scheduler.profiling.min.js index 0c2584331b84..72b2abede07f 100644 --- a/packages/scheduler/npm/umd/scheduler.profiling.min.js +++ b/packages/scheduler/npm/umd/scheduler.profiling.min.js @@ -138,9 +138,5 @@ return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED .Scheduler.unstable_UserBlockingPriority; }, - get unstable_Profiling() { - return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED - .Scheduler.unstable_Profiling; - }, }); }); diff --git a/packages/scheduler/src/SchedulerProfiling.js b/packages/scheduler/src/SchedulerProfiling.js deleted file mode 100644 index 3036476afeeb..000000000000 --- a/packages/scheduler/src/SchedulerProfiling.js +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {PriorityLevel} from './SchedulerPriorities'; -import {enableProfiling} from './SchedulerFeatureFlags'; - -import {NoPriority} from './SchedulerPriorities'; - -let runIdCounter: number = 0; -let mainThreadIdCounter: number = 0; - -const isEnabledSharedArrayBuffer = - // $FlowFixMe Flow doesn't know about SharedArrayBuffer - typeof SharedArrayBuffer === 'function' && - // We only use SharedArrayBuffer when cross origin isolation is enabled. - typeof window !== 'undefined' && - window.crossOriginIsolated === true; - -const profilingStateSize = 4; -export const sharedProfilingBuffer = enableProfiling - ? isEnabledSharedArrayBuffer - ? new SharedArrayBuffer(profilingStateSize * Int32Array.BYTES_PER_ELEMENT) - : typeof ArrayBuffer === 'function' - ? new ArrayBuffer(profilingStateSize * Int32Array.BYTES_PER_ELEMENT) - : null // Don't crash the init path on IE9 - : null; - -const profilingState = - enableProfiling && sharedProfilingBuffer !== null - ? new Int32Array(sharedProfilingBuffer) - : []; // We can't read this but it helps save bytes for null checks - -const PRIORITY = 0; -const CURRENT_TASK_ID = 1; -const CURRENT_RUN_ID = 2; -const QUEUE_SIZE = 3; - -if (enableProfiling) { - profilingState[PRIORITY] = NoPriority; - // This is maintained with a counter, because the size of the priority queue - // array might include canceled tasks. - profilingState[QUEUE_SIZE] = 0; - profilingState[CURRENT_TASK_ID] = 0; -} - -// Bytes per element is 4 -const INITIAL_EVENT_LOG_SIZE = 131072; -const MAX_EVENT_LOG_SIZE = 524288; // Equivalent to 2 megabytes - -let eventLogSize = 0; -let eventLogBuffer = null; -let eventLog = null; -let eventLogIndex = 0; - -const TaskStartEvent = 1; -const TaskCompleteEvent = 2; -const TaskErrorEvent = 3; -const TaskCancelEvent = 4; -const TaskRunEvent = 5; -const TaskYieldEvent = 6; -const SchedulerSuspendEvent = 7; -const SchedulerResumeEvent = 8; - -function logEvent(entries) { - if (eventLog !== null) { - const offset = eventLogIndex; - eventLogIndex += entries.length; - if (eventLogIndex + 1 > eventLogSize) { - eventLogSize *= 2; - if (eventLogSize > MAX_EVENT_LOG_SIZE) { - // Using console['error'] to evade Babel and ESLint - console['error']( - "Scheduler Profiling: Event log exceeded maximum size. Don't " + - 'forget to call `stopLoggingProfilingEvents()`.', - ); - stopLoggingProfilingEvents(); - return; - } - const newEventLog = new Int32Array(eventLogSize * 4); - newEventLog.set(eventLog); - eventLogBuffer = newEventLog.buffer; - eventLog = newEventLog; - } - eventLog.set(entries, offset); - } -} - -export function startLoggingProfilingEvents(): void { - eventLogSize = INITIAL_EVENT_LOG_SIZE; - eventLogBuffer = new ArrayBuffer(eventLogSize * 4); - eventLog = new Int32Array(eventLogBuffer); - eventLogIndex = 0; -} - -export function stopLoggingProfilingEvents(): ArrayBuffer | null { - const buffer = eventLogBuffer; - eventLogSize = 0; - eventLogBuffer = null; - eventLog = null; - eventLogIndex = 0; - return buffer; -} - -export function markTaskStart( - task: { - id: number, - priorityLevel: PriorityLevel, - ... - }, - ms: number, -) { - if (enableProfiling) { - profilingState[QUEUE_SIZE]++; - - if (eventLog !== null) { - // performance.now returns a float, representing milliseconds. When the - // event is logged, it's coerced to an int. Convert to microseconds to - // maintain extra degrees of precision. - logEvent([TaskStartEvent, ms * 1000, task.id, task.priorityLevel]); - } - } -} - -export function markTaskCompleted( - task: { - id: number, - priorityLevel: PriorityLevel, - ... - }, - ms: number, -) { - if (enableProfiling) { - profilingState[PRIORITY] = NoPriority; - profilingState[CURRENT_TASK_ID] = 0; - profilingState[QUEUE_SIZE]--; - - if (eventLog !== null) { - logEvent([TaskCompleteEvent, ms * 1000, task.id]); - } - } -} - -export function markTaskCanceled( - task: { - id: number, - priorityLevel: PriorityLevel, - ... - }, - ms: number, -) { - if (enableProfiling) { - profilingState[QUEUE_SIZE]--; - - if (eventLog !== null) { - logEvent([TaskCancelEvent, ms * 1000, task.id]); - } - } -} - -export function markTaskErrored( - task: { - id: number, - priorityLevel: PriorityLevel, - ... - }, - ms: number, -) { - if (enableProfiling) { - profilingState[PRIORITY] = NoPriority; - profilingState[CURRENT_TASK_ID] = 0; - profilingState[QUEUE_SIZE]--; - - if (eventLog !== null) { - logEvent([TaskErrorEvent, ms * 1000, task.id]); - } - } -} - -export function markTaskRun( - task: { - id: number, - priorityLevel: PriorityLevel, - ... - }, - ms: number, -) { - if (enableProfiling) { - runIdCounter++; - - profilingState[PRIORITY] = task.priorityLevel; - profilingState[CURRENT_TASK_ID] = task.id; - profilingState[CURRENT_RUN_ID] = runIdCounter; - - if (eventLog !== null) { - logEvent([TaskRunEvent, ms * 1000, task.id, runIdCounter]); - } - } -} - -export function markTaskYield(task: {id: number, ...}, ms: number) { - if (enableProfiling) { - profilingState[PRIORITY] = NoPriority; - profilingState[CURRENT_TASK_ID] = 0; - profilingState[CURRENT_RUN_ID] = 0; - - if (eventLog !== null) { - logEvent([TaskYieldEvent, ms * 1000, task.id, runIdCounter]); - } - } -} - -export function markSchedulerSuspended(ms: number) { - if (enableProfiling) { - mainThreadIdCounter++; - - if (eventLog !== null) { - logEvent([SchedulerSuspendEvent, ms * 1000, mainThreadIdCounter]); - } - } -} - -export function markSchedulerUnsuspended(ms: number) { - if (enableProfiling) { - if (eventLog !== null) { - logEvent([SchedulerResumeEvent, ms * 1000, mainThreadIdCounter]); - } - } -} diff --git a/packages/scheduler/src/__tests__/SchedulerProfiling-test.js b/packages/scheduler/src/__tests__/SchedulerProfiling-test.js deleted file mode 100644 index b7828b14487f..000000000000 --- a/packages/scheduler/src/__tests__/SchedulerProfiling-test.js +++ /dev/null @@ -1,572 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - * @jest-environment node - */ - -/* eslint-disable no-for-of-loops/no-for-of-loops */ - -'use strict'; - -let Scheduler; -let sharedProfilingArray; -// let runWithPriority; -let ImmediatePriority; -let UserBlockingPriority; -let NormalPriority; -let LowPriority; -let IdlePriority; -let scheduleCallback; -let cancelCallback; -// let wrapCallback; -// let getCurrentPriorityLevel; -// let shouldYield; - -function priorityLevelToString(priorityLevel) { - switch (priorityLevel) { - case ImmediatePriority: - return 'Immediate'; - case UserBlockingPriority: - return 'User-blocking'; - case NormalPriority: - return 'Normal'; - case LowPriority: - return 'Low'; - case IdlePriority: - return 'Idle'; - default: - return null; - } -} - -describe('Scheduler', () => { - const {enableProfiling} = require('scheduler/src/SchedulerFeatureFlags'); - if (!enableProfiling) { - // The tests in this suite only apply when profiling is on - it('profiling APIs are not available', () => { - Scheduler = require('scheduler'); - expect(Scheduler.unstable_Profiling).toBe(null); - }); - return; - } - - beforeEach(() => { - jest.resetModules(); - jest.mock('scheduler', () => require('scheduler/unstable_mock')); - Scheduler = require('scheduler'); - - sharedProfilingArray = new Int32Array( - Scheduler.unstable_Profiling.sharedProfilingBuffer, - ); - - // runWithPriority = Scheduler.unstable_runWithPriority; - ImmediatePriority = Scheduler.unstable_ImmediatePriority; - UserBlockingPriority = Scheduler.unstable_UserBlockingPriority; - NormalPriority = Scheduler.unstable_NormalPriority; - LowPriority = Scheduler.unstable_LowPriority; - IdlePriority = Scheduler.unstable_IdlePriority; - scheduleCallback = Scheduler.unstable_scheduleCallback; - cancelCallback = Scheduler.unstable_cancelCallback; - // wrapCallback = Scheduler.unstable_wrapCallback; - // getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel; - // shouldYield = Scheduler.unstable_shouldYield; - }); - - const PRIORITY = 0; - const CURRENT_TASK_ID = 1; - const CURRENT_RUN_ID = 2; - const QUEUE_SIZE = 3; - - afterEach(() => { - if (sharedProfilingArray[QUEUE_SIZE] !== 0) { - throw Error( - 'Test exited, but the shared profiling buffer indicates that a task ' + - 'is still running', - ); - } - }); - - const TaskStartEvent = 1; - const TaskCompleteEvent = 2; - const TaskErrorEvent = 3; - const TaskCancelEvent = 4; - const TaskRunEvent = 5; - const TaskYieldEvent = 6; - const SchedulerSuspendEvent = 7; - const SchedulerResumeEvent = 8; - - function stopProfilingAndPrintFlamegraph() { - const eventBuffer = Scheduler.unstable_Profiling.stopLoggingProfilingEvents(); - if (eventBuffer === null) { - return '(empty profile)'; - } - - const eventLog = new Int32Array(eventBuffer); - - const tasks = new Map(); - const mainThreadRuns = []; - - let isSuspended = true; - let i = 0; - processLog: while (i < eventLog.length) { - const instruction = eventLog[i]; - const time = eventLog[i + 1]; - switch (instruction) { - case 0: { - break processLog; - } - case TaskStartEvent: { - const taskId = eventLog[i + 2]; - const priorityLevel = eventLog[i + 3]; - const task = { - id: taskId, - priorityLevel, - label: null, - start: time, - end: -1, - exitStatus: null, - runs: [], - }; - tasks.set(taskId, task); - i += 4; - break; - } - case TaskCompleteEvent: { - if (isSuspended) { - throw Error('Task cannot Complete outside the work loop.'); - } - const taskId = eventLog[i + 2]; - const task = tasks.get(taskId); - if (task === undefined) { - throw Error('Task does not exist.'); - } - task.end = time; - task.exitStatus = 'completed'; - i += 3; - break; - } - case TaskErrorEvent: { - if (isSuspended) { - throw Error('Task cannot Error outside the work loop.'); - } - const taskId = eventLog[i + 2]; - const task = tasks.get(taskId); - if (task === undefined) { - throw Error('Task does not exist.'); - } - task.end = time; - task.exitStatus = 'errored'; - i += 3; - break; - } - case TaskCancelEvent: { - const taskId = eventLog[i + 2]; - const task = tasks.get(taskId); - if (task === undefined) { - throw Error('Task does not exist.'); - } - task.end = time; - task.exitStatus = 'canceled'; - i += 3; - break; - } - case TaskRunEvent: - case TaskYieldEvent: { - if (isSuspended) { - throw Error('Task cannot Run or Yield outside the work loop.'); - } - const taskId = eventLog[i + 2]; - const task = tasks.get(taskId); - if (task === undefined) { - throw Error('Task does not exist.'); - } - task.runs.push(time); - i += 4; - break; - } - case SchedulerSuspendEvent: { - if (isSuspended) { - throw Error('Scheduler cannot Suspend outside the work loop.'); - } - isSuspended = true; - mainThreadRuns.push(time); - i += 3; - break; - } - case SchedulerResumeEvent: { - if (!isSuspended) { - throw Error('Scheduler cannot Resume inside the work loop.'); - } - isSuspended = false; - mainThreadRuns.push(time); - i += 3; - break; - } - default: { - throw Error('Unknown instruction type: ' + instruction); - } - } - } - - // Now we can render the tasks as a flamegraph. - const labelColumnWidth = 30; - // Scheduler event times are in microseconds - const microsecondsPerChar = 50000; - - let result = ''; - - const mainThreadLabelColumn = '!!! Main thread '; - let mainThreadTimelineColumn = ''; - let isMainThreadBusy = true; - for (const time of mainThreadRuns) { - const index = time / microsecondsPerChar; - mainThreadTimelineColumn += (isMainThreadBusy ? '█' : '░').repeat( - index - mainThreadTimelineColumn.length, - ); - isMainThreadBusy = !isMainThreadBusy; - } - result += `${mainThreadLabelColumn}│${mainThreadTimelineColumn}\n`; - - const tasksByPriority = Array.from(tasks.values()).sort( - (t1, t2) => t1.priorityLevel - t2.priorityLevel, - ); - - for (const task of tasksByPriority) { - let label = task.label; - if (label === undefined) { - label = 'Task'; - } - let labelColumn = `Task ${task.id} [${priorityLevelToString( - task.priorityLevel, - )}]`; - labelColumn += ' '.repeat(labelColumnWidth - labelColumn.length - 1); - - // Add empty space up until the start mark - let timelineColumn = ' '.repeat(task.start / microsecondsPerChar); - - let isRunning = false; - for (const time of task.runs) { - const index = time / microsecondsPerChar; - timelineColumn += (isRunning ? '█' : '░').repeat( - index - timelineColumn.length, - ); - isRunning = !isRunning; - } - - const endIndex = task.end / microsecondsPerChar; - timelineColumn += (isRunning ? '█' : '░').repeat( - endIndex - timelineColumn.length, - ); - - if (task.exitStatus !== 'completed') { - timelineColumn += `🡐 ${task.exitStatus}`; - } - - result += `${labelColumn}│${timelineColumn}\n`; - } - - return '\n' + result; - } - - function getProfilingInfo() { - const queueSize = sharedProfilingArray[QUEUE_SIZE]; - if (queueSize === 0) { - return 'Empty Queue'; - } - const priorityLevel = sharedProfilingArray[PRIORITY]; - if (priorityLevel === 0) { - return 'Suspended, Queue Size: ' + queueSize; - } - return ( - `Task: ${sharedProfilingArray[CURRENT_TASK_ID]}, ` + - `Run: ${sharedProfilingArray[CURRENT_RUN_ID]}, ` + - `Priority: ${priorityLevelToString(priorityLevel)}, ` + - `Queue Size: ${sharedProfilingArray[QUEUE_SIZE]}` - ); - } - - it('creates a basic flamegraph', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - - Scheduler.unstable_advanceTime(100); - scheduleCallback( - NormalPriority, - () => { - Scheduler.unstable_advanceTime(300); - Scheduler.unstable_yieldValue(getProfilingInfo()); - scheduleCallback( - UserBlockingPriority, - () => { - Scheduler.unstable_yieldValue(getProfilingInfo()); - Scheduler.unstable_advanceTime(300); - }, - {label: 'Bar'}, - ); - Scheduler.unstable_advanceTime(100); - Scheduler.unstable_yieldValue('Yield'); - return () => { - Scheduler.unstable_yieldValue(getProfilingInfo()); - Scheduler.unstable_advanceTime(300); - }; - }, - {label: 'Foo'}, - ); - expect(Scheduler).toFlushAndYieldThrough([ - 'Task: 1, Run: 1, Priority: Normal, Queue Size: 1', - 'Yield', - ]); - Scheduler.unstable_advanceTime(100); - expect(Scheduler).toFlushAndYield([ - 'Task: 2, Run: 2, Priority: User-blocking, Queue Size: 2', - 'Task: 1, Run: 3, Priority: Normal, Queue Size: 1', - ]); - - expect(getProfilingInfo()).toEqual('Empty Queue'); - - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │██░░░░░░░░██░░░░░░░░░░░░ -Task 2 [User-blocking] │ ░░░░██████ -Task 1 [Normal] │ ████████░░░░░░░░██████ -`, - ); - }); - - it('marks when a task is canceled', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - - const task = scheduleCallback(NormalPriority, () => { - Scheduler.unstable_yieldValue(getProfilingInfo()); - Scheduler.unstable_advanceTime(300); - Scheduler.unstable_yieldValue('Yield'); - return () => { - Scheduler.unstable_yieldValue('Continuation'); - Scheduler.unstable_advanceTime(200); - }; - }); - - expect(Scheduler).toFlushAndYieldThrough([ - 'Task: 1, Run: 1, Priority: Normal, Queue Size: 1', - 'Yield', - ]); - Scheduler.unstable_advanceTime(100); - - cancelCallback(task); - - Scheduler.unstable_advanceTime(1000); - expect(Scheduler).toFlushWithoutYielding(); - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │░░░░░░██████████████████████ -Task 1 [Normal] │██████░░🡐 canceled -`, - ); - }); - - it('marks when a task errors', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - - scheduleCallback(NormalPriority, () => { - Scheduler.unstable_advanceTime(300); - throw Error('Oops'); - }); - - expect(Scheduler).toFlushAndThrow('Oops'); - Scheduler.unstable_advanceTime(100); - - Scheduler.unstable_advanceTime(1000); - expect(Scheduler).toFlushWithoutYielding(); - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │░░░░░░██████████████████████ -Task 1 [Normal] │██████🡐 errored -`, - ); - }); - - it('marks when multiple tasks are canceled', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - - const task1 = scheduleCallback(NormalPriority, () => { - Scheduler.unstable_yieldValue(getProfilingInfo()); - Scheduler.unstable_advanceTime(300); - Scheduler.unstable_yieldValue('Yield'); - return () => { - Scheduler.unstable_yieldValue('Continuation'); - Scheduler.unstable_advanceTime(200); - }; - }); - const task2 = scheduleCallback(NormalPriority, () => { - Scheduler.unstable_yieldValue(getProfilingInfo()); - Scheduler.unstable_advanceTime(300); - Scheduler.unstable_yieldValue('Yield'); - return () => { - Scheduler.unstable_yieldValue('Continuation'); - Scheduler.unstable_advanceTime(200); - }; - }); - - expect(Scheduler).toFlushAndYieldThrough([ - 'Task: 1, Run: 1, Priority: Normal, Queue Size: 2', - 'Yield', - ]); - Scheduler.unstable_advanceTime(100); - - cancelCallback(task1); - cancelCallback(task2); - - // Advance more time. This should not affect the size of the main - // thread row, since the Scheduler queue is empty. - Scheduler.unstable_advanceTime(1000); - expect(Scheduler).toFlushWithoutYielding(); - - // The main thread row should end when the callback is cancelled. - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │░░░░░░██████████████████████ -Task 1 [Normal] │██████░░🡐 canceled -Task 2 [Normal] │░░░░░░░░🡐 canceled -`, - ); - }); - - it('handles cancelling a task that already finished', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - - const task = scheduleCallback(NormalPriority, () => { - Scheduler.unstable_yieldValue('A'); - Scheduler.unstable_advanceTime(1000); - }); - expect(Scheduler).toFlushAndYield(['A']); - cancelCallback(task); - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │░░░░░░░░░░░░░░░░░░░░ -Task 1 [Normal] │████████████████████ -`, - ); - }); - - it('handles cancelling a task multiple times', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - - scheduleCallback( - NormalPriority, - () => { - Scheduler.unstable_yieldValue('A'); - Scheduler.unstable_advanceTime(1000); - }, - {label: 'A'}, - ); - Scheduler.unstable_advanceTime(200); - const task = scheduleCallback( - NormalPriority, - () => { - Scheduler.unstable_yieldValue('B'); - Scheduler.unstable_advanceTime(1000); - }, - {label: 'B'}, - ); - Scheduler.unstable_advanceTime(400); - cancelCallback(task); - cancelCallback(task); - cancelCallback(task); - expect(Scheduler).toFlushAndYield(['A']); - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │████████████░░░░░░░░░░░░░░░░░░░░ -Task 1 [Normal] │░░░░░░░░░░░░████████████████████ -Task 2 [Normal] │ ░░░░░░░░🡐 canceled -`, - ); - }); - - it('handles delayed tasks', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - scheduleCallback( - NormalPriority, - () => { - Scheduler.unstable_advanceTime(1000); - Scheduler.unstable_yieldValue('A'); - }, - { - delay: 1000, - }, - ); - expect(Scheduler).toFlushWithoutYielding(); - - Scheduler.unstable_advanceTime(1000); - - expect(Scheduler).toFlushAndYield(['A']); - - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │████████████████████░░░░░░░░░░░░░░░░░░░░ -Task 1 [Normal] │ ████████████████████ -`, - ); - }); - - it('handles cancelling a delayed task', () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - const task = scheduleCallback( - NormalPriority, - () => Scheduler.unstable_yieldValue('A'), - {delay: 1000}, - ); - cancelCallback(task); - expect(Scheduler).toFlushWithoutYielding(); - expect(stopProfilingAndPrintFlamegraph()).toEqual( - ` -!!! Main thread │ -`, - ); - }); - - it('automatically stops profiling and warns if event log gets too big', async () => { - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - - spyOnDevAndProd(console, 'error'); - - // Increase infinite loop guard limit - const originalMaxIterations = global.__MAX_ITERATIONS__; - global.__MAX_ITERATIONS__ = 120000; - - let taskId = 1; - while (console.error.calls.count() === 0) { - taskId++; - const task = scheduleCallback(NormalPriority, () => {}); - cancelCallback(task); - expect(Scheduler).toFlushAndYield([]); - } - - expect(console.error).toHaveBeenCalledTimes(1); - expect(console.error.calls.argsFor(0)[0]).toBe( - "Scheduler Profiling: Event log exceeded maximum size. Don't forget " + - 'to call `stopLoggingProfilingEvents()`.', - ); - - // Should automatically clear profile - expect(stopProfilingAndPrintFlamegraph()).toEqual('(empty profile)'); - - // Test that we can start a new profile later - Scheduler.unstable_Profiling.startLoggingProfilingEvents(); - scheduleCallback(NormalPriority, () => { - Scheduler.unstable_advanceTime(1000); - }); - expect(Scheduler).toFlushAndYield([]); - - // Note: The exact task id is not super important. That just how many tasks - // it happens to take before the array is resized. - expect(stopProfilingAndPrintFlamegraph()).toEqual(` -!!! Main thread │░░░░░░░░░░░░░░░░░░░░ -Task ${taskId} [Normal] │████████████████████ -`); - - global.__MAX_ITERATIONS__ = originalMaxIterations; - }); -}); diff --git a/packages/scheduler/src/forks/SchedulerDOM.js b/packages/scheduler/src/forks/SchedulerDOM.js index 6c7dc446c9a6..e8a969f90dc9 100644 --- a/packages/scheduler/src/forks/SchedulerDOM.js +++ b/packages/scheduler/src/forks/SchedulerDOM.js @@ -10,7 +10,6 @@ import { enableSchedulerDebugging, - enableProfiling, enableSetImmediate, } from '../SchedulerFeatureFlags'; @@ -24,19 +23,6 @@ import { LowPriority, IdlePriority, } from '../SchedulerPriorities'; -import { - sharedProfilingBuffer, - markTaskRun, - markTaskYield, - markTaskCompleted, - markTaskCanceled, - markTaskErrored, - markSchedulerSuspended, - markSchedulerUnsuspended, - markTaskStart, - stopLoggingProfilingEvents, - startLoggingProfilingEvents, -} from '../SchedulerProfiling'; import {enableIsInputPending} from '../SchedulerFeatureFlags'; @@ -128,10 +114,6 @@ function advanceTimers(currentTime) { pop(timerQueue); timer.sortIndex = timer.expirationTime; push(taskQueue, timer); - if (enableProfiling) { - markTaskStart(timer, currentTime); - timer.isQueued = true; - } } else { // Remaining timers are pending. return; @@ -158,10 +140,6 @@ function handleTimeout(currentTime) { } function flushWork(hasTimeRemaining, initialTime) { - if (enableProfiling) { - markSchedulerUnsuspended(initialTime); - } - // We'll need a host callback the next time work is scheduled. isHostCallbackScheduled = false; if (isHostTimeoutScheduled) { @@ -173,29 +151,12 @@ function flushWork(hasTimeRemaining, initialTime) { isPerformingWork = true; const previousPriorityLevel = currentPriorityLevel; try { - if (enableProfiling) { - try { - return workLoop(hasTimeRemaining, initialTime); - } catch (error) { - if (currentTask !== null) { - const currentTime = getCurrentTime(); - markTaskErrored(currentTask, currentTime); - currentTask.isQueued = false; - } - throw error; - } - } else { - // No catch in prod code path. - return workLoop(hasTimeRemaining, initialTime); - } + // No catch in prod code path. + return workLoop(hasTimeRemaining, initialTime); } finally { currentTask = null; currentPriorityLevel = previousPriorityLevel; isPerformingWork = false; - if (enableProfiling) { - const currentTime = getCurrentTime(); - markSchedulerSuspended(currentTime); - } } } @@ -219,17 +180,11 @@ function workLoop(hasTimeRemaining, initialTime) { currentTask.callback = null; currentPriorityLevel = currentTask.priorityLevel; const didUserCallbackTimeout = currentTask.expirationTime <= currentTime; - markTaskRun(currentTask, currentTime); const continuationCallback = callback(didUserCallbackTimeout); currentTime = getCurrentTime(); if (typeof continuationCallback === 'function') { currentTask.callback = continuationCallback; - markTaskYield(currentTask, currentTime); } else { - if (enableProfiling) { - markTaskCompleted(currentTask, currentTime); - currentTask.isQueued = false; - } if (currentTask === peek(taskQueue)) { pop(taskQueue); } @@ -359,9 +314,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { expirationTime, sortIndex: -1, }; - if (enableProfiling) { - newTask.isQueued = false; - } if (startTime > currentTime) { // This is a delayed task. @@ -381,10 +333,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { } else { newTask.sortIndex = expirationTime; push(taskQueue, newTask); - if (enableProfiling) { - markTaskStart(newTask, currentTime); - newTask.isQueued = true; - } // Schedule a host callback, if needed. If we're already performing work, // wait until the next time we yield. if (!isHostCallbackScheduled && !isPerformingWork) { @@ -413,14 +361,6 @@ function unstable_getFirstCallbackNode() { } function unstable_cancelCallback(task) { - if (enableProfiling) { - if (task.isQueued) { - const currentTime = getCurrentTime(); - markTaskCanceled(task, currentTime); - task.isQueued = false; - } - } - // Null out the callback to indicate the task has been canceled. (Can't // remove from the queue because you can't remove arbitrary nodes from an // array based heap, only the first one.) @@ -615,11 +555,3 @@ export { getCurrentTime as unstable_now, forceFrameRate as unstable_forceFrameRate, }; - -export const unstable_Profiling = enableProfiling - ? { - startLoggingProfilingEvents, - stopLoggingProfilingEvents, - sharedProfilingBuffer, - } - : null; diff --git a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js index b869a1b9ccb9..0ae2f540f7f3 100644 --- a/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js +++ b/packages/scheduler/src/forks/SchedulerFeatureFlags.www.js @@ -11,5 +11,3 @@ export const { enableSchedulerDebugging, enableSetImmediate, } = require('SchedulerFeatureFlags'); - -export const enableProfiling = false; diff --git a/packages/scheduler/src/forks/SchedulerMock.js b/packages/scheduler/src/forks/SchedulerMock.js index 9ec5504b243f..32166bf19081 100644 --- a/packages/scheduler/src/forks/SchedulerMock.js +++ b/packages/scheduler/src/forks/SchedulerMock.js @@ -8,10 +8,7 @@ /* eslint-disable no-var */ -import { - enableSchedulerDebugging, - enableProfiling, -} from '../SchedulerFeatureFlags'; +import {enableSchedulerDebugging} from '../SchedulerFeatureFlags'; import {push, pop, peek} from '../SchedulerMinHeap'; // TODO: Use symbols? @@ -22,19 +19,6 @@ import { LowPriority, IdlePriority, } from '../SchedulerPriorities'; -import { - sharedProfilingBuffer, - markTaskRun, - markTaskYield, - markTaskCompleted, - markTaskCanceled, - markTaskErrored, - markSchedulerSuspended, - markSchedulerUnsuspended, - markTaskStart, - stopLoggingProfilingEvents, - startLoggingProfilingEvents, -} from '../SchedulerProfiling'; // Max 31 bit integer. The max integer size in V8 for 32-bit systems. // Math.pow(2, 30) - 1 @@ -92,10 +76,6 @@ function advanceTimers(currentTime) { pop(timerQueue); timer.sortIndex = timer.expirationTime; push(taskQueue, timer); - if (enableProfiling) { - markTaskStart(timer, currentTime); - timer.isQueued = true; - } } else { // Remaining timers are pending. return; @@ -122,10 +102,6 @@ function handleTimeout(currentTime) { } function flushWork(hasTimeRemaining, initialTime) { - if (enableProfiling) { - markSchedulerUnsuspended(initialTime); - } - // We'll need a host callback the next time work is scheduled. isHostCallbackScheduled = false; if (isHostTimeoutScheduled) { @@ -137,29 +113,12 @@ function flushWork(hasTimeRemaining, initialTime) { isPerformingWork = true; const previousPriorityLevel = currentPriorityLevel; try { - if (enableProfiling) { - try { - return workLoop(hasTimeRemaining, initialTime); - } catch (error) { - if (currentTask !== null) { - const currentTime = getCurrentTime(); - markTaskErrored(currentTask, currentTime); - currentTask.isQueued = false; - } - throw error; - } - } else { - // No catch in prod code path. - return workLoop(hasTimeRemaining, initialTime); - } + // No catch in prod code path. + return workLoop(hasTimeRemaining, initialTime); } finally { currentTask = null; currentPriorityLevel = previousPriorityLevel; isPerformingWork = false; - if (enableProfiling) { - const currentTime = getCurrentTime(); - markSchedulerSuspended(currentTime); - } } } @@ -183,17 +142,11 @@ function workLoop(hasTimeRemaining, initialTime) { currentTask.callback = null; currentPriorityLevel = currentTask.priorityLevel; const didUserCallbackTimeout = currentTask.expirationTime <= currentTime; - markTaskRun(currentTask, currentTime); const continuationCallback = callback(didUserCallbackTimeout); currentTime = getCurrentTime(); if (typeof continuationCallback === 'function') { currentTask.callback = continuationCallback; - markTaskYield(currentTask, currentTime); } else { - if (enableProfiling) { - markTaskCompleted(currentTask, currentTime); - currentTask.isQueued = false; - } if (currentTask === peek(taskQueue)) { pop(taskQueue); } @@ -323,9 +276,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { expirationTime, sortIndex: -1, }; - if (enableProfiling) { - newTask.isQueued = false; - } if (startTime > currentTime) { // This is a delayed task. @@ -345,10 +295,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { } else { newTask.sortIndex = expirationTime; push(taskQueue, newTask); - if (enableProfiling) { - markTaskStart(newTask, currentTime); - newTask.isQueued = true; - } // Schedule a host callback, if needed. If we're already performing work, // wait until the next time we yield. if (!isHostCallbackScheduled && !isPerformingWork) { @@ -377,14 +323,6 @@ function unstable_getFirstCallbackNode() { } function unstable_cancelCallback(task) { - if (enableProfiling) { - if (task.isQueued) { - const currentTime = getCurrentTime(); - markTaskCanceled(task, currentTime); - task.isQueued = false; - } - } - // Null out the callback to indicate the task has been canceled. (Can't // remove from the queue because you can't remove arbitrary nodes from an // array based heap, only the first one.) @@ -626,11 +564,3 @@ export { unstable_advanceTime, reset, }; - -export const unstable_Profiling = enableProfiling - ? { - startLoggingProfilingEvents, - stopLoggingProfilingEvents, - sharedProfilingBuffer, - } - : null; diff --git a/packages/scheduler/src/forks/SchedulerNoDOM.js b/packages/scheduler/src/forks/SchedulerNoDOM.js index e1a4a5e946d3..d590ca82b8b6 100644 --- a/packages/scheduler/src/forks/SchedulerNoDOM.js +++ b/packages/scheduler/src/forks/SchedulerNoDOM.js @@ -8,10 +8,7 @@ /* eslint-disable no-var */ -import { - enableSchedulerDebugging, - enableProfiling, -} from '../SchedulerFeatureFlags'; +import {enableSchedulerDebugging} from '../SchedulerFeatureFlags'; import {push, pop, peek} from '../SchedulerMinHeap'; // TODO: Use symbols? @@ -22,19 +19,6 @@ import { LowPriority, IdlePriority, } from '../SchedulerPriorities'; -import { - sharedProfilingBuffer, - markTaskRun, - markTaskYield, - markTaskCompleted, - markTaskCanceled, - markTaskErrored, - markSchedulerSuspended, - markSchedulerUnsuspended, - markTaskStart, - stopLoggingProfilingEvents, - startLoggingProfilingEvents, -} from '../SchedulerProfiling'; // Max 31 bit integer. The max integer size in V8 for 32-bit systems. // Math.pow(2, 30) - 1 @@ -94,10 +78,6 @@ function advanceTimers(currentTime) { pop(timerQueue); timer.sortIndex = timer.expirationTime; push(taskQueue, timer); - if (enableProfiling) { - markTaskStart(timer, currentTime); - timer.isQueued = true; - } } else { // Remaining timers are pending. return; @@ -124,10 +104,6 @@ function handleTimeout(currentTime) { } function flushWork(hasTimeRemaining, initialTime) { - if (enableProfiling) { - markSchedulerUnsuspended(initialTime); - } - // We'll need a host callback the next time work is scheduled. isHostCallbackScheduled = false; if (isHostTimeoutScheduled) { @@ -139,29 +115,12 @@ function flushWork(hasTimeRemaining, initialTime) { isPerformingWork = true; const previousPriorityLevel = currentPriorityLevel; try { - if (enableProfiling) { - try { - return workLoop(hasTimeRemaining, initialTime); - } catch (error) { - if (currentTask !== null) { - const currentTime = getCurrentTime(); - markTaskErrored(currentTask, currentTime); - currentTask.isQueued = false; - } - throw error; - } - } else { - // No catch in prod code path. - return workLoop(hasTimeRemaining, initialTime); - } + // No catch in prod code path. + return workLoop(hasTimeRemaining, initialTime); } finally { currentTask = null; currentPriorityLevel = previousPriorityLevel; isPerformingWork = false; - if (enableProfiling) { - const currentTime = getCurrentTime(); - markSchedulerSuspended(currentTime); - } } } @@ -185,17 +144,11 @@ function workLoop(hasTimeRemaining, initialTime) { currentTask.callback = null; currentPriorityLevel = currentTask.priorityLevel; const didUserCallbackTimeout = currentTask.expirationTime <= currentTime; - markTaskRun(currentTask, currentTime); const continuationCallback = callback(didUserCallbackTimeout); currentTime = getCurrentTime(); if (typeof continuationCallback === 'function') { currentTask.callback = continuationCallback; - markTaskYield(currentTask, currentTime); } else { - if (enableProfiling) { - markTaskCompleted(currentTask, currentTime); - currentTask.isQueued = false; - } if (currentTask === peek(taskQueue)) { pop(taskQueue); } @@ -325,9 +278,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { expirationTime, sortIndex: -1, }; - if (enableProfiling) { - newTask.isQueued = false; - } if (startTime > currentTime) { // This is a delayed task. @@ -347,10 +297,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { } else { newTask.sortIndex = expirationTime; push(taskQueue, newTask); - if (enableProfiling) { - markTaskStart(newTask, currentTime); - newTask.isQueued = true; - } // Schedule a host callback, if needed. If we're already performing work, // wait until the next time we yield. if (!isHostCallbackScheduled && !isPerformingWork) { @@ -379,14 +325,6 @@ function unstable_getFirstCallbackNode() { } function unstable_cancelCallback(task) { - if (enableProfiling) { - if (task.isQueued) { - const currentTime = getCurrentTime(); - markTaskCanceled(task, currentTime); - task.isQueued = false; - } - } - // Null out the callback to indicate the task has been canceled. (Can't // remove from the queue because you can't remove arbitrary nodes from an // array based heap, only the first one.) @@ -461,11 +399,3 @@ export { getCurrentTime as unstable_now, forceFrameRate as unstable_forceFrameRate, }; - -export const unstable_Profiling = enableProfiling - ? { - startLoggingProfilingEvents, - stopLoggingProfilingEvents, - sharedProfilingBuffer, - } - : null; diff --git a/packages/scheduler/src/forks/SchedulerPostTask.js b/packages/scheduler/src/forks/SchedulerPostTask.js index c07f7f03819c..1f33b7d16e72 100644 --- a/packages/scheduler/src/forks/SchedulerPostTask.js +++ b/packages/scheduler/src/forks/SchedulerPostTask.js @@ -243,6 +243,3 @@ export function unstable_continueExecution() {} export function unstable_getFirstCallbackNode() { return null; } - -// Currently no profiling build -export const unstable_Profiling = null; diff --git a/packages/scheduler/src/forks/SchedulerPostTaskOnly.js b/packages/scheduler/src/forks/SchedulerPostTaskOnly.js index 8ee96e2d16e5..00857f05b53e 100644 --- a/packages/scheduler/src/forks/SchedulerPostTaskOnly.js +++ b/packages/scheduler/src/forks/SchedulerPostTaskOnly.js @@ -8,10 +8,7 @@ /* eslint-disable no-var */ -import { - enableSchedulerDebugging, - enableProfiling, -} from '../SchedulerFeatureFlags'; +import {enableSchedulerDebugging} from '../SchedulerFeatureFlags'; import {push, pop, peek} from '../SchedulerMinHeap'; @@ -23,19 +20,6 @@ import { LowPriority, IdlePriority, } from '../SchedulerPriorities'; -import { - sharedProfilingBuffer, - markTaskRun, - markTaskYield, - markTaskCompleted, - markTaskCanceled, - markTaskErrored, - markSchedulerSuspended, - markSchedulerUnsuspended, - markTaskStart, - stopLoggingProfilingEvents, - startLoggingProfilingEvents, -} from '../SchedulerProfiling'; import {enableIsInputPending} from '../SchedulerFeatureFlags'; @@ -119,10 +103,6 @@ function advanceTimers(currentTime) { pop(timerQueue); timer.sortIndex = timer.expirationTime; push(taskQueue, timer); - if (enableProfiling) { - markTaskStart(timer, currentTime); - timer.isQueued = true; - } } else { // Remaining timers are pending. return; @@ -149,10 +129,6 @@ function handleTimeout(currentTime) { } function flushWork(hasTimeRemaining, initialTime) { - if (enableProfiling) { - markSchedulerUnsuspended(initialTime); - } - // We'll need a host callback the next time work is scheduled. isHostCallbackScheduled = false; if (isHostTimeoutScheduled) { @@ -164,29 +140,12 @@ function flushWork(hasTimeRemaining, initialTime) { isPerformingWork = true; const previousPriorityLevel = currentPriorityLevel; try { - if (enableProfiling) { - try { - return workLoop(hasTimeRemaining, initialTime); - } catch (error) { - if (currentTask !== null) { - const currentTime = getCurrentTime(); - markTaskErrored(currentTask, currentTime); - currentTask.isQueued = false; - } - throw error; - } - } else { - // No catch in prod code path. - return workLoop(hasTimeRemaining, initialTime); - } + // No catch in prod code path. + return workLoop(hasTimeRemaining, initialTime); } finally { currentTask = null; currentPriorityLevel = previousPriorityLevel; isPerformingWork = false; - if (enableProfiling) { - const currentTime = getCurrentTime(); - markSchedulerSuspended(currentTime); - } } } @@ -210,17 +169,11 @@ function workLoop(hasTimeRemaining, initialTime) { currentTask.callback = null; currentPriorityLevel = currentTask.priorityLevel; const didUserCallbackTimeout = currentTask.expirationTime <= currentTime; - markTaskRun(currentTask, currentTime); const continuationCallback = callback(didUserCallbackTimeout); currentTime = getCurrentTime(); if (typeof continuationCallback === 'function') { currentTask.callback = continuationCallback; - markTaskYield(currentTask, currentTime); } else { - if (enableProfiling) { - markTaskCompleted(currentTask, currentTime); - currentTask.isQueued = false; - } if (currentTask === peek(taskQueue)) { pop(taskQueue); } @@ -350,9 +303,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { expirationTime, sortIndex: -1, }; - if (enableProfiling) { - newTask.isQueued = false; - } if (startTime > currentTime) { // This is a delayed task. @@ -372,10 +322,6 @@ function unstable_scheduleCallback(priorityLevel, callback, options) { } else { newTask.sortIndex = expirationTime; push(taskQueue, newTask); - if (enableProfiling) { - markTaskStart(newTask, currentTime); - newTask.isQueued = true; - } // Schedule a host callback, if needed. If we're already performing work, // wait until the next time we yield. if (!isHostCallbackScheduled && !isPerformingWork) { @@ -404,14 +350,6 @@ function unstable_getFirstCallbackNode() { } function unstable_cancelCallback(task) { - if (enableProfiling) { - if (task.isQueued) { - const currentTime = getCurrentTime(); - markTaskCanceled(task, currentTime); - task.isQueued = false; - } - } - // Null out the callback to indicate the task has been canceled. (Can't // remove from the queue because you can't remove arbitrary nodes from an // array based heap, only the first one.) @@ -586,11 +524,3 @@ export { getCurrentTime as unstable_now, forceFrameRate as unstable_forceFrameRate, }; - -export const unstable_Profiling = enableProfiling - ? { - startLoggingProfilingEvents, - stopLoggingProfilingEvents, - sharedProfilingBuffer, - } - : null;