/toolkit/xre/EventTracer.cpp

http://github.com/zpao/v8monkey · C++ · 234 lines · 99 code · 29 blank · 106 comment · 34 complexity · d9f260b1f21b88168f9ef564dd04b7ca MD5 · raw file

  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an "AS IS" basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is mozilla.org code.
  15. *
  16. * The Initial Developer of the Original Code is
  17. * The Mozilla Foundation.
  18. * Portions created by the Initial Developer are Copyright (C) 2011
  19. * the Initial Developer. All Rights Reserved.
  20. *
  21. * Contributor(s):
  22. * Ted Mielczarek <ted.mielczarek@gmail.com>
  23. *
  24. * Alternatively, the contents of this file may be used under the terms of
  25. * either the GNU General Public License Version 2 or later (the "GPL"), or
  26. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. * in which case the provisions of the GPL or the LGPL are applicable instead
  28. * of those above. If you wish to allow use of your version of this file only
  29. * under the terms of either the GPL or the LGPL, and not to allow others to
  30. * use your version of this file under the terms of the MPL, indicate your
  31. * decision by deleting the provisions above and replace them with the notice
  32. * and other provisions required by the GPL or the LGPL. If you do not delete
  33. * the provisions above, a recipient may use your version of this file under
  34. * the terms of any one of the MPL, the GPL or the LGPL.
  35. *
  36. * ***** END LICENSE BLOCK ***** */
  37. /*
  38. * Event loop instrumentation. This code attempts to measure the
  39. * latency of the UI-thread event loop by firing native events at it from
  40. * a background thread, and measuring how long it takes for them
  41. * to be serviced. The measurement interval (kMeasureInterval, below)
  42. * is also used as the upper bound of acceptable response time.
  43. * When an event takes longer than that interval to be serviced,
  44. * a sample will be written to the log.
  45. *
  46. * Usage:
  47. *
  48. * Set MOZ_INSTRUMENT_EVENT_LOOP=1 in the environment to enable
  49. * this instrumentation. Currently only the UI process is instrumented.
  50. *
  51. * Set MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT in the environment to a
  52. * file path to contain the log output, the default is to log to stdout.
  53. *
  54. * Set MOZ_INSTRUMENT_EVENT_LOOP_THRESHOLD in the environment to an
  55. * integer number of milliseconds to change the threshold for reporting.
  56. * The default is 20 milliseconds. Unresponsive periods shorter than this
  57. * threshold will not be reported.
  58. *
  59. * Set MOZ_INSTRUMENT_EVENT_LOOP_INTERVAL in the environment to an
  60. * integer number of milliseconds to change the maximum sampling frequency.
  61. * This variable controls how often events will be sent to the main
  62. * thread's event loop to sample responsiveness. The sampler will not
  63. * send events twice within LOOP_INTERVAL milliseconds.
  64. * The default is 10 milliseconds.
  65. *
  66. * All logged output lines start with MOZ_EVENT_TRACE. All timestamps
  67. * output are milliseconds since the epoch (PRTime / 1000).
  68. *
  69. * On startup, a line of the form:
  70. * MOZ_EVENT_TRACE start <timestamp>
  71. * will be output.
  72. *
  73. * On shutdown, a line of the form:
  74. * MOZ_EVENT_TRACE stop <timestamp>
  75. * will be output.
  76. *
  77. * When an event servicing time exceeds the threshold, a line of the form:
  78. * MOZ_EVENT_TRACE sample <timestamp> <duration>
  79. * will be output, where <duration> is the number of milliseconds that
  80. * it took for the event to be serviced.
  81. */
  82. #include "sampler.h"
  83. #include "EventTracer.h"
  84. #include <stdio.h>
  85. #include "mozilla/TimeStamp.h"
  86. #include "mozilla/WidgetTraceEvent.h"
  87. #include <limits.h>
  88. #include <prenv.h>
  89. #include <prinrval.h>
  90. #include <prthread.h>
  91. #include <prtime.h>
  92. using mozilla::TimeDuration;
  93. using mozilla::TimeStamp;
  94. using mozilla::FireAndWaitForTracerEvent;
  95. namespace {
  96. PRThread* sTracerThread = NULL;
  97. bool sExit = false;
  98. /*
  99. * The tracer thread fires events at the native event loop roughly
  100. * every kMeasureInterval. It will sleep to attempt not to send them
  101. * more quickly, but if the response time is longer than kMeasureInterval
  102. * it will not send another event until the previous response is received.
  103. *
  104. * The output defaults to stdout, but can be redirected to a file by
  105. * settting the environment variable MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT
  106. * to the name of a file to use.
  107. */
  108. void TracerThread(void *arg)
  109. {
  110. // These are the defaults. They can be overridden by environment vars.
  111. // This should be set to the maximum latency we'd like to allow
  112. // for responsiveness.
  113. PRIntervalTime threshold = PR_MillisecondsToInterval(20);
  114. // This is the sampling interval.
  115. PRIntervalTime interval = PR_MillisecondsToInterval(10);
  116. sExit = false;
  117. FILE* log = NULL;
  118. char* envfile = PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT");
  119. if (envfile) {
  120. log = fopen(envfile, "w");
  121. }
  122. if (log == NULL)
  123. log = stdout;
  124. char* thresholdenv = PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP_THRESHOLD");
  125. if (thresholdenv && *thresholdenv) {
  126. int val = atoi(thresholdenv);
  127. if (val != 0 && val != INT_MAX && val != INT_MIN) {
  128. threshold = PR_MillisecondsToInterval(val);
  129. }
  130. }
  131. char* intervalenv = PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP_INTERVAL");
  132. if (intervalenv && *intervalenv) {
  133. int val = atoi(intervalenv);
  134. if (val != 0 && val != INT_MAX && val != INT_MIN) {
  135. interval = PR_MillisecondsToInterval(val);
  136. }
  137. }
  138. fprintf(log, "MOZ_EVENT_TRACE start %llu\n", PR_Now() / PR_USEC_PER_MSEC);
  139. while (!sExit) {
  140. TimeStamp start(TimeStamp::Now());
  141. SAMPLER_RESPONSIVENESS(start);
  142. PRIntervalTime next_sleep = interval;
  143. //TODO: only wait up to a maximum of interval; return
  144. // early if that threshold is exceeded and dump a stack trace
  145. // or do something else useful.
  146. if (FireAndWaitForTracerEvent()) {
  147. TimeDuration duration = TimeStamp::Now() - start;
  148. // Only report samples that exceed our measurement threshold.
  149. if (duration.ToMilliseconds() > threshold) {
  150. fprintf(log, "MOZ_EVENT_TRACE sample %llu %d\n",
  151. PR_Now() / PR_USEC_PER_MSEC,
  152. int(duration.ToSecondsSigDigits() * 1000));
  153. }
  154. if (next_sleep > duration.ToMilliseconds()) {
  155. next_sleep -= int(duration.ToMilliseconds());
  156. }
  157. else {
  158. // Don't sleep at all if this event took longer than the measure
  159. // interval to deliver.
  160. next_sleep = 0;
  161. }
  162. }
  163. if (next_sleep != 0 && !sExit) {
  164. PR_Sleep(next_sleep);
  165. }
  166. }
  167. fprintf(log, "MOZ_EVENT_TRACE stop %llu\n", PR_Now() / PR_USEC_PER_MSEC);
  168. if (log != stdout)
  169. fclose(log);
  170. }
  171. } // namespace
  172. namespace mozilla {
  173. bool InitEventTracing()
  174. {
  175. if (sTracerThread)
  176. return true;
  177. // Initialize the widget backend.
  178. if (!InitWidgetTracing())
  179. return false;
  180. // Create a thread that will fire events back at the
  181. // main thread to measure responsiveness.
  182. NS_ABORT_IF_FALSE(!sTracerThread, "Event tracing already initialized!");
  183. sTracerThread = PR_CreateThread(PR_USER_THREAD,
  184. TracerThread,
  185. NULL,
  186. PR_PRIORITY_NORMAL,
  187. PR_GLOBAL_THREAD,
  188. PR_JOINABLE_THREAD,
  189. 0);
  190. return sTracerThread != NULL;
  191. }
  192. void ShutdownEventTracing()
  193. {
  194. if (!sTracerThread)
  195. return;
  196. sExit = true;
  197. // Ensure that the tracer thread doesn't hang.
  198. SignalTracerThread();
  199. if (sTracerThread)
  200. PR_JoinThread(sTracerThread);
  201. sTracerThread = NULL;
  202. // Allow the widget backend to clean up.
  203. CleanUpWidgetTracing();
  204. }
  205. } // namespace mozilla