JSEventLoopWatchdog.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. /**
  2. * Copyright (c) Facebook, Inc. and its 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. * @format
  8. * @flow
  9. */
  10. 'use strict';
  11. const infoLog = require('../Utilities/infoLog');
  12. const performanceNow = require('fbjs/lib/performanceNow');
  13. type Handler = {
  14. onIterate?: () => void,
  15. onStall: (params: {
  16. lastInterval: number,
  17. busyTime: number,
  18. ...
  19. }) => ?string,
  20. ...
  21. };
  22. /**
  23. * A utility for tracking stalls in the JS event loop that prevent timers and
  24. * other events from being processed in a timely manner.
  25. *
  26. * The "stall" time is defined as the amount of time in access of the acceptable
  27. * threshold, which is typically around 100-200ms. So if the treshold is set to
  28. * 100 and a timer fires 150 ms later than it was scheduled because the event
  29. * loop was tied up, that would be considered a 50ms stall.
  30. *
  31. * By default, logs stall events to the console when installed. Can also be
  32. * queried with `getStats`.
  33. */
  34. const JSEventLoopWatchdog = {
  35. getStats: function(): Object {
  36. return {stallCount, totalStallTime, longestStall, acceptableBusyTime};
  37. },
  38. reset: function() {
  39. infoLog('JSEventLoopWatchdog: reset');
  40. totalStallTime = 0;
  41. stallCount = 0;
  42. longestStall = 0;
  43. lastInterval = performanceNow();
  44. },
  45. addHandler: function(handler: Handler) {
  46. handlers.push(handler);
  47. },
  48. install: function({thresholdMS}: {thresholdMS: number, ...}) {
  49. acceptableBusyTime = thresholdMS;
  50. if (installed) {
  51. return;
  52. }
  53. installed = true;
  54. lastInterval = performanceNow();
  55. function iteration() {
  56. const now = performanceNow();
  57. const busyTime = now - lastInterval;
  58. if (busyTime >= thresholdMS) {
  59. const stallTime = busyTime - thresholdMS;
  60. stallCount++;
  61. totalStallTime += stallTime;
  62. longestStall = Math.max(longestStall, stallTime);
  63. let msg =
  64. `JSEventLoopWatchdog: JS thread busy for ${busyTime}ms. ` +
  65. `${totalStallTime}ms in ${stallCount} stalls so far. `;
  66. handlers.forEach(handler => {
  67. msg += handler.onStall({lastInterval, busyTime}) || '';
  68. });
  69. infoLog(msg);
  70. }
  71. handlers.forEach(handler => {
  72. handler.onIterate && handler.onIterate();
  73. });
  74. lastInterval = now;
  75. setTimeout(iteration, thresholdMS / 5);
  76. }
  77. iteration();
  78. },
  79. };
  80. let acceptableBusyTime = 0;
  81. let installed = false;
  82. let totalStallTime = 0;
  83. let stallCount = 0;
  84. let longestStall = 0;
  85. let lastInterval = 0;
  86. const handlers: Array<Handler> = [];
  87. module.exports = JSEventLoopWatchdog;