1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {PriorityLevel} from './SchedulerPriorities';
    
  11. import {enableProfiling} from './SchedulerFeatureFlags';
    
  12. 
    
  13. let runIdCounter: number = 0;
    
  14. let mainThreadIdCounter: number = 0;
    
  15. 
    
  16. // Bytes per element is 4
    
  17. const INITIAL_EVENT_LOG_SIZE = 131072;
    
  18. const MAX_EVENT_LOG_SIZE = 524288; // Equivalent to 2 megabytes
    
  19. 
    
  20. let eventLogSize = 0;
    
  21. let eventLogBuffer = null;
    
  22. let eventLog = null;
    
  23. let eventLogIndex = 0;
    
  24. 
    
  25. const TaskStartEvent = 1;
    
  26. const TaskCompleteEvent = 2;
    
  27. const TaskErrorEvent = 3;
    
  28. const TaskCancelEvent = 4;
    
  29. const TaskRunEvent = 5;
    
  30. const TaskYieldEvent = 6;
    
  31. const SchedulerSuspendEvent = 7;
    
  32. const SchedulerResumeEvent = 8;
    
  33. 
    
  34. function logEvent(entries: Array<number | PriorityLevel>) {
    
  35.   if (eventLog !== null) {
    
  36.     const offset = eventLogIndex;
    
  37.     eventLogIndex += entries.length;
    
  38.     if (eventLogIndex + 1 > eventLogSize) {
    
  39.       eventLogSize *= 2;
    
  40.       if (eventLogSize > MAX_EVENT_LOG_SIZE) {
    
  41.         // Using console['error'] to evade Babel and ESLint
    
  42.         console['error'](
    
  43.           "Scheduler Profiling: Event log exceeded maximum size. Don't " +
    
  44.             'forget to call `stopLoggingProfilingEvents()`.',
    
  45.         );
    
  46.         stopLoggingProfilingEvents();
    
  47.         return;
    
  48.       }
    
  49.       const newEventLog = new Int32Array(eventLogSize * 4);
    
  50.       // $FlowFixMe[incompatible-call] found when upgrading Flow
    
  51.       newEventLog.set(eventLog);
    
  52.       eventLogBuffer = newEventLog.buffer;
    
  53.       eventLog = newEventLog;
    
  54.     }
    
  55.     eventLog.set(entries, offset);
    
  56.   }
    
  57. }
    
  58. 
    
  59. export function startLoggingProfilingEvents(): void {
    
  60.   eventLogSize = INITIAL_EVENT_LOG_SIZE;
    
  61.   eventLogBuffer = new ArrayBuffer(eventLogSize * 4);
    
  62.   eventLog = new Int32Array(eventLogBuffer);
    
  63.   eventLogIndex = 0;
    
  64. }
    
  65. 
    
  66. export function stopLoggingProfilingEvents(): ArrayBuffer | null {
    
  67.   const buffer = eventLogBuffer;
    
  68.   eventLogSize = 0;
    
  69.   eventLogBuffer = null;
    
  70.   eventLog = null;
    
  71.   eventLogIndex = 0;
    
  72.   return buffer;
    
  73. }
    
  74. 
    
  75. export function markTaskStart(
    
  76.   task: {
    
  77.     id: number,
    
  78.     priorityLevel: PriorityLevel,
    
  79.     ...
    
  80.   },
    
  81.   ms: number,
    
  82. ) {
    
  83.   if (enableProfiling) {
    
  84.     if (eventLog !== null) {
    
  85.       // performance.now returns a float, representing milliseconds. When the
    
  86.       // event is logged, it's coerced to an int. Convert to microseconds to
    
  87.       // maintain extra degrees of precision.
    
  88.       logEvent([TaskStartEvent, ms * 1000, task.id, task.priorityLevel]);
    
  89.     }
    
  90.   }
    
  91. }
    
  92. 
    
  93. export function markTaskCompleted(
    
  94.   task: {
    
  95.     id: number,
    
  96.     priorityLevel: PriorityLevel,
    
  97.     ...
    
  98.   },
    
  99.   ms: number,
    
  100. ) {
    
  101.   if (enableProfiling) {
    
  102.     if (eventLog !== null) {
    
  103.       logEvent([TaskCompleteEvent, ms * 1000, task.id]);
    
  104.     }
    
  105.   }
    
  106. }
    
  107. 
    
  108. export function markTaskCanceled(
    
  109.   task: {
    
  110.     id: number,
    
  111.     priorityLevel: PriorityLevel,
    
  112.     ...
    
  113.   },
    
  114.   ms: number,
    
  115. ) {
    
  116.   if (enableProfiling) {
    
  117.     if (eventLog !== null) {
    
  118.       logEvent([TaskCancelEvent, ms * 1000, task.id]);
    
  119.     }
    
  120.   }
    
  121. }
    
  122. 
    
  123. export function markTaskErrored(
    
  124.   task: {
    
  125.     id: number,
    
  126.     priorityLevel: PriorityLevel,
    
  127.     ...
    
  128.   },
    
  129.   ms: number,
    
  130. ) {
    
  131.   if (enableProfiling) {
    
  132.     if (eventLog !== null) {
    
  133.       logEvent([TaskErrorEvent, ms * 1000, task.id]);
    
  134.     }
    
  135.   }
    
  136. }
    
  137. 
    
  138. export function markTaskRun(
    
  139.   task: {
    
  140.     id: number,
    
  141.     priorityLevel: PriorityLevel,
    
  142.     ...
    
  143.   },
    
  144.   ms: number,
    
  145. ) {
    
  146.   if (enableProfiling) {
    
  147.     runIdCounter++;
    
  148. 
    
  149.     if (eventLog !== null) {
    
  150.       logEvent([TaskRunEvent, ms * 1000, task.id, runIdCounter]);
    
  151.     }
    
  152.   }
    
  153. }
    
  154. 
    
  155. export function markTaskYield(task: {id: number, ...}, ms: number) {
    
  156.   if (enableProfiling) {
    
  157.     if (eventLog !== null) {
    
  158.       logEvent([TaskYieldEvent, ms * 1000, task.id, runIdCounter]);
    
  159.     }
    
  160.   }
    
  161. }
    
  162. 
    
  163. export function markSchedulerSuspended(ms: number) {
    
  164.   if (enableProfiling) {
    
  165.     mainThreadIdCounter++;
    
  166. 
    
  167.     if (eventLog !== null) {
    
  168.       logEvent([SchedulerSuspendEvent, ms * 1000, mainThreadIdCounter]);
    
  169.     }
    
  170.   }
    
  171. }
    
  172. 
    
  173. export function markSchedulerUnsuspended(ms: number) {
    
  174.   if (enableProfiling) {
    
  175.     if (eventLog !== null) {
    
  176.       logEvent([SchedulerResumeEvent, ms * 1000, mainThreadIdCounter]);
    
  177.     }
    
  178.   }
    
  179. }